Skip to content

docs(dev/lazy): update design and implementation#10079

Merged
graphite-app[bot] merged 1 commit into
mainfrom
07-02-docs_dev_lazy_update_design_and_implementation
Jul 2, 2026
Merged

docs(dev/lazy): update design and implementation#10079
graphite-app[bot] merged 1 commit into
mainfrom
07-02-docs_dev_lazy_update_design_and_implementation

Conversation

@h-a-n-a

@h-a-n-a h-a-n-a commented Jul 2, 2026

Copy link
Copy Markdown
Member

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 fix(dev): make init errors in lazy-compiled modules catchable #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, fix(rolldown_plugin_lazy_compilation): use loadExports for fetched proxy to preserve original export names #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 (fix(rolldown_plugin_lazy_compilation): escape request ID in proxy modules #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 (test(dev): reject unknown lazy compile modules #9969), napi surfaces Failed 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).
  • 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, snippetast_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 (fix(dev): serve assets emitted during HMR/lazy compile (vite#22596) #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 (fix(dev): cancel pending full reload on build error #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

h-a-n-a commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

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

netlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit b07f953
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6a4606a04e45db0008d90079

@h-a-n-a
h-a-n-a marked this pull request as ready for review July 2, 2026 06:24
@h-a-n-a
h-a-n-a requested review from hyfdev and shulaoda July 2, 2026 06:24

h-a-n-a commented Jul 2, 2026

Copy link
Copy Markdown
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)
@graphite-app
graphite-app Bot force-pushed the 07-02-docs_dev_lazy_update_design_and_implementation branch from ee6b390 to b07f953 Compare July 2, 2026 06:35
@graphite-app
graphite-app Bot merged commit b07f953 into main Jul 2, 2026
33 checks passed
@graphite-app
graphite-app Bot deleted the 07-02-docs_dev_lazy_update_design_and_implementation branch July 2, 2026 06:37
@rolldown-guard rolldown-guard Bot mentioned this pull request Jul 8, 2026
shulaoda added a commit that referenced this pull request Jul 8, 2026
## [1.1.5] - 2026-07-08

### 🚀 Features

- detect top-level import-binding reads as execution-order sensitive (#10180) by @hyf0
- sourcemap_filenames: add a sourcemapFileNames option (#9271) by @V1OL3TF0X
- binding: record plugin hook result kind in tracing spans (#10154) by @IWANABETHATGUY
- linking: skip side-effect-free modules in per-entry reachability (#10111) by @IWANABETHATGUY
- improve error message for unresolved virtual imports (#10156) by @sapphi-red
- add descriptive metadata to plugin API (#10106) by @sapphi-red
- add `--configLoader=native` option (#10118) by @sapphi-red

### 🐛 Bug Fixes

- improve invalid annotation warnings (#10185) by @hyf0
- keep deduplicated asset filenames stable once they can be observed (#10191) by @shulaoda
- sourcemap_filenames: use public option name in pattern errors (#10188) by @IWANABETHATGUY
- sourcemap_filenames: hash prepared sourcemap content (#10178) by @hyf0
- tree-shake unused circular declarators exported via export list (#10166) by @IWANABETHATGUY
- dev: don't panic when an HMR rebuild hits an unresolved import (#10162) by @shulaoda
- propagate errors from output.globals function (#9880) by @shulaoda
- dev: revert cache mutations when a partial scan fails (#10110) by @shulaoda
- dev: update importer relationships of cached modules in incremental build (#10107) by @shulaoda
- hmr: fall back to full reload when a changed module is not registered as executed (#10132) by @shulaoda
- chunk-optimizer: follow entry facade edges in runtime placement cycle check (#10101) by @hyf0
- dev: ignore watcher events after close (#10113) by @hyf0
- emit async wrapper for TLA modules under onDemandWrapping (#10086) by @IWANABETHATGUY
- gate sideEffects:false modules' side effects on body demand (#10080) by @IWANABETHATGUY
- rolldown_plugin_vite_resolve: return empty object for `browser: false` mapped modules (#10082) by @sapphi-red
- reset the word-boundary state on newline in Hires::Boundary sourcemaps (#10025) by @shulaoda
- trim an emptied chunk's outro/intro instead of skipping past it (#10029) by @shulaoda
- test each edited chunk's own start against indent exclude ranges (#10026) by @shulaoda
- preserve sourcemap mappings for indented lines when a CJS module shares the chunk (#10074) by @hyf0

### 🚜 Refactor

- separate tree-shaking side effects from execution order sensitivity (#10168) by @hyf0
- type construct_vite_preload_call to take an ObjectPattern (#10135) by @shulaoda
- treeshake: single-source the own-export classification shared with the lazy-barrel loader (#10098) by @IWANABETHATGUY
- dev: reuse Vite's bundledDev server (#10081) by @h-a-n-a
- clippy: ban std HashMap/HashSet in favour of FxHashMap/FxHashSet (#10108) by @Boshen
- treeshake: make body demand a second module bit instead of a stmt multimap (#10097) by @IWANABETHATGUY
- seal used_symbol_refs by construction after its last writer (#10091) by @hyf0
- treeshake: replace inclusion mutual recursion with a worklist engine (#10096) by @IWANABETHATGUY
- treeshake: split include_statements.rs into focused modules (#10095) by @IWANABETHATGUY
- drop redundant is_user_defined filter on partitioned entries (#10050) by @shulaoda
- project the retained export interface out of used_symbol_refs (#10089) by @hyf0
- track used external symbols separately from used_symbol_refs (#10088) by @hyf0
- make module namespace inclusion an explicit linking metadata field (#10087) by @hyf0
- rename statement evaluation metadata (#10078) by @hyf0

### 📚 Documentation

- virtual modules user-facing id convention (#10155) by @sapphi-red
- cli: clarify disabling boolean/object flags like codeSplitting (#10153) by @IWANABETHATGUY
- chore: remove Vite+ alpha banner (#10105) by @mdong1909
- write down the used_symbol_refs contract (#10090) by @hyf0
- dev/lazy: update design and implementation (#10079) by @h-a-n-a

### ⚡ Performance

- ast_scanner: stop order-sensitivity checks once a module is flagged (#10190) by @IWANABETHATGUY
- return impl ExactSizeIterator from slice-backed accessors (#10133) by @Boshen
- binding: box dev and watcher napi futures (#10103) by @Boshen

### 🧪 Testing

- move string_wizard replace unit tests to the JS magic-string suite (#10176) by @IWANABETHATGUY
- dev: assert incremental scan state matches a fresh full build after each HMR step (#10115) by @shulaoda
- dev: restore runtime assertions of delete_file_not_used_anymore (#10112) by @shulaoda
- dev: fix flaky dev server tests in CI (#10152) by @h-a-n-a
- add regression test for #10099 (lazyBarrel drops default-import binding but keeps its property reads) (#10109) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- deploy website to Void via GitHub OIDC (#10192) by @Boshen
- deps: update oxc to 0.139.0 (#10161) by @shulaoda
- deps: update test262 submodule for tests (#10160) by @rolldown-guard[bot]
- rolldown_plugin_utils: remove dead asset-url and css scaffolding (#10131) by @shulaoda
- deps: revert vite-plus to v0.2.1 (#10148) by @shulaoda
- deps: update github actions (#10141) by @renovate[bot]
- deps: update dependency rust to v1.96.1 (#10145) by @renovate[bot]
- deps: update npm packages (#10142) by @renovate[bot]
- deps: update rust crates (#10143) by @renovate[bot]
- deps: update napi to v3.10.3 (#10121) by @renovate[bot]
- rolldown_utils: remove unused time module (#10138) by @shulaoda
- remove dead CopyModulePlugin::is_active method (#10129) by @shulaoda
- remove dead LazyCompilationContext::is_lazy_module method (#10128) by @shulaoda
- remove dead BuildDiagnostic::downcast_ref method (#10127) by @shulaoda
- deps: update dependency vite-plus to v0.2.2 (#10084) by @renovate[bot]
- deps: update rust crate oxc_sourcemap to v8.1.0 (#10122) by @renovate[bot]
- deps: update crate-ci/typos action to v1.48.0 (#10124) by @renovate[bot]
- enable more clippy restriction lints (#10114) by @Boshen
- deps: update rust dependencies (#10100) by @Boshen
- deps: update oxc resolver to v11.23.0 (#10083) by @renovate[bot]

### ◀️ Revert

- Revert "chore(deps): revert vite-plus to v0.2.1" (#10157) by @h-a-n-a
- "fix(hmr): fall back to full reload when a changed module is not registered as executed (#10132)" (#10151) by @shulaoda

### ❤️ New Contributors

* @V1OL3TF0X made their first contribution in [#9271](#9271)

Co-authored-by: shulaoda <[email protected]>
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