vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.5k stars 6.18k forks source link

Can't properly exclude an unneeded node polyfill for browser Text-Encoder from an imported library #1819

Closed davay42 closed 3 years ago

davay42 commented 3 years ago

Describe the bug

When using a library meant to be used both in the browser and node, I can't properly exclude the node polyfills from the build. They bloat the bundle size significantly. I'm using the rollupOptions to exclude them like that

  build: {
    rollupOptions: {
      external: ['text-encoding', '@peculiar/webcrypto'],
    },
  },

But then in when I serve the built code I get error in the console "TypeError: Failed to resolve module specifier "text-encoding". Relative references must start with either "/", "./", or "../".

The original code in the library is here https://github.com/amark/gun/blob/master/sea.js#L175

    api.TextEncoder = window.TextEncoder
      api.TextDecoder = window.TextDecoder
    if (!api.TextDecoder) {
      const { TextEncoder, TextDecoder } = require('text-encoding')
      api.TextDecoder = TextDecoder
      api.TextEncoder = TextEncoder
    }

May vite or rollup recognize these checks against modern browser window object while building for browsers?

Reproduction

Here's the repo https://github.com/DeFUCC/etovoteto The built 'vendor.js' is around 700kB until you add external: ['text-encoding'] to rollupOptions. With these line the vendor.js is just 94 kB as expected, but the browser throws an error. Only commenting out the lines in the actual node_modules script does help. Please, help us find a solution, either via vite or rollup config or even with proper edits in the source code of the dependency.

System Info

yyx990803 commented 3 years ago

Adding something to rollup external only means "don't bundle it, but expect it to be provided in some other way". Using external means your application still needs that module to work properly.

If you want to "empty" a module, use a custom plugin:

{
  resolveId(id) { if (id === 'text-encoder') return id },
  load(id) { if (id === 'text-encoder') return `export default {}` }
}
davay42 commented 3 years ago

Thank you! I've tried to use the plugin, but it seems rollup doesn't perform these transformations for dependency code. I think it's because vite pre-bundles them. So this plugin code doesn't help.

 build: {
    rollupOptions: {
      plugins: [
        {
          name: 'no-text-encoder-polyfill',
          resolveId(id) {
            if (id === 'text-encoding') return id
          },
          load(id) {
            if (id === 'text-encoding')
              return `export default { TextEncoder:window.TextEncoder, TextDecoder:window.TextDecoder}`
          },
        },
      ],
      external: ['text-encoding', '@peculiar/webcrypto'],
    },
  },
jojobyte commented 3 years ago

@DeFUCC move it up to the root plugins for vite instead of in the build.rollupOptions.plugins and check for it in the ID without expecting the ID to exactly match it.

this is working for me.

export default {
  plugins: [
    vue(),
    VitePWA(),
    VitePluginHtml({
      minify: true,
    }),
    {
      name: 'no-text-encoder-polyfill',
      resolveId(id) {
        // console.log(id) // never equals 'text-encoding' exactly
        if (id.indexOf('text-encoding') > -1) return id // but has it in the id
      },
      load(id) {
        if (id.indexOf('text-encoding') > -1) return `export default {}`
      },
    },
  ],
}

Thanks @yyx990803 for putting us on the right path!