index.js 3.8 KB

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