Skip to content

feat(bundled-dev): support hotUpdate and handleHotUpdate hook#22956

Open
h-a-n-a wants to merge 1 commit into
mainfrom
feat/hot-update
Open

feat(bundled-dev): support hotUpdate and handleHotUpdate hook#22956
h-a-n-a wants to merge 1 commit into
mainfrom
feat/hot-update

Conversation

@h-a-n-a

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

Copy link
Copy Markdown
Member

Makes Vite's hotUpdate and handleHotUpdate plugin hooks work in bundled dev. Built on top of rolldown's new dev-only hotUpdate hook (rolldown/rolldown#10305).

Problem

In bundled dev, the rolldown engine owns the module graph and the update flow. When a file changes, hooks run inside the engine, and the engine speaks plain module ids. But Vite plugins expect Vite's types: EnvironmentModuleNode objects, a moduleGraph, and a shared context per event. Without a bridge, every plugin that uses these hooks does nothing in bundled dev.

Design

flowchart TB
  A[Vite plugin<br/>hotUpdate with module nodes] --> B[adapter in bundledDevHmr.ts<br/>one wrapper per plugin]
  B --> C[rolldown hotUpdate hook<br/>plain module ids]
  C --> D[rolldown dev engine<br/>owns graph and update flow]
Loading

Two pieces, both new in bundledDevHmr.ts:

  • A module graph facade (BundledModuleGraph). The engine owns the real graph. The facade creates a Vite module node on demand for each id, and creates it only once — plugins compare nodes by reference and store state on them, so identity matters. A node's info, importers, and importedModules are read live from the engine, so they stay correct after every rebuild without any bookkeeping. Writes to nodes do nothing, because the engine owns the graph.
  • A hook adapter (BundledDevHotUpdateAdapter). Each Vite plugin with either hook gets its own wrapper, registered as that plugin's rolldown hotUpdate hook. So plugin order and the replace-chain rules come from rolldown's own driver — the adapter does not re-implement them. The wrapper translates ids to nodes on the way in, and nodes back to ids on the way out.

The main design rules:

  • Each layer keeps its own types. The engine uses ids. Vite plugins use nodes. Translation happens only at the boundary, in the adapter.
  • Only top-level Vite plugins are wrapped. A plugin swapped in per environment with applyToEnvironment is a rolldown plugin. Its hotUpdate already expects ids and must stay unwrapped.
  • One shared state per changed file, like Vite. A pre-ordered hook opens the event state and expands the set to all modules of the file (so query-variant modules like file.vue?type=style are included). All wrappers for that file share one options object, so a change made by one plugin (for example a reassigned read) is visible to the next. A post-ordered hook merges modules buffered with moduleGraph.invalidateModule and closes the state.
  • invalidateAll means full rebuild plus page reload. That is the closest match to its meaning in bundled dev.
  • The legacy hook behaves like in Vite. handleHotUpdate gets the shared HmrContext with mixed module nodes, fires only for updates, and warns about its deprecation.

Tests

End-to-end coverage lives in the rolldown repo's dev-server test suite (hmr-hot-update-hook-vite, 8 browser specs). It covers the common plugin patterns: replacing the set, invalidate buffering (tailwind style), custom messages with read() and hot.send (markdown-pages style), the legacy shared-context read chain (unocss with plugin-vue), file-to-submodule expansion (SFC style), suppress, and invalidateAll. Unit tests in this repo are planned as a follow-up.

@h-a-n-a
h-a-n-a changed the base branch from main to feat/client-side-hmr July 16, 2026 09:37
@h-a-n-a
h-a-n-a force-pushed the feat/client-side-hmr branch 2 times, most recently from 3082b1b to d4ba516 Compare July 21, 2026 03:49
Base automatically changed from feat/client-side-hmr to renovate/rolldown-related-dependencies July 21, 2026 13:35
Base automatically changed from renovate/rolldown-related-dependencies to main July 22, 2026 04:29
@h-a-n-a
h-a-n-a force-pushed the feat/hot-update branch 2 times, most recently from ae0a77c to 1030097 Compare July 22, 2026 12:57
@h-a-n-a
h-a-n-a marked this pull request as ready for review July 23, 2026 07:12
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.

1 participant