3

I'm building a chrome extension using Vite as my build tool. The main problem is during minification and mangling there are a lot of global variables created. After injecting my script to the page they conflict with already defined variables on window object.

I imagine the perfect solution would be to have my entire script wrapped in IIFE. I tried using esbuild.format = 'iife'. The resulting build is in fact wrapped in IIFE, however all the imports are not inlined. Instead resulting script is like 15 lines long with a bunch of require statements, which obviously does not work in the browser.

This is my config file:

export default defineConfig({
  plugins: [
    vue(),
  ],
  esbuild: {
    format: 'iife',
  },
  build: {
    emptyOutDir: false,
    rollupOptions: {
      input: resolve(__dirname, './src/web/index.ts'),
      output: {
        dir: resolve(__dirname, './dist'),
        entryFileNames: 'web.js',
        assetFileNames: 'style.css',
      },
    },
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
});

I'm currently using this hack so to say to wrap my build in IIFE (for this I removed the esbuild.format option).

Kamil Latosinski
  • 756
  • 5
  • 28

2 Answers2

4

Hey I am doing the exact same thing! And I also noticed the unminified variables and functions can clash with random code in a webpage.

From what I researched myself on this topic, you shouldn't change esbuild build options with Vite as that will prevent Rollup from transforming the output. Instead, you should use format: 'iife' in the rollupOptions of your vite.config. However, in my case (and yours I believe), I have to output multiple bundles since the extension code can't share modules amongst each other. Which will crash when you set the format to 'iife' due to:

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

The only solution in my case seems to be to either use multiple vite.configs (I already have two) for each of my bundle with single input entry point and format as 'iife'. Or, as you did, just write the self-invoking function yourself with some hacky script. Seems though there aren't any perfect solutions as of now.

EDIT: Okay, got it working. This is my vite.config.ts (the project):

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

import path from 'path'

/** @type {import('vite').UserConfig} */
export default defineConfig({
  plugins: [svelte({}), tsconfigPaths()],
  build: {
    minify: false,
    rollupOptions: {
      output: {
        chunkFileNames: '[name].js',
        entryFileNames: '[name].js'
      },
      input: {
        inject: path.resolve('./src/inject.ts'),
        proxy: path.resolve('./src/proxy.ts'),
        'pop-up': path.resolve('./pop-up.html')
      },
      plugins: [
        {
          name: 'wrap-in-iife',
          generateBundle(outputOptions, bundle) {
            Object.keys(bundle).forEach((fileName) => {
              const file = bundle[fileName]
              if (fileName.slice(-3) === '.js' && 'code' in file) {
                file.code = `(() => {\n${file.code}})()`
              }
            })
          }
        }
      ]
    }
  }
})
TeemuK
  • 2,095
  • 1
  • 18
  • 17
  • 1
    I finally made it by adding the `iife` into rollup options. In my case I have separate vite config for each script (background, web and content). Honestly I'm not sure why I ended up with three configs, but as for now I'm happy with the solution. – Kamil Latosinski Nov 09 '22 at 23:21
1

Okay, I made it working with this config:

export default defineConfig({
  plugins: [
    vue(),
  ],
  build: {
    emptyOutDir: false,
    rollupOptions: {
      input: resolve(__dirname, './src/web/index.ts'),
      output: {
        format: 'iife',
        dir: resolve(__dirname, './dist'),
        entryFileNames: 'web.js',
        assetFileNames: 'style.css',
      },
    },
  },
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
    },
  },
});

They key part is format: 'iife' inside build.rollupOptions.output.

Kamil Latosinski
  • 756
  • 5
  • 28