Repository navigation
Compile lazy barrels, star re-exports and npm aliases in bundled packages - #1427
Merged
Merged
Conversation
A loader `L = () => M ??= NS` over a lazily initialized module's namespace (optionally through __toESM) is respelled at every `L().name` as the module's initializer call followed by the export's live read; loaders whose result escapes as a value stay refused. The canonical export table keeps a getter it cannot chase as a live accessor, so a lazy re-export still initializes its module at the first read. An ES-module importer copies those accessors once when it links the CommonJS module, in the lexer's order, exactly like Node's loader; require() keeps reading them live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wering fixes
- A star re-export of a lazily initialized module into the bundle's
exports (__reExport(main, (init_x(), __toCommonJS(x_exports))))
initializes the module at the statement and adds its names to the
export table after the explicit ones, without default.
- The rewrite keeps the original's lexer answer whenever its export
table holds accessors (Node's lexer stops at the first one).
- An npm alias directory of a selected package ("x-updated":
"npm:x@6.3.0") resolves to that package's JavaScript in both resolvers.
- "use strict" code may read arguments in functions with parameters.
- A comma expression ending in null/undefined coerces its result.
- A void conditional return lowers as control flow.
- A detached method of a local class drops its empty capture list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The numbers collided with other open changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renumbers this branch's npm cases 2564-2566 to 2580-2582: #1411 now uses 2564-2566. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lazy loaders that share a var statement each removed their declarators on their own, and declarators of one statement removed separately leave overlapping edits, of which applyEdits keeps the first. A later loader in the statement (one using __toESM, say) stayed behind and the interop pass refused the package. Every removed declarator of the pass (the interop requires, the loaders and the unused export tables) now goes into one set, removed once. A call through a loader (`load().fn()`) respelled the callee as the export's live read, which drops the namespace receiver. Loader calls now take the rule namespace calls already use: the callee must be a top-level function that never reads `this`; other calls keep the package refused.
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 #1422 (which is stacked on #1411). Review those first; this branch is based on
opt/npm-esm-wrappers.Summary
Memoized lazy loaders of bundled namespaces
A bundle plugin can turn a barrel's re-exports into lazy loaders over esbuild's lazily initialized modules:
load_import_x().namebecomes the module's initializer call followed by the export's live read. The first access still initializes the module, once, and later accesses read the binding. A loader whose result escapes as a value stays refused.anyfallback, so a lazy re-export still initializes its module at the first read.require()reads these accessors live, as Node does.Star re-exports of lazily initialized modules
__reExport(main, (init_x(), __toCommonJS(x_exports)), module.exports)comes fromexport * fromin a bundle entry. The module now initializes at that statement, and its names join the export table after the explicit ones, withoutdefault, which is what__reExportcopies.npm aliases
"x-updated": "npm:x@6.3.0"installs packagexunder another directory name. Whenxis selected, an import through the alias now resolves to that copy's JavaScript in both resolvers. Before, scriptc's resolver decided on the specifier's name and answered the copy's.d.ts.Lowering fixes found in bundled third-party code
"use strict"andarguments. Code under"use strict"may readargumentsin functions with named parameters. Strict code never aliases them; before, only ES modules were trusted.return log(), nullno longer leaves a bare unit literal; the literal takes the result's representation. This was an internal compiler error.return c ? f() : g()over void calls lowers as control flow. This was an internal compiler error.local-class-detached-method.tscorpus program.Evidence
New npm corpus cases. All are real esbuild output, using a generic lazy-barrel plugin where needed:
2580-lazy-barrel: an ES-module import of the barrel, plain and whitespace/syntax-minified. Node initializes every module at link and the imports are snapshots.2581-lazy-barrel-require:require()reads each getter at its read.2582-npm-alias-two-releases: two releases of one package in a pnpm store layout, the newer one through an alias.New corpus programs:
local-class-detached-method.ts(an internal compiler error on main);comma-null-union-return.cjs(an internal compiler error on main);strict-arguments-parameters.cjs(a runtime fence on main).Tests.
npm-static.test.ts, the compiler unit tests andorder-paritypass.npm.test.ts > node24-url-authorityand library-mode K10 under ASan also fail on main in the sandbox;Checks.
pnpm lint,pnpm format:checkandtest:ts7 --baselines-onlypass; baselines are recorded for the new entries.self-hosting-native-driver.test.tspasses.🤖 Generated with Claude Code