codekingpro/portable-devtools
114k
1const signals = require('./signals.js')
2
3// for testing, expose the process being used
4module.exports = Object.assign(fn => setup(fn), { process })
5
6// do all of this in a setup function so that we can call it
7// multiple times for multiple reifies that might be going on.
8// Otherwise, Arborist.reify() is a global action, which is a
9// new constraint we'd be adding with this behavior.
10const setup = fn => {
11 const { process } = module.exports
12
13 const sigListeners = { loaded: false }
14
15 const unload = () => {
16 if (!sigListeners.loaded) {
17 return
18 }
19 for (const sig of signals) {
20 try {
21 process.removeListener(sig, sigListeners[sig])
22 } catch {
23 // ignore errors
24 }
25 }
26 process.removeListener('beforeExit', onBeforeExit)
27 sigListeners.loaded = false
28 }
29
30 const onBeforeExit = () => {
31 // this trick ensures that we exit with the same signal we caught
32 // Ie, if you press ^C and npm gets a SIGINT, we'll do the rollback
33 // and then exit with a SIGINT signal once we've removed the handler.
34 // The timeout is there because signals are asynchronous, so we need
35 // the process to NOT exit on its own, which means we have to have
36 // something keeping the event loop looping. Hence this hack.
37 unload()
38 process.kill(process.pid, signalReceived)
39 setTimeout(() => {}, 500)
40 }
41
42 let signalReceived = null
43 const listener = (sig, fn) => () => {
44 signalReceived = sig
45
46 // if we exit normally, but caught a signal which would have been fatal,
47 // then re-send it once we're done with whatever cleanup we have to do.
48 unload()
49 if (process.listeners(sig).length < 1) {
50 process.once('beforeExit', onBeforeExit)
51 }
52
53 fn({ signal: sig })
54 }
55
56 // do the actual loading here
57 for (const sig of signals) {
58 sigListeners[sig] = listener(sig, fn)
59 const max = process.getMaxListeners()
60 try {
61 // if we call this a bunch of times, avoid triggering the warning
62 const { length } = process.listeners(sig)
63 if (length >= max) {
64 process.setMaxListeners(length + 1)
65 }
66 process.on(sig, sigListeners[sig])
67 } catch {
68 // ignore errors
69 }
70 }
71 sigListeners.loaded = true
72
73 return unload
74}
75 