docs(dev/lazy): update design and implementation#10079
Merged
graphite-app[bot] merged 1 commit intoJul 2, 2026
Merged
Conversation
Member
Author
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. |
✅ Deploy Preview for rolldown-rs canceled.
|
shulaoda
approved these changes
Jul 2, 2026
hyf0
approved these changes
Jul 2, 2026
Member
Author
Merge activity
|
### Description
Rewrites `internal-docs/lazy-compilation/{design,implementation}.md` to match the current implementation. The docs dated back to the initial POC (split out in #9826) and had drifted from the code in several load-bearing places. Every claim was re-verified against current `main` (file/line spot-checks, cross-checked with the playground specs).
#### Corrections to stale / wrong content
- **Proxy templates**: both inline code blocks are now the real `proxy-module-template{,-fetched}.js` verbatim. The stub evicts its runtime cache entry and ends with `return await loadExports($STABLE_PROXY_MODULE_ID)['rolldown:exports']` — the two-level promise chain from #9981 that makes init errors catchable at the consumer's `await import()`. The fetched template returns `loadExports($STABLE_MODULE_ID)` instead of the import namespace (export-name preservation in shared chunks, #9132).
- **Module IDs**: design.md's TL;DR claimed absolute paths are used "consistently throughout the runtime" — runtime lookups actually use stable (cwd-relative) ids; absolute paths survive only in the `/@vite/lazy?id=` param and the fetched template's `import($MODULE_ID)`. Also documents the four template placeholders and their JSON-quoted rendering (#9102).
- **Error handling**: replaced "`Err` or panic is fine for POC" with the actual contract — unknown ids are rejected as a cache-key-only security gate (#9969), napi surfaces `Failed to compile lazy entry:` → HTTP 500, and init errors are catchable on both cold and warm paths (#9975/#9981).
- **Dedup**: the race-condition section's "potential future enhancement" runtime guard is long implemented — lazy chunks render `createEsm/CjsInitializer(stableId, factory, 1)` with a dedup flag (HMR patches deliberately omit it so updates re-execute). Rewrote the section around the two-layer dedup: per-client `executed_modules` pruning + the runtime flag.
- **Rebuild id normalization**: the old note said the `ModuleChanged` rebuild "should resolve or normalize" the proxy id to a real module id — the raw proxy id is deliberately correct (it is the incremental-cache invalidation key for the proxy whose content changed; normalizing would leave the stale stub cached). Note removed and the actual mechanism documented.
- Misc: `lazyMagic` helper (never existed), `__export` → `__exportAll`, `snippet` → `ast_factory` in the Lessons Learned snippets, `registerModule(stableId, { exports })` shape.
#### New coverage
- **Enabling / wiring**: `experimental.devMode.lazy: true`, inner-plugin registration, the shared `LazyCompilationContext` handed to the `DevEngine`
- **Lazy chunk rendering**: initializer wrappers + `__rolldown_module_id__` param, the nested-lazy `import()` → `/@vite/lazy` rewrite, entry init call, `lazy_compile_{n}.js` naming
- **Emitted assets**: delivered via `onAdditionalAssets` before the code returns (#9815)
- **Build output refresh**: `update_watch_paths()` runs first (what makes post-fetch edits watchable at all), silent output swap for connected clients, failure paths (#9903)
- **Editing a fetched lazy module**: per-client HMR outcomes; a non-accepting proxy escalates to FullReload → HmrRebuild with a deferred reload
- **Client sessions**: implicit creation on `hmr:module-registered`, removal on ws disconnect, the `"rolldown-tests"` escape hatch; clientId prunes the patch — nothing is "routed"
- **Known limitations**: link-stage-synthesized exports (JSON/text/base64/dataurl register `{}` inside lazy chunks until rebuild + refresh), CSS error deferral, assets require load-hook plugins, sourcemaps (inline-only for lazy chunks — the map asset is discarded on the lazy path, unlike `HmrPatch`)
- **Test coverage table**: the 7 playground specs + `dev-lazy-compile.test.ts` and what each pins
Docs only — no runtime change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
ee6b390 to
b07f953
Compare
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.

Description
Rewrites
internal-docs/lazy-compilation/{design,implementation}.mdto match the current implementation. The docs dated back to the initial POC (split out in #9826) and had drifted from the code in several load-bearing places. Every claim was re-verified against currentmain(file/line spot-checks, cross-checked with the playground specs).Corrections to stale / wrong content
proxy-module-template{,-fetched}.jsverbatim. The stub evicts its runtime cache entry and ends withreturn await loadExports($STABLE_PROXY_MODULE_ID)['rolldown:exports']— the two-level promise chain from fix(dev): make init errors in lazy-compiled modules catchable #9981 that makes init errors catchable at the consumer'sawait import(). The fetched template returnsloadExports($STABLE_MODULE_ID)instead of the import namespace (export-name preservation in shared chunks, fix(rolldown_plugin_lazy_compilation): use loadExports for fetched proxy to preserve original export names #9132)./@vite/lazy?id=param and the fetched template'simport($MODULE_ID). Also documents the four template placeholders and their JSON-quoted rendering (fix(rolldown_plugin_lazy_compilation): escape request ID in proxy modules #9102).Error panic is fine for POC" with the actual contract — unknown ids are rejected as a cache-key-only security gate (test(dev): reject unknown lazy compile modules #9969), napi surfacesFailed to compile lazy entry:→ HTTP 500, and init errors are catchable on both cold and warm paths (test(dev): error in lazy module should be catchable #9975/fix(dev): make init errors in lazy-compiled modules catchable #9981).createEsm/CjsInitializer(stableId, factory, 1)with a dedup flag (HMR patches deliberately omit it so updates re-execute). Rewrote the section around the two-layer dedup: per-clientexecuted_modulespruning + the runtime flag.ModuleChangedrebuild "should resolve or normalize" the proxy id to a real module id — the raw proxy id is deliberately correct (it is the incremental-cache invalidation key for the proxy whose content changed; normalizing would leave the stale stub cached). Note removed and the actual mechanism documented.lazyMagichelper (never existed),__export→__exportAll,snippet→ast_factoryin the Lessons Learned snippets,registerModule(stableId, { exports })shape.New coverage
experimental.devMode.lazy: true, inner-plugin registration, the sharedLazyCompilationContexthanded to theDevEngine__rolldown_module_id__param, the nested-lazyimport()→/@vite/lazyrewrite, entry init call,lazy_compile_{n}.jsnamingonAdditionalAssetsbefore the code returns (fix(dev): serve assets emitted during HMR/lazy compile (vite#22596) #9815)update_watch_paths()runs first (what makes post-fetch edits watchable at all), silent output swap for connected clients, failure paths (fix(dev): cancel pending full reload on build error #9903)hmr:module-registered, removal on ws disconnect, the"rolldown-tests"escape hatch; clientId prunes the patch — nothing is "routed"{}inside lazy chunks until rebuild + refresh), CSS error deferral, assets require load-hook plugins, sourcemaps (inline-only for lazy chunks — the map asset is discarded on the lazy path, unlikeHmrPatch)dev-lazy-compile.test.tsand what each pinsDocs only — no runtime change.
🤖 Generated with Claude Code