Search code examples
typescriptvuejs3service-workerrollupvite

Prevent service-worker.js from being bundled with vite / rollup


I have a TypeScript-based Vuejs project compiled and bundled using Vite.

I am trying to configure the build system to compile my custom service worker (src/service-worker.ts) and place the output in dist/service-worker.js. In particular, I don't want it included as part of the application's JS bundle, because it needs to be served at that well-known URL as part of a static website.

The existing structure is like:

index.html
public/
 favicon.ico
src/
 service-worker.ts
 main.ts
 /* etc */

And I would like the output to be something like:

dist/
 index.html
 assets/index.[hash].js
 assets/vendor.[hash].js
 /* ... */
 service-worker.js  # <-- I would like the file emitted here

If the service worker didn't need to be transpiled/compiled, I know I could simply include it in public/ and it would be copied to the dist/ folder unchanged.

I've looked at vite-plugin-pwa but that is rather opaque and tied to workbox.

Other related questions relate to situations where people want to exclude files altogether from their build output, which is not quite what I'm after.

How can I compile a TypeScript file but leave its output unbundled in my dist folder?


Solution

  • You can configure the entry point and output locations via build.rollupOptions in your Vite config:

    1. Add an entry point for the service-worker.ts file (via build.rollupOptions.input).

    2. Add a formatter for output filenames that puts the service worker file in the root of the output directory (via build.rollupOptions.output.entryFilenames).

    // vite.config.ts
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    export default defineConfig({
      plugins: [vue()],
      build: {
        rollupOptions: {
          input: {
            // the default entry point
            app: './index.html',
    
            // 1️⃣
            'service-worker': './src/service-worker.ts',
          },
          output: {
            // 2️⃣
            entryFileNames: assetInfo => {
              return assetInfo.name === 'service-worker'
                 ? '[name].js'                  // put service worker in root
                 : 'assets/js/[name]-[hash].js' // others in `assets/js/`
            }
          },
        },
      },
    })
    

    demo