fix(treeshake): drop dead star re-export sources of wrapped barrels#9709
Closed
IWANABETHATGUY wants to merge 2 commits into
Closed
fix(treeshake): drop dead star re-export sources of wrapped barrels#9709IWANABETHATGUY wants to merge 2 commits into
IWANABETHATGUY wants to merge 2 commits into
Conversation
Member
Author
How to use the Graphite Merge QueueAdd the label graphite: merge-when-ready to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
✅ Deploy Preview for rolldown-rs canceled.
|
This was referenced Jun 15, 2026
IWANABETHATGUY
added a commit
that referenced
this pull request
Jun 29, 2026
… barrels; fix #9806, #9691, #9964 Add `experimental.wrappedModuleTreeshaking` (off by default), a new side-effect tree-shaking implementation for wrapped ESM modules. The legacy impl force-includes a wrapped `sideEffects:false` re-export barrel; the new impl prunes it — the used binding's canonical owner is initialized via the binding-use path, and the finalizer follows canonical owners when a barrel is pruned. A module is wrapped (WrapKind::Esm) by many triggers — strictExecutionOrder, require(esm), code-splitting / CJS interop — so this gate is the legacy-vs-new switch for ALL wrap paths, not just strictExecutionOrder. It fixes the #9961 family of over-retention / dangling-init ReferenceErrors. Safety guards (so flag-on never produces broken output): - namespace-taint: weakly-connected import components containing a star-import edge revert to legacy wrapping (prevents included-but-empty wrapped modules / code-splitting panics; member-resolution-aware pruning is future work). - external-barrel: never prune a barrel that directly imports from an external module (its import would be dropped while still referenced). - off by default => byte-identical legacy output. Also fix #9806, #9691, #9964 (lazyBarrel, default-on) — one root cause: lazyBarrel deferred an import record whose binding is read by a retained statement of a wrapped module (a barrel-own `const Bar = Foo.Bar;` for #9806; a re-exported `sideEffects:false` leaf's top-level read for #9691/#9964), so the finalizer dropped the import declaration while the reading statement survived => ReferenceError. The loader now force-loads any such record (BarrelState::initialize_barrel_tracking), keeping the import and initializing it before the read. This also removes the lazyBarrel crash-layer of #9961. (#9691 supersedes the correctness goal of PR #9709; its dead-star-source pruning is now reachable via the wrappedModuleTreeshaking flag — see the #9691 flag variant snapshot.) Plumbing: ExperimentalOptions + napi binding + TS type/validator/bindingify + config-variant test support + auto-regenerated schema. A shadow-mode diagnostic pass (init_obligations.rs, env-gated by ROLLDOWN_DUMP_INIT_OBLIGATIONS) validates the model without affecting output. Tests: node-executed regression fixtures — #9961 (bug/fix), #9964 (require(esm) wrap) and #9691 (codeSplitting:false wrap; flag variant prunes the dead star source), each verified necessary (crashes on the parent commit with the documented ReferenceError, passes here), exports_chain prune, namespace-taint revert, per-binding activation, side-effect soundness, external-barrel guard, #9806, and a non-strict require(esm) witness. Full integration suite green. Design: internal-docs/linking/strict-execution-order-rewrite-proposal.md
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.

Description
Fixes #9691.
With
codeSplitting: false, a dynamicimport()wraps its target, and the wrap propagates throughexport *barrels into every star source.reference_needed_symbols.rsthen force-marked the barrel'sexport *statements as side-effectful, so asideEffects: falsestar source whose exports are all unused was force-included anyway:lazyBarrel(which drops the imports the dead body needs, since its exports are never requested), the retained body reads an undefined binding at top level — theReferenceError: dep_b is not definedcrash reported with@aws-sdk/client-s3.Fix
Treat
export *from an ESM-wrapped importee like a named re-export: let tree-shaking drop the statement, and rely on the finalizer's existing fallback for excluded re-export statements (generate_transitive_esm_init), which re-creates theinit_*()call at the statement's position only when the star source is actually included. Initialization order is preserved, while a fully-unused, side-effect-free star source disappears from the bundle.The statement is still forced eager when the lazy fallback can't substitute for it:
__reExport(...)namespace copy must run;await.Tests
tests/rolldown/issues/9691mirrors the@aws-sdk/client-s3shape. Without the fix, its node-execution step fails with the originalReferenceError: dep_b is not defined; with the fix, the dead handler module is fully dropped in both thecodeSplitting: falsebuild and thecode-splitting-onvariant, and both execute cleanly.export_forms_common_jsdrops a no-opinit_awrapper, andstrict_execution_order/issue_8777no longer emits duplicateinit_*()calls (the force-includedexport *path used to bypass thegenerated_init_esm_importee_idsdedup).🤖 Generated with Claude Code