You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					154 lines
				
				4.1 KiB
			
		
		
			
		
	
	
					154 lines
				
				4.1 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								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;
							 | 
						||
| 
								 | 
							
								};
							 |