Skip to content

Commit

Permalink
add support for handling "unknown" options not registered with the pa…
Browse files Browse the repository at this point in the history
…rser.
  • Loading branch information
caitp authored and James Halliday committed Aug 11, 2014
1 parent 13077b3 commit 6f3cc5d
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 17 deletions.
49 changes: 32 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
module.exports = function (args, opts) {
if (!opts) opts = {};

var flags = { bools : {}, strings : {} };
var flags = { bools : {}, strings : {}, unknownFn: null };

if (typeof opts['unknown'] === 'function') {
flags.unknownFn = opts['unknown'];
}

if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
flags.allBools = true;
Expand Down Expand Up @@ -42,7 +46,16 @@ module.exports = function (args, opts) {
args = args.slice(0, args.indexOf('--'));
}

function setArg (key, val) {
function argDefined(key, arg) {
return (flags.allBools && /^--[^=]+$/.test(arg)) ||
flags.strings[key] || flags.bools[key] || aliases[key];
}

function setArg (key, val, arg) {
if (arg && flags.unknownFn && !argDefined(key, arg)) {
if (flags.unknownFn(arg) === false) return;
}

var value = !flags.strings[key] && isNumber(val)
? Number(val) : val
;
Expand All @@ -61,11 +74,11 @@ module.exports = function (args, opts) {
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
setArg(m[1], m[2]);
setArg(m[1], m[2], arg);
}
else if (/^--no-.+/.test(arg)) {
var key = arg.match(/^--no-(.+)/)[1];
setArg(key, false);
setArg(key, false, arg);
}
else if (/^--.+/.test(arg)) {
var key = arg.match(/^--(.+)/)[1];
Expand All @@ -74,15 +87,15 @@ module.exports = function (args, opts) {
&& !flags.bools[key]
&& !flags.allBools
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
setArg(key, next);
setArg(key, next, arg);
i++;
}
else if (/^(true|false)$/.test(next)) {
setArg(key, next === 'true');
setArg(key, next === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true);
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
else if (/^-[^-]+/.test(arg)) {
Expand All @@ -93,24 +106,24 @@ module.exports = function (args, opts) {
var next = arg.slice(j+2);

if (next === '-') {
setArg(letters[j], next)
setArg(letters[j], next, arg)
continue;
}

if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next);
setArg(letters[j], next, arg);
broken = true;
break;
}

if (letters[j+1] && letters[j+1].match(/\W/)) {
setArg(letters[j], arg.slice(j+2));
setArg(letters[j], arg.slice(j+2), arg);
broken = true;
break;
}
else {
setArg(letters[j], flags.strings[letters[j]] ? '' : true);
setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
}
}

Expand All @@ -119,22 +132,24 @@ module.exports = function (args, opts) {
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
&& !flags.bools[key]
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
setArg(key, args[i+1]);
setArg(key, args[i+1], arg);
i++;
}
else if (args[i+1] && /true|false/.test(args[i+1])) {
setArg(key, args[i+1] === 'true');
setArg(key, args[i+1] === 'true', arg);
i++;
}
else {
setArg(key, flags.strings[key] ? '' : true);
setArg(key, flags.strings[key] ? '' : true, arg);
}
}
}
else {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
);
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
);
}
if (opts.stopEarly) {
argv._.push.apply(argv._, args.slice(i + 1));
break;
Expand Down
3 changes: 3 additions & 0 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ argument names to use as aliases
first non-option
* `opts['--']` - when true, populate `argv._` with everything before the `--`
and `argv['--']` with everything after the `--`. Here's an example:
* `opts.unknown` - a function which is invoked with a command line parameter not
defined in the `opts` configuration object. If the function returns `false`, the
unknown option is not added to `argv`.

```
> require('./')('one two three -- four five --six'.split(' '), { '--': true })
Expand Down
101 changes: 101 additions & 0 deletions test/unknown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
var parse = require('../');
var test = require('tape');

test('boolean and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'true', '--derp', 'true' ];
var regular = [ '--herp', 'true', '-d', 'true' ];
var opts = {
alias: { h: 'herp' },
boolean: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);

t.same(unknown, ['--derp', '-d']);
t.end();
});

test('flag boolean true any double hyphen argument is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var argv = parse(['--honk', '--tacos=good', 'cow', '-p', '55'], {
boolean: true,
unknown: unknownFn
});
t.same(unknown, ['--tacos=good', 'cow', '-p']);
t.same(argv, {
honk: true,
_: []
});
t.end();
});

test('string and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello', '--derp', 'goodbye' ];
var regular = [ '--herp', 'hello', '-d', 'moon' ];
var opts = {
alias: { h: 'herp' },
string: 'h',
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);

t.same(unknown, ['--derp', '-d']);
t.end();
});

test('default and alias is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '-h', 'hello' ];
var regular = [ '--herp', 'hello' ];
var opts = {
default: { 'h': 'bar' },
alias: { 'h': 'herp' },
unknown: unknownFn
};
var aliasedArgv = parse(aliased, opts);
var propertyArgv = parse(regular, opts);

t.same(unknown, []);
t.end();
});

test('value following -- is not unknown', function (t) {
var unknown = [];
function unknownFn(arg) {
unknown.push(arg);
return false;
}
var aliased = [ '--bad', '--', 'good', 'arg' ];
var opts = {
'--': true,
unknown: unknownFn
};
var argv = parse(aliased, opts);

t.same(unknown, ['--bad']);
t.same(argv, {
'--': ['good', 'arg'],
'_': []
})
t.end();
});

0 comments on commit 6f3cc5d

Please sign in to comment.