From 538324f9c26244628386bd073c7fb26ef934df7b Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 27 Jul 2026 11:13:51 +0000 Subject: [PATCH 1/4] refactor(paths): extract `relativeTo` helper for formatting paths against a directory --- packages/nuxt-cli/src/utils/paths.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/nuxt-cli/src/utils/paths.ts b/packages/nuxt-cli/src/utils/paths.ts index f8f76ace..db1aec5a 100644 --- a/packages/nuxt-cli/src/utils/paths.ts +++ b/packages/nuxt-cli/src/utils/paths.ts @@ -29,10 +29,15 @@ export function resolveRootDir(args: { cwd?: string, rootDir?: string }): string } export function relativeToProcess(path: string) { - return link(path, { - cwd, - formatter: absolute => relative(cwd, absolute) || absolute, - }) + return relativeTo(cwd, path) +} + +/** Format `path` relative to `dir`, optionally as a clickable terminal link. */ +export function relativeTo(dir: string, path: string, options: { link?: boolean } = {}) { + if (options.link === false) { + return relative(dir, resolve(dir, path)) || path + } + return link(path, { cwd: dir, formatter: absolute => relative(dir, absolute) || absolute }) } export function withNodePath(path: string) { From 6111b2273282a56d13cb2302c8f05c770e1f1cc5 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 27 Jul 2026 11:13:51 +0000 Subject: [PATCH 2/4] feat(dev): always report why the dev server reloaded or restarted --- packages/nuxt-cli/src/commands/dev.ts | 20 +++-- packages/nuxt-cli/src/dev/index.ts | 25 +++--- packages/nuxt-cli/src/dev/reason.ts | 75 ++++++++++++++++ packages/nuxt-cli/src/dev/utils.ts | 59 ++++++++----- .../nuxt-cli/test/e2e/dev-restart.spec.ts | 2 +- .../nuxt-cli/test/unit/restart-reason.spec.ts | 85 +++++++++++++++++++ 6 files changed, 225 insertions(+), 41 deletions(-) create mode 100644 packages/nuxt-cli/src/dev/reason.ts create mode 100644 packages/nuxt-cli/test/unit/restart-reason.spec.ts diff --git a/packages/nuxt-cli/src/commands/dev.ts b/packages/nuxt-cli/src/commands/dev.ts index b17e8924..aa1bb5d0 100644 --- a/packages/nuxt-cli/src/commands/dev.ts +++ b/packages/nuxt-cli/src/commands/dev.ts @@ -1,19 +1,20 @@ import type { ParsedArgs } from 'citty' import type { HTTPSOptions } from '../dev/cert' import type { DevListenOverrides } from '../dev/listen' +import type { DevRestartReason } from '../dev/reason' import type { NuxtDevContext } from '../dev/utils' import process from 'node:process' import { defineCommand } from 'citty' -import colors from 'picocolors' import { isBun, isTest } from 'std-env' import { satisfies } from 'verkit' import { initialize } from '../dev' import { closeInspector, openInspector, resolveInspectOptions } from '../dev/inspect' import { ForkPool } from '../dev/pool' +import { formatRestartReason } from '../dev/reason' import { setupShortcuts } from '../dev/shortcuts' import { debug, logger } from '../utils/logger' import { resolveRootDir } from '../utils/paths' @@ -161,7 +162,7 @@ const command = defineCommand({ // Disable forking when profiling to capture all activity in one process if (!ctx.args.fork || ctx.args.profile) { - setupShortcuts({ listener, close, onReady, restart: () => reload('Restart requested') }) + setupShortcuts({ listener, close, onReady, restart: () => reload({ type: 'shortcut' }) }) setupSignalHandlers(close) return { listener, @@ -191,7 +192,9 @@ const command = defineCommand({ // On hard restart, use a fork from the pool let cleanupCurrentFork: (() => Promise) | undefined - async function restartWithFork() { + async function restartWithFork(reason?: DevRestartReason) { + logger.info(formatRestartReason(reason, { rootDir: cwd, hard: true })) + // Get a fork from the pool (warm if available, cold otherwise) const context: NuxtDevContext = { cwd, args: ctx.args } @@ -210,19 +213,18 @@ const command = defineCommand({ } else if (message.type === 'nuxt:internal:dev:restart') { // Fork is requesting another restart - void restartWithFork() + void restartWithFork(message.reason) } else if (message.type === 'nuxt:internal:dev:rejection') { - logger.info(`Restarting Nuxt due to error: ${colors.cyan(message.message)}`) - void restartWithFork() + void restartWithFork({ type: 'error', message: message.message }) } }) } - async function restart() { + async function restart(reason?: DevRestartReason) { // Close the in-process dev server await close() - await restartWithFork() + await restartWithFork(reason) } onRestart(restart) @@ -232,7 +234,7 @@ const command = defineCommand({ await close() } - setupShortcuts({ listener, close: closeAll, onReady, restart }) + setupShortcuts({ listener, close: closeAll, onReady, restart: () => restart({ type: 'shortcut' }) }) setupSignalHandlers(closeAll) return { diff --git a/packages/nuxt-cli/src/dev/index.ts b/packages/nuxt-cli/src/dev/index.ts index fa5d2f84..dd98f252 100644 --- a/packages/nuxt-cli/src/dev/index.ts +++ b/packages/nuxt-cli/src/dev/index.ts @@ -1,5 +1,6 @@ import type { NuxtConfig } from '@nuxt/schema' import type { DevListenOverrides, Listener } from './listen' +import type { DevRestartReason } from './reason' import type { NuxtDevContext, NuxtDevIPCMessage, NuxtParentIPCMessage } from './utils' import process from 'node:process' @@ -13,6 +14,10 @@ import { NuxtDevServer } from './utils' const start = Date.now() +function formatErrorMessage(error: unknown): string { + return error instanceof Error ? error.toString() : 'Unhandled Rejection' +} + interface InitializeOptions { data?: { overrides?: NuxtConfig @@ -33,7 +38,7 @@ class IPC { process.exit(0) }) process.once('unhandledRejection', (reason) => { - this.send({ type: 'nuxt:internal:dev:rejection', message: reason instanceof Error ? reason.toString() : 'Unhandled Rejection' }) + this.send({ type: 'nuxt:internal:dev:rejection', message: formatErrorMessage(reason) }) process.exit() }) } @@ -61,11 +66,11 @@ interface InitializeReturn { listener: Listener close: () => Promise /** Reload Nuxt in place, keeping the current listener. */ - reload: (reason?: string) => Promise + reload: (reason?: DevRestartReason) => Promise onReady: (callback: (address: string) => void) => void /** Called the first time a watched file changes, before Nuxt reloads. */ onFileChange: (callback: () => void) => void - onRestart: (callback: (devServer: NuxtDevServer) => void) => void + onRestart: (callback: (reason?: DevRestartReason) => void) => void } export async function initialize(devContext: NuxtDevContext, ctx: InitializeOptions = {}): Promise { @@ -113,8 +118,8 @@ export async function initialize(devContext: NuxtDevContext, ctx: InitializeOpti devServer.on('loading', (message) => { ipc.send({ type: 'nuxt:internal:dev:loading', message }) }) - devServer.on('restart', () => { - ipc.send({ type: 'nuxt:internal:dev:restart' }) + devServer.on('restart', (reason) => { + ipc.send({ type: 'nuxt:internal:dev:restart', reason }) }) devServer.on('ready', (payload) => { ipc.send({ type: 'nuxt:internal:dev:ready', address: payload }) @@ -149,7 +154,7 @@ export async function initialize(devContext: NuxtDevContext, ctx: InitializeOpti return { listener: devServer.listener, - reload: (reason?: string) => devServer.load(true, reason), + reload: (reason?: DevRestartReason) => devServer.load(true, reason), close: () => { closePromise ??= (async () => { devServer.closeWatchers() @@ -176,14 +181,14 @@ export async function initialize(devContext: NuxtDevContext, ctx: InitializeOpti onFileChange: (callback: () => void) => { devServer.once('change', callback) }, - onRestart: (callback: (devServer: NuxtDevServer) => void) => { + onRestart: (callback: (reason?: DevRestartReason) => void) => { let restarted = false - function restart() { + function restart(reason?: DevRestartReason) { if (!restarted) { restarted = true process.off('uncaughtException', restartOnError) process.off('unhandledRejection', restartOnError) - callback(devServer) + callback(reason) } } function restartOnError(error: unknown) { @@ -191,7 +196,7 @@ export async function initialize(devContext: NuxtDevContext, ctx: InitializeOpti debug('Ignoring broken pipe:', error) return } - restart() + restart({ type: 'error', message: formatErrorMessage(error) }) } devServer.once('restart', restart) process.on('uncaughtException', restartOnError) diff --git a/packages/nuxt-cli/src/dev/reason.ts b/packages/nuxt-cli/src/dev/reason.ts new file mode 100644 index 00000000..b7409dc0 --- /dev/null +++ b/packages/nuxt-cli/src/dev/reason.ts @@ -0,0 +1,75 @@ +import { relativeTo } from '../utils/paths' + +/** Structured cause of a dev server reload or restart. */ +export type DevRestartReason + = | { type: 'config', files: string[], keys?: string[] } + | { type: 'dist-removed' } + | { type: 'hook' } + | { type: 'shortcut' } + | { type: 'error', message: string } + +export interface FormatRestartReasonOptions { + rootDir: string + /** A new process is taking over, rather than reloading in place. */ + hard?: boolean + /** Render file names as terminal hyperlinks. Disable for non-terminal output. */ + link?: boolean +} + +/** + * Combine a queued reason with a newer one, so a debounced reload can report + * every file that changed within the window. + */ +export function mergeRestartReasons(previous: DevRestartReason | undefined, next: DevRestartReason): DevRestartReason { + if (previous?.type !== 'config' || next.type !== 'config') { + return next + } + return { + type: 'config', + files: [...new Set([...previous.files, ...next.files])], + keys: previous.keys || next.keys + ? [...new Set([...previous.keys || [], ...next.keys || []])] + : undefined, + } +} + +/** Describe what caused a reload or restart, without saying what happens next. */ +export function formatRestartCause(reason: DevRestartReason, options: FormatRestartReasonOptions): string { + switch (reason.type) { + case 'config': { + const keys = reason.keys?.length ? ` (${reason.keys.join(', ')})` : '' + return `${formatFileList(reason.files, options)} changed${keys}` + } + case 'dist-removed': + return 'Build output was removed' + case 'hook': + return 'Nuxt requested a restart' + case 'shortcut': + return 'Restart requested' + case 'error': + return `Unhandled error: ${reason.message}` + } +} + +/** One line saying what caused a reload or restart and what is happening as a result. */ +export function formatRestartReason(reason: DevRestartReason | undefined, options: FormatRestartReasonOptions): string { + const action = options.hard ? 'Restarting Nuxt in a new process' : 'Reloading Nuxt' + if (!reason) { + return `${action}...` + } + return `${formatRestartCause(reason, options)}. ${action}...` +} + +const MAX_LISTED_FILES = 2 + +function formatFileList(files: string[], options: FormatRestartReasonOptions): string { + const names = files.map(file => options.link === false ? relativeTo(options.rootDir, file, { link: false }) : relativeTo(options.rootDir, file)) + if (names.length === 0) { + return 'A file' + } + if (names.length <= MAX_LISTED_FILES) { + return names.join(' and ') + } + const remaining = names.length - MAX_LISTED_FILES + return `${names.slice(0, MAX_LISTED_FILES).join(', ')} and ${remaining} other file${remaining === 1 ? '' : 's'}` +} diff --git a/packages/nuxt-cli/src/dev/utils.ts b/packages/nuxt-cli/src/dev/utils.ts index c8442df8..99004fa8 100644 --- a/packages/nuxt-cli/src/dev/utils.ts +++ b/packages/nuxt-cli/src/dev/utils.ts @@ -7,6 +7,7 @@ import type { Server as HttpServer, IncomingMessage, RequestListener, ServerResp import type { ResolvedCertificate } from './cert' import type { InspectOptions } from './inspect' import type { DevListenOverrides, Listener, ListenOptions } from './listen' +import type { DevRestartReason } from './reason' import EventEmitter from 'node:events' import { existsSync, readdirSync, statSync, watch } from 'node:fs' import { mkdir } from 'node:fs/promises' @@ -31,6 +32,7 @@ import { withNodePath } from '../utils/paths' import { renderError, renderErrorAnsi } from './error' import { listen } from './listen' import { resolvePortlessURLs } from './portless' +import { formatRestartReason, mergeRestartReasons } from './reason' export type NuxtParentIPCMessage = | { type: 'nuxt:internal:dev:context', context: NuxtDevContext, listenOverrides: DevListenOverrides, inspect?: InspectOptions } @@ -39,7 +41,7 @@ export type NuxtDevIPCMessage = | { type: 'nuxt:internal:dev:fork-ready' } | { type: 'nuxt:internal:dev:ready', address: string } | { type: 'nuxt:internal:dev:loading', message: string } - | { type: 'nuxt:internal:dev:restart' } + | { type: 'nuxt:internal:dev:restart', reason?: DevRestartReason } | { type: 'nuxt:internal:dev:rejection', message: string } | { type: 'nuxt:internal:dev:loading:error', error: Error } @@ -152,7 +154,7 @@ interface DevServerEventMap { 'loading:error': [error: Error] 'loading': [loadingMessage: string] 'ready': [address: string] - 'restart': [] + 'restart': [reason?: DevRestartReason] 'change': [] } @@ -169,15 +171,20 @@ export class NuxtDevServer extends EventEmitter { #inflightResponses = new Set() #lockCleanup?: () => void #lockedBuildDir?: string + #pendingReason?: DevRestartReason - loadDebounced: (reload?: boolean, reason?: string) => void + loadDebounced: () => void handler: RequestListener listener!: Listener constructor(private options: NuxtDevServerOptions) { super() - this.loadDebounced = debounce(this.load) + this.loadDebounced = debounce(() => { + const reason = this.#pendingReason + this.#pendingReason = undefined + return this.load(true, reason) + }) let _initResolve: () => void const _initPromise = new Promise((resolve) => { @@ -268,7 +275,16 @@ export class NuxtDevServer extends EventEmitter { this.#configWatcher?.() } - async load(reload?: boolean, reason?: string): Promise { + /** + * Queue a debounced in-place reload, accumulating the reasons for every + * change that arrives within the debounce window. + */ + scheduleReload(reason: DevRestartReason): void { + this.#pendingReason = mergeRestartReasons(this.#pendingReason, reason) + this.loadDebounced() + } + + async load(reload?: boolean, reason?: DevRestartReason): Promise { try { this.closeWatchers() @@ -435,10 +451,10 @@ export class NuxtDevServer extends EventEmitter { const unsub = this.#currentNuxt.hooks.hook('restart', async (options) => { unsub() // We use this instead of `hookOnce` for Nuxt Bridge support if (options?.hard) { - this.emit('restart') + this.emit('restart', { type: 'hook' }) return } - await this.load(true) + await this.load(true, { type: 'hook' }) }) if (this.#currentNuxt.server && 'upgrade' in this.#currentNuxt.server) { @@ -503,7 +519,7 @@ export class NuxtDevServer extends EventEmitter { return } - this.loadDebounced(true, '.nuxt/dist directory has been removed') + this.scheduleReload({ type: 'dist-removed' }) }) if ('handler' in this.#currentNuxt.server) { @@ -592,15 +608,16 @@ export class NuxtDevServer extends EventEmitter { this.#websocketConnections.clear() } - async #load(reload?: boolean, reason?: string): Promise { - const action = reload ? 'Restarting' : 'Starting' - this.#loadingMessage = `${reason ? `${reason}. ` : ''}${action} Nuxt...` + async #load(reload?: boolean, reason?: DevRestartReason): Promise { + this.#loadingMessage = reload + ? formatRestartReason(reason, { rootDir: this.#cwd, link: false }) + : 'Starting Nuxt...' this.#handler = undefined this.#settleInflightResponses() this.emit('loading', this.#loadingMessage) if (reload) { // eslint-disable-next-line no-console - console.info(this.#loadingMessage) + console.info(formatRestartReason(reason, { rootDir: this.#cwd })) } await this.close() @@ -617,10 +634,10 @@ export class NuxtDevServer extends EventEmitter { this.#configWatcher = createConfigWatcher( this.#cwd, this.options.dotenv.fileName, - () => this.emit('restart'), + file => this.emit('restart', { type: 'config', files: [file] }), (file) => { this.emit('change') - this.loadDebounced(true, `${file} updated`) + this.scheduleReload({ type: 'config', files: [file] }) }, getLocalLayerDirs(this.#currentNuxt?.options._layers ?? [], this.#cwd), ) @@ -690,12 +707,12 @@ export function getLocalLayerDirs(layers: ReadonlyArray<{ cwd?: string, config?: return [...dirs] } -function createConfigWatcher(cwd: string, dotenvFileName: string | string[] = '.env', onRestart: () => void, onReload: (file: string) => void, layerDirs: string[] = []) { +function createConfigWatcher(cwd: string, dotenvFileName: string | string[] = '.env', onRestart: (file: string) => void, onReload: (file: string) => void, layerDirs: string[] = []) { const dotenvFileNames = new Set(Array.isArray(dotenvFileName) ? dotenvFileName : [dotenvFileName]) // each local layer dir is watched alongside the root, but only the root restarts on dotenv changes. const closers = [ - watchConfigDir(cwd, onReload, file => dotenvFileNames.has(file) && onRestart()), + watchConfigDir(cwd, onReload, (file, path) => dotenvFileNames.has(file) && onRestart(path)), ...layerDirs.map(dir => watchConfigDir(dir, onReload)), ] @@ -706,7 +723,7 @@ function createConfigWatcher(cwd: string, dotenvFileName: string | string[] = '. } } -function watchConfigDir(dir: string, onReload: (file: string) => void, onFile?: (file: string) => void) { +function watchConfigDir(dir: string, onReload: (path: string) => void, onFile?: (file: string, path: string) => void) { const fileWatcher = new FileChangeTracker() fileWatcher.prime(dir) const watcher = watch(dir) @@ -717,10 +734,10 @@ function watchConfigDir(dir: string, onReload: (file: string) => void, onFile?: return } - onFile?.(file) + onFile?.(file, resolve(dir, file)) if (RESTART_RE.test(file)) { - onReload(file) + onReload(resolve(dir, file)) } if (file === '.config') { @@ -734,7 +751,7 @@ function watchConfigDir(dir: string, onReload: (file: string) => void, onFile?: } } -function createConfigDirWatcher(cwd: string, onReload: (file: string) => void) { +function createConfigDirWatcher(cwd: string, onReload: (path: string) => void) { const configDir = join(cwd, '.config') const fileWatcher = new FileChangeTracker() @@ -746,7 +763,7 @@ function createConfigDirWatcher(cwd: string, onReload: (file: string) => void) { } if (RESTART_RE.test(file)) { - onReload(file) + onReload(resolve(configDir, file)) } }) diff --git a/packages/nuxt-cli/test/e2e/dev-restart.spec.ts b/packages/nuxt-cli/test/e2e/dev-restart.spec.ts index 89003009..48360371 100644 --- a/packages/nuxt-cli/test/e2e/dev-restart.spec.ts +++ b/packages/nuxt-cli/test/e2e/dev-restart.spec.ts @@ -32,7 +32,7 @@ describe('dev server reload', () => { expect(started).toBe(true) }, { timeout: 30_000, interval: 100 }) - await reload('test reload') + await reload({ type: 'shortcut' }) const timer = new Promise<'hanging'>(resolve => setTimeout(resolve, 10_000, 'hanging').unref()) await expect(Promise.race([inflight, timer])).resolves.toBe('status:503') diff --git a/packages/nuxt-cli/test/unit/restart-reason.spec.ts b/packages/nuxt-cli/test/unit/restart-reason.spec.ts new file mode 100644 index 00000000..cdff2308 --- /dev/null +++ b/packages/nuxt-cli/test/unit/restart-reason.spec.ts @@ -0,0 +1,85 @@ +import { join } from 'pathe' +import { describe, expect, it } from 'vitest' + +import { formatRestartCause, formatRestartReason, mergeRestartReasons } from '../../src/dev/reason' + +const rootDir = '/project' +const options = { rootDir, link: false } as const + +describe('formatRestartCause', () => { + it('should name a changed config file relative to the root directory', () => { + expect(formatRestartCause({ type: 'config', files: [join(rootDir, 'nuxt.config.ts')] }, options)) + .toBe('nuxt.config.ts changed') + }) + + it('should name nested config files relative to the root directory', () => { + expect(formatRestartCause({ type: 'config', files: [join(rootDir, '.config/nuxt.ts')] }, options)) + .toBe('.config/nuxt.ts changed') + }) + + it('should list two files without truncating', () => { + const files = [join(rootDir, 'nuxt.config.ts'), join(rootDir, '.nuxtrc')] + expect(formatRestartCause({ type: 'config', files }, options)) + .toBe('nuxt.config.ts and .nuxtrc changed') + }) + + it('should truncate longer file lists with a count', () => { + const files = ['nuxt.config.ts', '.nuxtrc', '.nuxtignore', '.env'].map(file => join(rootDir, file)) + expect(formatRestartCause({ type: 'config', files }, options)) + .toBe('nuxt.config.ts, .nuxtrc and 2 other files changed') + }) + + it('should use the singular form for a single remaining file', () => { + const files = ['nuxt.config.ts', '.nuxtrc', '.nuxtignore'].map(file => join(rootDir, file)) + expect(formatRestartCause({ type: 'config', files }, options)) + .toBe('nuxt.config.ts, .nuxtrc and 1 other file changed') + }) + + it('should append changed config keys when known', () => { + expect(formatRestartCause({ type: 'config', files: [join(rootDir, 'nuxt.config.ts')], keys: ['ssr', 'modules'] }, options)) + .toBe('nuxt.config.ts changed (ssr, modules)') + }) + + it('should describe non-file causes', () => { + expect(formatRestartCause({ type: 'dist-removed' }, options)).toBe('Build output was removed') + expect(formatRestartCause({ type: 'hook' }, options)).toBe('Nuxt requested a restart') + expect(formatRestartCause({ type: 'shortcut' }, options)).toBe('Restart requested') + expect(formatRestartCause({ type: 'error', message: 'Error: boom' }, options)).toBe('Unhandled error: Error: boom') + }) +}) + +describe('formatRestartReason', () => { + it('should distinguish an in-place reload from a hard restart', () => { + const reason = { type: 'shortcut' } as const + expect(formatRestartReason(reason, options)).toBe('Restart requested. Reloading Nuxt in place...') + expect(formatRestartReason(reason, { ...options, hard: true })).toBe('Restart requested. Restarting Nuxt in a new process...') + }) + + it('should fall back to the action alone when no reason is known', () => { + expect(formatRestartReason(undefined, options)).toBe('Reloading Nuxt in place...') + }) +}) + +describe('mergeRestartReasons', () => { + it('should combine config files and drop duplicates', () => { + const merged = mergeRestartReasons( + { type: 'config', files: ['/project/nuxt.config.ts'] }, + { type: 'config', files: ['/project/nuxt.config.ts', '/project/.nuxtrc'] }, + ) + expect(merged).toEqual({ type: 'config', files: ['/project/nuxt.config.ts', '/project/.nuxtrc'], keys: undefined }) + }) + + it('should combine known config keys', () => { + const merged = mergeRestartReasons( + { type: 'config', files: ['/project/nuxt.config.ts'], keys: ['ssr'] }, + { type: 'config', files: ['/project/nuxt.config.ts'], keys: ['modules'] }, + ) + expect(merged).toMatchObject({ keys: ['ssr', 'modules'] }) + }) + + it('should prefer the newer reason when the types differ', () => { + expect(mergeRestartReasons({ type: 'config', files: ['/project/nuxt.config.ts'] }, { type: 'dist-removed' })) + .toEqual({ type: 'dist-removed' }) + expect(mergeRestartReasons(undefined, { type: 'shortcut' })).toEqual({ type: 'shortcut' }) + }) +}) From 975d980963aeb1df208be29b3fc1c9ad4fb580f1 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 27 Jul 2026 12:34:39 +0100 Subject: [PATCH 3/4] test: fix assertion --- packages/nuxt-cli/test/unit/restart-reason.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt-cli/test/unit/restart-reason.spec.ts b/packages/nuxt-cli/test/unit/restart-reason.spec.ts index cdff2308..64c3ebcd 100644 --- a/packages/nuxt-cli/test/unit/restart-reason.spec.ts +++ b/packages/nuxt-cli/test/unit/restart-reason.spec.ts @@ -51,12 +51,12 @@ describe('formatRestartCause', () => { describe('formatRestartReason', () => { it('should distinguish an in-place reload from a hard restart', () => { const reason = { type: 'shortcut' } as const - expect(formatRestartReason(reason, options)).toBe('Restart requested. Reloading Nuxt in place...') + expect(formatRestartReason(reason, options)).toBe('Restart requested. Reloading Nuxt...') expect(formatRestartReason(reason, { ...options, hard: true })).toBe('Restart requested. Restarting Nuxt in a new process...') }) it('should fall back to the action alone when no reason is known', () => { - expect(formatRestartReason(undefined, options)).toBe('Reloading Nuxt in place...') + expect(formatRestartReason(undefined, options)).toBe('Reloading Nuxt...') }) }) From f9a53e5072bd96ac3e643776058437d62c529f7e Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 27 Jul 2026 12:08:20 +0000 Subject: [PATCH 4/4] test: assert native path separators in restart reason spec --- packages/nuxt-cli/test/unit/restart-reason.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nuxt-cli/test/unit/restart-reason.spec.ts b/packages/nuxt-cli/test/unit/restart-reason.spec.ts index 64c3ebcd..c534589d 100644 --- a/packages/nuxt-cli/test/unit/restart-reason.spec.ts +++ b/packages/nuxt-cli/test/unit/restart-reason.spec.ts @@ -1,3 +1,5 @@ +import { sep } from 'node:path' + import { join } from 'pathe' import { describe, expect, it } from 'vitest' @@ -14,7 +16,7 @@ describe('formatRestartCause', () => { it('should name nested config files relative to the root directory', () => { expect(formatRestartCause({ type: 'config', files: [join(rootDir, '.config/nuxt.ts')] }, options)) - .toBe('.config/nuxt.ts changed') + .toBe(`.config${sep}nuxt.ts changed`) }) it('should list two files without truncating', () => {