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