index.js (5708B)
1 // Note: since nyc uses this module to output coverage, any lines 2 // that are in the direct sync flow of nyc's outputCoverage are 3 // ignored, since we can never get coverage for them. 4 // grab a reference to node's real process object right away 5 var process = global.process 6 7 const processOk = function (process) { 8 return process && 9 typeof process === 'object' && 10 typeof process.removeListener === 'function' && 11 typeof process.emit === 'function' && 12 typeof process.reallyExit === 'function' && 13 typeof process.listeners === 'function' && 14 typeof process.kill === 'function' && 15 typeof process.pid === 'number' && 16 typeof process.on === 'function' 17 } 18 19 // some kind of non-node environment, just no-op 20 /* istanbul ignore if */ 21 if (!processOk(process)) { 22 module.exports = function () { 23 return function () {} 24 } 25 } else { 26 var assert = require('assert') 27 var signals = require('./signals.js') 28 var isWin = /^win/i.test(process.platform) 29 30 var EE = require('events') 31 /* istanbul ignore if */ 32 if (typeof EE !== 'function') { 33 EE = EE.EventEmitter 34 } 35 36 var emitter 37 if (process.__signal_exit_emitter__) { 38 emitter = process.__signal_exit_emitter__ 39 } else { 40 emitter = process.__signal_exit_emitter__ = new EE() 41 emitter.count = 0 42 emitter.emitted = {} 43 } 44 45 // Because this emitter is a global, we have to check to see if a 46 // previous version of this library failed to enable infinite listeners. 47 // I know what you're about to say. But literally everything about 48 // signal-exit is a compromise with evil. Get used to it. 49 if (!emitter.infinite) { 50 emitter.setMaxListeners(Infinity) 51 emitter.infinite = true 52 } 53 54 module.exports = function (cb, opts) { 55 /* istanbul ignore if */ 56 if (!processOk(global.process)) { 57 return function () {} 58 } 59 assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler') 60 61 if (loaded === false) { 62 load() 63 } 64 65 var ev = 'exit' 66 if (opts && opts.alwaysLast) { 67 ev = 'afterexit' 68 } 69 70 var remove = function () { 71 emitter.removeListener(ev, cb) 72 if (emitter.listeners('exit').length === 0 && 73 emitter.listeners('afterexit').length === 0) { 74 unload() 75 } 76 } 77 emitter.on(ev, cb) 78 79 return remove 80 } 81 82 var unload = function unload () { 83 if (!loaded || !processOk(global.process)) { 84 return 85 } 86 loaded = false 87 88 signals.forEach(function (sig) { 89 try { 90 process.removeListener(sig, sigListeners[sig]) 91 } catch (er) {} 92 }) 93 process.emit = originalProcessEmit 94 process.reallyExit = originalProcessReallyExit 95 emitter.count -= 1 96 } 97 module.exports.unload = unload 98 99 var emit = function emit (event, code, signal) { 100 /* istanbul ignore if */ 101 if (emitter.emitted[event]) { 102 return 103 } 104 emitter.emitted[event] = true 105 emitter.emit(event, code, signal) 106 } 107 108 // { <signal>: <listener fn>, ... } 109 var sigListeners = {} 110 signals.forEach(function (sig) { 111 sigListeners[sig] = function listener () { 112 /* istanbul ignore if */ 113 if (!processOk(global.process)) { 114 return 115 } 116 // If there are no other listeners, an exit is coming! 117 // Simplest way: remove us and then re-send the signal. 118 // We know that this will kill the process, so we can 119 // safely emit now. 120 var listeners = process.listeners(sig) 121 if (listeners.length === emitter.count) { 122 unload() 123 emit('exit', null, sig) 124 /* istanbul ignore next */ 125 emit('afterexit', null, sig) 126 /* istanbul ignore next */ 127 if (isWin && sig === 'SIGHUP') { 128 // "SIGHUP" throws an `ENOSYS` error on Windows, 129 // so use a supported signal instead 130 sig = 'SIGINT' 131 } 132 /* istanbul ignore next */ 133 process.kill(process.pid, sig) 134 } 135 } 136 }) 137 138 module.exports.signals = function () { 139 return signals 140 } 141 142 var loaded = false 143 144 var load = function load () { 145 if (loaded || !processOk(global.process)) { 146 return 147 } 148 loaded = true 149 150 // This is the number of onSignalExit's that are in play. 151 // It's important so that we can count the correct number of 152 // listeners on signals, and don't wait for the other one to 153 // handle it instead of us. 154 emitter.count += 1 155 156 signals = signals.filter(function (sig) { 157 try { 158 process.on(sig, sigListeners[sig]) 159 return true 160 } catch (er) { 161 return false 162 } 163 }) 164 165 process.emit = processEmit 166 process.reallyExit = processReallyExit 167 } 168 module.exports.load = load 169 170 var originalProcessReallyExit = process.reallyExit 171 var processReallyExit = function processReallyExit (code) { 172 /* istanbul ignore if */ 173 if (!processOk(global.process)) { 174 return 175 } 176 process.exitCode = code || /* istanbul ignore next */ 0 177 emit('exit', process.exitCode, null) 178 /* istanbul ignore next */ 179 emit('afterexit', process.exitCode, null) 180 /* istanbul ignore next */ 181 originalProcessReallyExit.call(process, process.exitCode) 182 } 183 184 var originalProcessEmit = process.emit 185 var processEmit = function processEmit (ev, arg) { 186 if (ev === 'exit' && processOk(global.process)) { 187 /* istanbul ignore else */ 188 if (arg !== undefined) { 189 process.exitCode = arg 190 } 191 var ret = originalProcessEmit.apply(this, arguments) 192 /* istanbul ignore next */ 193 emit('exit', process.exitCode, null) 194 /* istanbul ignore next */ 195 emit('afterexit', process.exitCode, null) 196 /* istanbul ignore next */ 197 return ret 198 } else { 199 return originalProcessEmit.apply(this, arguments) 200 } 201 } 202 }