index.js 5.6 KB

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