Skip to content

fix(dev/lazy): avoid module reinitialization in lazy compilation patches#9179

Merged
graphite-app[bot] merged 1 commit intomainfrom
04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches
Apr 27, 2026
Merged

fix(dev/lazy): avoid module reinitialization in lazy compilation patches#9179
graphite-app[bot] merged 1 commit intomainfrom
04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches

Conversation

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

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

Summary

When a module appears in two lazy-compilation chunks served concurrently
(e.g. two compileLazyEntry requests racing for shared.js), the browser
runs the module body twice. Non-idempotent module code blows up — concretely,
Object.defineProperty(this, "key", { configurable: false }) on the second
run throws Cannot redefine property.

This PR makes module initializers opt into runtime-side deduping:

  • createEsmInitializer / createCjsInitializer take a new dedup flag.
    When truthy and the stable id is already in runtime.modules, the factory
    is skipped (or, for CJS, the registered exports are reused).
  • The Rust emitter (HmrAstFinalizer) gains a dedup_module_initializer
    field. compile_lazy_entry sets it to true; the two HMR-patch render
    paths set it to false so HMR keeps re-running the factory and replacing
    exports — that's the whole point of a patch.
  • The stable module id is now passed into the factory as
    __rolldown_module_id__, so registerModule and createModuleHotContext
    reference it by identifier instead of duplicating the string literal.
  • The lazy proxy template deletes its own runtime.modules entry before
    triggering the real chunk, so the dedup gate on the real module's id
    starts from a clean slate.

The dedup gate is a workaround. The proper fix is a runtime API for module
disposal that HMR can call before re-execution; see the TODO on
HmrAstFinalizer::dedup_module_initializer.

Test plan

  • cargo test -p rolldown --test integration (1690 passed, 70 ignored)
  • cargo clippy -p rolldown --tests --no-deps clean

Copy link
Copy Markdown
Member Author

h-a-n-a commented Apr 21, 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.

@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch 2 times, most recently from ff3c657 to d2c0b1d Compare April 21, 2026 10:25
@h-a-n-a h-a-n-a force-pushed the 04-16-fix_rolldown_plugin_lazy_compilation_use_loadexports_for_fetched_proxy_to_preserve_original_export_names branch from 7487a99 to 496f339 Compare April 21, 2026 10:25
@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from d2c0b1d to 01b0ad7 Compare April 21, 2026 10:27
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

Open in StackBlitz

@rolldown/browser

npm i https://pkg.pr.new/@rolldown/browser@9179

@rolldown/debug

npm i https://pkg.pr.new/@rolldown/debug@9179

@rolldown/pluginutils

npm i https://pkg.pr.new/@rolldown/pluginutils@9179

rolldown

npm i https://pkg.pr.new/rolldown@9179

@rolldown/binding-android-arm64

npm i https://pkg.pr.new/@rolldown/binding-android-arm64@9179

@rolldown/binding-darwin-arm64

npm i https://pkg.pr.new/@rolldown/binding-darwin-arm64@9179

@rolldown/binding-darwin-x64

npm i https://pkg.pr.new/@rolldown/binding-darwin-x64@9179

@rolldown/binding-freebsd-x64

npm i https://pkg.pr.new/@rolldown/binding-freebsd-x64@9179

@rolldown/binding-linux-arm-gnueabihf

npm i https://pkg.pr.new/@rolldown/binding-linux-arm-gnueabihf@9179

@rolldown/binding-linux-arm64-gnu

npm i https://pkg.pr.new/@rolldown/binding-linux-arm64-gnu@9179

@rolldown/binding-linux-arm64-musl

npm i https://pkg.pr.new/@rolldown/binding-linux-arm64-musl@9179

@rolldown/binding-linux-ppc64-gnu

npm i https://pkg.pr.new/@rolldown/binding-linux-ppc64-gnu@9179

@rolldown/binding-linux-s390x-gnu

npm i https://pkg.pr.new/@rolldown/binding-linux-s390x-gnu@9179

@rolldown/binding-linux-x64-gnu

npm i https://pkg.pr.new/@rolldown/binding-linux-x64-gnu@9179

@rolldown/binding-linux-x64-musl

npm i https://pkg.pr.new/@rolldown/binding-linux-x64-musl@9179

@rolldown/binding-openharmony-arm64

npm i https://pkg.pr.new/@rolldown/binding-openharmony-arm64@9179

@rolldown/binding-wasm32-wasi

npm i https://pkg.pr.new/@rolldown/binding-wasm32-wasi@9179

