index.js 4.0 KB

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