Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a61956d
feat(nuxi): add `q` and `quit` shortcuts to dev server
userquin Jul 23, 2026
f0ff0b2
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
cde0f75
chore: move logic to nuxt dev server
userquin Jul 23, 2026
a3dae4a
chore: move readline logic after emitting ready event
userquin Jul 23, 2026
6bfa8ea
chore: apply gh copilot suggestions
userquin Jul 23, 2026
1db0e92
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
c243b0d
chore: move cleanup readline to close, init nuxt should recreate it
userquin Jul 23, 2026
fb0a35f
Merge remote-tracking branch 'origin/add-q-dev-shortcut' into add-q-d…
userquin Jul 23, 2026
a381a70
chore: keep original close at dev entry
userquin Jul 23, 2026
2c24c5f
chore: remove async and cleanup readline stuff also at #quitListener
userquin Jul 23, 2026
cd85045
chore: add `onBeforeQuit` and `onQuit` to allow for cleanup before a …
userquin Jul 23, 2026
ac9e76e
chore: let caller mutate the options
userquin Jul 23, 2026
b127aa0
chore: remove prepareQuit and use `onRestart` to override `onBeforeQuit`
userquin Jul 23, 2026
de0628f
chore: apply gh copilot suggestion
userquin Jul 23, 2026
11d7fa9
chore: stop also profiler gracefully
userquin Jul 23, 2026
6fe397d
chore: add initialize dev server spec
userquin Jul 23, 2026
c326b7f
chore: use `once` instead `on` when registering `closing` listener to…
userquin Jul 23, 2026
2f9697a
chore: apply gh copilot suggestion on closing test
userquin Jul 23, 2026
8d1cb9a
chore: .
userquin Jul 23, 2026
1967b94
chore: delegate onBeforeQuit to allow restarting via IPC
userquin Jul 23, 2026
03c5954
chore: add the test suggested by the damn rabbit
userquin Jul 23, 2026
ecbf37b
chore: mock error to prevent `boom` error in the terminal
userquin Jul 23, 2026
576a5cb
feat(dev): add `q + enter` shortcut to quit
danielroe Jul 25, 2026
9971ef8
chore: merge origin/main
danielroe Jul 25, 2026
ba79b09
fix(dev): make dev server close idempotent
danielroe Jul 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions packages/nuxt-cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import type { DevListenOverrides } from '../dev/listen'
import type { NuxtDevContext } from '../dev/utils'

import process from 'node:process'
import { createInterface } from 'node:readline'

import { defineCommand } from 'citty'
import { resolve } from 'pathe'
import colors from 'picocolors'
import { isBun, isTest } from 'std-env'
import { isBun, isCI, isTest } from 'std-env'
import { satisfies } from 'verkit'

import { initialize } from '../dev'
Expand Down Expand Up @@ -133,6 +134,7 @@ const command = defineCommand({

// Disable forking when profiling to capture all activity in one process
if (!ctx.args.fork || ctx.args.profile) {
setupQuitShortcut(close, onReady)
return {
listener,
close,
Expand Down Expand Up @@ -187,14 +189,15 @@ const command = defineCommand({
await restartWithFork()
})

async function closeAll() {
cleanupCurrentFork?.()
await close()
}

setupQuitShortcut(closeAll, onReady)

return {
async close() {
cleanupCurrentFork?.()
await Promise.all([
listener.close(),
close(),
])
},
close: closeAll,
}
},
})
Expand All @@ -203,6 +206,35 @@ export default command

// --- Internal ---

function setupQuitShortcut(close: () => Promise<void>, onReady: (callback: (address: string) => void) => void) {
if (!process.stdin.isTTY || isCI || isTest) {
return
}

onReady(() => {
// eslint-disable-next-line no-console
console.log(`\n ${colors.dim('press')} ${colors.bold('q + enter')} ${colors.dim('to quit')}\n`)
})

// No output stream is passed, so readline stays in non-terminal mode and
// does not intercept Ctrl-C or put stdin into raw mode
const rl = createInterface({ input: process.stdin })
rl.on('line', async (line) => {
if (!['q', 'quit', 'exit'].includes(line.trim().toLowerCase())) {
return
}
rl.close()
try {
await close()
}
catch (error) {
console.error(error)
process.exitCode = 1
}
process.exit()
})
}

type ArgsT = Exclude<
Awaited<typeof command.args>,
undefined | ((...args: unknown[]) => unknown)
Expand Down
23 changes: 16 additions & 7 deletions packages/nuxt-cli/src/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,24 @@ export async function initialize(devContext: NuxtDevContext, ctx: InitializeOpti
}
}

let closePromise: Promise<void> | undefined

return {
listener: devServer.listener,
close: async () => {
devServer.closeWatchers()
await Promise.all([
devServer.listener.close(),
devServer.close(),
])
devServer.releaseLock()
close: () => {
closePromise ??= (async () => {
devServer.closeWatchers()
try {
await Promise.all([
devServer.listener.close(),
devServer.close(),
])
}
finally {
devServer.releaseLock()
}
})()
return closePromise
},
onReady: (callback: (address: string) => void) => {
if (address) {
Expand Down
Loading