Repository navigation
Hoist lazily initialized ES module classes; follow on-demand workspace imports - #1487
Merged
Merged
Conversation
…pace imports
A class a bundled ES module's __esm initializer stores into a module
binding (`Multi = class extends import_stream.Readable { … }`) becomes a
class declaration at the same text position: the initializer's body splits
around it into run-once pieces, so a class over a runtime-provided base no
longer has to exist as a stored class value. Only definitions whose
evaluation runs no code (no static fields, static blocks or computed names;
heritage absent, a builtin module member, or a hoisted class), declared
once, assigned only there and read only inside functions after the
assignment move.
npm selection now also scans the program modules a literal import()
reaches, with their static graphs: a workspace package loaded on demand
had its npm dependencies refused as "not part of the compiled module
graph".
`new import_x.C()` respelled `new require("x").C()`, which constructs the
require call; the interop binding now becomes `(require("x"))` there.
util.inherits onto a runtime-provided class (stream.Readable, the from2
and multistream shape) fences where it is written instead of failing in
Node's argument validation at run time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sting The self-hosted compiler needs the same record type on both sides of the call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renumbers this branch's npm case 2572 to 2588, after the renumbered polyfill cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # tests/harness/npm-cases.ts
The on-demand import scan tested each file's text for `import(` before
reading its calls, so `import /* note */ ("pkg")` was never followed:
the workspace package it loads and that package's npm dependencies were
left out of the module graph, and the dependency was refused with
SC2013. A file is now skipped only when it does not contain the keyword,
and its literal import() calls are read from the syntax. The
workspace-dynamic-import case loads its package with such a comment.
This was referenced Oct 20, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1458 (
opt/npm-polyfill-shims), which is stacked on #1434. Review those first.Summary
Classes defined in a lazy ES module initializer. esbuild scope-hoists an ES module that some code
require()s and runs its body from an__esminitializer, so its classes become assignments to module bindings:A class whose base is runtime-provided (a stream, an EventEmitter) has no stored class value, so this was refused. The lazy-initializer rewrite now turns such an assignment into a class declaration in the bundle's scope, at the same text position. The initializer body splits around the class into run-once pieces that call each other in order, so the class text stays where it was and only its binding changes. Line structure is preserved.
The move applies only where defining the class earlier can't be observed:
Everything else stays an assignment and keeps its existing treatment.
On-demand workspace packages. npm package selection scanned the startup module order only. A workspace package that the program loads through a literal
import()(and that package's own static graph) was never scanned, so its npm dependencies were refused with "its implementation is not part of the compiled module graph". Selection now follows literalimport()edges from program modules, including their static imports. Package files stay with the package machinery.newthrough a respelled interop binding.new import_x.C()becamenew require("x").C(), which parses as constructing therequirecall. The respelling now producesnew (require("x")).C().util.inheritsonto a runtime-provided class.util.inherits(Ctor, stream.Readable)is the from2 and multistream shape. It used to pass the class's identity token to the runtime, where Node's argument validation threwERR_INVALID_ARG_TYPE("superCtor.prototype must be of type object"). It now fences where it is written, with theextendshint, like the program-class case.Evidence
2588-bundled-esm-classes: real esbuild 0.27 output of a CommonJS entry that requires an ES module whose ES-module importer uses two classes, one overstream.Readableand one over that class. Before this change the package was refused.workspace-dynamic-import: a workspace package loaded throughimport()whose npm dependency is installed only in the workspace package. Before this change it failed with SC2013.newthrough an interop binding.util-inherits-runtime-class.ts.npm-staticsuite (121), the npm workspace cases, the diagnostics corpus (200), the compiler unit tests, the corpus selection,pnpm lint, the formatting of the touched files andtest:ts7 --baselines-onlyall pass.self-hosting-native-driver.test.tspasses on the re-merged stack top.🤖 Generated with Claude Code