Code Monkey home page Code Monkey logo

vite-plugin-singlefile's People

Contributors

aloisklink avatar atomiechen avatar daniel-kun avatar fadenfire avatar greenheart avatar kidonng avatar kingju777 avatar lukekaalim avatar lukeschaefer avatar mojoaxel avatar paintedbicycle avatar prog-rajkamal avatar richardtallent avatar richardtallent-erm avatar smac89 avatar taybart avatar trana8525 avatar valtism avatar yash-singh1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

vite-plugin-singlefile's Issues

Doesn't work with minify: false

The following regex matches both cases - minified and not - however I have only tested it for a couple of individual use cases

/(\s*<script type="module" crossorigin>\s*)\(function[\s\S]*?\}\)\(\);/

The removed segment is \(\)\{ after function - when not minified, the polyfill is a named function

How to make it work with multiple inputs?

I have the issue that the plugin fails when I try to bundle multiple files. The error it throws is:

Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.

This is my vite.config.js file:

import { defineConfig } from 'vite';
import { viteSingleFile } from 'vite-plugin-singlefile';
import { resolve } from 'path';

export default defineConfig({
  plugins: [viteSingleFile()],
  build: {
    emptyOutDir: false,
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        other: resolve(__dirname, 'other.html'),
      },
      output: {
        inlineDynamicImports: false,
      },
    },
  },
});

It only works if I either disable the plugin or only keep one input. The output.inlineDynamicImports setting doesn't do the trick, I still get the same error.

Thanks for your help.

replaceScript method is "__VITE_PRELOAD__" replace first only.

replace() is replace first target.

export function replaceScript(html: string, scriptFilename: string, scriptCode: string, removeViteModuleLoader = false): string {
const reScript = new RegExp(`<script([^>]*?) src="[./]*${scriptFilename}"([^>]*)></script>`)
const preloadMarker = '"__VITE_PRELOAD__"'
const newCode = scriptCode.replace(preloadMarker, "void 0")
const inlined = html.replace(reScript, (_, beforeSrc, afterSrc) => `<script${beforeSrc}${afterSrc}>\n${newCode}\n</script>`)
return removeViteModuleLoader ? _removeViteModuleLoader(inlined) : inlined
}

It seems to me that replaceAll() should be used.

const newCode = scriptCode.replace(preloadMarker, "void 0")

โ†“

	const newCode = scriptCode.replaceAll(preloadMarker, "void 0")

Add option to disable debug comments

The plugin includes a comment above every inlined .js file. While this may be useful for debugging, it should be possible to disable this behaviour with an option like addDebugComments: false. (Maybe it should be disabled by default?)

Ref

Error: Invalid value for option "output.manualChunks"

Receiving error on build:

error during build:
Error: Invalid value for option "output.manualChunks" - this option is not supported for "output.inlineDynamicImports".

Vite config:

export default defineConfig({
  plugins: [react(), viteSingleFile()],
});

Vite version: 2.8.0
vite-single-file version: 0.9.0

updating chalk from 4.1.0 to 5.0.0 causes issues

failed to load config from www/vite.config.js error during build: Error [ERR_REQUIRE_ESM]: require() of ES Module www/node_modules/vite-plugin-singlefile/node_modules/chalk/source/index.js from www/node_modules/vite-plugin-singlefile/dist/index.js not supported. Instead change the require of www/node_modules/vite-plugin-singlefile/node_modules/chalk/source/index.js in www/node_modules/vite-plugin-singlefile/dist/index.js to a dynamic import() which is available in all CommonJS modules. at Object.<anonymous> (www/node_modules/vite-plugin-singlefile/dist/index.js:7:33) at async Promise.all (index 0) at async loadConfigFromFile (www/node_modules/vite/dist/node/chunks/dep-9c153816.js:71335:31) at async resolveConfig (www/node_modules/vite/dist/node/chunks/dep-9c153816.js:70873:28) at async doBuild (www/node_modules/vite/dist/node/chunks/dep-9c153816.js:38971:20) at async build (www/node_modules/vite/dist/node/chunks/dep-9c153816.js:38959:16) at async CAC.<anonymous> (www/node_modules/vite/dist/node/cli.js:738:9)

Asset not inlined comments

Hi there.

I'm using vite-imagetools to add other image formats on a page, importing the images in the JS.
When building, my html ends up with several commented lines like these:

<!-- ASSET NOT INLINED: assets/image.9e86b0ad.avif -->
<!-- ASSET NOT INLINED: assets/image.8317db15.webp -->

Is there a way to avoid this output?

cheers!

Is it possible to only include small bundles?

Hello.
Thanks for this plugin.
I was wondering if it is possible to use it to only inline certain chunks. For example those smaller than 5kb to get a faster app startup.

