fix(lifecycle): use ProcessLifecycle for coordinated shutdown

Replace per-Watcher SIGINT handlers with a single ProcessLifecycle.install()
in TsWatch.start(). This eliminates competing signal handler races that left
orphaned child processes. Add @push.rocks/smartexit as direct dependency.
This commit is contained in:
2026-03-03 23:43:26 +00:00
parent 91b3e273de
commit e8bd8da3c7
5 changed files with 34 additions and 42 deletions

View File

@@ -181,34 +181,13 @@ export class Watcher {
}
/**
* this method sets up a clean exit strategy
* Sets up timeout-based cleanup if configured.
* Signal handling (SIGINT/SIGTERM) is managed globally by ProcessLifecycle in TsWatch.
*/
private async setupCleanup() {
// Last-resort synchronous cleanup — 'exit' event cannot await async work.
// By this point, SIGINT handler should have already called stop().
process.on('exit', () => {
if (this.currentExecution && !this.currentExecution.childProcess.killed) {
const pid = this.currentExecution.childProcess.pid;
if (pid) {
try {
process.kill(pid, 'SIGKILL');
} catch {
// Process may already be dead
}
}
}
});
process.on('SIGINT', async () => {
console.log('');
console.log('ok! got SIGINT We are exiting! Just cleaning up to exit neatly :)');
await this.stop();
process.exit(0);
});
// handle timeout
if (this.options.timeout) {
plugins.smartdelay.delayFor(this.options.timeout).then(async () => {
console.log(`timed out afer ${this.options.timeout} milliseconds! exiting!`);
console.log(`timed out after ${this.options.timeout} milliseconds! exiting!`);
await this.stop();
process.exit(0);
});