123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- var RSVP = require('rsvp');
- var exit;
- var handlers = [];
- var lastTime;
- var isExiting = false;
- process.on('beforeExit', function (code) {
- if (handlers.length === 0) { return; }
- var own = lastTime = module.exports._flush(lastTime, code)
- .finally(function () {
- // if an onExit handler has called process.exit, do not disturb
- // `lastTime`.
- //
- // Otherwise, clear `lastTime` so that we know to synchronously call the
- // real `process.exit` with the given exit code, when our captured
- // `process.exit` is called during a `process.on('exit')` handler
- //
- // This is impossible to reason about, don't feel bad. Just look at
- // test-natural-exit-subprocess-error.js
- if (own === lastTime) {
- lastTime = undefined;
- }
- });
- });
- // This exists only for testing
- module.exports._reset = function () {
- module.exports.releaseExit();
- handlers = [];
- lastTime = undefined;
- isExiting = false;
- firstExitCode = undefined;
- }
- /*
- * To allow cooperative async exit handlers, we unfortunately must hijack
- * process.exit.
- *
- * It allows a handler to ensure exit, without that exit handler impeding other
- * similar handlers
- *
- * for example, see: https://github.com/sindresorhus/ora/issues/27
- *
- */
- module.exports.releaseExit = function() {
- if (exit) {
- process.exit = exit;
- exit = null;
- }
- };
- var firstExitCode;
- module.exports.captureExit = function() {
- if (exit) {
- // already captured, no need to do more work
- return;
- }
- exit = process.exit;
- process.exit = function(code) {
- if (handlers.length === 0 && lastTime === undefined) {
- // synchronously exit.
- //
- // We do this brecause either
- //
- // 1. The process exited due to a call to `process.exit` but we have no
- // async work to do because no handlers had been attached. It
- // doesn't really matter whether we take this branch or not in this
- // case.
- //
- // 2. The process exited naturally. We did our async work during
- // `beforeExit` and are in this function because someone else has
- // called `process.exit` during an `on('exit')` hook. The only way
- // for us to preserve the exit code in this case is to exit
- // synchronously.
- //
- return exit.call(process, code);
- }
- if (firstExitCode === undefined) {
- firstExitCode = code;
- }
- var own = lastTime = module.exports._flush(lastTime, firstExitCode)
- .then(function() {
- // if another chain has started, let it exit
- if (own !== lastTime) { return; }
- exit.call(process, firstExitCode);
- })
- .catch(function(error) {
- // if another chain has started, let it exit
- if (own !== lastTime) {
- throw error;
- }
- console.error(error);
- exit.call(process, 1);
- });
- };
- };
- module.exports._handlers = handlers;
- module.exports._flush = function(lastTime, code) {
- isExiting = true;
- var work = handlers.splice(0, handlers.length);
- return RSVP.Promise.resolve(lastTime).
- then(function() {
- var firstRejected;
- return RSVP.allSettled(work.map(function(handler) {
- return RSVP.resolve(handler.call(null, code)).catch(function(e) {
- if (!firstRejected) {
- firstRejected = e;
- }
- throw e;
- });
- })).then(function(results) {
- if (firstRejected) {
- throw firstRejected;
- }
- });
- });
- };
- module.exports.onExit = function(cb) {
- if (!exit) {
- throw new Error('Cannot install handler when exit is not captured. Call `captureExit()` first');
- }
- if (isExiting) {
- throw new Error('Cannot install handler while `onExit` handlers are running.');
- }
- var index = handlers.indexOf(cb);
- if (index > -1) { return; }
- handlers.push(cb);
- };
- module.exports.offExit = function(cb) {
- var index = handlers.indexOf(cb);
- if (index < 0) { return; }
- handlers.splice(index, 1);
- };
- module.exports.exit = function() {
- exit.apply(process, arguments);
- };
- module.exports.listenerCount = function() {
- return handlers.length;
- };
|