again, thanks and regards

vite-plugin-singlefile makes sourcemaps useless

using viteSingleFile() makes sourcemaps useless.

export default defineConfig({
  root: "./src",
  plugins: [react(), viteSingleFile()], 
  build: {
    target: "esnext",
    sourcemap: true, // sourcemap: "inline",
    assetsInlineLimit: 100000000,
    chunkSizeWarningLimit: 100000000,
    cssCodeSplit: false,
    outDir: "../dist",
  }, 
});

Setting a breakpoint is not possible anymore, as the sourcemaps point to the wrong position.
This is the case for
sourcemap: "inline" and sourcemap: true.

When removing viteSingleFile the breakpoints work as expected.

This is a problem when trying to debug a figma plugin. It is only allowed to have one html file

Requires Node >= 15

When using Node 14 I get this error:

[vite:singlefile] scriptCode.replaceAll is not a function
error during build:
TypeError: scriptCode.replaceAll is not a function
    at replaceScript (file:///Users/username/Projects/project/node_modules/vite-plugin-singlefile/dist/esm/index.js:7:32)
    at Object.generateBundle (file:///Users/username/Projects/project/node_modules/vite-plugin-singlefile/dist/esm/index.js:36:44)
    at file:///Users/username/Projects/project/node_modules/rollup/dist/es/shared/rollup.js:22710:40
    at processTicksAndRejections (internal/process/task_queues.js:95:5)

Doing some googling it turns out replaceAll() wasn't introduced until Node 15. I upgraded to 16 and it works now.

It's a pretty easy fix since 16 is the latest LTS, but I just wanted to report it in case anyone else is searching for the issue like I was.

[QUESTION]No working in multi page mode

import { defineConfig } from 'vite';

import { resolve, dirname } from 'path';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { viteSingleFile } from 'vite-plugin-singlefile';
import { minifyHtml } from 'vite-plugin-html';

import { fileURLToPath } from 'url';
const ___filename = fileURLToPath(import.meta.url);
const ___dirname = dirname(___filename);

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [svelte(), viteSingleFile(), minifyHtml()],
  build: {
    target: 'es2015',
    assetsInlineLimit: 100000000,
    chunkSizeWarningLimit: 100000000,
    cssCodeSplit: false,
    brotliSize: false,
    rollupOptions: {
      inlineDynamicImports: true,
      input: {
        pure: resolve(___dirname, 'pure/index.html'),
        simple: resolve(___dirname, 'simple/index.html'),
      },
       output: {
         manualChunks: () => 'everything.js',
       },
    },
  },
});

It compse all the js files

dist/pure/index.html             4.08 KiB
dist/simple/index.html           4.08 KiB
dist/assets/pure.d4df3b1d.js     3.68 KiB
dist/assets/style.4513d045.css   0.13 KiB

npm run build error

Hello,

Thank you for sharing this great plugin. I've just tried it but seem to run in to an issue. When running npm run build I get the following error:

> [email protected] build
> vuedx-typecheck . && vite build

