Skip to content

Commit

Permalink
merge master
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiyelee committed Oct 11, 2015
2 parents 619c648 + 4d421f9 commit 0a14d4c
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 37 deletions.
17 changes: 13 additions & 4 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,15 @@ program
.version('0.0.1')
.command('install [name]', 'install one or more packages')
.command('search [query]', 'search with optional query')
.command('list', 'list packages installed')
.command('list', 'list packages installed', {isDefault: true})
.parse(process.argv);
```

When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.
The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`.

Options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the option from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified.

If the program is designed to be installed globally, make sure the executables have proper modes, like `755`.

### `--harmony`
Expand Down Expand Up @@ -266,28 +268,35 @@ Examples:
```

## .outputHelp()
## .outputHelp(cb)

Output help information without exiting.
Optional callback cb allows post-processing of help text before it is displayed.

If you want to display help by default (e.g. if no command was provided), you can use something like:

```js
var program = require('commander');
var colors = require('colors');

program
.version('0.0.1')
.command('getstream [url]', 'get stream URL')
.parse(process.argv);

if (!process.argv.slice(2).length) {
program.outputHelp();
program.outputHelp(make_red);
}

function make_red(txt) {
return colors.red(txt); //display the help text in red on the console
}
```

## .help()
## .help(cb)

Output help information and exit immediately.
Optional callback cb allows post-processing of help text before it is displayed.

## Examples

Expand Down
20 changes: 13 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function Command(name) {
this._execs = {};
this._allowUnknownOption = false;
this._args = [];
this._name = name;
this._name = name || '';
}

/**
Expand Down Expand Up @@ -165,6 +165,7 @@ Command.prototype.command = function(name, desc, opts) {
cmd.description(desc);
this.executables = true;
this._execs[cmd._name] = true;
if (opts.isDefault) this.defaultExecutable = cmd._name;
}

cmd._noHelp = !!opts.noHelp;
Expand Down Expand Up @@ -445,8 +446,8 @@ Command.prototype.parse = function(argv) {
// guess name
this._name = this._name || basename(argv[1], '.js');

// git-style sub-commands with no sub-command
if (this.executables && argv.length < 3) {
// github-style sub-commands with no sub-command
if (this.executables && argv.length < 3 && !this.defaultExecutable) {
// this user needs help
argv.push('--help');
}
Expand All @@ -461,6 +462,10 @@ Command.prototype.parse = function(argv) {
var name = result.args[0];
if (this._execs[name] && typeof this._execs[name] != "function") {
return this.executeSubCommand(argv, args, parsed.unknown);
} else if (this.defaultExecutable) {
// use the default subcommand
args.unshift(name = this.defaultExecutable);
return this.executeSubCommand(argv, args, parsed.unknown);
}

return result;
Expand Down Expand Up @@ -1010,8 +1015,9 @@ Command.prototype.helpInformation = function() {
* @api public
*/

Command.prototype.outputHelp = function() {
process.stdout.write(this.helpInformation());
Command.prototype.outputHelp = function(cb) {
if (!cb) cb = function(passthru) { return passthru; } //supply benign callback if none specified
process.stdout.write(cb(this.helpInformation()));
this.emit('--help');
};

Expand All @@ -1021,8 +1027,8 @@ Command.prototype.outputHelp = function() {
* @api public
*/

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

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "commander",
"version": "2.8.1",
"version": "2.8.2",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"command",
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ program
.command('search [query]', 'search with optional query')
.command('list', 'list packages installed')
.command('publish', 'publish or update package')
.command('default', 'default command', {noHelp: true, isDefault: true})
.parse(process.argv);
2 changes: 2 additions & 0 deletions test/fixtures/pm-default
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
console.log('default');
51 changes: 26 additions & 25 deletions test/test.command.allowUnknownOption.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,58 @@
/**
* Module dependencies.
*/

var util = require('util');
var program = require('../')
, should = require('should');
, sinon = require('sinon').sandbox.create()
, should = require('should');

var oldProcessExit = process.exit;
var oldConsoleError = console.error;
var stubError = sinon.stub(console, 'error');
var stubExit = sinon.stub(process, 'exit');

program
.version('0.0.1')
.option('-p, --pepper', 'add pepper');
program.parse('node test -m'.split(' '));

var consoleErrors = [];
process.exit = function () {
};
console.error = function () {
consoleErrors.push(util.format.apply(util, arguments));
};
stubError.callCount.should.equal(3);

program.parse('node test -m'.split(' '));
consoleErrors.length.should.equal(3);

// test subcommand
var consoleErrors = [];
resetStubStatus();
program
.command('sub')
.action(function () {
});
program.parse('node test sub -m'.split(' '));
consoleErrors.length.should.equal(3);

consoleErrors = [];
stubError.callCount.should.equal(3);
stubExit.calledOnce.should.be.true();

// command with `allowUnknownOption`
resetStubStatus();
program
.version('0.0.1')
.option('-p, --pepper', 'add pepper');

program
.allowUnknownOption()
.parse('node test -m'.split(' '));
consoleErrors.length.should.equal(0);

// test subcommand
var consoleErrors = [];
stubError.callCount.should.equal(0);
stubExit.calledOnce.should.be.false();

// subcommand with `allowUnknownOption`
resetStubStatus();
program
.command('sub')
.command('sub2')
.allowUnknownOption()
.action(function () {
});
program.parse('node test sub -m'.split(' '));
consoleErrors.length.should.equal(3);
program.parse('node test sub2 -m'.split(' '));

stubError.callCount.should.equal(0);
stubExit.calledOnce.should.be.false();

process.exit = oldProcessExit;
console.error = oldConsoleError;

function resetStubStatus() {
stubError.reset();
stubExit.reset();
}
46 changes: 46 additions & 0 deletions test/test.command.executableSubcommandDefault.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var exec = require('child_process').exec
, path = require('path')
, should = require('should');



var bin = path.join(__dirname, './fixtures/pm')
// success case
exec(bin + ' default', function(error, stdout, stderr) {
stdout.should.equal('default\n');
});

// success case (default)
exec(bin, function(error, stdout, stderr) {
stdout.should.equal('default\n');
});

// not exist
exec(bin + ' list', function (error, stdout, stderr) {
//stderr.should.equal('\n pm-list(1) does not exist, try --help\n\n');
// TODO error info are not the same in between <=v0.8 and later version
should.notEqual(0, stderr.length);
});

// success case
exec(bin + ' install', function (error, stdout, stderr) {
stdout.should.equal('install\n');
});

// subcommand bin file with explicit extension
exec(bin + ' publish', function (error, stdout, stderr) {
stdout.should.equal('publish\n');
});

// spawn EACCES
exec(bin + ' search', function (error, stdout, stderr) {
// TODO error info are not the same in between <v0.10 and v0.12
should.notEqual(0, stderr.length);
});

// when `bin` is a symbol link for mocking global install
var bin = path.join(__dirname, './fixtures/pmlink')
// success case
exec(bin + ' install', function (error, stdout, stderr) {
stdout.should.equal('install\n');
});
3 changes: 3 additions & 0 deletions test/test.options.defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ program
.option('-r, --crust <type>', 'What kind of crust would you like?', 'hand-tossed')
.option('-c, --cheese [type]', 'optionally specify the type of cheese', 'mozzarella');

program.should.have.property('_name', '');

program.parse(['node', 'test']);
program.should.have.property('_name', 'test');
program.should.not.have.property('anchovies');
program.should.not.have.property('onions');
program.should.not.have.property('olives');
Expand Down

0 comments on commit 0a14d4c

Please sign in to comment.