-
Notifications
You must be signed in to change notification settings - Fork 5
/
cli.ts
105 lines (89 loc) · 2.5 KB
/
cli.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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { ChildProcess, fork } from 'child_process'
import { extname, resolve } from 'path'
import kleur from 'kleur'
import type * as worker from './worker/index.js'
import { fileURLToPath } from 'url'
type Event = Event.Start | Event.Env | Event.Heartbeat
namespace Event {
export interface Start {
type: 'start'
}
export interface Env {
type: 'shared'
body: string
}
export interface Heartbeat {
type: 'heartbeat'
}
}
let child: ChildProcess
process.env.CORDIS_SHARED = JSON.stringify({
startTime: Date.now(),
})
function createWorker(options: worker.Options) {
let timer: 0 | NodeJS.Timeout | undefined
let started = false
const filename = fileURLToPath(import.meta.url)
child = fork(resolve(filename, `../worker/main${extname(filename)}`), [], {
execArgv: [
...process.execArgv,
...options.daemon?.execArgv || [],
],
env: {
...process.env,
CORDIS_LOADER_OPTIONS: JSON.stringify(options),
},
})
child.on('message', (message: Event) => {
if (message.type === 'start') {
started = true
timer = options.daemon?.heartbeatTimeout && setTimeout(() => {
// eslint-disable-next-line no-console
console.log(kleur.red('daemon: heartbeat timeout'))
child.kill('SIGKILL')
}, options.daemon?.heartbeatTimeout)
} else if (message.type === 'shared') {
process.env.CORDIS_SHARED = message.body
} else if (message.type === 'heartbeat') {
if (timer) timer.refresh()
}
})
// https://nodejs.org/api/process.html#signal-events
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
const signals: NodeJS.Signals[] = [
'SIGABRT',
'SIGBREAK',
'SIGBUS',
'SIGFPE',
'SIGHUP',
'SIGILL',
'SIGINT',
'SIGKILL',
'SIGSEGV',
'SIGSTOP',
'SIGTERM',
]
function shouldExit(code: number, signal: NodeJS.Signals) {
// start failed
if (!started) return true
// exit manually
if (code === 0) return true
if (signals.includes(signal)) return true
// restart manually
if (code === 51) return false
if (code === 52) return true
// fallback to autoRestart
return !options.daemon?.autoRestart
}
child.on('exit', (code, signal) => {
if (shouldExit(code!, signal!)) {
process.exit(code!)
}
createWorker(options)
})
}
export async function start(options: worker.Options) {
if (options.daemon) return createWorker(options)
const worker = await import('./worker/index.js')
worker.start(options)
}