index.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. "use strict";
  2. var _a;
  3. Object.defineProperty(exports, "__esModule", { value: true });
  4. exports.unload = exports.load = exports.onExit = exports.signals = void 0;
  5. // Note: since nyc uses this module to output coverage, any lines
  6. // that are in the direct sync flow of nyc's outputCoverage are
  7. // ignored, since we can never get coverage for them.
  8. // grab a reference to node's real process object right away
  9. const signals_js_1 = require("./signals.js");
  10. Object.defineProperty(exports, "signals", { enumerable: true, get: function () { return signals_js_1.signals; } });
  11. const processOk = (process) => !!process &&
  12. typeof process === 'object' &&
  13. typeof process.removeListener === 'function' &&
  14. typeof process.emit === 'function' &&
  15. typeof process.reallyExit === 'function' &&
  16. typeof process.listeners === 'function' &&
  17. typeof process.kill === 'function' &&
  18. typeof process.pid === 'number' &&
  19. typeof process.on === 'function';
  20. const kExitEmitter = Symbol.for('signal-exit emitter');
  21. const global = globalThis;
  22. const ObjectDefineProperty = Object.defineProperty.bind(Object);
  23. // teeny special purpose ee
  24. class Emitter {
  25. emitted = {
  26. afterExit: false,
  27. exit: false,
  28. };
  29. listeners = {
  30. afterExit: [],
  31. exit: [],
  32. };
  33. count = 0;
  34. id = Math.random();
  35. constructor() {
  36. if (global[kExitEmitter]) {
  37. return global[kExitEmitter];
  38. }
  39. ObjectDefineProperty(global, kExitEmitter, {
  40. value: this,
  41. writable: false,
  42. enumerable: false,
  43. configurable: false,
  44. });
  45. }
  46. on(ev, fn) {
  47. this.listeners[ev].push(fn);
  48. }
  49. removeListener(ev, fn) {
  50. const list = this.listeners[ev];
  51. const i = list.indexOf(fn);
  52. /* c8 ignore start */
  53. if (i === -1) {
  54. return;
  55. }
  56. /* c8 ignore stop */
  57. if (i === 0 && list.length === 1) {
  58. list.length = 0;
  59. }
  60. else {
  61. list.splice(i, 1);
  62. }
  63. }
  64. emit(ev, code, signal) {
  65. if (this.emitted[ev]) {
  66. return false;
  67. }
  68. this.emitted[ev] = true;
  69. let ret = false;
  70. for (const fn of this.listeners[ev]) {
  71. ret = fn(code, signal) === true || ret;
  72. }
  73. if (ev === 'exit') {
  74. ret = this.emit('afterExit', code, signal) || ret;
  75. }
  76. return ret;
  77. }
  78. }
  79. class SignalExitBase {
  80. }
  81. const signalExitWrap = (handler) => {
  82. return {
  83. onExit(cb, opts) {
  84. return handler.onExit(cb, opts);
  85. },
  86. load() {
  87. return handler.load();
  88. },
  89. unload() {
  90. return handler.unload();
  91. },
  92. };
  93. };
  94. class SignalExitFallback extends SignalExitBase {
  95. onExit() {
  96. return () => { };
  97. }
  98. load() { }
  99. unload() { }
  100. }
  101. class SignalExit extends SignalExitBase {
  102. // "SIGHUP" throws an `ENOSYS` error on Windows,
  103. // so use a supported signal instead
  104. /* c8 ignore start */
  105. #hupSig = process.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
  106. /* c8 ignore stop */
  107. #emitter = new Emitter();
  108. #process;
  109. #originalProcessEmit;
  110. #originalProcessReallyExit;
  111. #sigListeners = {};
  112. #loaded = false;
  113. constructor(process) {
  114. super();
  115. this.#process = process;
  116. // { <signal>: <listener fn>, ... }
  117. this.#sigListeners = {};
  118. for (const sig of signals_js_1.signals) {
  119. this.#sigListeners[sig] = () => {
  120. // If there are no other listeners, an exit is coming!
  121. // Simplest way: remove us and then re-send the signal.
  122. // We know that this will kill the process, so we can
  123. // safely emit now.
  124. const listeners = this.#process.listeners(sig);
  125. let { count } = this.#emitter;
  126. // This is a workaround for the fact that signal-exit v3 and signal
  127. // exit v4 are not aware of each other, and each will attempt to let
  128. // the other handle it, so neither of them do. To correct this, we
  129. // detect if we're the only handler *except* for previous versions
  130. // of signal-exit, and increment by the count of listeners it has
  131. // created.
  132. /* c8 ignore start */
  133. const p = process;
  134. if (typeof p.__signal_exit_emitter__ === 'object' &&
  135. typeof p.__signal_exit_emitter__.count === 'number') {
  136. count += p.__signal_exit_emitter__.count;
  137. }
  138. /* c8 ignore stop */
  139. if (listeners.length === count) {
  140. this.unload();
  141. const ret = this.#emitter.emit('exit', null, sig);
  142. /* c8 ignore start */
  143. const s = sig === 'SIGHUP' ? this.#hupSig : sig;
  144. if (!ret)
  145. process.kill(process.pid, s);
  146. /* c8 ignore stop */
  147. }
  148. };
  149. }
  150. this.#originalProcessReallyExit = process.reallyExit;
  151. this.#originalProcessEmit = process.emit;
  152. }
  153. onExit(cb, opts) {
  154. /* c8 ignore start */
  155. if (!processOk(this.#process)) {
  156. return () => { };
  157. }
  158. /* c8 ignore stop */
  159. if (this.#loaded === false) {
  160. this.load();
  161. }
  162. const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
  163. this.#emitter.on(ev, cb);
  164. return () => {
  165. this.#emitter.removeListener(ev, cb);
  166. if (this.#emitter.listeners['exit'].length === 0 &&
  167. this.#emitter.listeners['afterExit'].length === 0) {
  168. this.unload();
  169. }
  170. };
  171. }
  172. load() {
  173. if (this.#loaded) {
  174. return;
  175. }
  176. this.#loaded = true;
  177. // This is the number of onSignalExit's that are in play.
  178. // It's important so that we can count the correct number of
  179. // listeners on signals, and don't wait for the other one to
  180. // handle it instead of us.
  181. this.#emitter.count += 1;
  182. for (const sig of signals_js_1.signals) {
  183. try {
  184. const fn = this.#sigListeners[sig];
  185. if (fn)
  186. this.#process.on(sig, fn);
  187. }
  188. catch (_) { }
  189. }
  190. this.#process.emit = (ev, ...a) => {
  191. return this.#processEmit(ev, ...a);
  192. };
  193. this.#process.reallyExit = (code) => {
  194. return this.#processReallyExit(code);
  195. };
  196. }
  197. unload() {
  198. if (!this.#loaded) {
  199. return;
  200. }
  201. this.#loaded = false;
  202. signals_js_1.signals.forEach(sig => {
  203. const listener = this.#sigListeners[sig];
  204. /* c8 ignore start */
  205. if (!listener) {
  206. throw new Error('Listener not defined for signal: ' + sig);
  207. }
  208. /* c8 ignore stop */
  209. try {
  210. this.#process.removeListener(sig, listener);
  211. /* c8 ignore start */
  212. }
  213. catch (_) { }
  214. /* c8 ignore stop */
  215. });
  216. this.#process.emit = this.#originalProcessEmit;
  217. this.#process.reallyExit = this.#originalProcessReallyExit;
  218. this.#emitter.count -= 1;
  219. }
  220. #processReallyExit(code) {
  221. /* c8 ignore start */
  222. if (!processOk(this.#process)) {
  223. return 0;
  224. }
  225. this.#process.exitCode = code || 0;
  226. /* c8 ignore stop */
  227. this.#emitter.emit('exit', this.#process.exitCode, null);
  228. return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
  229. }
  230. #processEmit(ev, ...args) {
  231. const og = this.#originalProcessEmit;
  232. if (ev === 'exit' && processOk(this.#process)) {
  233. if (typeof args[0] === 'number') {
  234. this.#process.exitCode = args[0];
  235. /* c8 ignore start */
  236. }
  237. /* c8 ignore start */
  238. const ret = og.call(this.#process, ev, ...args);
  239. /* c8 ignore start */
  240. this.#emitter.emit('exit', this.#process.exitCode, null);
  241. /* c8 ignore stop */
  242. return ret;
  243. }
  244. else {
  245. return og.call(this.#process, ev, ...args);
  246. }
  247. }
  248. }
  249. const process = globalThis.process;
  250. // wrap so that we call the method on the actual handler, without
  251. // exporting it directly.
  252. _a = signalExitWrap(processOk(process) ? new SignalExit(process) : new SignalExitFallback()),
  253. /**
  254. * Called when the process is exiting, whether via signal, explicit
  255. * exit, or running out of stuff to do.
  256. *
  257. * If the global process object is not suitable for instrumentation,
  258. * then this will be a no-op.
  259. *
  260. * Returns a function that may be used to unload signal-exit.
  261. */
  262. exports.onExit = _a.onExit,
  263. /**
  264. * Load the listeners. Likely you never need to call this, unless
  265. * doing a rather deep integration with signal-exit functionality.
  266. * Mostly exposed for the benefit of testing.
  267. *
  268. * @internal
  269. */
  270. exports.load = _a.load,
  271. /**
  272. * Unload the listeners. Likely you never need to call this, unless
  273. * doing a rather deep integration with signal-exit functionality.
  274. * Mostly exposed for the benefit of testing.
  275. *
  276. * @internal
  277. */
  278. exports.unload = _a.unload;
  279. //# sourceMappingURL=index.js.map