48

I'm having a devil of a time figuring out how to build a single .js file from Vite in my Svelte project that includes all of the built javascript and CSS from my Svelte projects. By default, Vite bundles the app into one html file (this is ok), two .js files (why??), and one .css file (just want this bundled into the one js file).

I ran this very basic command to get a starter project:
npx degit sveltejs/template myproject

I tried adding a couple of plugins, but nothing I added achieved the results I wanted. Primarily, the plugins I found seem to want to create a single HTML file with everything in it. It seems like PostCSS might be able to help, but I don't understand what configuration I can set via Vite to get it to do what I want.

What is the magic set of plugins and config that will output a single HTML file with a single js file that renders my Svelte app and its CSS onto the page?

1
  • 1
    What's your project structure look like? Your vite configuration? Are you using any dynamic imports? Commented Jun 4, 2021 at 22:05

10 Answers 10

43

Two steps,

Final result,

import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";

export default defineConfig({
  plugins: [cssInjectedByJsPlugin()],
  build: {
    rollupOptions: {
      output: {
        manualChunks: undefined,
      },
    },
  },
});

As suggested by @TheRockerRush below, you may want to use vite-plugin-singlefile to bundle all code into a single .html file, although the OP is asking for a single .js file.

Sign up to request clarification or add additional context in comments.

2 Comments

What should the import look like? I'm struggling to get this to work with tailwind, vue, postcss. Also, that output is not assignable to type 'RollupOptions'
manualChunks undefined doesn't seem to work (anymore?), but defining a function does (see my other answer)
13

If you're looking for a solution to this, you might want to take a look at vite-plugin-singlefile.

4 Comments

This solution does not support multiple output files
> This solution does not support multiple output files As per the name of the plug-in lol
Note: this plugin specifically outputs only an html file with all assets embedded, mostly for exceptional use cases or offline distribution and usage, whereas the OP is asking for assets to be bundled into a single javascript file. The plugin in the accepted answer satisfies the OP requirement.
This comment thread is fantastic 🍿just to add something, the docs for the plugin actually say "Last but not least, this is a single file plugin. As in, it creates one HTML file. Hence the name -- maybe this is a common mixup
5

That doesn't come out of the box for vite but you can write a quick plugin which will be doing exactly that

const bundle_filename = ''
const css_filename = 'style.css'

defineConfig({
  build: {
    lib: {
      entry: 'src/mycomponent.js',
      name: 'mycomponent.js',
      fileName: () => 'mycomponent.js',
      formats: ['iife'],
    },
    cssCodeSplit: false,
    rollupOptions: {
      plugins: [
        {
          apply: 'build',
          enforce: 'post',
          name: 'pack-css',
          generateBundle(opts, bundle) {
            const {
              [css_filename]: { source: rawCss },
              [bundle_filename]: component,
            } = bundle

            const IIFEcss = `
            (function() {
              try {
                  var elementStyle = document.createElement('style');
                  elementStyle.innerText = ${JSON.stringify(rawCss)}
                  document.head.appendChild(elementStyle)
              } catch(error) {
                console.error(error, 'unable to concat style inside the bundled file')
              }
            })()`

            component.code += IIFEcss

            // remove from final bundle
            delete bundle[css_filename]
          },
        },
      ],
    },
  },
})

3 Comments

This code is now outdated, build fails.
Yes, answer needs some editing but the general idea is solid and works as expected.
any hint on what to change that this is working again?
3

I had the same issue and was able to fix by editing vite.config.ts as followed (tested on [email protected]):

export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: undefined,
      },
    },
  },
};

1 Comment

This config does not insert the CSS into the single .js asset emitted, as requested by OP.
2

I created a boilerplate Vite project for this problem:
https://github.com/mvsde/svelte-micro-frontend

Maybe the configuration helps with your case:

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  plugins: [
    svelte({
      emitCss: false
    })
  ],

  build: {
    assetsDir: '',
    sourcemap: true,
    lib: {
      entry: 'src/main.js',
      formats: ['iife'],
      name: 'SvelteMicroFrontend',
      fileName: 'svelte-micro-frontend'
    }
  }
})

1 Comment

This config does not insert the CSS into the single .js asset emitted, as requested by OP.
2

If you are working with Svelte, you can use emitCss:

export default defineConfig({
    plugins: [svelte({
        emitCss: false,
    })],
})

Comments

2

Now you can use:

https://www.npmjs.com/package/vite-plugin-singlefile

npm i vite-plugin-singlefile

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

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

Comments

1

For combining the JS files, you can specify a function for manualChunks that returns the same value for all files:

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: (filename) =>
          'x'
      },
    },
  },
});

Comments

0

As manualChunks are no longer working in a latest versions of Vite, there's no any way to combine all the chunks into one.

But found a hacky solution to have an index.html + bundle.js after the build: https://github.com/d-velopment/SvelteKit-One-Bundle - it rewraps the project's initial .js files to go from bundle.js, which could be loaded from index.html or external project.

Comments

0

Manual chunks

In your vite.config.js you can specify with manualChunks(id) how vite should 'chunk' your code in output.

Example:

  build: {
    rollupOptions: {
      external: ['/config/settings.json'],
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return ['vuetify', 'vue', 'pinia'].some(pkg => id.includes(pkg)) ? 'VueStuff' : null;
          }
          if (id.includes('/src/store/')) return 'Stores';
          if (id.includes('/src/lib/')) return 'lib';
          if (id.includes('Admin')) return 'Admin';
          if (id.includes('User')) return 'User';

          return null;
        }
      }
    }
  }

unfortunately this is not documented by the vite team or I am not able to search properly on that site.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.