fix(code-splitting): align dynamicImports metadata with the rewritten import() specifier#10430
Merged
Merged
Conversation
✅ Deploy Preview for rolldown-rs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Member
Author
Merge activity
|
Merging this PR will not alter performance
Comparing Footnotes
|
IWANABETHATGUY
approved these changes
Jul 24, 2026
… import() specifier (#10430) The emitted code and the chunk metadata could disagree about which file a dynamic import loads. The code was always right; `chunk.dynamicImports` was wrong. This PR makes the metadata name the same file the emitted `import()` names. ```js // a.js — emitted code (correct) import("./chunks/target.js") // a.js — metadata before this PR (wrong) dynamicImports: ["chunks/dyn.js"] ``` ### Problem A dynamically imported module becomes a dynamic entry. Normally its body lives in its own entry chunk, so the file you import is the file that holds the code — no ambiguity. The two diverge when only a facade file remains at the entry (importing the body chunk and triggering init) while the body is hosted elsewhere: - order-wrap entry facades under `strictExecutionOrder`; - with strict order off: facade elimination skipped (top-level await anywhere in the graph, or `chunkOptimization.mergeCommonChunks: false`) while a manual group — or a chunk shared by static+dynamic importers — hosts the body. The root cause is two different lookup tables: the finalizer rewrites the `import()` specifier through `entry_module_to_entry_chunk` (→ the facade), while `collect_depended_symbols` recorded the metadata edge from `module_to_chunk[importee]` (→ the body chunk). Runtime behavior is unaffected; only metadata consumers get the wrong edge: `build.manifest` (backend preload tags point at the body chunk and miss the facade — an extra request waterfall), the devtools chunk graph, and the bundle analyzer. Vite's `__vitePreload` and SSR manifest re-parse the emitted code, so runtime preloading was never affected. ### Fix Record the edge through the same mapping the finalizer uses: `entry_module_to_entry_chunk` first, `module_to_chunk` as fallback. When the mapping is present, every rewrite path derives the emitted path from it; when it is absent the finalizer emits `void 0`, and the fallback keeps today's metadata byte-for-byte. `cross_chunk_dynamic_imports` feeds only metadata surfaces (RollupRenderedChunk, `OutputChunk.dynamicImports`, devtools trace), so emitted code cannot change. Two pre-existing wrinkles are deliberately left alone (a mapping-absent `void 0` import and a same-chunk merged entry can still record an edge with no emitted request): both record identically before and after this change; tracked in #10294. ### Validation - Two vitest fixtures (`output/dynamic-imports-metadata/{strict-facade,kept-facade-tla}`) pin both shapes from the public JS API surface: red on an unfixed build (`['chunks/dyn.js']` / `['chunks/grp.js']` recorded while the code names the facade), green with the fix. The dual static+dynamic combo was verified with a one-off probe on both builds. - Full Rust fixture sweep (`--include-ignored`): failure list identical to unmodified main on the same machine (the pre-existing local environment set), zero snapshot changes. clippy and fmt clean. Part of #10294.
graphite-app
Bot
force-pushed
the
fix/dynamic-imports-metadata-entry-chunk
branch
from
July 24, 2026 16:19
f94e537 to
7e13de4
Compare
19 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The emitted code and the chunk metadata could disagree about which file a dynamic import loads. The code was always right;
chunk.dynamicImportswas wrong. This PR makes the metadata name the same file the emittedimport()names.Problem
A dynamically imported module becomes a dynamic entry. Normally its body lives in its own entry chunk, so the file you import is the file that holds the code — no ambiguity. The two diverge when only a facade file remains at the entry (importing the body chunk and triggering init) while the body is hosted elsewhere:
strictExecutionOrder;chunkOptimization.mergeCommonChunks: false) while a manual group — or a chunk shared by static+dynamic importers — hosts the body.The root cause is two different lookup tables: the finalizer rewrites the
import()specifier throughentry_module_to_entry_chunk(→ the facade), whilecollect_depended_symbolsrecorded the metadata edge frommodule_to_chunk[importee](→ the body chunk).Runtime behavior is unaffected; only metadata consumers get the wrong edge:
build.manifest(backend preload tags point at the body chunk and miss the facade — an extra request waterfall), the devtools chunk graph, and the bundle analyzer. Vite's__vitePreloadand SSR manifest re-parse the emitted code, so runtime preloading was never affected.Fix
Record the edge through the same mapping the finalizer uses:
entry_module_to_entry_chunkfirst,module_to_chunkas fallback. When the mapping is present, every rewrite path derives the emitted path from it; when it is absent the finalizer emitsvoid 0, and the fallback keeps today's metadata byte-for-byte.cross_chunk_dynamic_importsfeeds only metadata surfaces (RollupRenderedChunk,OutputChunk.dynamicImports, devtools trace), so emitted code cannot change.Two pre-existing wrinkles are deliberately left alone (a mapping-absent
void 0import and a same-chunk merged entry can still record an edge with no emitted request): both record identically before and after this change; tracked in #10294.Validation
output/dynamic-imports-metadata/{strict-facade,kept-facade-tla}) pin both shapes from the public JS API surface: red on an unfixed build (['chunks/dyn.js']/['chunks/grp.js']recorded while the code names the facade), green with the fix. The dual static+dynamic combo was verified with a one-off probe on both builds.--include-ignored): failure list identical to unmodified main on the same machine (the pre-existing local environment set), zero snapshot changes. clippy and fmt clean.Part of #10294.