Skip to content

feat: verify & harden hot-reload (HMR) across bundler plugins — Vite, Rollup, Webpack, Rspack #94

Description

@dean0x

Summary

Ensure MDS works correctly inside the hot-reload / HMR dev workflows of every bundler we ship — Vite, Rollup, Webpack — and add first-class Rspack support. During a dev session, editing a .mds file (or any transitively @import-ed dependency) must reliably trigger a recompile and browser/app update, with compile errors surfaced in the dev overlay and cleared on the next good build.

This is a verify-and-harden task: we have the transform plumbing, but no one has exercised these plugins end-to-end against a live dev server. Where a bundler's hot-reload path is broken or unverified, we make it work.

Motivation

The mds watch CLI (#57) covers the standalone iterate-on-prompt loop. But most consumers embed MDS in a JS/TS app via a bundler plugin, where the expectation is the bundler's native HMR/watch: save a .mds, see the app update without a manual rebuild. If that path silently fails (stale module served, edit ignored, error not surfaced), the plugins look broken in exactly the workflow users live in. We ship four integration surfaces and have validated the hot path on none of them.

Current State (code audit)

  • Vitepackages/vite-plugin/src/index.ts
    • handleHotUpdate fires a full-reload on .mds change; transitive deps registered via addWatchFile in transform. Errors thrown for the Vite error overlay.
    • Status: wired up but unverified against a running dev server. Full-reload (not granular HMR) is an intentional v0.1.0 choice (MDS modules export plain strings — nothing to hot-swap).
  • Rolluppackages/rollup-plugin/src/index.ts
    • Deps registered via addWatchFilerollup -w rebuilds on change. Rollup has no dev-server/HMR concept, so "hot reload" here is watch-mode rebuild.
    • Status: plausible but unverified; need to confirm @import dep edits re-trigger and errors don't kill the watcher.
  • Webpackpackages/webpack-loader/src/index.ts
    • Deps registered via addDependency → watch-mode recompiles. No explicit module.hot self-accept handling — behavior under webpack-dev-server HMR is unverified (may fall back to full reload, may serve stale, may warn).
    • Status: most likely gap.
  • Rspack — no package today. Rspack is webpack-loader- and Vite-plugin-compatible, so our existing loader probably loads, but it is entirely unverified, and we've committed to first-class support.

The existing __test__/plugin.spec.mjs suites validate the transform in isolation; none drive a dev server or assert HMR/watch behavior.

Scope

Hot-reload verification matrix — for each, an automated (or scripted-reproducible) dev-session smoke test:

Bundler Mode under test Ship today?
Vite vite dev server (HMR) yes
Rollup rollup -w watch yes
Webpack webpack serve (HMR) yes
Rspack rspack serve (HMR) new

For each: confirm (a) editing a .mds entry triggers reload, (b) editing a transitive @import dependency triggers reload, (c) a compile error is surfaced in the dev overlay/console and the watcher stays alive, (d) the next successful edit clears the error and serves fresh output.

Rspack: add first-class support. Prefer reusing the webpack-loader compat path; introduce a dedicated @mdscript/rspack package only if clean HMR integration requires it (e.g. distinct plugin hooks or builtin-loader registration). Decision to be recorded in the issue once verified.

Design Considerations

  • Dependency-driven invalidation: the import graph already flows through CompileOutput.dependenciesaddWatchFile / addDependency. Verify it survives a dev session, not just a one-shot build, and that newly-added @imports register on the next compile.
  • Error resilience: a compile error during a dev session must never terminate the watcher/server. Vite throws for the overlay; Rollup uses this.error(); webpack/rspack pass the error to the loader callback — confirm each keeps the session alive and recovers.
  • Webpack/Rspack HMR acceptance: determine whether the emitted module needs module.hot.accept self-acceptance (or an HMR runtime hint) to avoid stale modules vs. relying on parent-module bubbling to a full reload. Pick the simplest correct behavior and document it.
  • Test harness: prefer programmatic dev-server APIs (Vite createServer, webpack/rspack watch + compiler.watch, rollup.watch) driven from .mjs specs so this runs in CI on ubuntu, rather than manual example apps. Example apps under examples/ are a fine secondary deliverable for docs but should not be the only verification.
  • Consistency with mds watch: dependency tracking and error-recovery semantics should match the CLI watch behavior (feat: mds watch command (file watcher with auto-recompile) #57) so the two stories don't diverge.

Acceptance Criteria

  • Vite dev server: editing a .mds and a transitive @import each trigger a reload; errors show in the overlay and clear on recovery — covered by an automated test.
  • Rollup watch: .mds and @import edits re-trigger the rebuild; an error does not kill the watcher — covered by an automated test.
  • Webpack dev server (HMR): .mds and @import edits update the app without manual rebuild; stale-module behavior is understood and documented; errors surface and recover — covered by an automated test.
  • Rspack: first-class support confirmed working under rspack serve HMR (reusing webpack-loader compat or a new @mdscript/rspack package); same edit/error/recovery guarantees — covered by an automated test.
  • Any gaps found during verification are fixed (not just documented).
  • README / docs note the supported HMR behavior per bundler (full-reload vs. watch rebuild).

Out of Scope (future)

  • Granular HMR for Vite (hot-swapping only the importing modules instead of full-reload) — a later optimization once the module-graph integration is validated; tracked separately if pursued.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ecosystemBindings, packages, and integrationsenhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions