From 0e154bc43ebee9b612780420d1731114e4caff34 Mon Sep 17 00:00:00 2001 From: rbetree Date: Fri, 16 Jan 2026 20:51:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(cli):=20dev/dev:offline=20Ctrl-C=20?= =?UTF-8?q?=E9=80=80=E5=87=BA=E9=98=B2=E9=87=8D=E5=85=A5=E5=B9=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E4=BA=8C=E6=AC=A1=E5=BC=BA=E5=88=B6=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/dev-offline.js | 46 ++++++++++++++++++++++++++++++++++++------ scripts/dev.js | 43 +++++++++++++++++++++++++++++++++------ scripts/serve-dist.js | 30 ++++++++++++++++++++++----- 3 files changed, 102 insertions(+), 17 deletions(-) diff --git a/scripts/dev-offline.js b/scripts/dev-offline.js index fa191d7..205a343 100644 --- a/scripts/dev-offline.js +++ b/scripts/dev-offline.js @@ -6,12 +6,35 @@ const { startServer } = require('./serve-dist'); const log = createLogger('dev:offline'); let serverRef = null; +let shuttingDown = false; function runNode(scriptPath) { const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' }); return result && Number.isFinite(result.status) ? result.status : 1; } +function closeServer(server, exitCode) { + if (!server) { + process.exit(exitCode); + return; + } + + try { + if (typeof server.closeIdleConnections === 'function') server.closeIdleConnections(); + if (typeof server.closeAllConnections === 'function') server.closeAllConnections(); + } catch { + // ignore + } + + const forceTimer = setTimeout(() => process.exit(exitCode), 2000); + if (typeof forceTimer.unref === 'function') forceTimer.unref(); + + server.close(() => { + clearTimeout(forceTimer); + process.exit(exitCode); + }); +} + async function main() { const elapsedMs = startTimer(); log.info('开始', { version: process.env.npm_package_version }); @@ -37,13 +60,24 @@ async function main() { log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` }); - const shutdown = () => { - log.info('正在关闭...'); - if (!serverRef) process.exit(0); - serverRef.close(() => process.exit(0)); + const shutdown = (signal) => { + if (shuttingDown) return; + shuttingDown = true; + + // 让 Ctrl-C 的 ^C 与日志分行显示 + process.stdout.write('\n'); + + log.info('正在关闭...', { signal }); + + // 再次 SIGINT:直接强制退出 + process.once('SIGINT', () => process.exit(130)); + + const exit = signal === 'SIGINT' ? 130 : 0; + closeServer(serverRef, exit); }; - process.on('SIGINT', shutdown); - process.on('SIGTERM', shutdown); + + process.once('SIGINT', () => shutdown('SIGINT')); + process.once('SIGTERM', () => shutdown('SIGTERM')); } if (require.main === module) { diff --git a/scripts/dev.js b/scripts/dev.js index 9d5d71c..5b01b22 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -6,12 +6,35 @@ const { startServer } = require('./serve-dist'); const log = createLogger('dev'); let serverRef = null; +let shuttingDown = false; function runNode(scriptPath) { const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' }); return result && Number.isFinite(result.status) ? result.status : 1; } +function closeServer(server, exitCode) { + if (!server) { + process.exit(exitCode); + return; + } + + try { + if (typeof server.closeIdleConnections === 'function') server.closeIdleConnections(); + if (typeof server.closeAllConnections === 'function') server.closeAllConnections(); + } catch { + // ignore + } + + const forceTimer = setTimeout(() => process.exit(exitCode), 2000); + if (typeof forceTimer.unref === 'function') forceTimer.unref(); + + server.close(() => { + clearTimeout(forceTimer); + process.exit(exitCode); + }); +} + async function main() { const elapsedMs = startTimer(); log.info('开始', { version: process.env.npm_package_version }); @@ -45,13 +68,21 @@ async function main() { log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` }); - const shutdown = () => { - log.info('正在关闭...'); - if (!serverRef) process.exit(0); - serverRef.close(() => process.exit(0)); + const shutdown = (signal) => { + if (shuttingDown) return; + shuttingDown = true; + + process.stdout.write('\n'); + log.info('正在关闭...', { signal }); + + process.once('SIGINT', () => process.exit(130)); + + const exit = signal === 'SIGINT' ? 130 : 0; + closeServer(serverRef, exit); }; - process.on('SIGINT', shutdown); - process.on('SIGTERM', shutdown); + + process.once('SIGINT', () => shutdown('SIGINT')); + process.once('SIGTERM', () => shutdown('SIGTERM')); } if (require.main === module) { diff --git a/scripts/serve-dist.js b/scripts/serve-dist.js index 733bac6..62abcf2 100644 --- a/scripts/serve-dist.js +++ b/scripts/serve-dist.js @@ -170,13 +170,33 @@ async function main() { log.ok('就绪', { url: `http://localhost:${actualPort}` }); - const shutdown = () => { - log.info('正在关闭...'); - server.close(() => process.exit(0)); + let shuttingDown = false; + const shutdown = (signal) => { + if (shuttingDown) return; + shuttingDown = true; + + process.stdout.write('\n'); + log.info('正在关闭...', { signal }); + + try { + if (typeof server.closeIdleConnections === 'function') server.closeIdleConnections(); + if (typeof server.closeAllConnections === 'function') server.closeAllConnections(); + } catch { + // ignore + } + + const exit = signal === 'SIGINT' ? 130 : 0; + const forceTimer = setTimeout(() => process.exit(exit), 2000); + if (typeof forceTimer.unref === 'function') forceTimer.unref(); + + server.close(() => { + clearTimeout(forceTimer); + process.exit(exit); + }); }; - process.on('SIGINT', shutdown); - process.on('SIGTERM', shutdown); + process.once('SIGINT', () => shutdown('SIGINT')); + process.once('SIGTERM', () => shutdown('SIGTERM')); } if (require.main === module) {