fix(cli): dev/dev:offline Ctrl-C 退出防重入并支持二次强制退出

This commit is contained in:
rbetree
2026-01-16 20:51:10 +08:00
parent 87d1f0244c
commit 0e154bc43e
3 changed files with 102 additions and 17 deletions

View File

@@ -6,12 +6,35 @@ const { startServer } = require('./serve-dist');
const log = createLogger('dev:offline'); const log = createLogger('dev:offline');
let serverRef = null; let serverRef = null;
let shuttingDown = false;
function runNode(scriptPath) { function runNode(scriptPath) {
const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' }); const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' });
return result && Number.isFinite(result.status) ? result.status : 1; 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() { async function main() {
const elapsedMs = startTimer(); const elapsedMs = startTimer();
log.info('开始', { version: process.env.npm_package_version }); log.info('开始', { version: process.env.npm_package_version });
@@ -37,13 +60,24 @@ async function main() {
log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` }); log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` });
const shutdown = () => { const shutdown = (signal) => {
log.info('正在关闭...'); if (shuttingDown) return;
if (!serverRef) process.exit(0); shuttingDown = true;
serverRef.close(() => process.exit(0));
// 让 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) { if (require.main === module) {

View File

@@ -6,12 +6,35 @@ const { startServer } = require('./serve-dist');
const log = createLogger('dev'); const log = createLogger('dev');
let serverRef = null; let serverRef = null;
let shuttingDown = false;
function runNode(scriptPath) { function runNode(scriptPath) {
const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' }); const result = spawnSync(process.execPath, [scriptPath], { stdio: 'inherit' });
return result && Number.isFinite(result.status) ? result.status : 1; 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() { async function main() {
const elapsedMs = startTimer(); const elapsedMs = startTimer();
log.info('开始', { version: process.env.npm_package_version }); log.info('开始', { version: process.env.npm_package_version });
@@ -45,13 +68,21 @@ async function main() {
log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` }); log.ok('就绪', { ms: elapsedMs(), url: `http://localhost:${actualPort}` });
const shutdown = () => { const shutdown = (signal) => {
log.info('正在关闭...'); if (shuttingDown) return;
if (!serverRef) process.exit(0); shuttingDown = true;
serverRef.close(() => process.exit(0));
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) { if (require.main === module) {

View File

@@ -170,13 +170,33 @@ async function main() {
log.ok('就绪', { url: `http://localhost:${actualPort}` }); log.ok('就绪', { url: `http://localhost:${actualPort}` });
const shutdown = () => { let shuttingDown = false;
log.info('正在关闭...'); const shutdown = (signal) => {
server.close(() => process.exit(0)); 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.once('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', shutdown); process.once('SIGTERM', () => shutdown('SIGTERM'));
} }
if (require.main === module) { if (require.main === module) {