Skip to content

fix: prevent chunk merging from leaking entry side effects#8979

Merged
graphite-app[bot] merged 1 commit intomainfrom
03-23-fix_8849
Apr 1, 2026
Merged

fix: prevent chunk merging from leaking entry side effects#8979
graphite-app[bot] merged 1 commit intomainfrom
03-23-fix_8849

Conversation

@IWANABETHATGUY
Copy link
Copy Markdown
Member

@IWANABETHATGUY IWANABETHATGUY commented Apr 1, 2026

Summary

  • Use transitive reachability (BFS) instead of direct dependency checks when deciding whether to merge a common chunk into a side-effectful entry chunk
  • Previously, shared modules were incorrectly merged into entry chunks when dynamic imports depended on them transitively, causing dynamic chunk loading to trigger unrelated entry side effects
  • Fixes [Bug]: Dynamic common chunk imports entry module #8849

Test plan

  • Added issues/8849 test with runtime assertion that loading main.js does not import from admin.js
  • Added transitive_dynamic_dep_should_not_merge_into_side_effectful_entry test that spawns a separate node process to verify dist/main.js only outputs dynamic1:shared-b (no entry-a leak)
  • Updated inline snapshot for sanitize-filename/function JS test
  • All 18 snapshot updates verified: shared modules correctly extracted into separate chunks instead of being merged into side-effectful entries

🤖 Generated with Claude Code

Copy link
Copy Markdown
Member Author

IWANABETHATGUY commented Apr 1, 2026


How to use the Graphite Merge Queue

Add 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.

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 1, 2026

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit acf2087
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69ccf036c5b06a00075e583d

