vite icon indicating copy to clipboard operation
vite copied to clipboard

Ignore browser field when bundling in library mode

Open Sara2009 opened this issue 3 years ago • 8 comments

Describe the bug

use vite to build a lib for nodejs

The source code in axios is like below:

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('../adapters/xhr.js');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('../adapters/http');
  }
  return adapter;
}

After building. The result bundle is not correct. Http adapter is resolved as xhr.

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== "undefined") {
    adapter = xhr;
  } else if (typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]") {
    adapter = xhr;
  }
  return adapter;
}

Then i got the error: UnhandledRejection ReferenceError: XMLHttpRequest is not defined

Reproduction

https://github.com/Sara2009/vite-require

System Info

"vite": "^2.9.13",

Used Package Manager

yarn

Logs

No response

Validations

Sara2009 avatar Jul 04 '22 04:07 Sara2009

vite does not support require

lovetingyuan avatar Jul 04 '22 15:07 lovetingyuan

I think the require here is still valid as it's used within a dependency (axios). Looks like the issue here is that axios specifies a browser field which Vite respects, which changed the http import to xhr.

Looks like library mode is focused on browser targets at the moment which caused this bug. It should be agnostic and leave it as is.

A workaround is to add axios as a dependency and put it in rollupOptions.external.

bluwy avatar Jul 05 '22 08:07 bluwy

set resolve.browserField to false will indicate Vite to ignore browser filed when resolving packages. I think it's enough for bundling universal bundles. 🤔

fi3ework avatar Dec 27 '22 08:12 fi3ework

set resolve.browserField to false will indicate Vite to ignore browser filed when resolving packages. I think it's enough for bundling universal bundles. 🤔

Now the resolve.browserField field is marked as deprecated, but the behavior of mainFields configured as [ "node", "module", "jsnext:main", "jsnext" ] is inconsistent, is there any solution?

rxliuli avatar Jun 30 '23 11:06 rxliuli

Setting 'browserField' to false seems to not work for me. Vitest and SSR seem to avoid this problem, so it must be possible. Any ideas?

justin0mcateer avatar Aug 16 '23 12:08 justin0mcateer

Vitest and SSR seem to avoid this problem, so it must be possible. Any ideas?

Vitest uses rollup directly :-( https://github.com/vitest-dev/vitest/blob/main/packages/vitest/rollup.config.js

Too bad, because vite has a lot better DX than rollup, and the ecosystem is growing.

wmertens avatar Mar 15 '24 08:03 wmertens

That said, setting build.ssr to true and ssr: { noExternal: true } works for me. It doesn't import browser versions of dependencies.

working example:

import { nodeExternals } from 'rollup-plugin-node-externals'

export default defineConfig(({ mode }) => ({
  plugins: [
    // Don't bundle any nodejs builtins
    nodeExternals(),
  ],
  ssr: { noExternal: true },
  build: {
    ssr: true,
    lib: {
      entry: ['src/index.ts', 'src/bin/cli.ts'],
      formats: ['cjs'],
    },
    target: 'node18',
    outDir: 'build',
    sourcemap: true,
    minify: mode === 'production' && 'esbuild',
  },
}))

wmertens avatar Mar 15 '24 08:03 wmertens

I think this is already resolved by config:

If you build a browser-targeting library, use the default config. If you build a node-targeting library, remove browser from resolve.mainFields.

I only wish there were a more explicit way to specify that one is building a node/bun/deno library. build.target is just not suitable imho.

silverwind avatar May 23 '24 22:05 silverwind