Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make process.exit() injectable. #576

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 47 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ function Command(name) {
this._allowUnknownOption = false;
this._args = [];
this._name = name || '';
this._env = {
exit(exitCode) {
process.exit(exitCode);
},
};
}

/**
Expand All @@ -94,6 +99,32 @@ function Command(name) {

Command.prototype.__proto__ = EventEmitter.prototype;

/**
* By default, commander interacts with the environment via calls to
* process.exit(), process.stdout.write(), etc. To intercept these calls,
* specify an environment object.
*
* @param {{exit: function(?number):void}} env
* @api public
*/

Command.prototype.setEnvironment = function(env) {
this._env = env;
};

/**
* Note that normally, the caller may expect the process to terminate as a
* result of invoking this method; howerver, if setEnvironment() has been
* called, then this may not be the case. Callers should not assume that code
* that is called after _exit() is unreachable.
* @param {?number} exitCode
*/

Command.prototype._exit = function(exitCode) {
this._env.exit(exitCode);
};


/**
* Add command `name`.
*
Expand Down Expand Up @@ -159,6 +190,7 @@ Command.prototype.command = function(name, desc, opts) {
opts = opts || {};
var args = name.split(/ +/);
var cmd = new Command(args.shift());
cmd._env = this._env; // Inherit the environment of your parent.

if (desc) {
cmd.description(desc);
Expand Down Expand Up @@ -265,7 +297,7 @@ Command.prototype.action = function(fn) {
var parsed = self.parseOptions(unknown);

// Output help if necessary
outputHelpIfNecessary(self, parsed.unknown);
outputHelpIfNecessary(self, parsed.unknown, this._exit.bind(this));

// If there are still any unknown options, then we simply
// die, unless someone asked for help, in which case we give it
Expand Down Expand Up @@ -551,14 +583,14 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) {
proc = spawn(process.execPath, args, { stdio: 'inherit'});
}

proc.on('close', process.exit.bind(process));
proc.on('error', function(err) {
proc.on('close', () => this._exit());
proc.on('error', (err) => {
if (err.code == "ENOENT") {
console.error('\n %s(1) does not exist, try --help\n', bin);
} else if (err.code == "EACCES") {
console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
}
process.exit(1);
this._exit(1);
});

// Store the reference to the child process
Expand Down Expand Up @@ -630,7 +662,7 @@ Command.prototype.parseArgs = function(args, unknown) {
this.emit('*', args);
}
} else {
outputHelpIfNecessary(this, unknown);
outputHelpIfNecessary(this, unknown, this._exit.bind(this));

// If there were no args and we have unknown options,
// then they are extraneous and we need to error.
Expand Down Expand Up @@ -765,7 +797,7 @@ Command.prototype.missingArgument = function(name) {
console.error();
console.error(" error: missing required argument `%s'", name);
console.error();
process.exit(1);
this._exit(1);
};

/**
Expand All @@ -784,7 +816,7 @@ Command.prototype.optionMissingArgument = function(option, flag) {
console.error(" error: option `%s' argument missing", option.flags);
}
console.error();
process.exit(1);
this._exit(1);
};

/**
Expand All @@ -799,7 +831,7 @@ Command.prototype.unknownOption = function(flag) {
console.error();
console.error(" error: unknown option `%s'", flag);
console.error();
process.exit(1);
this._exit(1);
};

/**
Expand All @@ -813,7 +845,7 @@ Command.prototype.variadicArgNotLast = function(name) {
console.error();
console.error(" error: variadic arguments must be last `%s'", name);
console.error();
process.exit(1);
this._exit(1);
};

/**
Expand All @@ -833,9 +865,9 @@ Command.prototype.version = function(str, flags) {
this._version = str;
flags = flags || '-V, --version';
this.option(flags, 'output the version number');
this.on('version', function() {
this.on('version', () => {
process.stdout.write(str + '\n');
process.exit(0);
this._exit(0);
});
return this;
};
Expand Down Expand Up @@ -1051,7 +1083,7 @@ Command.prototype.outputHelp = function(cb) {

Command.prototype.help = function(cb) {
this.outputHelp(cb);
process.exit();
this._exit();
};

/**
Expand Down Expand Up @@ -1087,15 +1119,16 @@ function pad(str, width) {
*
* @param {Command} command to output help for
* @param {Array} array of options to search for -h or --help
* @param {function(?number):void} exit
* @api private
*/

function outputHelpIfNecessary(cmd, options) {
function outputHelpIfNecessary(cmd, options, exit) {
options = options || [];
for (var i = 0; i < options.length; i++) {
if (options[i] == '--help' || options[i] == '-h') {
cmd.outputHelp();
process.exit(0);
exit(0);
}
}
}
Expand Down