diff --git a/index.js b/index.js index baa3554d6..cd3c54810 100644 --- a/index.js +++ b/index.js @@ -215,37 +215,42 @@ Command.prototype.action = function(fn) { args = args || []; unknown = unknown || []; - var parsed = self.parseOptions(unknown); + var parsed = self.parseOptions(args.concat(unknown)); - // Output help if necessary - outputHelpIfNecessary(self, parsed.unknown); - - // If there are still any unknown options, then we simply - // die, unless someone asked for help, in which case we give it - // to them, and then we die. - if (parsed.unknown.length > 0) { - self.unknownOption(parsed.unknown[0]); + if (parsed.args.length && self.listeners(parsed.args[0]).length) { + for (var i = 0; i < parsed.args.length; i++) { + args.shift(); + } + var result = self.parseArgs(parsed.args, parsed.unknown); } + else { + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + self.unknownOption(parsed.unknown[0]); + } - // Leftover arguments need to be pushed back. Fixes issue #56 - if (parsed.args.length) args = parsed.args.concat(args); - - self._args.forEach(function(arg, i) { - if (arg.required && null == args[i]) { - self.missingArgument(arg.name); - } - }); + self._args.forEach(function(arg, i){ + if (arg.required && null == parsed.args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + parsed.args[self._args.length] = self; + } else { + parsed.args.push(self); + } - // Always append ourselves to the end of the arguments, - // to make sure we match the number of arguments the user - // expects - if (self._args.length) { - args[self._args.length] = self; - } else { - args.push(self); + fn.apply(self, parsed.args); } - - fn.apply(self, args); }; this.parent.on(this._name, listener); if (this._alias) this.parent.on(this._alias, listener); @@ -697,7 +702,7 @@ Command.prototype.version = function(str, flags) { */ Command.prototype.description = function(str) { - if (0 == arguments.length) return this._description; + if (0 == arguments.length) return this._description || ''; this._description = str; return this; }; diff --git a/test/test.options.command-context.js b/test/test.options.command-context.js new file mode 100644 index 000000000..acb1410b7 --- /dev/null +++ b/test/test.options.command-context.js @@ -0,0 +1,21 @@ +/** + * Module dependencies. + */ + +var program = require('../') + , should = require('should'); + +program + .version('0.0.1'); + +var commandContext; +var optionContext; + +var setup = program.command('setup') + .description('run setup commands for all envs') + .action(function(env, options){ + commandContext = this; + }); + +program.parse(['node', 'test', 'setup']); +commandContext._name.should.equal('setup'); diff --git a/test/test.options.nested-commands.js b/test/test.options.nested-commands.js new file mode 100644 index 000000000..4d6360f7e --- /dev/null +++ b/test/test.options.nested-commands.js @@ -0,0 +1,88 @@ +/** + * Module dependencies. + */ + +var program = require('../') + , should = require('should'); + +var commandContext; + +program + .version('0.0.1') + .on('--help', function() { + helpCommand = this._name; + }); + +var setupCommand = program + .command('setup') + .action(function(env, options){ + commandContext = this; + }) + .on('--help', function() { + helpCommand = this._name; + }); + +var addCommand = setupCommand + .command('add') + .option('--value [value]') + .action(function() { + commandContext = this; + }) + .on('--help', function() { + helpCommand = this._name; + }); + +var remoteCommand = addCommand + .command('remote') + .action(function() { + commandContext = this; + }) + .on('--help', function() { + helpCommand = this._name; + }); + +program.parse(['node', 'test', 'setup']); +commandContext._name.should.equal('setup'); + +program.parse(['node', 'test', 'setup', 'add']); +commandContext._name.should.equal('add'); + +program.parse(['node', 'test', 'setup', 'add', '--value', 'true']); +commandContext._name.should.equal('add'); + +program.parse(['node', 'test', 'setup', 'add', 'remote']); +commandContext._name.should.equal('remote'); + +// Make sure we still catch errors with required values for options +var exceptionOccurred = false; +var oldProcessExit = process.exit; +var oldConsoleError = console.error; +process.exit = function() { exceptionOccurred = true; throw new Error(); }; +console.error = function() {}; + +try { + program.parse(['node', 'test', '--help']); +} catch(ex) { +} +helpCommand.should.equal('test'); + +try { + program.parse(['node', 'test', 'setup', '--help']); +} catch(ex) { +} +helpCommand.should.equal('setup'); + +try { + program.parse(['node', 'test', 'setup', 'add', '--help']); +} catch(ex) { +} +helpCommand.should.equal('add'); + +try { + program.parse(['node', 'test', 'setup', 'add', 'remote', '--help']); +} catch(ex) { +} +helpCommand.should.equal('remote'); + +process.exit = oldProcessExit; +exceptionOccurred.should.be.true;