@rolldown/binding-win32-arm64-msvc

npm i https://pkg.pr.new/@rolldown/binding-win32-arm64-msvc@9179

@rolldown/binding-win32-x64-msvc

npm i https://pkg.pr.new/@rolldown/binding-win32-x64-msvc@9179

commit: 01b0ad7

@h-a-n-a h-a-n-a changed the title fix: fix race condition of module initialization in HMR patches fix: avoid module reinitialization in lazy compilation patches Apr 23, 2026
@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from 01b0ad7 to 43390b3 Compare April 27, 2026 07:05
@h-a-n-a h-a-n-a changed the base branch from 04-16-fix_rolldown_plugin_lazy_compilation_use_loadexports_for_fetched_proxy_to_preserve_original_export_names to graphite-base/9179 April 27, 2026 07:29
@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from 43390b3 to f3c3c87 Compare April 27, 2026 07:29
@h-a-n-a h-a-n-a force-pushed the graphite-base/9179 branch from 496f339 to 7f39bc5 Compare April 27, 2026 07:29
@h-a-n-a h-a-n-a changed the base branch from graphite-base/9179 to 04-21-fix_visit_identifier_references_for_runtime_rewrites_in_hmr_finalizer April 27, 2026 07:30
@h-a-n-a h-a-n-a changed the title fix: avoid module reinitialization in lazy compilation patches fix(dev/lazy): avoid module reinitialization in lazy compilation patches Apr 27, 2026
@h-a-n-a h-a-n-a marked this pull request as ready for review April 27, 2026 07:33
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 27, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches (27026df) with main (5a5f8f5)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 (27026df) during the generation of this report, so 5a5f8f5 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 requested a review from sapphi-red April 27, 2026 07:40
@h-a-n-a h-a-n-a changed the base branch from 04-21-fix_visit_identifier_references_for_runtime_rewrites_in_hmr_finalizer to graphite-base/9179 April 27, 2026 07:40
@h-a-n-a h-a-n-a force-pushed the graphite-base/9179 branch from 7f39bc5 to b8fc6ba Compare April 27, 2026 07:43
@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from f3c3c87 to b4dc062 Compare April 27, 2026 07:43
@h-a-n-a h-a-n-a changed the base branch from graphite-base/9179 to 04-21-fix_visit_identifier_references_for_runtime_rewrites_in_hmr_finalizer April 27, 2026 07:43
@h-a-n-a h-a-n-a force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from b4dc062 to 90d94c1 Compare April 27, 2026 07:45
@graphite-app
Copy link
Copy Markdown
Contributor

graphite-app Bot commented Apr 27, 2026

Merge activity

