Visitar URL original
fix: keep dynamic entry as the facade of its `codeSplitting` group chunk by danielroe · Pull Request #11507 · rolldown/rolldown · GitHub
Skip to content

fix: keep dynamic entry as the facade of its codeSplitting group chunk - #11507

Open
danielroe wants to merge 5 commits into
rolldown:mainfrom
danielroe:fix/group-dynamic-entry-facade
Open

danielroe wants to merge 5 commits into
rolldown:mainfrom
danielroe:fix/group-dynamic-entry-facade

Conversation

@danielroe

Copy link
Copy Markdown
Contributor

this 'stacks' on ##11506, so only the last commit is new.

I noticed this - while trying to group Nuxt layouts - that when a group captures the target of a dynamic import(), the resulting chunk loses its facade:

// main.js
import('./layout.js').then(m => m.default())
// layout.js
import { header } from './header.js'
export default () => console.log(header)
await bundle.generate({ codeSplitting: { groups: [{ name: 'layout', test: /layout\.js$/ }] } })

without the group, the layout chunk has isDynamicEntry: true and facadeModuleId: '/.../layout.js'. with it, it has isDynamicEntry: false, facadeModuleId: null, and moduleIds gains rolldown/runtime.js, because the importer now goes through import('./layout.js').then((n) => n.t) and the chunk builds the namespace with __exportAll.

in Vite this means the chunk is no longer in the manifest under its source file, so things like modulepreload and CSS links for it go missing.

I think this is the facade elimination in find_facade_chunk_merge_ops, which folds the empty dynamic entry chunk into the group chunk and simulates its namespace.

so this PR goes the other way when the group chunk holds no other entry module: the group's modules move into the dynamic entry chunk (keeping the group's name), just like what already happens for a user-defined entry whose module lands in a group. (rollup does the same for manualChunks in the simple case with manual chunk name + dynamic entry as facade).

a few existing fixtures would otherwise have stopped going through facade elimination, so I added a second dynamic entry for each....

let me know if you have any better ideas on how to fix it, and feel free to close and fix differently, or just push to this PR 🙏

@danielroe danielroe changed the title Fix/group dynamic entry facade fix: keep dynamic entry as the facade of its codeSplitting group chunk Nov 5, 2026
@danielroe danielroe changed the title fix: keep dynamic entry as the facade of its codeSplitting group chunk fix: keep dynamic entry as the facade of its codeSplitting group chunk Nov 5, 2026
@danielroe
danielroe force-pushed the fix/group-dynamic-entry-facade branch from 57fd9b0 to fa6206a Compare November 5, 2026 15:13
@codspeed

codspeed Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 4 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 7 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing danielroe:fix/group-dynamic-entry-facade (dac1886) with main (f72d64f)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 (b7d11d1) during the generation of this report, so f72d64f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@hyfdev hyfdev self-assigned this Nov 11, 2026
…time host assertion

`debug_assert_runtime_host_keeps_evaluation_order` re-sorts the imports of each importer of the runtime host with the keys of `assign_chunk_exec_orders`, but it keyed an entry chunk on its entry module. When an entry chunk holds modules that execute before its entry module, the re-sorted list differed from the real one even though the host moved nothing, and debug builds panicked. Share the entry chunk key through `entry_chunk_exec_order`.
…ntry module

A `codeSplitting` group with `includeDependenciesRecursively: false` can take an entry module and leave its dependencies in the entry chunk, which then imports the group chunk. Keyed on those dependencies, the entry chunk sorted before the group chunk in the imports of other chunks, and when the two chunks import each other the group chunk's modules ran before those dependencies. An entry chunk whose entry module is in another chunk keys on its entry module.
…just the entry's exports

When a `codeSplitting` group that captures a dynamic entry moves into the entry chunk, every binding that other chunks import from the group becomes an export of the entry chunk, and `import()` of the entry gives it as an extra key. As Rollup's `canModuleBeFacade` does, fold the group only when every binding that other chunks import from it is an export of the entry module; otherwise keep the facade-elimination path and its simulated namespace. `bindings_imported_from_other_chunks` predicts these bindings from the imports of the modules instead of running the cross-chunk link pass.

The folded chunk also takes over the group's `chunk_reason_type`, so the runtime placement does not make it the runtime host for other consumers, as it does not choose the group chunk. The helpers that those consumers import would otherwise be keys of the namespace.

`issue_5276_2`, `dynamic_import_host_exporting_then` and `dynamic_import_host_exporting_aliased_then` use their original inputs again, and their output is the same as on main. `facade_elimination_with_shared_cjs_dep` uses its original input too: `en_US` folds, and `ar_EG`, whose group holds a CommonJS module that `en_US` also imports, keeps the simulated namespace.
@hyfdev

hyfdev commented Nov 11, 2026

Copy link
Copy Markdown
Member

I pushed a commit on top of this PR, after the two commits from #11506.

The group is folded into the dynamic entry only when other chunks import nothing from it but the entry module's exports, as Rollup's canModuleBeFacade does. Otherwise import() of the entry gets extra keys, for example with a vendor group that also holds a statically imported package.

The folded chunk keeps the group's chunk reason, so the runtime is not moved into it for other chunks.

issue_5276_2, dynamic_import_host_exporting_then, dynamic_import_host_exporting_aliased_then and facade_elimination_with_shared_cjs_dep use their original inputs again.

This needs to land after #11506.

@netlify

netlify Bot commented Nov 11, 2026

Copy link
Copy Markdown

✅ Deploy Preview for rolldown-rs ready!

Name Link
🔨 Latest commit dac1886
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6af456fae4b8c44c24edd7fd
😎 Deploy Preview https://deploy-preview-11507--rolldown-rs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

@hyfdev hyfdev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

This branch has not been deployed

No deployments
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.

2 participants