davidmyersdev / vite-plugin-node-polyfills

A Vite plugin to polyfill Node's Core Modules for browser environments.
MIT License
263 stars 17 forks source link

Uncaught TypeError: Uint8Array is undefined #36

Closed 2manyvcos closed 10 months ago

2manyvcos commented 10 months ago

After upgrading from v0.9.0 to v0.11.3, the page provided by the vite dev server (v4.4.7) crashes because of the following error:

This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support.
Uncaught TypeError: Uint8Array is undefined

However, Buffer.TYPED_ARRAY_SUPPORT yields true in the Browser console and when running the following function directly (extracted from the source), it also returns true:

function typedArraySupport() {
  try {
    const arr = new Uint8Array(1);
    const proto = { foo: function() {
      return 42;
    } };
    Object.setPrototypeOf(proto, Uint8Array.prototype);
    Object.setPrototypeOf(arr, proto);
    return arr.foo() === 42;
  } catch (e) {
    return false;
  }
}

The code which crashes is marked to be from the following module:

node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
davidmyersdev commented 10 months ago

Hey there. I appreciate the context. Would you mind putting together a reproduction on StackBlitz or CodeSandbox so that I can debug it?

2manyvcos commented 10 months ago

Sure, here you go:

https://codesandbox.io/p/sandbox/vite-plugin-node-polyfills-issue-36-984z5j?file=/src/App.jsx:12,9

The issue occurs when importing anything from semantic-ui-react.

2manyvcos commented 10 months ago

So, I just looked at the source code again and I think this may be related to 'lodash-es', which is a dependency of semantic-ui-react.

I found the following in the same source file where the error occurs:

// node_modules/lodash-es/_Uint8Array.js
var import_dist124 = __toESM(require_dist(), 1);
var Uint8Array = root_default.Uint8Array;
var Uint8Array_default = Uint8Array;

Here is a fork of the Sandbox which directly imports lodash-es instead of semantic-ui-react:

https://codesandbox.io/p/sandbox/vite-plugin-node-polyfills-issue-36-forked-46fv9j?file=/src/App.jsx:4,1

When downgrading to vite-plugin-node-polyfills@0.11.1, the sandbox runs flawlessly.

jdstapleton commented 10 months ago

We have seen similar issues to this, And can reproduce it here: https://stackblitz.com/edit/vitejs-vite-jachkt I don't know if this is a vite-plugin-node-polyfills issue directly or a more general vite issue. Its the way vite is bundling the modules together. That variable 'Uint8Array' is being hoisted up to the module scope, overshadowing the global Uint8Array. But the to assign the value hasn't executed yet, so UInt8Array is undefined by the time its first being referenced.

To get around this issue, we turned on minification in our vite dev server via a vite.config.js change:

 optimizeDeps: {
   esbuildOptions: {
      minifyIdentifiers: true,
   }
 },

Which fixes this because then the lodash-es declaration is renamed to a minified variable name.

davidmyersdev commented 10 months ago

Thanks for sharing, @jdstapleton! I'll have to look into that config option to see if it makes sense to change in this plugin or if it should be something left to end users. At the very least, maybe I can make it configurable if it will help others too.

achin commented 10 months ago

Thanks for your help, @davidmyersdev. Minifying identifiers helps us deal with this issue, but it seems like there's something deeper at the core.

We played with esbuild's bundling options. Normally, when there are two identifiers with the same name in two different modules that are being bundled together, esbuild lets the first module use the name as is and modifies the second one (and all subsequent ones in other modules).

For example, if you're bundling a.js, b.js, and c.js and they all have a top-level variable foo, then the bundle will contain the concatenated contents of the three files, with foo renamed to foo, foo2, and foo3 in the respective sections for a.js, b.js, and c.js.

https://github.com/evanw/esbuild/blob/main/docs/architecture.md#scope-hoisting

In the case of lodash-es, Uint8Array (from _Uint8Array.js) seems to be colliding with the polyfills from the node.js buffer polyfill (which is present earlier in the bundle). Because lodash-es' Uint8Array is just called Uint8Array, it seems like esbuild isn't detecting the name collision.

I don't know much about esbuild plugins, but could this be because the vite-plugin-node-polyfills are injected into the bundle, somehow bypassing esbuild's normal scope hosting logic?

https://github.com/davidmyersdev/vite-plugin-node-polyfills/blob/main/src/index.ts#L209C15-L209C28

konnng-dev commented 10 months ago

Came here w/ the same problem after updating dependencies for a project. Took a while to figure out why.

I ended up using downgrading to vite-plugin-node-polyfills@0.11.1 as workaround.

ConcernedHobbit commented 10 months ago

Running into the same issue; without these polyfills there's no Stream, but as soon as I enable the plugin (without including any polyfills), Uint8Array becomes undefined.

adrian-gierakowski commented 10 months ago

similar issue here