richardtallent / vite-plugin-singlefile

Vite plugin for inlining JavaScript and CSS resources
MIT License
875 stars 59 forks source link

Multiple index.html Pages with vite #83

Closed Adioz01 closed 10 months ago

Adioz01 commented 1 year ago

I am using vite + your plugin to create Google Apps Scripts (AddOns for Google Sheets).

All JS and CSS have to be inlined, so that works perfectly.

The issue that i have is, that i need multiple view alá index.html files.

How can i achieve this?

My current approach is this:

vite.config.js

export default defineConfig({
  plugins: [viteSingleFile()],
  build: {
    minify: true,
    outDir: resolve(__dirname, 'dist/ui'),
    rollupOptions: {
        input: {
            shareOfVoice: resolve(__dirname, './html/shareOfVoice/index.html'),
            metaDataSERPcheck: resolve(__dirname, './html/metaDataSERPcheck/index.html'),
        },
    },
  }
});

but i get this error: Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.

Thank you for your support!

Darkade commented 1 year ago

I'm trying a very similar setup. I am able to get some stuff working by using the useRecommendedBuildConfig: false and adding the default config from this plugin into the build option for rollup. But I can't get the JS to inline. I'm not sure why

import { resolve } from 'path';

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteSingleFile } from "vite-plugin-singlefile"

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    viteSingleFile({
      useRecommendedBuildConfig: false,
      inlinePattern: [],
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  build: {
    rollupOptions: {
      input: {
        sidebar: resolve(__dirname, "./index.html"),
        dialog: resolve(__dirname, "./index_dialog.html"),
      }
    },
    assetsInlineLimit: 100000000,
    chunkSizeWarningLimit: 100000000,
    cssCodeSplit: false,
  }
})

I actually came here to ask this very question

Darkade commented 1 year ago

After a lot of testing I have some progress but I'm getting to a point that I don't know enough about Vite to fix it.

I don't know why (but I'm sure it makes sense) when we use rollupOptions.input the javascript is referenced as a <link> in the bundled html instead of a <script> so the regex in this plugin code can't find it

This small patch to plugin.ts will do the trick and inline the js

export function replaceScript(html: string, scriptFilename: string, scriptCode: string, removeViteModuleLoader = false): string {
    const reScript = new RegExp(`<script([^>]*?) src="[./]*${scriptFilename}"([^>]*)></script>`)
    const reScript2 = new RegExp(`<link([^>]*?) href="[./]*${scriptFilename}"([^>]*)>`)
    // we can't use String.prototype.replaceAll since it isn't supported in Node.JS 14
    const preloadMarker = /"__VITE_PRELOAD__"/g
    const newCode = scriptCode.replace(preloadMarker, "void 0")

    // console.log(scriptFilename, reScript2,html.match(reScript2))
    let inlined = html.replace(reScript, (_, beforeSrc, afterSrc) => `<script${beforeSrc}${afterSrc}>\n${newCode}\n</script>`)
    inlined = inlined.replace(reScript2, (_, beforeSrc, afterSrc) => `<script${beforeSrc}${afterSrc}>\n${newCode}\n</script>`)
    return removeViteModuleLoader ? _removeViteModuleLoader(inlined) : inlined
}

However this is not enough. Since the assets generated now look like modules they have exports, and they aren't instantiated. And that's where I'm stuck. I'm sure I'm just a couple tests away from getting this to work. But it feels like there should be a vite option for the scripts not to be bundled as modules so that the simple regex that this script is built around continues working without further modifications

Darkade commented 1 year ago

On further inspection I'm pretty sure this is failing because it's trying to do dynamic imports and according to this rollup link inlining dynamic imports is not possible with multiple inputs. I'm trying to figure out if it's possible not use dynamic imports

https://rollupjs.org/configuration-options/#output-inlinedynamicimports

EDIT: and I think it's been brought up in rollupitself https://github.com/rollup/rollup/issues/2756#issue-421951224

Darkade commented 1 year ago

With the patch that I shared yesterday and this configuration it sort of works. I'm seeing all of my console logs printing, but I'm not seeing my UI loading. It's confusing but I'm on the right path, I think.

Note that import { viteSingleFile } from "./plugin" is my local patch for this plugin

import { resolve } from 'path';

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// import { viteSingleFile } from "vite-plugin-singlefile"

import { viteSingleFile } from "./plugin"

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    viteSingleFile({
      useRecommendedBuildConfig: false,
      inlinePattern: [],
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  build: {
    rollupOptions: {
      input: {
        sidebar: resolve(__dirname, "./index.html"),
        dialog: resolve(__dirname, "./index_dialog.html"),
      },
      output: {
          manualChunks: () => '',
      },
    },
    assetsInlineLimit: 100000000,
    chunkSizeWarningLimit: 100000000,
    cssCodeSplit: false,
  }
})

My bundles are getting mixed, tho. eg, I have the content of index_dialog inside of index I'm figuring out why

EDIT: no. NVM I still have issues with exports and imports

Darkade commented 1 year ago

For the time being I'm falling back into using multiple vite.config.ts files 🤷🏽‍♀️

Adioz01 commented 1 year ago

Wow thank you @Darkade for all your testing and support. I think you are on the right way, to solve this issue!

Adioz01 commented 11 months ago

Are there any other solutions to this problem @richardtallent ?

richardtallent commented 11 months ago

Hi @Adioz01 . As I've mentioned many times before when this has come up, the purpose of this plugin is simply to bundle a web site into a single file. Not multiple files. Not multiple pages with their own connected dependencies. Just one file. I made it because I had a need to be able to ship demo mini-apps to clients via email and to launch mini cross-platform tools from the file system with no installation.

If someone can get it working with multiple entry pages, awesome! If they can send a PR to add tests so we don't accidentally break that in the future, great! But it's not a use case I have any time to invest in myself, so it would need to be community-driven.

richardtallent commented 10 months ago

Closing this, since this plugin is for single files, not multiple files.

am-maneaters commented 3 months ago

@Darkade It's been a little while, but did you just end up figuring anything else out?

rosskevin commented 1 month ago

@Darkade @am-maneaters the following build.ts script, executed with tsx, will run vite multiple times and achieve the individually isolated output files. For example, I'm using this for external web component embeds.

build.ts

import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { build } from 'vite'
import * as glob from 'glob'

const __dirname = fileURLToPath(new URL('.', import.meta.url))

const entries = glob.sync(path.resolve(__dirname, 'src', '*', '*.html'))

for (const entry of entries) {
  const componentName = path.basename(path.dirname(entry))
  console.log('entry', componentName)
  await build({
    root: path.resolve(__dirname, './src'),
    build: {
      outDir: '../dist', // build to dist directory at root (omitting the src dir)
      assetsDir: componentName,
      rollupOptions: {
        input: path.resolve(__dirname, `./src/${componentName}/index.html`),
        output: {
          entryFileNames: `${componentName}/element.js`,
        },
      },
    },
  })
}

and sample execution: rm -rf dist; yarn tsx ./build.ts

This accomplishes: