-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(dev): make browser Node polyfills opt-in #7269
feat(dev): make browser Node polyfills opt-in #7269
Conversation
🦋 Changeset detectedLatest commit: a837a9e The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@@ -156,11 +149,12 @@ export const create = async ( | |||
): Promise<Compiler> => { | |||
let compiler = await esbuild.context({ | |||
...createEsbuildConfig(ctx, refs), | |||
write: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to manually write the result of the browser build to disk so that esbuild-plugins-node-modules-polyfill can check the build output in memory for any missing/unconfigured polyfills after tree-shaking has occured. This is important in the context of Remix since route files often depend on Node builtins regardless of whether they're used in browser code. If write
is enabled, the outputFiles
are not kept in memory and are not passed to the onEnd
callback of the esbuild plugin.
If you're interested, here's the PR where I implemented this: imranbarbhuiya/esbuild-plugins-node-modules-polyfill#152
@@ -100,7 +101,8 @@ export let create = async (ctx: Context): Promise<Compiler> => { | |||
// js compilation (implicitly writes artifacts/js) | |||
let js = await tasks.js; | |||
if (!js.ok) throw error ?? js.error; | |||
let { metafile, hmr } = js.value; | |||
let { metafile, outputFiles, hmr } = js.value; | |||
writes.js = JS.write(ctx.config, outputFiles); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As explained in my other comment, we now need to manage writing the output of the JS build to disk.
// rely on tree-shaking to remove all unused polyfills and fallbacks. | ||
fallback: "empty", | ||
}), | ||
externalPlugin(/^node:.*/, { sideEffects: false }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since esbuild-plugins-node-modules-polyfill is configured to manage fallbacks, this usage of externalPlugin
was redundant.
globals: ctx.config.browserNodeBuiltinsPolyfill?.globals ?? {}, | ||
// Mark any unpolyfilled Node builtins in the build output as errors. | ||
fallback: "error", | ||
formatError({ moduleName, importer, polyfillExists }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that we could customise errors for Remix consumers who don't have direct access to the esbuild plugin, I added support for custom error formatting here: imranbarbhuiya/esbuild-plugins-node-modules-polyfill#153.
modules: ctx.config.browserNodeBuiltinsPolyfill?.modules ?? {}, | ||
globals: ctx.config.browserNodeBuiltinsPolyfill?.globals ?? {}, | ||
// Mark any unpolyfilled Node builtins in the build output as errors. | ||
fallback: "error", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added support for built-time errors for unpolyfilled/unconfigured modules here: imranbarbhuiya/esbuild-plugins-node-modules-polyfill#152
packages/remix-dev/compiler/server/plugins/serverNodeBuiltinsPolyfill.ts
Show resolved
Hide resolved
8e3540e
to
6aac9e0
Compare
🤖 Hello there, We just published version Thanks! |
This PR makes it so we no longer polyfill built-in Node modules by default. To allow consumers to opt back in to polyfills, this also adds a new
browserNodeBuiltinsPolyfill
option that mirrors the existingserverNodeBuiltinsPolyfill
.This also adds a
globals
property to both of these options so Node globals likeBuffer
can be enabled with a simple flag rather than needing to manually change production code to modify the global environment.A couple of the tests have been updated since they were relying on Node builtins in the browser.
I originally planned on making the
browserNodeBuiltinsPolyfill
default to the value ofserverNodeBuiltinsPolyfill
if it was provided, but in practice I found it confusing that a Remix config with justserverNodeBuiltinsPolyfill
defined would also have browser polyfills. It does mean there's a bit more boilerplate if you want to use the same polyfills across both, but I think it's better to be explicit, and it's easy to share a single config object across both since the Remix config is a JS file anyway.✨ This PR also adds a bonus feature where the dev server restarts when the Remix config changes. (Thanks @pcattori!)