Skip to content

fix: correct circular dependency check in facade elimination#9047

Merged
graphite-app[bot] merged 1 commit intomainfrom
04-09-fix_correct_circular_dependency_check_in_facade_elimination
Apr 10, 2026
Merged

fix: correct circular dependency check in facade elimination#9047
graphite-app[bot] merged 1 commit intomainfrom
04-09-fix_correct_circular_dependency_check_in_facade_elimination

Conversation

@h-a-n-a
Copy link
Copy Markdown
Member

@h-a-n-a h-a-n-a commented Apr 9, 2026

Summary

This PR fixes an case of false-positive circular dependency check in facade chunk elimination that caused unnecessary facade chunks. The following is from a minimal reproduction case that is derived from vitejs/vite#21884. You may refer to the test case in this PR for details.

common.js is a shared CJS dependency of both ar_EG.js and en_US.js. Due to
includeDependenciesRecursively, common.js gets pulled into whichever group is processed first
(i18n-ar). This makes i18n-en-US cross-chunk-depend on i18n-ar to access require_common:

i18n-en-US → i18n-ar → runtime

Since i18n-ar depends on the runtime, the old check — simulating merging runtime into i18n-en-US
falsely constructs a cycle:

i18n-en-US → i18n-ar → (runtime = i18n-en-US) ← cycle here

Fix

Replaced would_create_circular_dependency with is_reachable. The old check is to simulate merging runtime into the target (i18n-en-US) instead of just checking if runtime has any reference to the target chunk. This causes a false positive on the case where there're multiple chunks that transitively or directly imports runtime. In this case, it's i18n-en-US that imports i18n-ar that imports runtime, which will be considered as cyclic deps.

Changing to is_reachable fixes this as it only checks if runtime chunk has any imports to the target chunk. This makes more sense because we are not going to merge runtime into any of those chunks.

Test plan

  • New fixture facade_elimination_with_shared_cjs_dep — 4 chunks, no facades
  • Existing snapshot updates from improved facade elimination

Related issues

Reduces facade chunks in Lobehub's case. Check out the report made by claude here: vitejs/vite#21884 (comment)

Copy link
Copy Markdown
Member Author

h-a-n-a commented Apr 9, 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 9, 2026

Deploy Preview for rolldown-rs ready!

Name Link
🔨 Latest commit f4aed8f
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69d889fbca4ab9000874dea5
😎 Deploy Preview https://deploy-preview-9047--rolldown-rs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@h-a-n-a h-a-n-a force-pushed the 04-09-fix_correct_circular_dependency_check_in_facade_elimination branch 2 times, most recently from 871500d to 32579f9 Compare April 9, 2026 08:33
@h-a-n-a h-a-n-a marked this pull request as ready for review April 9, 2026 09:27
@h-a-n-a h-a-n-a requested a review from IWANABETHATGUY April 9, 2026 09:27
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 9, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing 04-09-fix_correct_circular_dependency_check_in_facade_elimination (f4aed8f) with main (d4253e2)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 (f4aed8f) during the generation of this report, so d4253e2 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@h-a-n-a h-a-n-a force-pushed the 04-09-fix_correct_circular_dependency_check_in_facade_elimination branch from 32579f9 to 4aa5a26 Compare April 9, 2026 09:50
@h-a-n-a h-a-n-a force-pushed the 04-09-fix_correct_circular_dependency_check_in_facade_elimination branch 2 times, most recently from 28cb430 to f6a94a0 Compare April 9, 2026 18:14
@graphite-app
Copy link
Copy Markdown
Contributor

graphite-app Bot commented Apr 10, 2026

Merge activity

## Summary

This PR fixes an case of false-positive circular dependency check in facade chunk elimination that caused unnecessary facade chunks. The following is from a minimal reproduction case that is derived from vitejs/vite#21884. You may refer to the test case in this PR for details.

`common.js` is a shared CJS dependency of both `ar_EG.js` and `en_US.js`. Due to
`includeDependenciesRecursively`, `common.js` gets pulled into whichever group is processed first
(`i18n-ar`). This makes `i18n-en-US` cross-chunk-depend on `i18n-ar` to access `require_common`:

i18n-en-US → i18n-ar → runtime

Since `i18n-ar` depends on the runtime, the old check — simulating merging runtime into `i18n-en-US` —
falsely constructs a cycle:

i18n-en-US → i18n-ar → (runtime = i18n-en-US)  ← cycle here

## Fix

Replaced `would_create_circular_dependency` with `is_reachable`. The old check is to simulate merging runtime into the target (i18n-en-US) instead of just checking if runtime has any reference to the target chunk. This causes a false positive on the case where there're multiple chunks that transitively or directly imports runtime. In this case, it's i18n-en-US that imports i18n-ar that imports runtime, which will be considered as cyclic deps.

Changing to `is_reachable` fixes this as it only checks if runtime chunk has any imports to the target chunk. This makes more sense because we are not going to merge runtime into any of those chunks.

## Test plan

- [x] New fixture `facade_elimination_with_shared_cjs_dep` — 4 chunks, no facades
- [x] Existing snapshot updates from improved facade elimination

## Related issues

