fix(mcs): entriesAware should calculate sizes without duplication#8887
Conversation
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. |
abe4f5b to
efad975
Compare
5ea0f64 to
071d65a
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes manualCodeSplitting.groups[].entriesAware size calculation so shared dependencies aren’t double-counted across per-entry subgroups, which previously caused incorrect entriesAwareMergeThreshold decisions.
Changes:
- Refactors manual code splitting to collect a flat
entriesAwaregroup first, then split into bitset-based subgroups and merge small subgroups by threshold. - Adds an integration test fixture + snapshot ensuring shared deps don’t inflate subgroup sizes and incorrectly prevent merging.
- Adds a design doc explaining
entriesAware, why “flat-then-split” matters, and how subgroup merging works.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
crates/rolldown/src/stages/generate_stage/manual_code_splitting.rs |
Implements flat collection for entriesAware, post-split subgroup sizing, and updated merge flow. |
crates/rolldown/tests/rolldown/function/advanced_chunks/entries_aware_merge_shared_deps/_config.json |
New regression test configuration for the shared-deps + merge-threshold scenario. |
crates/rolldown/tests/rolldown/function/advanced_chunks/entries_aware_merge_shared_deps/_test.mjs |
Asserts no per-entry vendor chunks are produced when subgroups should merge. |
crates/rolldown/tests/rolldown/function/advanced_chunks/entries_aware_merge_shared_deps/artifacts.snap |
Snapshot validating the expected chunk layout/output. |
crates/rolldown/tests/rolldown/function/advanced_chunks/entries_aware_merge_shared_deps/{entry-a.js,entry-b.js,lib-a.js,lib-b.js,shared-dep.js} |
Test fixture modules used to reproduce the duplication/inflated-size bug. |
meta/design/manual-code-splitting.md |
New documentation describing entriesAware mechanics and merge behavior. |
crates/rolldown/src/stages/generate_stage/manual_code_splitting.rs
Outdated
Show resolved
Hide resolved
Merging this PR will not alter performance
Comparing Footnotes
|
071d65a to
60ca9c0
Compare
✅ Deploy Preview for rolldown-rs canceled.
|
Merge activity
|
…8887) ## Summary - **Fix**: `entriesAware` subgroups no longer have duplicated shared dependencies, which inflated sizes and broke `entriesAwareMergeThreshold` comparisons - **Refactor**: `module_groups` uses `IndexVec` for regular groups; entries-aware groups stay in a `Vec` for flexible post-processing (split by bitset, merge, then push into IndexVec) - **New test**: `entries_aware_merge_shared_deps` — fails on `main` (shared deps duplicated → inflated sizes → no merge), passes after fix - **Design doc**: `meta/design/manual-code-splitting.md` — visual explanation of `entriesAware` flow ## The bug On `main`, `ModuleGroupId` includes each module's own bitset, creating separate subgroups during build. With `includeDependenciesRecursively`, shared dependencies get added to **each** subgroup independently: ``` lib-a.js (bits={A}) → subgroup {A}, deps add shared-dep.js lib-b.js (bits={B}) → subgroup {B}, deps add shared-dep.js ← DUPLICATE subgroup {A} = [lib-a, shared-dep] size: 150 (inflated) subgroup {B} = [lib-b, shared-dep] size: 150 (inflated) ``` Inflated sizes cause `entriesAwareMergeThreshold` to skip merges that should happen. ## The fix Build entries-aware groups as a **flat group first**, then split by bitset as post-processing. Each module appears in exactly one subgroup based on its own bits: ``` flat group = [lib-a, lib-b, shared-dep] split by bits: {A} → [lib-a] size: 30 ← correct {B} → [lib-b] size: 30 ← correct {A, B} → [shared-dep] size: 100 ← counted once ``` ## Test plan - [x] `cargo test -p rolldown -- entries_aware` (5 tests pass) - [x] `cargo test -p rolldown -- advanced_chunks` (25 tests pass) - [x] New test `entries_aware_merge_shared_deps` fails on `main`, passes on this branch 🤖 Generated with [Claude Code](https://claude.com/claude-code)
54e2cba to
7833704
Compare
## [1.0.0-rc.12] - 2026-03-25 ### 🚀 Features - chunk-optimizer: skip circular dependency check when strict execution order is enabled (#8886) by @hyf0 ### 🐛 Bug Fixes - emit build warnings during watch mode rebuilds (#8897) by @IWANABETHATGUY - lazy-barrel: load import-then-export specifiers when barrel has local exports (#8895) by @shulaoda - correct execution order of transferred CJS init calls (#8877) by @IWANABETHATGUY - mcs: `entriesAware` should calculate sizes without duplication (#8887) by @hyf0 - non-deterministic chunk generation (#8882) by @sapphi-red - `is_top_level` incorrectly treats strict-mode scopes as top-level (#8878) by @Dunqing ### 🚜 Refactor - treeshake: migrate SideEffectDetector to Oxc's MayHaveSideEffects trait (#8624) by @Dunqing ### 🧪 Testing - make dev server tests deterministic by replacing fixed sleeps with event-driven polling (#8561) by @Boshen ### ⚙️ Miscellaneous Tasks - deps: update dependency vite-plus to v0.1.14 (#8902) by @camc314 - deps: update dependency oxfmt to ^0.42.0 (#8891) by @renovate[bot] - deps: update rust crate oxc_sourcemap to v6.1.1 (#8890) by @renovate[bot] - remove Rolldown MF plan (#8883) by @shulaoda - deps: update rollup submodule for tests to v4.60.0 (#8881) by @sapphi-red - deps: update test262 submodule for tests (#8880) by @sapphi-red - deps: upgrade oxc crates to 0.122.0 (#8879) by @shulaoda Co-authored-by: shulaoda <[email protected]>

Summary
entriesAwaresubgroups no longer have duplicated shared dependencies, which inflated sizes and brokeentriesAwareMergeThresholdcomparisonsmodule_groupsusesIndexVecfor regular groups; entries-aware groups stay in aVecfor flexible post-processing (split by bitset, merge, then push into IndexVec)entries_aware_merge_shared_deps— fails onmain(shared deps duplicated → inflated sizes → no merge), passes after fixmeta/design/manual-code-splitting.md— visual explanation ofentriesAwareflowThe bug
On
main,ModuleGroupIdincludes each module's own bitset, creating separate subgroups during build. WithincludeDependenciesRecursively, shared dependencies get added to each subgroup independently:Inflated sizes cause
entriesAwareMergeThresholdto skip merges that should happen.The fix
Build entries-aware groups as a flat group first, then split by bitset as post-processing. Each module appears in exactly one subgroup based on its own bits:
Test plan
cargo test -p rolldown -- entries_aware(5 tests pass)cargo test -p rolldown -- advanced_chunks(25 tests pass)entries_aware_merge_shared_depsfails onmain, passes on this branch🤖 Generated with Claude Code