diff --git a/README.md b/README.md index 82b49f7..921f1b1 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ This code is an evolution of that parser. - [Basic options](#basic-options) - [Commands](#commands) - [Active options/commands](#active-optionscommands) + - [Pattern options](#pattern-options) - [Nested parsers](#nested-parsers) - [Stopping](#stopping) - [Error reporting](#error-reporting) @@ -335,6 +336,7 @@ For more info on available `.type` and `.collect` handlers see: respectively. + ### Commands The only difference between an _option_ and a _command_ is the prefix (`"-"` vs. `"@"`) @@ -383,6 +385,26 @@ it expects), `argv.js` poses no restrictions on full or partial manual handling of arguments by options/commands. + +### Pattern options + +Pattern option/command keys enable partial input key matching. +```javascript + '-prefix-*': { + doc: 'Pattern option', + handler: function(rest, key){ + // ... + } }, +``` + +The above code will match any _unhandled_ input option starting with +`-prefix-`/`--prefix-` and push the explicitly `false` value back to the option +queue. + +A pattern option/command is any option with a key containing `"*"`. + + + ### Nested parsers An options/command handler can also be a full fledged parser. @@ -551,6 +573,7 @@ Options: -c - command --active - basic active option -s, --shorthand-active - shorthand-active + --prefix-* - Pattern option --then - then --stop - stop --error - error diff --git a/argv.js b/argv.js index a292a00..06bd202 100644 --- a/argv.js +++ b/argv.js @@ -429,10 +429,11 @@ object.Constructor('Parser', { prefix = prefix.length == 0 ? [OPTION_PREFIX] : prefix + var attrs = object.deepKeys(that, Parser.prototype) return prefix .map(function(prefix){ var handlers = {} - object.deepKeys(that, Parser.prototype) + attrs .forEach(function(opt){ if(!opt.startsWith(prefix)){ return } @@ -496,6 +497,28 @@ object.Constructor('Parser', { requiredArguments: function(){ return this.requiredOptions('allArguments') }, + // + // .patternArguments() + // -> list + // + // Get list of pattern args that key matches... + // .patternArguments(key) + // -> list + // + // NOTE: list is sorted by option length... + // NOTE: pattern->pattern aliases are not currently supported... + // NOTE: output is of the same format as .options(..) + // NOTE: when changing this revise a corresponding section in .handler(..) + patternArguments: function(key){ + return this.allArguments() + .filter(function([[opt]]){ + return opt.includes('*') + && (key == null + || (new RegExp(`^${ opt.split('*').join('.*') }$`)).test(key)) }) + // sort longest first... + .sort(function(a, b){ + return b[0][0].length - a[0][0].length }) }, + // Get handler... // // .handler(key) @@ -523,6 +546,19 @@ object.Constructor('Parser', { // report loop... 'loop', [...seen, key]] } seen.add(key) } + // check pattern options... + // NOTE: we are not using .patternArguments(..) because .options(..) + // used there uses .handler(..) and this breaks things... + if(!(key in this) && key != '-*'){ + key = object.deepKeys(this, Parser.prototype) + .filter(function(opt){ + return opt.includes('*') + && (key == null + || (new RegExp(`^${ opt.split('*').join('.*') }$`)) + .test(key)) }) + .sort(function(a, b){ + return b[0][0].length - a[0][0].length })[0] + || key } return [key, this[key], // report dead-end if this[key] is undefined... ...(this[key] ? diff --git a/examples/options.js b/examples/options.js index 0983c0f..7740c40 100644 --- a/examples/options.js +++ b/examples/options.js @@ -120,6 +120,12 @@ argv.Parser({ // ... }, + '-prefix-*': { + doc: 'Pattern option', + handler: function(rest, key){ + // ... + }, + }, '@nested': argv.Parser({ // ... diff --git a/package.json b/package.json index b68aa48..408d34d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-argv", - "version": "2.15.7", + "version": "2.16.0", "description": "simple argv parser", "main": "argv.js", "scripts": { diff --git a/test.js b/test.js index c645c3c..9fbc412 100644 --- a/test.js +++ b/test.js @@ -28,14 +28,14 @@ var setups = test.Setups({ bare: function(){ return require('./examples/bare').parser }, - options: function(){ + opts: function(){ return require('./examples/options').parser }, lang: function(){ return require('./examples/lang').parser }, chain: function(){ return require('./examples/chain').parser }, - // NOTE: this will also load .bare, .options and .lang + // NOTE: this will also load .bare, .opts and .lang basic: function(assert){ return argv.Parser({ // disable exit on error... @@ -175,13 +175,15 @@ test.Setups({ '@bare': setups.bare(assert), - '@opts': setups.options(assert), + '@opts': setups.opts(assert), '@lang': setups.lang(assert), '@chain': setups.chain(assert), // collision test... // NOTE: values of these will shadow the API... + // XXX need to either warn the user of this or think of a + // way to avoid this... '@options': {}, '-handler': {}, })