Skip to content

PATTERN Cited by 1 source

Vite hotUpdate directive-triggered rebuild

Vite hotUpdate directive-triggered rebuild is the HMR integration pattern where an SDK plugin hooks into Vite's hotUpdate lifecycle to trigger a secondary build only when the saved file contains a specific directive string (e.g. "use workflow", "use step"). The directive regex-test is cheap; the secondary build (esbuild over the SDK's handler bundles) is expensive. Gating one on the other keeps HMR fast.

Canonical verbatim + snippet

From Vercel's 2026-04-21 WDK post: "When you save a workflow file in SvelteKit, three things happen: (1) Vite's hotUpdate hook fires with the changed file; (2) We check for \"use workflow\" or \"use step\" directives; (3) If found, trigger an esbuild rebuild."

async hotUpdate({ file, read }) {
  const content = await read();
  const useWorkflowPattern = /^\s*(['"])use workflow\1;?\s*$/m;
  const useStepPattern = /^\s*(['"])use step\1;?\s*$/m;
  if (!useWorkflowPattern.test(content) && !useStepPattern.test(content)) {
    // Not a workflow/step file — let Vite handle HMR normally
  }
  await enqueue(() => builder.build());
}

Three-step dev loop

  1. User saves a file — Vite fires hotUpdate({ file, read }) to every registered plugin.
  2. SDK plugin reads the file + regex-tests for directive — cheap text operation. Two regexes, one per directive kind. If neither matches, the plugin returns and lets Vite's default HMR path handle the file (component reload etc.).
  3. If a directive matches, the plugin enqueues a build (builder.build() — WDK's esbuild-backed handler-bundle builder). The enqueue primitive coalesces rapid consecutive saves into a single rebuild.

Why directive-gating is load-bearing

Without the gate:

  • Every file save triggers a full esbuild rebuild of the SDK's handler bundles — slow, often irrelevant (most saves are UI components, not workflow files).

With the gate:

  • Only saves to files containing the SDK's directives trigger the rebuild — matches the frequency of workflow edits specifically, which is rare compared to UI edits.
  • Non-SDK files flow through Vite's default HMR path unchanged — no regression of the framework's own HMR semantics.

The gate also implements use-directive compilation markers at the HMR layer — the same directives WDK's SWC plugin reads at build time for three-mode transforms are the ones the HMR hook tests for.

Regex shape

The regex /^\s*(['"])use workflow\1;?\s*$/m matches:

  • Line start (^\s*)
  • Matching single or double quotes around use workflow ((['"])use workflow\1) — back-reference \1 forces same quote kind.
  • Optional semicolon (;?)
  • Trailing whitespace + end of line (\s*$)
  • Multiline flag (m) so ^/$ match line boundaries.

Cheap enough to run on every saved file's content before deciding to rebuild.

When to use

  • Vite-based SDK integrations where rebuilds are expensive enough to warrant gating.
  • SDKs using directive-style compilation markers (React Server Components, WDK, etc.).
  • HMR pipelines where the SDK's concerns are orthogonal to the framework's default HMR (component reload, etc.).

When NOT to use

  • Non-Vite frameworks (though the pattern is portable — any HMR lifecycle with a file-content hook works).
  • When rebuilds are cheap enough to not warrant gating.
  • When the SDK has no source-level marker the HMR hook can inspect (fall back to watch-all + debounce).

Canonical instance

Vercel Workflow DevKit (WDK) uses this pattern in its Vite-based integrations for SvelteKit / Astro / Nuxt. The code snippet comes from the 2026-04-21 post's "A minimal example of Workflow DevKit's HMR in Vite-based frameworks" figure caption.

Seen in

Last updated · 476 distilled / 1,218 read