Skip to content

fix(vtz): trigger HMR and clean module graph on file delete#2768

Merged
viniciusdacal merged 2 commits into
mainfrom
viniciusdacal/hmr-on-delete
Apr 17, 2026
Merged

fix(vtz): trigger HMR and clean module graph on file delete#2768
viniciusdacal merged 2 commits into
mainfrom
viniciusdacal/hmr-on-delete

Conversation

@viniciusdacal
Copy link
Copy Markdown
Contributor

Summary

Test plan

  • cargo test --all — green (new unit + parity tests for Remove path)
  • cargo clippy --all-targets -- -D warnings — green
  • cargo fmt --all -- --check — green
  • GitHub CI green
  • Manual: delete a .tsx file in a dev-server session; confirm dependents hot-update without stale modules

Closes #2764.

viniciusdacal and others added 2 commits April 17, 2026 16:27
The dev server's change loop called compile_for_browser on every event,
which fails for a deleted file — pushing the flow into the compilation-
error branch and skipping graph/cache cleanup. Dependents of the deleted
file were never HMR-invalidated, so clients kept using stale modules.

Fix:
- watcher::process_file_change now calls graph.remove_module() on Remove
  events so ghost nodes don't contaminate future invalidation cascades.
- server change loop branches on FileChangeKind::Remove to skip the
  compile attempt entirely; process_file_change still runs, so dependents
  are invalidated and HMR broadcasts an Update (or FullReload when the
  entry file is deleted).

Tests:
- Unit test in watcher/mod.rs verifying graph cleanup + dependent cache
  invalidation on Remove.
- Parity tests #41/#42 verifying the HMR Update message for a dependent
  delete and FullReload for entry-file delete.

Closes #2764

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Addresses should-fix findings from the adversarial review of #2764.

1. Race between read and write lock in process_file_change.
   The previous two-phase pattern (read-lock to snapshot dependents,
   release, write-lock to remove_module) allowed a concurrent browser
   fetch's graph.update_module() to re-add the deleted module with
   fresh back-edges in the window between phases. Now holds a single
   write lock across the dependents snapshot and the graph mutation.

2. CSS-only delete broadcasts a CssUpdate for a 404ing file.
   A standalone .css delete was treated as CSS-only (no JS dependents),
   so the HMR client tried to re-fetch the stylesheet URL — which 404s
   post-delete. is_css_only now additionally requires !is_remove so
   Remove events escalate to ModuleUpdate(vec![]), handled benignly
   by the client. Unit-tested.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@viniciusdacal viniciusdacal merged commit cde05ee into main Apr 17, 2026
6 checks passed
viniciusdacal added a commit that referenced this pull request Apr 18, 2026
Main added native/vtz/tests/parity/hmr.rs (from #2768) after the
initial FrameworkPlugin->VtzPlugin rename commit. Apply the rename
there too so the Rust test suite compiles after rebase.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
viniciusdacal added a commit that referenced this pull request Apr 18, 2026
…2773)

* docs(vtz): design for plugin rename + React deletion

Design doc and six self-contained phase files for renaming the Rust
`FrameworkPlugin` trait to `VtzPlugin`, deleting `ReactPlugin`, renaming
the TS `createVertzBunPlugin` → `createVertzBuildPlugin`, and removing
six orphaned `bun-plugin-shim.ts` files. Approved after three adversarial
reviews (DX, Product, Technical).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(vtz): delete ReactPlugin end-to-end

Remove the React framework plugin entirely — the `--plugin` CLI flag,
PluginChoice enum, .vertzrc plugin field, package.json auto-detect,
ReactPlugin impl (726 lines), embedded React fast-refresh JS assets,
and ~15 associated tests. Only VertzPlugin remains.

This is part of the vtz-plugin-system cleanup (DESIGN.md §13 decisions 1–2).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(vtz): rename FrameworkPlugin trait to VtzPlugin

Pure mechanical rename across 17 Rust files. The trait is vtz-specific
and its only impl (VertzPlugin) is framework-internal, so "Framework"
was misleading. Signatures unchanged.

Part of the vtz-plugin-system cleanup (DESIGN.md §6).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ui-server): rename bun-plugin to build-plugin

Rename the production-build factory from createVertzBunPlugin to
createVertzBuildPlugin. Purpose (production build) — not runtime (bun) —
drives the name. Dev uses vtz; only the production pipeline consumes this.

- Directory: packages/ui-server/src/bun-plugin -> src/build-plugin
- Factory: createVertzBunPlugin -> createVertzBuildPlugin
- Types: VertzBunPluginOptions/Result -> VertzBuildPluginOptions/Result
- Subpath: @vertz/ui-server/bun-plugin -> @vertz/ui-server/build-plugin
- vertz re-export: ui-server-bun-plugin.ts -> ui-server-build-plugin.ts

Part of the vtz-plugin-system cleanup (DESIGN.md §4, §6).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(cli,benchmarks): fix Phase 3 review findings

- Rename mockCreateVertzBunPlugin -> mockCreateVertzBuildPlugin in
  orchestrator.test.ts and ui-build-pipeline.test.ts (matches the
  renamed factory consumed by CLI tests).
- Update benchmarks/vertz/index.html script src from
  dist/bun-plugin/fast-refresh-runtime.js -> dist/build-plugin/...
- Update ui-server plugin.ts file-header comment:
  "Unified Bun plugin" -> "Unified production-build plugin"
  (consistent with the Build-vs-Bun naming rationale).

Phase 3 adversarial review in
reviews/vtz-plugin-rename-cleanup/phase-03-ts-rename.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: delete 6 orphaned bun-plugin-shim.ts files

These shims claimed to bridge to bunfig.toml, but no bunfig.toml
exists anywhere in the repo. The scaffold stopped generating them
a while ago. Deletes dead code from the pre-vtz era.

Deleted:
- examples/task-manager/bun-plugin-shim.ts
- examples/linear/bun-plugin-shim.ts
- examples/entity-todo/bun-plugin-shim.ts
- benchmarks/vertz/bun-plugin-shim.ts
- packages/landing/bun-plugin-shim.ts
- packages/component-docs/bun-plugin-shim.ts

No script, package.json, or config references any of these files
(verified via repo-wide grep). Apps run on vtz, not bun+bunfig.toml.

Part of the vtz-plugin-system cleanup (DESIGN.md §5, §12 Phase 4).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: update for bun-plugin -> build-plugin rename + add changeset

- Delete docs/fullstack-app-setup.md (documented a bunfig.toml setup
  that no longer works; all six shim files deleted in Phase 4).
- packages/ui/README.md: "Bun Setup" -> "Build Setup"; reference
  @vertz/ui-server/build-plugin instead of bun-plugin.
- packages/site/pages/guides/deploy/static-sites.mdx: rename
  createVertzBunPlugin -> createVertzBuildPlugin and retarget the
  import to @vertz/ui-server/build-plugin.
- Add .changeset/vtz-plugin-rename-cleanup.md documenting the full
  breaking-change set for @vertz/ui-server, vertz, @vertz/runtime
  (subpath removal, identifier renames, --plugin CLI flag removal,
  ReactPlugin removal).

Part of the vtz-plugin-system cleanup (DESIGN.md §2, §5, §13).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: fix stale bunfig.toml reference in test preload comment

The preload file is wired via test.preload in
packages/ui-server/vertz.config.ts, not bunfig.toml (which no
longer exists in the repo). Update the doc comment accordingly.

Sweep from Phase 5 review nit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(vtz): rename FrameworkPlugin in parity/hmr.rs

Main added native/vtz/tests/parity/hmr.rs (from #2768) after the
initial FrameworkPlugin->VtzPlugin rename commit. Apply the rename
there too so the Rust test suite compiles after rebase.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

HMR: file deletion does not propagate (graph + cache stale, dependents not updated)

1 participant