Reduces facade chunks in Lobehub's case. Check out the report made by claude here: vitejs/vite#21884 (comment)
@graphite-app graphite-app Bot force-pushed the 04-09-fix_correct_circular_dependency_check_in_facade_elimination branch from f6a94a0 to f4aed8f Compare April 10, 2026 05:26
@graphite-app graphite-app Bot merged commit f4aed8f into main Apr 10, 2026
32 checks passed
@graphite-app graphite-app Bot deleted the 04-09-fix_correct_circular_dependency_check_in_facade_elimination branch April 10, 2026 05:30
This was referenced Apr 15, 2026
shulaoda added a commit that referenced this pull request Apr 16, 2026
## [1.0.0-rc.16] - 2026-04-16

### 🚀 Features

- const enum cross-module inlining support (#8796) by @Dunqing
- implement module tagging system for code splitting (#9045) by @hyf0

### 🐛 Bug Fixes

- rolldown_plugin_vite_manifest: handle duplicate chunk names for CSS entries (#9059) by @sapphi-red
- improve error message for invalid return values in function options (#9125) by @shulaoda
- await async export-star init wrappers (#9101) by @thezzisu
- never panic during diagnostic emission (#9091) by @IWANABETHATGUY
- include array rest pattern in binding_identifiers (#9112) by @IWANABETHATGUY
- rolldown: set worker thread count with ROLLDOWN_WORKER_THREADS (#9086) by @fpotter
- rolldown_plugin_lazy_compilation: escape request ID in proxy modules (#9102) by @h-a-n-a
- treat namespace member access as side-effect-free (#9099) by @IWANABETHATGUY
- relax overly conservative side-effect leak check in chunk optimizer (#9085) by @IWANABETHATGUY
- runtime: release `cb` reference after `__commonJS` factory initialization (#9067) by @hyf0-agent
- `@__NO_SIDE_EFFECTS__` wrapper should not remove dynamic imports (#9075) by @IWANABETHATGUY
- rolldown_plugin_vite_import_glob: use POSIX path join/normalize for glob resolution (#9077) by @shulaoda
- emit REQUIRE_TLA error when require() loads a module with top-level await (#9071) by @jaehafe
- emit namespace declaration for empty modules in manual chunks (#8993) by @privatenumber
- rolldown_plugin_vite_import_glob: keep common base on path segment boundary (#9070) by @shulaoda
- prevent circular runtime helper imports during facade elimination (#8989) (#9057) by @IWANABETHATGUY
- correct circular dependency check in facade elimination (#9047) by @h-a-n-a
- docs: correct dead link in CodeSplittingGroup.tags JSDoc (#9051) by @hyf0
- emit DUPLICATE_SHEBANG warning when banner contains shebang (#9026) by @IWANABETHATGUY

### 🚜 Refactor

- use semantic reference flags for member write detection (#9060) by @Dunqing
- extract UsedSymbolRefs newtype wrapper (#9130) by @IWANABETHATGUY
- dedupe await wrapping in export-star init emit (#9119) by @IWANABETHATGUY
- calculate side-effect-free function symbols on demand (#9120) by @IWANABETHATGUY
- extract duplicated top-level await handling into shared helper (#9087) by @IWANABETHATGUY
- rolldown_plugin_vite_import_glob: use split_first for get_common_base (#9069) by @shulaoda
- simplify ESM init deduplication with idiomatic insert check (#9044) by @IWANABETHATGUY

### 📚 Documentation

- document runtime module placement strategy in code-splitting design (#9062) by @IWANABETHATGUY
- clarify `options` hook behavior difference with Rollup in watch mode (#9053) by @sapphi-red
- meta/design: introduce module tags (#9017) by @hyf0

### ⚡ Performance

- convert `generate_transitive_esm_init` to iterative (#9046) by @IWANABETHATGUY

### 🧪 Testing

- merge strict/non_strict test variants using configVariants (#9089) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- disable Renovate auto-updates for oxc packages (#9129) by @IWANABETHATGUY
- upgrade oxc@0.126.0 (#9127) by @Dunqing
- deps: update napi to v3.8.5 (#9126) by @renovate[bot]
- deps: update dependency @napi-rs/cli to v3.6.2 (#9123) by @renovate[bot]
- move lazy-compilation design doc (#9117) by @h-a-n-a
- deps: update dependency vite-plus to v0.1.18 (#9118) by @renovate[bot]
- deps: update dependency vite-plus to v0.1.17 (#9113) by @renovate[bot]
- deps: update oxc to v0.125.0 (#9094) by @renovate[bot]
- deps: update dependency follow-redirects to v1.16.0 [security] (#9103) by @renovate[bot]
- deps: update test262 submodule for tests (#9097) by @sapphi-red
- deps: update crate-ci/typos action to v1.45.1 (#9096) by @renovate[bot]
- deps: update rust crates (#9081) by @renovate[bot]
- deps: update npm packages (#9080) by @renovate[bot]
- remove outdated TODO in determine_module_exports_kind (#9072) by @jaehafe
- rust/test: support `extendedTests: false` shorthand in test config (#9050) by @hyf0
- ci: extract shared infra-changes anchor in path filters (#9054) by @hyf0
- add docs build check to catch dead links in PRs (#9052) by @hyf0

### ❤️ New Contributors

* @thezzisu made their first contribution in [#9101](#9101)
* @fpotter made their first contribution in [#9086](#9086)
* @jaehafe made their first contribution in [#9071](#9071)
* @privatenumber made their first contribution in [#8993](#8993)

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.

3 participants