Running for /{path}/test-project
vite.config.ts:4:33 - error 7016: Could not find a declaration file for module 'vite-plugin-singlefile'. '/{path}/test-project/node_modules/vite-plugin-singlefile/dist/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/vite-plugin-singlefile` if it exists or add a new declaration (.d.ts) file containing `declare module 'vite-plugin-singlefile';`
 1 | import { defineConfig } from "vite"
 2 | import vue from "@vitejs/plugin-vue"
 3 | import { viteSingleFile } from "vite-plugin-singlefile"
   |                                ~~~~~~~~~~~~~~~~~~~~~~~~
 4 |
 5 | export default defineConfig({

Found 1 error.npm ERR! code 2
npm ERR! path /{path}/test-project
npm ERR! command failed
npm ERR! command sh -c vuedx-typecheck . && vite build

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/test/.npm/_logs/2021-01-20T16_25_51_953Z-debug.log

If I run npm i --save-dev @types/vite-plugin-singlefil I get the following error:

npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@types%2fvite-plugin-singlefile - Not found
npm ERR! 404
npm ERR! 404  '@types/vite-plugin-singlefile@*' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/test/.npm/_logs/2021-01-20T16_33_45_355Z-debug.log

Thanks for your help on this. I'm very new to this set up myself so not sure how to resolve.

Does not install with Vite 3.0

Getting the following error on install:

โฏ npm i vite-plugin-singlefile -D
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/vite
npm ERR!   dev vite@"^3.0.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer vite@"^2.9.13" from [email protected]
npm ERR! node_modules/vite-plugin-singlefile
npm ERR!   dev vite-plugin-singlefile@"*" from the root project

Can be installed with --force, and seems to work fine, but would be nice if the peer dependencies were updated so this error doesn't occur.

New config option request: specify what to inline, css or javascript

It will be handy to specify what exactly we should inline. Sometimes it's handy to inline just css and leave javascript to be loaded.

In my use case I build browser extension and it would be great to save a tiny loading time by not doing extra request to load stylesheets. In other hand those extensions prohibit inline scripts and I'd want to keep lazy loading too.

Even though for my use case I can find solutions, I believe it's logical for this plugin to configure what exactly we want to inline.

Problem with non-full embedding

The problem is when you have some unembedding content in public folder. Even if you have unembedding content you have to embed js to neutralize CORS. So js in fact is moving to parent folder (from assets) and all links of imported from public pictures became broken.
This situation is typical for using relative paths: base: ''
To fix this problem we should move js to the root folder using rollupOptions:

   {
      base: '',
      plugins: [
          vue(),
          viteSingleFile({
              useRecommendedBuildConfig: false,
              inlinePattern: [
                  '**/*.js'
              ]
          }),
      ],
      build: {
          rollupOptions: {
              output: {
                  chunkFileNames: '[name]-[hash].js',
                  entryFileNames: '[name]-[hash].js',
              },
          },
      }
    }

We should change this rollup options automatically if we have jsAssets or just describe it in help.

Invalid value for option "output.inlineDynamicImports"

This is a great idea and exactly what I am looking for. Thank you for your effort. But is this plugin still working?

I have issues to get working with my Svelte project: https://github.com/exislow/freeDSP-Aurora-Svelte-Frontend/tree/singlefile

Every time I try to build my project, this happens:

$ npm run build

> [email protected] build
> vite build

9:29:01 PM [vite-plugin-svelte] you are building for production but compilerOptions.dev is true, forcing it to false

vite v4.4.11 building SSR bundle for production...
transforming (1) node_modules/@sveltejs/kit/src/runtime/server/index.js9:29:01 PM [vite-plugin-svelte]

[...]

โœ“ 97 modules transformed.
Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.
โœ“ built in 1.37s
error during build:
RollupError: Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.
[...]

I have even followed this tutorial: https://sean.eulenberg.de/posts/single-file-applications-with-svelte/

Do you have any idea whats wrong?

Preload in newer Vite version breaks this

In newer versions of Vite, a vendor file is generated and imported from the main script file regardless of other options. This seems to be non-trivial to unwire, since the imports from that vendor file are aliased when imported.

I don't know if this is something that is under Vite's control or Rollup's, but somehow, we need a way to disable all forms of code-splitting so we have a single JavaScript output.

i18n plugin issue

Hi, I'm using your plugin . It's awesome! But when I used i18n in my project, it got wrong. It can't run normal and console with TypeError. I don't know how to fix this problem. Can you help me?

stylesheet replace fails

stylesheet replace fails because href and not src is used for stylesheet tag. and there is no end tag for me. see #4

defineExpose() function does not work after rollup

I have a use case where I'm trying to compile a single file where a window level function can update data in a rendered Vue app. While I am able to get this working using the defineExposed() function in Vue3/Composition API locally via npm run dev, after I compile with npm run build the same working function now returns an error:

Uncaught TypeError: Cannot read properties of null (reading 'exposed')
    at logTest (index.html:41:23)
    at HTMLButtonElement.onclick (index.html:56:49)

It appears that the defineExposed() function is not compatible with the rollup, but I'm not super familiar with vue or this plugin so I'm having trouble trying to troubleshoot.

Basic test code is as follows:

Index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test App</title>
    <script>
      function logTest(newData) {
        // eventually newData will be a passed JSON object
        var item = {
          "name": "TEST NEW ROW 1",
          "regions": []
        }
        var item2 = {
          "name": "TEST NEW ROW 2",
          "regions": []
        }
        var update = {
          "labelRows": [item, item2]
        };
        app._instance.exposed.logTest(update);
      }
    </script>
  </head>
  <body class="m-0 p-0">
    <div id="app" class="w-full"></div>
    <script type="module" src="/src/main.ts"></script>
    <button onclick="logTest('This is a test')">TEST UPDATE</button>
  </body>
</html>

main.ts

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
window.app = app;
app.mount('#app')

App.vue

<script setup lang="ts">
  import { ref } from 'vue'
  import data from './assets/defaultData.json'

  const tableData = ref(data)

  function logTest (newData) {
    console.log("Test Reactivity Function");
    alert(newData);
    tableData.value = newData;
  }

  defineExpose({
    logTest
  })
</script>

<template>
  <div class="px-4">
    <table class="w-full">
      <tr v-for="row in tableData.rows">
         loop code here
      </tr>
    </table>
  </div>
  <h1>TEST VALUE: {{ tableData }}</h1>
</template>

Suggestions on using images?

Edit I just realized that this plugin automatically makes the base64 images. I feel silly. and pleasantly surprised

Add support for .mjs (and other minified/named) files.

Seems like the new inlinePattern config does only work if the file still ends with .js or .css but what about .mjs (or simmilar minified files)? I think it would be usefull to have an option to configure for example a regex pattern, or to give a list with file extionsion which are allowed to be inlined.

Doesn't work if `base` is set to a folder

I'm working on a vite app that I am deploying to a subfolder, like http://example.com/my-app/

So I had set the base option in vite to "/my-app/".

Later I realized a single file bundle made sense, so I tried using this plugin. It failed silently by not inlining any CSS/JS but still deleting my CSS/JS files.

Deleting the base option (which doesn't really do anything when bundling to a single file, I think) fixes this problem. But I figure you should at least show an error message in the case where you notice a CSS/JS file, try to inline it in index.html, but actually the regex doesn't match anything so no replacement happens. Or, make the regex a little more broad so it supports arbitrary values in base.

[ERR_REQUIRE_ESM]: Must use import to load ES Module

Hello,

Following the instructions from the readme lead me to this error

error during build:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: `PATH\node_modules\vite-plugin-singlefile\node_modules\chalk\source\index.js
require() of ES modules is not supported.
require() of PATH\node_modules\vite-plugin-singlefile\node_modules\chalk\source\index.js from PATH\node_modules\vite-plugin-singlefile\dist\index.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename PATH\node_modules\vite-plugin-singlefile\node_modules\chalk\source\index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from PATH\node_modules\vite-plugin-singlefile\node_modules\chalk\package.json.

Cannot Use "type": "module" in "package.json"

In my project I currently use both Electron and Vite for different parts of my software, but they share a common package.json, and are ultimately built into a single executable. I tried using this plugin for the Vite side and got the ERR_REQUIRE_ESM error from #23. So, following the comments from that issue, I added "type": "module" to my package.json. Now I get this error from my electron build:

Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\...\project\dist\backend\main.js from C:\Users\...\project\node_modules\electron\dist\resources\default_app.asar\main.js not supported.  
C:\Users\...\project\dist\backend\main.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename C:\Users\...\project\dist\backend\main.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in C:\Users\...\project\package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

    at Function.c._load (node:electron/js2c/asar_bundle:5:13343)
    at loadApplicationPackage (C:\Users\...\project\node_modules\electron\dist\resources\default_app.asar\main.js:110:16)
    at Object.<anonymous> (C:\Users\...\project\node_modules\electron\dist\resources\default_app.asar\main.js:222:9)
    at Function.c._load (node:electron/js2c/asar_bundle:5:13343)
    at Object.<anonymous> (node:electron/js2c/browser_init:185:3104)
    at Object../lib/browser/init.ts (node:electron/js2c/browser_init:185:3308)
    at __webpack_require__ (node:electron/js2c/browser_init:1:128)
    at node:electron/js2c/browser_init:1:1200
    at node:electron/js2c/browser_init:1:1267
    at Function.c._load (node:electron/js2c/asar_bundle:5:13343)

Renaming that output file to .cjs worked, but it just leads to a bunch of other issues, which I'm currently trying to resolve. Is there a way to use this plugin without setting "type": "module" in package.json?

error while use with vite-plugin-ssr

Here is my steps :

  1. init project with vite-plugin-ssr, and choose vue-ts template
  2. add vite-plugin-singlefile
  3. vite.config.ts like this
import vue from "@vitejs/plugin-vue";
import ssr from "vite-plugin-ssr/plugin";
import { UserConfig } from "vite";
import { viteSingleFile } from "vite-plugin-singlefile";

const config: UserConfig = {
  plugins: [vue(), ssr(), viteSingleFile()],
};

export default config;

  1. package.json like this
{
  "scripts": {
    "dev": "npm run server",
    "prod": "npm run build && npm run server:prod",
    "build": "vite build",
    "server": "ts-node ./server",
    "server:prod": "cross-env NODE_ENV=production ts-node ./server"
  },
  "dependencies": {
    "@types/compression": "^1.7.2",
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.31",
    "@vitejs/plugin-vue": "^3.0.0",
    "@vue/compiler-sfc": "^3.2.37",
    "@vue/server-renderer": "^3.2.37",
    "compression": "^1.7.4",
    "cross-env": "^7.0.3",
    "express": "^4.18.1",
    "sirv": "^2.0.2",
    "ts-node": "^10.7.0",
    "typescript": "^4.6.4",
    "vite": "^2.9.14",
    "vite-plugin-ssr": "^0.4.9",
    "vite-plugin-singlefile": "^0.10.0",
    "vue": "^3.2.37"
  }
}

  1. run the command npm run prod and got the error like this
Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.
error during build:
Error: Invalid value for option "output.inlineDynamicImports" - multiple inputs are not supported when "output.inlineDynamicImports" is true.
    at error (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:1858:30)
    at getInlineDynamicImports (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23442:16)
    at normalizeOutputOptions (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23344:34)
    at getOutputOptions (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23735:12)
    at getOutputOptionsAndPluginDriver (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23730:12)
    at handleGenerateWrite (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23705:74)
    at Object.write (file:///Users/jonescai/git/github/server/node_modules/rollup/dist/es/shared/rollup.js:23673:20)
    at generate (file:///Users/jonescai/git/github/server/node_modules/vite/dist/node/chunks/dep-561c5231.js:43424:64)
    at doBuild (file:///Users/jonescai/git/github/server/node_modules/vite/dist/node/chunks/dep-561c5231.js:43437:26)
    at async build (file:///Users/jonescai/git/github/server/node_modules/vite/dist/node/chunks/dep-561c5231.js:43261:16)

Regular expression to match the script tag to inline is too specific

Hi.
So I came across this issue because my particular vite configuration outputs the script tags as:

<script async type="module" crossorigin src="/assets/whatever.cffa7373.js"></script>

This is not covered by the regular expression at L33

const reScript = new RegExp(`<script type="module"[^>]*?src="[\./]*${o.fileName}"[^>]*?></script>`)

This Regex looks for a <script> tag that starts with type="module" and then contains a src attribute with the filename.

AFAICT the only thing the script should be looking for is a <script> tag that contains the src attribute with the filename we want to embed. so that Regex should be maybe something like:
<script[^>]*?src="[\./]*${o.fileName}"[^>]*?></script>

This is how I sorted my use case which may be different than the generic use case you're trying to solve, so this solution may not please everybody, although I would think that's generic enough though.

Fails if base is set to '' in vite config

Vite requires, base to be '' for using relative assets url.

this plugin fails to inline assets.

If base is not not set '', then vue router uses url with '/' as base, which fails for navigation

Unterminated "<script>" inside strings in injected javascript makes HTML invalid

Testing this with a React application.

There's the following line in the built HTML which breaks the page:

...else{switch(o=n.nodeType===9?n:n.ownerDocument,e===ls&&(e=mc(i)),e===ls?i==="script"?(e=o.createElement("div"),e.innerHTML="<script></script>",e=e.removeChild(e.firstChild))...

The part with e.innerHTML="<script></script>" breaks the page and the rest of the javascript is rendered as text on the page.

I've checked the application code and I don't do this anywhere.

Only code that is similar is this little snippet:

  return new Promise((resolve) => {
    const scriptEl = document.createElement('script');
    scriptEl.src = url;
    scriptEl.onload = () => resolve();
    document.body.appendChild(scriptEl);
  });

Is this some optimization vite does, that it changes createElement calls into innerHTML?

[BUG] Cannot read properties of undefined (reading 'fileName')

I investigated into this bug a bit and it turns out some of the assets are undefined in the inner loop when going through the CSS and Javascript. I have multiple entrypoints specified inside build.rollupOptions.input and I disabled inlineDynamicImports. I am also using the @vite/plugin-legacy because the environment I am targeting doesn't support imports or exports. It turns out that all the entrypoints point to the same JS file that is generated by the legacy plugin, and in the code you run the delete on JS assets that are already iterated over. I can create a patch for this that runs delete in the outside loop.

Assets not included

I have tested this plugin with vite default example. The assets are built in a dir in the dist folder. I also use
{ useRecommendedBuildConfig: true } but nothing change.
What am I missing? How can I get a single html file in the dist folder after build?

script inlined into different location

Thanks so much for making this, just trying it out for the first time, excited about using it for my projects!

I noticed right away that the plugin seems to remove my original script tag from the bottom of the body element and add a new script tag to the bottom of the head element. I was expecting my script tag to stay put, since the location of the script tag can affect the script's behavior, for example the state of the DOM when the top level script code runs. If I remember correctly, inlined JS code inside the body element is supposed to go inside an HTML comment nested immediately inside the script tag, to prevent it from being rendered as part of the document under some circumstances. And in fact I think that works even if the inlined script is in the head.

Haven't looked into any of this too deeply yet. Curious to hear your thoughts.

$computed is not defined

Hello,

I tried using your package with my vite project but I get the following error in the console:

ReferenceError: $computed is not defined

Here is my vite.config.js:

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()
  ]
})

Doing that and opening the page result in that error. What can I do?

Inline SVG assets

Dear maintainer,

first of all, thanks for your great plugin! I would like to export vue apps, containing svg graphics. Unfortunately, these files don't get inlined and I receive messages during build like:

rendering chunks (1)...WARNING: asset not inlined: assets/games.e8418186.svg

Other graphic formats, like png are handled correctly.
In order to reproduce this error, I created a minimum demo project https://codeberg.org/MintApps/test

I would really appreciate any tips/help,
Thomas

plugin don't respect basename from vite config

My config:


import { defineConfig } from "vite";
import preact from "@preact/preset-vite";

import { viteSingleFile } from "vite-plugin-singlefile"
import { createHtmlPlugin } from "vite-plugin-html";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [preact(), createHtmlPlugin(), viteSingleFile({ removeViteModuleLoader: true })],
  base: "/shri2023-performance/",
});

My css:

.event__icon_temp {
  background-image: url(assets/icon_temperature.svg);
}

I got link like this:

background-image:url(/assets/icon_temperature-b86a9b9d.svg)

Expected result:

background-image:url(/shri2023-performance/assets/icon_temperature-b86a9b9d.svg)

Doesn't work with System.import (e.g. @vitejs/plugin-legacy)

I have a pretty niche use case where I want all code in a single file but it also needs to support browser version outside of the defaults supported by vite. The intended solution was to use a vite config similar to this

import react from '@vitejs/plugin-react'
import legacy from '@vitejs/plugin-legacy'
import { viteSingleFile } from 'vite-plugin-singlefile'
export default {
  plugins: [
    react(),
    legacy({
      targets: ['Chrome >= 33'],
      renderModernChunks: false,
    }),
    viteSingleFile()
  ]
}

However, this fails to the legacy code getting loaded into the root index.html via System.import, the resulting index.html looks like this:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8"/>
		<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
		<title>Test</title>
	</head>
	<body>
		<div id="root"></div>
		<script crossorigin id="vite-legacy-polyfill">[CORRECTLY LOADED JAVASCRIPT IS INSERTED HERE]</script>
		<script crossorigin id="vite-legacy-entry" data-src="/assets/index-legacy.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
	</body>
</html>

As can be seen, the index.html is still referencing an external file in the script tag with "vite-legacy-entry" the plugin has also deleted the file that it is being referenced as expected.

The desired behaviour is that both the vite legacy plugin and vite single file plugin can be used together

Idea: Remove unused Vite module loader

Hi! First of all, thanks again for creating this plugin! I've used it for 4 projects all with great success! ๐ŸŽ‰

TL;DR: Tiny optimization suggestion to reduce the build output by ~ 1kb (โ‰ˆ 23% in my example project). Feel free to close the issue if it's out of scope!


Background

When importing any kind of JS modules, vite build automatically adds a minimal import wrapper in the front of the build output. This code seems to handle importing "modules" by adding script tags.

Notice how it's bound to the '/assets/' directory as a root path for loading new modules. However, we don't load any modules from '/assets/' since everything is inlined into index.html when using vite-plugin-singlefile.

!(function (e = '.', t = '__import__') {
    try {
        self[t] = new Function('u', 'return import(u)')
    } catch (r) {
        const n = new URL(e, location),
            o = (e) => {
                URL.revokeObjectURL(e.src), e.remove()
            }
        ;(self[t] = (e) =>
            new Promise((r, a) => {
                const i = new URL(e, n)
                if (self[t].moduleMap[i]) return r(self[t].moduleMap[i])
                const s = new Blob(
                        [
                            `import * as m from '${i}';`,
                            `${t}.moduleMap['${i}']=m;`,
                        ],
                        { type: 'text/javascript' }
                    ),
                    c = Object.assign(document.createElement('script'), {
                        type: 'module',
                        src: URL.createObjectURL(s),
                        onerror() {
                            a(new Error(`Failed to import: ${e}`)), o(c)
                        },
                        onload() {
                            r(self[t].moduleMap[i]), o(c)
                        },
                    })
                document.head.appendChild(c)
            })),
            (self[t].moduleMap = {})
    }
})('/assets/')
Raw module loader snippet !function(e=".",t="__import__"){try{self[t]=new Function("u","return import(u)")}catch(r){const n=new URL(e,location),o=e=>{URL.revokeObjectURL(e.src),e.remove()};self[t]=e=>new Promise(((r,a)=>{const i=new URL(e,n);if(self[t].moduleMap[i])return r(self[t].moduleMap[i]);const s=new Blob([`import * as m from '${i}';`,`${t}.moduleMap['${i}']=m;`],{type:"text/javascript"}),c=Object.assign(document.createElement("script"),{type:"module",src:URL.createObjectURL(s),onerror(){a(new Error(`Failed to import: ${e}`)),o(c)},onload(){r(self[t].moduleMap[i]),o(c)}});document.head.appendChild(c)})),self[t].moduleMap={}}}("/assets/");

The idea

Maybe we could remove the vite module loader?

I've tested it in one of my (tiny) projects and managed to reduce the JS output by 23% (~ 1kb).

Note: This project is currently in a major rewrite, so it's not the ideal reproduction environment. But at least the steps below explain the process:

  1. Run npm run build in the pagecrypt project
  2. Open /web/build/index.html and find the main script type="module" in the <head> section.
  3. Remove the raw module loader snippet (available above) from the build JS output.
  4. run npm run serve and see that the page still works on localhost:5000
JS output after building with vite-plugin-singlefile (3012 bytes) var e,t;!function(e=".",t="__import__"){try{self[t]=new Function("u","return import(u)")}catch(r){const n=new URL(e,location),o=e=>{URL.revokeObjectURL(e.src),e.remove()};self[t]=e=>new Promise(((r,a)=>{const i=new URL(e,n);if(self[t].moduleMap[i])return r(self[t].moduleMap[i]);const s=new Blob([`import * as m from '${i}';`,`${t}.moduleMap['${i}']=m;`],{type:"text/javascript"}),c=Object.assign(document.createElement("script"),{type:"module",src:URL.createObjectURL(s),onerror(){a(new Error(`Failed to import: ${e}`)),o(c)},onload(){r(self[t].moduleMap[i]),o(c)}});document.head.appendChild(c)})),self[t].moduleMap={}}}("/assets/");var r={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bits:6},n=function(e,t){return function(e,t,r){if(void 0===r&&(r={}),!t.codes){t.codes={};for(var n=0;n=8&&(i-=8,a[c++]=255&s>>i)}if(i>=t.bits||255&s<<8-i)throw new SyntaxError("Unexpected end of data");return a}(e,r,t)};const o=document.querySelector.bind(document),[a,i,s,c,d,l]=["input","iframe","header","#msg","#locked","#unlocked"].map(o);""===window.pl&&(a.disabled=!0,g("No encrypted payload."));const u=n(window.pl),w=u.slice(0,32),m=u.slice(32,48),p=u.slice(48),f=(null==(e=window.crypto)?void 0:e.subtle)||(null==(t=window.crypto)?void 0:t.webkitSubtle);function h(e){e.classList.remove("hidden")}function v(e){e.classList.add("hidden")}function g(e){c.innerText=e,s.classList.toggle("text-red-600",!0),s.classList.toggle("text-green-600",!1),h(d),v(l)}window.crypto.subtle||(g("Please use a modern browser."),a.disabled=!0),o("form").addEventListener("submit",(async e=>{e.preventDefault();try{t="Decrypting...",c.innerText=t,s.classList.toggle("text-green-600",!1),s.classList.toggle("text-red-600",!1),h(d),v(l),await async function(e){return new Promise((t=>setTimeout(t,e)))}(60);const e=await async function({salt:e,iv:t,ciphertext:r},n){const o=new TextDecoder,a=new TextEncoder,i=await f.importKey("raw",a.encode(n),"PBKDF2",!1,["deriveKey"]),s=await f.deriveKey({name:"PBKDF2",salt:e,iterations:1e6,hash:"SHA-256"},i,{name:"AES-GCM",length:256},!1,["decrypt"]),c=new Uint8Array(await f.decrypt({name:"AES-GCM",iv:t},s,r));return o.decode(c)}({salt:w,iv:m,ciphertext:p},a.value);if(!e)throw"Malformed data";!function(e){c.innerText=e,s.classList.toggle("text-green-600",!0),s.classList.toggle("text-red-600",!1),h(l),v(d)}("Success!"),i.srcdoc=e.replace("",''),window.setTimeout((()=>{o("main").remove(),h(i),document.querySelectorAll("script").forEach((e=>e.remove()))}),1e3)}catch(r){g("Wrong password."),a.value=""}var t}));
JS output after removing module loader (2381 bytes - 23% less code) var e,t,r={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bits:6},n=function(e,t){return function(e,t,r){if(void 0===r&&(r={}),!t.codes){t.codes={};for(var n=0;n=8&&(i-=8,a[c++]=255&s>>i)}if(i>=t.bits||255&s<<8-i)throw new SyntaxError("Unexpected end of data");return a}(e,r,t)};const o=document.querySelector.bind(document),[a,i,s,c,d,l]=["input","iframe","header","#msg","#locked","#unlocked"].map(o);""===window.pl&&(a.disabled=!0,g("No encrypted payload."));const u=n(window.pl),w=u.slice(0,32),m=u.slice(32,48),p=u.slice(48),f=(null==(e=window.crypto)?void 0:e.subtle)||(null==(t=window.crypto)?void 0:t.webkitSubtle);function h(e){e.classList.remove("hidden")}function v(e){e.classList.add("hidden")}function g(e){c.innerText=e,s.classList.toggle("text-red-600",!0),s.classList.toggle("text-green-600",!1),h(d),v(l)}window.crypto.subtle||(g("Please use a modern browser."),a.disabled=!0),o("form").addEventListener("submit",(async e=>{e.preventDefault();try{t="Decrypting...",c.innerText=t,s.classList.toggle("text-green-600",!1),s.classList.toggle("text-red-600",!1),h(d),v(l),await async function(e){return new Promise((t=>setTimeout(t,e)))}(60);const e=await async function({salt:e,iv:t,ciphertext:r},n){const o=new TextDecoder,a=new TextEncoder,i=await f.importKey("raw",a.encode(n),"PBKDF2",!1,["deriveKey"]),s=await f.deriveKey({name:"PBKDF2",salt:e,iterations:1e6,hash:"SHA-256"},i,{name:"AES-GCM",length:256},!1,["decrypt"]),c=new Uint8Array(await f.decrypt({name:"AES-GCM",iv:t},s,r));return o.decode(c)}({salt:w,iv:m,ciphertext:p},a.value);if(!e)throw"Malformed data";!function(e){c.innerText=e,s.classList.toggle("text-green-600",!0),s.classList.toggle("text-red-600",!1),h(l),v(d)}("Success!"),i.srcdoc=e.replace("",''),window.setTimeout((()=>{o("main").remove(),h(i),document.querySelectorAll("script").forEach((e=>e.remove()))}),1e3)}catch(r){g("Wrong password."),a.value=""}var t}));

Let me know if we need a better demo to reproduce and test this.

Why?

  • This would be a tiny optimization to save ~ 1kb. This is so small it's barely noticeable for a normal app with 50kb. But for a simple HTML file with a simple JS file of < 5 kb, this is a significant part of the build output.
  • Remove unused code so the entire build output is useful.

Announcement: I'm at VueConfUS

I'm currently at VueConf in Fort Lauderdale. If you want to say hi, please find me! I'm the almost-bald guy with a short beard in the diamond-patterned white shirt.

Manipulate the .html files in `generateBundle` hook instead of `transformIndexHtml`

Hey! I needed your plugin together with UnoCSS to generate one index.html where everything is inlined.
Because UnoCSS replaces the placeholders in the generateBundle hook it didn't generate the CSS correctly. The CSS from UnoCSS wasn't replaced and the placeholders where still in the code.

So I tried to fix this and tested a bit with the hooks and got a working plugin which 1. generates the bundle correctly and 2. deletes the entries of the bundle so they don't get generated into the assets dir.

The code is probably not very perfect and could be adjusted. I copied your Regex from your plugin.
With this code it should be possible to generate multiple .html files with inlined assets. Not sure if this works when you use transformIndexHtml? It's also my first plugin haha.

Keep in mind it doesn't include the extra functions like the custom build config yet. :)

{
  name: 'singlefile',
  apply: 'build',
  enforce: 'post',
  generateBundle: (_, bundle) => {
    const htmlFiles = Object.keys(bundle).filter((i) => i.endsWith('.html'))
    const cssAssets = Object.keys(bundle).filter((i) => i.endsWith('.css'))
    const jsAssets = Object.keys(bundle).filter((i) => i.endsWith('.js'))

    for (const name of htmlFiles) {
      const htmlChunk = bundle[name]
      let replacedHtml = htmlChunk.source

      for (const jsName of jsAssets) {
        const jsChunk = bundle[jsName]
        const reScript = new RegExp(
          `<script type="module"[^>]*?src="[\./]*${jsChunk.fileName}"[^>]*?></script>`
        )

        const code = `<script type="module">\n//${jsChunk.fileName}\n${jsChunk.code}\n</script>`
        replacedHtml = replacedHtml.replace(reScript, code)
        delete bundle[jsName]
      }

      for (const cssName of cssAssets) {
        const cssChunk = bundle[cssName]
        const reCSS = new RegExp(
          `<link rel="stylesheet"[^>]*?href="[\./]*${cssChunk.fileName}"[^>]*?>`
        )
        const code = `<style type="text/css">\n${cssChunk.source}\n</style>`
        replacedHtml = replacedHtml.replace(reCSS, code)
        delete bundle[cssName]
      }

      htmlChunk.source = replacedHtml
    }
  }
}

Let me know what you think! :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.