27

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?

ringmaster
  • 2,879
  • 3
  • 28
  • 31

7 Answers7

28

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.

aryzing
  • 4,982
  • 7
  • 39
  • 42
  • 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' – dras Jan 09 '23 at 04:33
6

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]
          },
        },
      ],
    },
  },
})
Sceat
  • 1,427
  • 14
  • 12
6

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

TheRockerRush
  • 69
  • 2
  • 5
  • 2
    This solution does not support multiple output files – Mike Post Sep 29 '22 at 01:59
  • 10
    > This solution does not support multiple output files As per the name of the plug-in lol – Maxim Therrien Sep 29 '22 at 03:23
  • 1
    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. – Shawn Erquhart Nov 17 '22 at 13:11
  • 1
    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 – salezica Feb 25 '23 at 03:03
3

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'
    }
  }
})
Fynn Becker
  • 88
  • 1
  • 4
3

I had the same issue and was able to fix by editing vite.config.ts as follows tested on vite@2.3.8

export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: undefined,
      },
    },
  },
};
Paolo
  • 20,112
  • 21
  • 72
  • 113
manse
  • 75
  • 4
2

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

export default defineConfig({
    plugins: [svelte({
        emitCss: false,
    })],
})
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.