index.js 5.5 KB

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