@IWANABETHATGUY IWANABETHATGUY changed the title fix: 8849 fix: prevent chunk merging from leaking entry side effects Apr 1, 2026
@IWANABETHATGUY IWANABETHATGUY marked this pull request as ready for review April 1, 2026 09:54
@IWANABETHATGUY IWANABETHATGUY requested review from hyf0 and shulaoda April 1, 2026 09:54
@shulaoda shulaoda requested a review from Copilot April 1, 2026 09:58
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 1, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing 03-23-fix_8849 (acf2087) with main (76ce0cf)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 (acf2087) during the generation of this report, so 76ce0cf was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an optimizer bug where merging shared modules into a side-effectful entry could cause dynamically imported chunks to import (and thus execute) an unrelated entry chunk, leaking entry side effects during dynamic chunk loading (fixes #8849).

Changes:

  • Update chunk-merge eligibility to use transitive reachability (BFS) when checking whether merging would make dynamic chunks depend on a side-effectful user entry.
  • Add regression coverage for #8849 and a dedicated transitive dynamic-dependency chunk-merging scenario.
  • Update multiple existing integration snapshots to reflect corrected chunk extraction / helper chunking behavior.

Reviewed changes

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

Show a summary per file
File Description
packages/rolldown/tests/fixtures/output/sanitize-filename/function/_config.ts Updates expected output file list due to chunking changes.
crates/rolldown/tests/rolldown/topics/live_bindings/named_exports_in_common_chunks/artifacts.snap Snapshot updated: shared exports extracted into a separate shared chunk.
crates/rolldown/tests/rolldown/topics/live_bindings/named_exports_in_common_chunks_cjs/artifacts.snap Snapshot updated: shared CJS exports routed via shared chunk with live-binding getters.
crates/rolldown/tests/rolldown/topics/hmr/issue_5159/artifacts.snap Snapshot updated: runtime helper moved into a dedicated chunk.
crates/rolldown/tests/rolldown/topics/hmr/dynamic_import/artifacts.snap Snapshot updated: shared runtime helpers extracted to chunk.js.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/main.js New fixture entry that dynamically imports dynamic1.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/entry-a.js New side-effectful entry fixture used to detect leakage.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/dynamic2.js New dynamic fixture depending on shared chain.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/dynamic1.js New dynamic fixture asserting expected stdout.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/common-b.js New shared leaf module fixture.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/common-a.js New shared module that imports common-b.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/artifacts.snap New snapshot capturing expected chunk boundaries (no entry-a leak).
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/_test.mjs New runtime assertion via spawning node on dist/main.js output.
crates/rolldown/tests/rolldown/optimization/chunk_merging/transitive_dynamic_dep_should_not_merge_into_side_effectful_entry/_config.json New test config with two inputs (main + entry-a).
crates/rolldown/tests/rolldown/issues/8849/value.js New regression fixture module.
crates/rolldown/tests/rolldown/issues/8849/main.js New regression entry (main).
crates/rolldown/tests/rolldown/issues/8849/common.js New regression dynamically imported module.
crates/rolldown/tests/rolldown/issues/8849/artifacts.snap New snapshot showing common.js no longer imports admin.js.
crates/rolldown/tests/rolldown/issues/8849/admin.js New regression entry (admin).
crates/rolldown/tests/rolldown/issues/8849/_test.mjs New assertion ensuring dist/common.js does not import from admin.js.
crates/rolldown/tests/rolldown/issues/8849/_config.json New test config enabling output stats snapshot for the regression.
crates/rolldown/tests/rolldown/issues/8053/artifacts.snap Snapshot updated to reflect helper extraction into utils chunk.
crates/rolldown/tests/rolldown/issues/7655/artifacts.snap Snapshot updated: runtime helpers moved to a dedicated chunk.
crates/rolldown/tests/rolldown/issues/6756/artifacts.snap Snapshot updated: shared code extracted to shared chunk.
crates/rolldown/tests/rolldown/issues/6660/artifacts.snap Snapshot updated: helper exports moved to chunk.js.
crates/rolldown/tests/rolldown/issues/6513/artifacts.snap Snapshot updated: helper functions provided via chunk.js require.
crates/rolldown/tests/rolldown/issues/4895/artifacts.snap Snapshot updated: shared constants extracted into separate libs.
crates/rolldown/tests/rolldown/issues/4289/artifacts.snap Snapshot updated: commonjs/toESM helpers extracted to chunk.js.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/issue_4684/artifacts.snap Snapshot updated: strict execution order output split into read.js + main.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/esbuild_issue_2598/strict/artifacts.snap Snapshot updated: user-lib extracted; main imports it.
crates/rolldown/tests/rolldown/function/experimental/strict_execution_order/esbuild_issue_2598/non_strict/artifacts.snap Snapshot updated: user-lib extracted; main imports it.
crates/rolldown/tests/rolldown/cjs_compat/dynamic_import_in_cjs/artifacts.snap Snapshot updated: cjs helper chunk introduced.
crates/rolldown/tests/esbuild/splitting/splitting_dynamic_common_js_into_es6/artifacts.snap Snapshot updated: entry and dynamic code now share a helper chunk.
crates/rolldown/src/stages/generate_stage/chunk_optimizer.rs Core fix: add transitive chunk reachability check to prevent side-effect leaks when merging common chunks.

@graphite-app
Copy link
Copy Markdown
Contributor

graphite-app bot commented Apr 1, 2026

Merge activity

## Summary
- Use transitive reachability (BFS) instead of direct dependency checks when deciding whether to merge a common chunk into a side-effectful entry chunk
- Previously, shared modules were incorrectly merged into entry chunks when dynamic imports depended on them transitively, causing dynamic chunk loading to trigger unrelated entry side effects
- Fixes #8849

## Test plan
- Added `issues/8849` test with runtime assertion that loading `main.js` does not import from `admin.js`
- Added `transitive_dynamic_dep_should_not_merge_into_side_effectful_entry` test that spawns a separate node process to verify `dist/main.js` only outputs `dynamic1:shared-b` (no `entry-a` leak)
- Updated inline snapshot for `sanitize-filename/function` JS test
- All 18 snapshot updates verified: shared modules correctly extracted into separate chunks instead of being merged into side-effectful entries

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app graphite-app bot merged commit acf2087 into main Apr 1, 2026
31 checks passed
@graphite-app graphite-app bot deleted the 03-23-fix_8849 branch April 1, 2026 10:19
@github-actions github-actions bot mentioned this pull request Apr 1, 2026
shulaoda added a commit that referenced this pull request Apr 1, 2026
## [1.0.0-rc.13] - 2026-04-01

### 🚀 Features

- add friendly error for unloadable virtual modules (#8955) by @sapphi-red
- better error message for unsupported CSS error (#8911) by @sapphi-red

### 🐛 Bug Fixes

- prevent chunk merging from leaking entry side effects (#8979) by @IWANABETHATGUY
- correct inlining based on module's def format and esModule flag (#8975) by @h-a-n-a
- generate init calls for excluded re-exports in strict execution order (#8858) by @IWANABETHATGUY
- consistent order for `meta.chunks` in `renderChunk` hook (#8956) by @sapphi-red
- subpath imports in glob imports failing to find files (#8885) by @kalvenschraut
- browser: bundle binding types in dts output (#8930) by @nyan-left
- ci: guard artifact download step in `vite-test-ubuntu` when build is skipped (#8934) by @Copilot
- track CJS re-export import records to fix inline const and tree-shaking (#8925) by @h-a-n-a
- use ImportKind::Import for common-chunk root computation (#8899) by @IWANABETHATGUY
- watch: clear emitted_filenames between rebuilds (#8914) by @IWANABETHATGUY
- ci: cache esbuild snapshots to avoid 429 rate limiting (#8921) by @IWANABETHATGUY
- always check circular deps in chunk optimizer (#8915) by @IWANABETHATGUY
- don't mark calls to reassigned bindings as pure (#8917) by @IWANABETHATGUY
- magic-string: throw TypeError for non-string content args (#8905) by @IWANABETHATGUY
- magic-string: add split-point validation and overwrite/update options (#8904) by @IWANABETHATGUY

### 🚜 Refactor

- pre-compute has_side_effects on ChunkCandidate (#8981) by @IWANABETHATGUY
- cleanup and simplify in dynamic_import.rs (#8927) by @ulrichstark
- rename came_from_cjs to came_from_commonjs for consistency (#8938) by @IWANABETHATGUY
- inline `create_ecma_view` return destructuring and remove redundant binding (#8932) by @shulaoda

### 📚 Documentation

- document ensure_lazy_module_initialization_order in code-splitting design doc (#8931) by @IWANABETHATGUY

### 🧪 Testing

- add regression test for runtime helper circular dependency (#8958) by @h-a-n-a
- enable 8 previously-skipped MagicString remove tests (#8945) by @IWANABETHATGUY
- add test for why PureAnnotation is needed in execution order check (#8933) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- add `@emnapi/runtime` and `@emnapi/core` as direct deps of `@rolldown/browser` (#8978) by @Copilot
- deps: update dependency vite-plus to v0.1.15 (#8970) by @renovate[bot]
- deps: update dependency oxfmt to ^0.43.0 (#8969) by @renovate[bot]
- deps: upgrade oxc to 0.123.0 (#8967) by @shulaoda
- justfile: deduplicate update-submodule as alias of setup-submodule (#8968) by @shulaoda
- deps: update rollup submodule for tests to v4.60.1 (#8965) by @sapphi-red
- deps: update test262 submodule for tests (#8966) by @sapphi-red
- remove unused `type-check` scripts (#8957) by @sapphi-red
- deps: update actions/cache action to v5 (#8953) by @renovate[bot]
- deps: update npm packages to v6 (major) (#8954) by @renovate[bot]
- deps: update npm packages (#8948) by @renovate[bot]
- deps: update rust crates (#8949) by @renovate[bot]
- deps: update github-actions (#8947) by @renovate[bot]
- deps: update napi (#8943) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to ^0.23.0 (#8944) by @renovate[bot]
- regenerate testing snapshots (#8928) by @ulrichstark
- deps: update dependency rust to v1.94.1 (#8923) by @renovate[bot]

### ❤️ New Contributors

* @kalvenschraut made their first contribution in [#8885](#8885)
* @nyan-left made their first contribution in [#8930](#8930)

Co-authored-by: shulaoda <[email protected]>
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]: Dynamic common chunk imports entry module

3 participants