UNPKG

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