richardtallent / vite-plugin-singlefile

Vite plugin for inlining JavaScript and CSS resources
MIT License
808 stars 53 forks source link

Multiple index.html Pages with vite #83

Closed Adioz01 closed 8 months ago

Adioz01 commented 10 months 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 10 months 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 10 months 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 10 months 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 10 months 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 10 months ago

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

Adioz01 commented 10 months 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 9 months ago

Are there any other solutions to this problem @richardtallent ?

richardtallent commented 9 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 8 months ago

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

am-maneaters commented 1 month ago

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