…hes (#9179)

## Summary

When a module appears in two lazy-compilation chunks served concurrently
  (e.g. two `compileLazyEntry` requests racing for `shared.js`), the browser
  runs the module body twice. Non-idempotent module code blows up — concretely,
  `Object.defineProperty(this, "key", { configurable: false })` on the second
  run throws `Cannot redefine property`.

  This PR makes module initializers opt into runtime-side deduping:

  - `createEsmInitializer` / `createCjsInitializer` take a new `dedup` flag.
    When truthy and the stable id is already in `runtime.modules`, the factory
    is skipped (or, for CJS, the registered exports are reused).
  - The Rust emitter (`HmrAstFinalizer`) gains a `dedup_module_initializer`
    field. `compile_lazy_entry` sets it to `true`; the two HMR-patch render
    paths set it to `false` so HMR keeps re-running the factory and replacing
    exports — that's the whole point of a patch.
  - The stable module id is now passed into the factory as
    `__rolldown_module_id__`, so `registerModule` and `createModuleHotContext`
    reference it by identifier instead of duplicating the string literal.
  - The lazy proxy template deletes its own `runtime.modules` entry before
    triggering the real chunk, so the dedup gate on the real module's id
    starts from a clean slate.

  The dedup gate is a workaround. The proper fix is a runtime API for module
  disposal that HMR can call before re-execution; see the TODO on
  `HmrAstFinalizer::dedup_module_initializer`.

  ## Test plan

  - [x] `cargo test -p rolldown --test integration` (1690 passed, 70 ignored)
  - [x] `cargo clippy -p rolldown --tests --no-deps` clean
@graphite-app graphite-app Bot force-pushed the 04-21-fix_visit_identifier_references_for_runtime_rewrites_in_hmr_finalizer branch from b8fc6ba to 0689e35 Compare April 27, 2026 08:09
@graphite-app graphite-app Bot force-pushed the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch from 90d94c1 to 27026df Compare April 27, 2026 08:10
Base automatically changed from 04-21-fix_visit_identifier_references_for_runtime_rewrites_in_hmr_finalizer to main April 27, 2026 08:14
@graphite-app graphite-app Bot merged commit 27026df into main Apr 27, 2026
30 checks passed
@graphite-app graphite-app Bot deleted the 04-21-fix_fix_race_condition_of_module_initialization_in_hmr_patches branch April 27, 2026 08:15
@rolldown-guard rolldown-guard Bot mentioned this pull request Apr 29, 2026
@rolldown-guard rolldown-guard Bot mentioned this pull request Apr 29, 2026
shulaoda added a commit that referenced this pull request Apr 29, 2026
## [1.0.0-rc.18] - 2026-04-29

### 💥 BREAKING CHANGES

- optimization: default unspecified inlineConst.mode to smart (#9248) by @IWANABETHATGUY

### 🐛 Bug Fixes

- rolldown_plugin_vite_import_glob: return error instead of panicking when virtual module uses a relative glob (#9241) by @shulaoda
- binding: treat empty inlineConst object as omitted (#9247) by @IWANABETHATGUY
- rolldown: keep enum declaration for optional-chain access (#9229) by @Dunqing
- link_stage: restore inline let-else in exports-kind filter (#9237) by @IWANABETHATGUY
- dev/lazy: avoid module reinitialization in lazy compilation patches (#9179) by @h-a-n-a
- dev: visit identifier references for runtime rewrites in HMR finalizer (#9191) by @h-a-n-a
- chunk-optimizer: pick dominator for runtime placement to avoid cycles (#9164) by @IWANABETHATGUY
- make `this.emitFile` chunk path synchronous to avoid deadlock (#9031) by @lazarv
- use sentinel id for `browser: false` ignored modules (#9192) by @shulaoda
- prevent chunk optimizer from creating import cycles (#9228) by @IWANABETHATGUY

### 🚜 Refactor

- replace tokio::sync::Mutex with std::sync::Mutex for non-IO data (#9176) by @shulaoda
- rolldown_plugin_vite_import_glob: do not rewrite import path for absolute base (#9195) by @shulaoda
- runtime_helper: wrap DependedRuntimeHelperMap in a struct (#9215) by @IWANABETHATGUY
- drop redundant clear() in determine_safely_merge_cjs_ns (#9206) by @IWANABETHATGUY
- clean up generate_lazy_export (#9208) by @IWANABETHATGUY
- bitset: return bool from set_bit to fuse guard-and-set (#9207) by @IWANABETHATGUY
- link_stage: simplify exports-kind filter and clarify safety comments (#9205) by @IWANABETHATGUY

### 📚 Documentation

- determine_module_exports_kind (#9252) by @IWANABETHATGUY
- fix dead link to esbuild ESM/CJS interop tests (#9230) by @Copilot
- remove CSS bundling references (#9234) by @shulaoda
- correct IncrementalFullBuild row in BundleMode table (#9214) by @IWANABETHATGUY
- design: add bundler data lifecycle design doc (#9212) by @hyf0
- remove minifier alpha status notices (#9202) by @sapphi-red

### ⚙️ Miscellaneous Tasks

- upgrade oxc to 0.128.0 (#9260) by @shulaoda
- deps: bump rolldown-ariadne to 0.6.0 (#9254) by @IWANABETHATGUY
- deps: update github actions (#9259) by @renovate[bot]
- deps: update github actions (#9258) by @renovate[bot]
- remove renovate overrides (#9257) by @Boshen
- use ubuntu-latest for security workflow (#9256) by @Boshen
- notify Discord around release publish (#9251) by @Boshen
- add release environment to npm publish workflow (#9250) by @Boshen
- justfile: drop the `--` separator before forwarded args in `vp run` (#9246) by @shulaoda
- deps: update test262 submodule for tests (#9243) by @sapphi-red
- add more tracing instrumentations (#9220) by @sapphi-red
- rolldown_plugin_vite_import_glob: remove outdated sourcemap doc comment (#9213) by @shulaoda
- update security workflow (#9201) by @Boshen

### ❤️ New Contributors

* @lazarv made their first contribution in [#9031](#9031)

Co-authored-by: shulaoda <165626830+shulaoda@users.noreply.github.com>
@rolldown-guard rolldown-guard Bot mentioned this pull request Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants