/
kill-on-signal.ts
55 lines (50 loc) · 1.92 KB
/
kill-on-signal.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import EventEmitter from 'events';
import { map } from 'rxjs/operators';
import { Command } from '../command';
import { FlowController } from './flow-controller';
/**
* Watches the main concurrently process for signals and sends the same signal down to each spawned
* command.
*/
export class KillOnSignal implements FlowController {
private readonly process: EventEmitter;
private readonly abortController?: AbortController;
constructor({
process,
abortController,
}: {
process: EventEmitter;
abortController?: AbortController;
}) {
this.process = process;
this.abortController = abortController;
}
handle(commands: Command[]) {
let caughtSignal: NodeJS.Signals;
(['SIGINT', 'SIGTERM', 'SIGHUP'] as NodeJS.Signals[]).forEach((signal) => {
this.process.on(signal, () => {
caughtSignal = signal;
this.abortController?.abort();
commands.forEach((command) => command.kill(signal));
});
});
return {
commands: commands.map((command) => {
const closeStream = command.close.pipe(
map((exitInfo) => {
const exitCode = caughtSignal === 'SIGINT' ? 0 : exitInfo.exitCode;
return { ...exitInfo, exitCode };
}),
);
// Return a proxy so that mutations happen on the original Command object.
// If either `Object.assign()` or `Object.create()` were used, it'd be hard to
// reflect the mutations on Command objects referenced by previous flow controllers.
return new Proxy(command, {
get(target, prop: keyof Command) {
return prop === 'close' ? closeStream : target[prop];
},
});
}),
};
}
}