feat(dev): hot-reload Vuetify config under SSR without a dev-server restart#380
Merged
Conversation
…ad builder:watch restart
…config The previous approach (addWatchFile + handleHotUpdate reload) worked only on tiny SSR module graphs (the e2e fixture) but not real apps: the vite-node SSR runner never re-executed the virtual config module, so SSR served stale config. Emit a dev-SSR-only side-effect import of the config sources from the virtual config module so the runner records a config-file -> virtual-module dependency edge; adding the config files to the client watcher then makes Nuxt's vite-node invalidate set cascade through that edge to the server entry on edit. Re-read the config and invalidate the SSR transforms inside an awaited handleHotUpdate returning [] (no full-reload, which would suspend SSR for non-browser requests). Verified against the real playground (SSR): theme edit reflected in ~0.7s with no dev-server restart.
… scope Address final code review: make the icons/date SSR-eviction gap explicit (no import edge -> not hot-reloaded under SSR, needs restart) in code comments and the docs caveat; clarify that addWatchFile alone does not evict the SSR runner (the dev-SSR import edge does); note the e2e guards no-restart + wiring but not the eviction mechanism; mark the 4.3.0 floor as a conservative inference.
The required @nuxt/vite-builder internals (useInvalidates/markInvalidate/ invalidateDepTree, environments.ssr.fetchModule) and the Vite Environment API (every Nuxt 4.x uses Vite 7) are present since 4.0.0 — verified by inspecting the published 4.0.0-4.3.1 dists and confirmed end-to-end on apps/playground running Nuxt 4.0.0 (config edit hot-reloaded, no dev-server restart). The 4.3.0 floor was an overly conservative untested guess. Nuxt 3 still uses the restart fallback.
The Vite 7 + Environment-API invalidation mechanism the fix relies on landed in @nuxt/vite-builder 3.18.0 (3.x line) and 4.0.0 (4.x line); Nuxt 3.15-3.17 ship Vite 6 with the older mechanism and don't qualify. Confirmed end-to-end on apps/playground at both Nuxt 3.21.8 and 4.0.0 (config edit hot-reloaded, no restart; pre-fix build stayed stale — valid control). Nuxt 3.15-3.17 keep the restart fallback.
…vironmentApi The previous code only captured the dedicated SSR dev server (flag off). With experimental.viteEnvironmentApi enabled there's a single server and that event never fires, so SSR config edits silently went stale. Capture the SSR graph from server.environments.ssr in that case too. Verified on apps/playground with the flag both off and on (config edit hot-reloaded in ~1s, no restart).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Main idea
In dev, editing a Vuetify config file under SSR triggered a full
nuxt.callHook('restart'). This makes those edits hot-reload instead — the SSR output refreshes and the open browser auto-reloads, with no dev-server restart — on Nuxt ≥ 3.18 (the Vite 7 line + all of Nuxt 4). Nuxt 3.15–3.17 (Vite 6) keep the restart fallback.The config is exposed as Vite virtual modules executed by Nuxt's vite-node SSR runner, whose cache the usual invalidation APIs don't evict. The fix gives that runner a real dependency edge to act on: the virtual config module emits a dev-SSR-only side-effect
importof the config file (so vite-node cascades a config edit → virtual module → server entry and re-evaluates it). On change we re-read the config intoctx, invalidate the SSR transforms, and broadcast a clientfull-reload.load()gained areloadflag that skips re-applyingnuxt.options(avoids a Nitrodev:reload+ a latent duplicate-head/css bug). AsupportsSsrConfigHmrgate (floor 3.18.0) selects HMR vs. the restart fallback.Scope: hot-reloads
theme,defaults,components,aliases,directives, locale messages. Icon-set / date-adapter and icon CDN/<head>changes still need a restart (documented). The mechanism leans on@nuxt/vite-buildervite-node internals; safe restart fallback below Nuxt 3.18.Verified: version-gate unit tests; a dev-SSR e2e asserting no-restart (boot-count probe); and the runner-eviction confirmed end-to-end on
apps/playgroundat both Nuxt 3.21.8 and 4.0.0 with a fix-out/fix-in control (pre-fix stays stale).References
@nuxt/vite-buildervite-node invalidation (useInvalidates/markInvalidate/invalidateDepTree,environments.ssr): https://github.com/nuxt/nuxt/blob/main/packages/vite/src/plugins/vite-node.tsthis.environment,moduleGraph.invalidateModule,hotUpdate): https://vite.dev/guide/api-environment-pluginsserver.environments.ssr): https://vite.dev/guide/api-environment-frameworkshandleHotUpdate/ returning[]/ws.send({ type: 'full-reload' }): https://vite.dev/guide/api-hmr