Skip to content

fix(code-splitting): initialize leaves behind a re-exported namespace import - #10694

Merged
shulaoda merged 3 commits into
mainfrom
issue-10690
Sep 23, 2026
Merged

shulaoda merged 3 commits into
mainfrom
issue-10690

Conversation

@hyfdev

@hyfdev hyfdev commented Aug 18, 2026 •

Copy link
Copy Markdown
Member

Fixes #10690

Notes:

  • Tree shaking statically folds every downstream cssFns.setOpacity read into a direct reference to the leaf vars, so the namespace object doesn't get included.
  • export { setOpacity } from './color.js' in the middle got skipped, so it doesn't get rewrite to init_xxx, which causes the issue
  • Only strictExecutionOrder builds are affected

Input

// css.js
import * as cssFns from './fns.js'; // fns.js: export { setOpacity } from './color.js'
globalThis.cssLoaded = true;
export { cssFns };

Before, the generated init_css never assigns the leaf:

globalThis.cssLoaded = true;
// setOpacity is never assigned

After, the leaf initializer runs first:

init_color();
globalThis.cssLoaded = true;

@netlify

netlify Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit 5399522
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6ab3c2da7b9f5c0008c1cf5a

@hyfdev
hyfdev marked this pull request as ready for review August 18, 2026 03:50
Copilot AI lite review requested due to automatic review settings August 18, 2026 03:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a strict-execution-order tree-shaking regression where excluded plain imports that declare bindings (notably namespace imports) could still have importer-local “binding demand” (re-exported facade / folded member reads), but their leaf initializers were skipped—leading to missing init_*() calls in wrapped shared chunks (issue #10690).

Changes:

  • Extends wrapped ESM init obligation routing to account for binding-demand on excluded plain imports, using used_symbol_refs to decide when routing is necessary.
  • Treats re-exported import bindings (including re-exported namespace bindings) as opaque for init-target routing so downstream consumers can’t be broken by statically folded reads.
  • Adds multiple strictExecutionOrder regression fixtures + snapshots covering pure barrels, effectful leaves, literal export * as ns, and folded-member reads; updates internal code-splitting design notes accordingly.

Reviewed changes

Copilot reviewed 46 out of 46 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal-docs/code-splitting/design.md Documents the expanded notion of init obligations (execution deps + importer-local binding demand).
crates/rolldown/src/stages/generate_stage/order_wrapping.rs Threads used_symbol_refs into wrapped init metadata computation.
crates/rolldown/src/stages/generate_stage/mod.rs Threads used_symbol_refs into wrapped init metadata computation.
crates/rolldown/src/stages/generate_stage/compute_wrapped_esm_init_metadata.rs Adds excluded-plain-import binding-demand routing and per-record target collection for metadata.
crates/rolldown/src/stages/generate_stage/code_splitting.rs Updates record_is_init_obligation caller to pass the importer module reference.
crates/rolldown/src/module_finalizers/mod.rs Updates record_is_init_obligation callers to pass the importer module reference.
crates/rolldown/src/esm_init_obligations.rs Expands init-obligation predicate + makes re-exported bindings opaque for routing.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/_config.json New strictExecutionOrder fixture config (on-demand + wrap-all variant).
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/_test.mjs Executes the built fixture output.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/artifacts.snap Snapshot asserting correct init ordering / leaf availability.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/color.js Leaf module for the pure-barrel namespace re-export fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/css.js Namespace import + re-export surface for the fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/first.js Imports through the re-export surface and asserts behavior.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/fns.js Pure barrel re-export hop.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/main.js Drives the two-chunk scenario via sequential dynamic imports.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/second.js Second consumer to ensure shared wrapper init remains correct.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_pure_barrel/theme.js Uses cssFns.setOpacity via the namespace chain.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/_config.json New strictExecutionOrder fixture config (on-demand + wrap-all variant).
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/_test.mjs Executes the built fixture output.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/artifacts.snap Snapshot asserting effectful leaf init executes exactly once.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/color.js Effectful leaf module to validate init ordering/retention.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/css.js Namespace import + re-export surface for the effectful-leaf fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/first.js Consumer asserting correct folded-read + effect ordering.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/fns.js Barrel re-export hop.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/main.js Drives the two-consumer scenario.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/second.js Second consumer asserting effect isn’t duplicated/dropped.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_namespace_effectful_leaf/theme.js Validates colorLoaded before using the namespace member.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/_config.json New strictExecutionOrder fixture config (on-demand + wrap-all variant).
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/_test.mjs Executes the built fixture output.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/artifacts.snap Snapshot for export * as ns chain + init behavior.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/color.js Leaf module for literal namespace binding fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/css.js Re-exports the literal namespace binding from a barrel hop.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/first.js Consumer asserting behavior through literal namespace binding.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/fns.js Declares export * as colorNs from './color.js'.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/main.js Drives two-consumer execution to validate shared behavior.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/second.js Second consumer asserting behavior remains correct.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/reexported_literal_namespace_binding/theme.js Uses colorNs.setOpacity via the re-export chain.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/_config.json New strictExecutionOrder fixture config (on-demand + wrap-all variant).
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/_test.mjs Executes the built fixture output.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/artifacts.snap Snapshot pinning folded-member behavior with namespace import present.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/color.js Leaf module for folded-member fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/css.js Performs folded member read from a namespace import.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/first.js Consumer asserting folded result.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/fns.js Barrel re-export hop for folded-member fixture.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/main.js Drives the two-consumer scenario.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/dead_namespace_import_folded_member/second.js Second consumer asserting folded result remains stable.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@codspeed

codspeed Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 7 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing issue-10690 (5399522) with main (ec5bab8)2

Open in CodSpeed

Footnotes

  1. 10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (ffb9395) during the generation of this report, so ec5bab8 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@doctor8296

Copy link
Copy Markdown

Waiting for this fix 🙏

@IWANABETHATGUY

Copy link
Copy Markdown
Member

Waiting for this fix 🙏

I will review this pr ASAP.

hyfdev commented Sep 2, 2026 •

Copy link
Copy Markdown
Member Author

Merge activity

  • Sep 2, 2:52 AM UTC: The merge label 'graphite: merge-when-ready' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Sep 2, 2:52 AM UTC: hyfdev added this pull request to the Graphite merge queue.
  • Sep 2, 2:53 AM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI: 'Rust Validation').

… import (#10694)

Fixes #10690

Notes:

- Tree shaking statically folds every downstream `cssFns.setOpacity` read into a direct reference to the leaf vars, so the `namespace object` doesn't get included.
- `export { setOpacity } from './color.js'` in the middle got skipped, so it doesn't get rewrite to `init_xxx`, which causes the issue
-  Only strictExecutionOrder builds are affected

Input

  ```js
  // css.js
  import * as cssFns from './fns.js'; // fns.js: export { setOpacity } from './color.js'
  globalThis.cssLoaded = true;
  export { cssFns };
  ```

  Before, the generated `init_css` never assigns the leaf:

  ```js
  globalThis.cssLoaded = true;
  // setOpacity is never assigned
  ```

  After, the leaf initializer runs first:

  ```js
  init_color();
  globalThis.cssLoaded = true;
  ```
@doctor8296

Copy link
Copy Markdown

Hey @hyfdev, any updates? Looks like this got stuck on CI. We’re still waiting for the fix 🙏

@shulaoda
shulaoda merged commit f57b1f5 into main Sep 23, 2026
36 of 37 checks passed
@shulaoda
shulaoda deleted the issue-10690 branch September 23, 2026 12:21
@rolldown-guard rolldown-guard Bot mentioned this pull request Sep 23, 2026
shulaoda added a commit that referenced this pull request Sep 23, 2026
## [1.2.10] - 2026-09-23

### 🚀 Features

- rolldown_plugin_esm_external_require: prefer 'module.exports' over a namespace spread (#10901) by @isker
- watch: support directories in addWatchFile (#10944) by @shulaoda
- label code-splitting timing rows (#10896) by @IWANABETHATGUY
- separate code-splitting group timing rows (#10892) by @IWANABETHATGUY
- clarify callback owners in the timing report (#10890) by @IWANABETHATGUY
- add checks.bundlerTimings with deprecated pluginTimings alias (#10883) by @IWANABETHATGUY

### 🐛 Bug Fixes

- code-splitting: initialize leaves behind a re-exported namespace import (#10694) by @hyfdev
- node: record the WASI target on the WebContainer binding fallback (#10952) by @shulaoda
- dev: force incremental build on in DevEngine::new (#10958) by @o-alexandrov
- tree-shaking: propagate side effects through cycles (#10893) by @IWANABETHATGUY
- deconflict CJS locals that shadow a renamed require wrapper (#10793) by @logaretm
- include group indexes in labeled `codeSplitting` timing rows (#10916) by @IWANABETHATGUY
- docs: type-check Algolia `indices` option (#10934) by @shulaoda
- docs: restore Algolia search (#10929) by @isker
- tell code-splitting timing rows apart by position (#10903) by @IWANABETHATGUY

### 🚜 Refactor

- rolldown_fs_watcher: remove the single-path API (#10950) by @shulaoda
- dev: register watch files through FsWatcher (#10949) by @shulaoda
- watch: let FsWatcher own the watched paths (#10948) by @shulaoda
- watch: identify watch files by normalized absolute path (#10943) by @shulaoda
- plugin: build a plugin-resolved `ResolvedId` in one place (#10921) by @IWANABETHATGUY

### 📚 Documentation

- dev: add HMR design, align dev docs and engine comments (#10271) by @h-a-n-a

### ⚡ Performance

- batch `sourcemapPathTransform` calls (#10763) by @IWANABETHATGUY
- batch `sourcemapIgnoreList` calls (#10762) by @IWANABETHATGUY
- hmr: drop the unused kind argument from registerFactory (#10915) by @h-a-n-a

### 🧪 Testing

- handle Windows paths in sourcemap callback tests (#10942) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- replace custom `ban-expect-assertions` JS lint with builtin `no-restricted-properties` (#10933) by @isker
- deps: update napi (#10959) by @renovate[bot]
- deps: upgrade oxc to 0.151.0 (#10939) by @camc314
- deps: update test262 submodule for tests (#10941) by @rolldown-guard[bot]
- deps: update npm packages (#10931) by @renovate[bot]
- deps: update napi (#10928) by @renovate[bot]
- deps: update dependency vite-plus to v0.3.3 (#10914) by @renovate[bot]
- deps: update rust crates (#10932) by @renovate[bot]
- deps: update github actions (#10930) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to v0.28.6 (#10924) by @renovate[bot]
- deps: update crate-ci/typos action to v1.50.2 (#10917) by @renovate[bot]
- deps: update dependency @napi-rs/cli to v3.10.4 (#10913) by @renovate[bot]
- deps: update napi (#10897) by @renovate[bot]

### ❤️ New Contributors

* @o-alexandrov made their first contribution in [#10958](#10958)

Co-authored-by: shulaoda <165626830+shulaoda@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Tree shaking drops initializer for a namespace re-export in a wrapped shared chunk

5 participants