Skip to content

fix(bundled-dev): preserve import.meta.hot.data across HMR updates - #23005

Merged
sapphi-red merged 1 commit into
renovate/rolldown-related-dependenciesfrom
fix/preserve-hot-data-bundled-dev
Jul 22, 2026
Merged

fix(bundled-dev): preserve import.meta.hot.data across HMR updates#23005
sapphi-red merged 1 commit into
renovate/rolldown-related-dependenciesfrom
fix/preserve-hot-data-bundled-dev

Conversation

@h-a-n-a

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

Copy link
Copy Markdown
Member

Description

Closes #23001.

In bundled-dev mode, BundledDevHMRClient.handleModuleCacheRemoval allocated a fresh object, handed it to the dispose callback, and overwrote the module's dataMap entry on every update:

handleModuleCacheRemoval(id: string): void {
  const data = {}
  const disposer = this.disposeMap.get(id)
  if (disposer) {
    disposer(data)
  }
  this.dataMap.set(id, data)
}

This dropped any direct mutation of import.meta.hot.data on every update and never showed the disposer the previous object — the inverse of Vite's documented contract:

The import.meta.hot.data object is persisted across different instances of the same updated module.

Fix

Pass the existing dataMap entry to the disposer and leave it in place, so a re-executed module reuses the same object via the HMRContext constructor (if (!dataMap.has(ownerPath)) dataMap.set(ownerPath, {})). This mirrors the shared HMR client exactly (shared/hmr.ts):

handleModuleCacheRemoval(id: string): void {
  const disposer = this.disposeMap.get(id)
  if (disposer) {
    disposer(this.dataMap.get(id))
  }
}

Test

Adds a data.js module to the hmr-full-bundle-mode playground that increments hot.data.count on each execution and a disposed counter inside its dispose() callback, plus a spec asserting both persist across successive updates (1/0 → 2/1 → 3/2). This covers both required behaviors: direct-mutation persistence and the disposer receiving the existing object.

The test is intentionally ordered before the invalidate test — that test's cleanup restores invalidation-child.js, whose hot.invalidate() propagates to a parent without a shipped factory and forces a full page reload that would otherwise wipe this test's hot.data.

pnpm test-serve hmr-full-bundle-mode passes.

Note

Targets the renovate/rolldown-related-dependencies branch (rolldown 1.2.0 + client-side HMR #22883), since that branch carries the renamed BundledDevHMRClient.

🤖 Generated with Claude Code

`handleModuleCacheRemoval` allocated a fresh object, handed it to the
disposer, and overwrote the `dataMap` entry on every update. This dropped
any direct mutation of `import.meta.hot.data` and never showed the disposer
the previous object, contradicting Vite's documented contract that
`hot.data` is persisted across module instances.

Pass the existing `dataMap` entry to the disposer and leave it in place, so
a re-executed module reuses the same object via the `HMRContext` ctor —
matching the shared HMR client (`shared/hmr.ts`).

Adds a `data.js` playground module + test asserting a directly-mutated
counter and a dispose-written counter both persist across updates.

Closes #23001
@h-a-n-a
h-a-n-a force-pushed the fix/preserve-hot-data-bundled-dev branch from 1966948 to 33186aa Compare July 22, 2026 03:28
@h-a-n-a
h-a-n-a requested a review from sapphi-red July 22, 2026 03:42
@sapphi-red sapphi-red added p3-minor-bug An edge case that only affects very specific usage (priority) feat: bundled-dev labels Jul 22, 2026
@sapphi-red
sapphi-red merged commit f9ec81f into renovate/rolldown-related-dependencies Jul 22, 2026
26 checks passed
@sapphi-red
sapphi-red deleted the fix/preserve-hot-data-bundled-dev branch July 22, 2026 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat: bundled-dev p3-minor-bug An edge case that only affects very specific usage (priority)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants