-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Support for ESM plugins and configs #8913
Conversation
# Conflicts: # packages/utils/node-resolver-core/src/lib.rs # packages/utils/node-resolver-rs/src/lib.rs
Benchmark ResultsKitchen Sink ✅
Timings
Cold Bundles
Cached Bundles
React HackerNews ✅
Timings
Cold BundlesNo bundle changes detected. Cached Bundles
AtlasKit Editor ✅
Timings
Cold Bundles
Cached Bundles
Three.js ✅
Timings
Cold BundlesNo bundle changes detected. Cached BundlesNo bundle changes detected. |
let module = lex(&contents)?; | ||
module | ||
.imports() | ||
// .par_bridge() |
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 want to make this run in parallel, but that only works if the file system is the native one and not provided by JS otherwise we'll deadlock. Could work if we make the packageManager.getInvalidations
API async but maybe that's a breaking change.
# Conflicts: # Cargo.lock
This doesn't sound good:
|
@mischnic where do you see that? |
In the windows unit tests, though that is only printed if there are no tests:
|
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.
LGTM! I like how all of the configurable packages lose their own bespoke invalidation logic.
I made an honest attempt to read through the native changes too 😆
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
@@ -123,7 +125,27 @@ export class NodePackageManager implements PackageManager { | |||
saveDev?: boolean, | |||
|}, | |||
): Promise<any> { | |||
let {resolved} = await this.resolve(name, from, opts); | |||
let {resolved, type} = await this.resolve(name, from, opts); | |||
if (type === 2) { |
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.
Ideally we'd have an enum somewhere for these magic numbers
Fixes #7639. Fixes #7330. Closes #8726.
This adds support for loading plugins and configs distributed using native ESM. It works by resolving the module type (ESM or CJS) based on the file extension (mjs/cjs) or
"type": "module"
field in package.json. Then it loads ES modules usingimport
.Since we cannot intercept import like we do require, we have to track dependencies to invalidate the cache differently. This is done using a fork of es-module-lexer, with Rust bindings and support for analyzing require as well as import. We recursively build up a graph of dependencies using this and the Rust resolver, and keep track of all of the invalidations found along the way.
There is basic support for dynamic expressions in
import()
andrequire()
functions. If the argument starts with a string and contains string concatenations, or is a template literal with interpolations, it will be compiled to a glob and then we attempt to match the glob against the file system to include all of the possible files that could be loaded in the cache key. If this is not possible (i.e. it is not statically analyzable), then we invalidate on startup.For now, this strategy is only applied to ESM dependencies and we use our old strategy for CJS. Eventually I'd like to switch everything over as it's more stable/predictable, but want to test it out first. ESM support will be marked experimental at first, and will emit a warning saying so.
For configs, we now also automatically add dev dependencies and invalidate on startup for JS files in core so plugins no longer need to do this.
To do