Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pizza details:
- cheese
```

`program.parse(arguments)` processes the arguments, leaving any args not consumed by the options as the `program.args` array.
`program.parse(arguments)` processes the arguments, leaving any args not consumed by the program options in the `program.args` array.

### Default option value

Expand Down Expand Up @@ -356,7 +356,7 @@ program
program.parse(process.argv);
```

The variadic argument is passed to the action handler as an array. (And this also applies to `program.args`.)
The variadic argument is passed to the action handler as an array.

### Action handler (sub)commands

Expand Down Expand Up @@ -559,7 +559,7 @@ program.on('option:verbose', function () {

// error on unknown commands
program.on('command:*', function () {
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args[0]]);
process.exit(1);
});
```
Expand Down
123 changes: 55 additions & 68 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,22 +334,19 @@ Command.prototype.action = function(fn) {
outputHelpIfRequested(self, parsed.unknown);
self._checkForMissingMandatoryOptions();

// 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 there are still any unknown options, then we simply die.
if (parsed.unknown.length > 0) {
self.unknownOption(parsed.unknown[0]);
}

// Leftover arguments need to be pushed back. Fixes issue #56
if (parsed.args.length) args = parsed.args.concat(args);
args = args.concat(parsed.operands, parsed.unknown);

self._args.forEach(function(arg, i) {
if (arg.required && args[i] == null) {
self.missingArgument(arg.name);
} else if (arg.variadic) {
self._args.forEach(function(expectedArg, i) {
if (expectedArg.required && args[i] == null) {
self.missingArgument(expectedArg.name);
} else if (expectedArg.variadic) {
if (i !== self._args.length - 1) {
self.variadicArgNotLast(arg.name);
self.variadicArgNotLast(expectedArg.name);
}

args[i] = args.splice(i);
Expand Down Expand Up @@ -641,11 +638,12 @@ Command.prototype.parse = function(argv) {
}

// process argv
var normalized = this.normalize(argv.slice(2));
var parsed = this.parseOptions(normalized);
var args = this.args = parsed.args;
const normalized = this.normalize(argv.slice(2));
const parsed = this.parseOptions(normalized);
const args = parsed.operands.concat(parsed.unknown);
this.args = args.slice();

var result = this.parseArgs(this.args, parsed.unknown);
var result = this.parseArgs(parsed.operands, parsed.unknown);

if (args[0] === 'help' && args.length === 1) this.help();

Expand Down Expand Up @@ -695,7 +693,7 @@ Command.prototype.parse = function(argv) {
}

if (this._execs.has(name)) {
return this.executeSubCommand(argv, args, parsed.unknown, subCommand ? subCommand._executableFile : undefined);
return this.executeSubCommand(argv, args, subCommand ? subCommand._executableFile : undefined);
}

return result;
Expand Down Expand Up @@ -725,9 +723,7 @@ Command.prototype.parseAsync = function(argv) {
* @api private
*/

Command.prototype.executeSubCommand = function(argv, args, unknown, executableFile) {
args = args.concat(unknown);

Command.prototype.executeSubCommand = function(argv, args, executableFile) {
if (!args.length) this.help();

var isExplicitJS = false; // Whether to use node to launch "executable"
Expand Down Expand Up @@ -889,16 +885,14 @@ Command.prototype.normalize = function(args) {
* @api private
*/

Command.prototype.parseArgs = function(args, unknown) {
var name;

if (args.length) {
name = args[0];
Command.prototype.parseArgs = function(operands, unknown) {
if (operands.length) {
const name = operands[0];
if (this.listeners('command:' + name).length) {
this.emit('command:' + args.shift(), args, unknown);
this.emit('command:' + operands[0], operands.slice(1), unknown);
} else {
this.emit('program-command', args, unknown);
this.emit('command:*', args, unknown);
this.emit('program-command', operands, unknown);
this.emit('command:*', operands, unknown);
}
} else {
outputHelpIfRequested(this, unknown);
Expand Down Expand Up @@ -926,11 +920,7 @@ Command.prototype.parseArgs = function(args, unknown) {
*/

Command.prototype.optionFor = function(arg) {
for (var i = 0, len = this.options.length; i < len; ++i) {
if (this.options[i].is(arg)) {
return this.options[i];
}
}
return this.options.find(option => option.is(arg));
};

/**
Expand All @@ -951,82 +941,79 @@ Command.prototype._checkForMissingMandatoryOptions = function() {
};

/**
* Parse options from `argv` returning `argv`
* void of these options.
* Parse options from `argv` removing known options,
* and return argv split into operands and unknown arguments.
*
* @param {Array} argv
* @return {{args: Array, unknown: Array}}
* Examples:
*
* argv => operands, unknown
* --known kkk op => [op], []
* op --known kkk => [op], []
* sub --unknown uuu op => [sub], [--unknown uuu op]
* sub -- --unknown uuu op => [sub --unknown uuu op], []
*
* @param {String[]} argv
* @return {{operands: String[], unknown: String[]}}
* @api public
*/

Command.prototype.parseOptions = function(argv) {
var args = [],
len = argv.length,
literal,
option,
arg;

var unknownOptions = [];
const operands = []; // operands, not options or values
const unknown = []; // first unknown option and remaining unknown args
let literal = false;
let dest = operands;

// parse options
for (var i = 0; i < len; ++i) {
arg = argv[i];
for (var i = 0; i < argv.length; ++i) {
const arg = argv[i];

// literal args after --
if (literal) {
args.push(arg);
dest.push(arg);
continue;
}

if (arg === '--') {
literal = true;
if (dest === unknown) dest.push('--');
continue;
}

// find matching Option
option = this.optionFor(arg);
const option = this.optionFor(arg);

// option is defined
// recognised option, call listener to assign value with possible custom processing
if (option) {
// requires arg
if (option.required) {
arg = argv[++i];
if (arg == null) return this.optionMissingArgument(option);
this.emit('option:' + option.name(), arg);
// optional arg
const value = argv[++i];
if (value === undefined) this.optionMissingArgument(option);
this.emit('option:' + option.name(), value);
} else if (option.optional) {
arg = argv[i + 1];
if (arg == null || (arg[0] === '-' && arg !== '-')) {
arg = null;
let value = argv[i + 1];
// do not use a following option as a value
if (value === undefined || (value[0] === '-' && value !== '-')) {
value = null;
} else {
++i;
}
this.emit('option:' + option.name(), arg);
this.emit('option:' + option.name(), value);
// flag
} else {
this.emit('option:' + option.name());
}
continue;
}

// looks like an option
// looks like an option, unknowns from here
if (arg.length > 1 && arg[0] === '-') {
unknownOptions.push(arg);

// If the next argument looks like it might be
// an argument for this option, we pass it on.
// If it isn't, then it'll simply be ignored
if ((i + 1) < argv.length && (argv[i + 1][0] !== '-' || argv[i + 1] === '-')) {
unknownOptions.push(argv[++i]);
}
continue;
dest = unknown;
}

// arg
args.push(arg);
dest.push(arg);
}

return { args: args, unknown: unknownOptions };
return { operands, unknown };
};

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/command.action.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test('when .action called then program.args only contains args', () => {
.command('info <file>')
.action(() => {});
program.parse(['node', 'test', 'info', 'my-file']);
expect(program.args).toEqual(['my-file']);
expect(program.args).toEqual(['info', 'my-file']);
});

test('when .action called with extra arguments then extras also passed to action', () => {
Expand Down
Loading