diff --git a/ADVANCED.md b/ADVANCED.md index 38ba8ae..e15b727 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -54,6 +54,7 @@ For basics see [README.md](./README.md) - [`.error(..)`](#parsererror-1) - [`.off(..)`](#parseroff) - [`(..)`](#parser-1) + - [`Parser.chain(..)` (EXPERIMENTAL)](#parserchain-experimental) - [Advanced parser API](#advanced-parser-api) - [`.print(..)` / `.printError(..)`](#parserprint--parserprinterror) - [`.handlerDefault(..)`](#parserhandlerdefault) @@ -763,6 +764,31 @@ be ignored, otherwise the whole list is processed as if `
` was its head. +### `Parser.chain(..)` (EXPERIMENTAL) + +Chain several parsers for staggering option/command processing. +``` +Parser.chain(, , ..) + -> +``` + +This creates a chain of parsers, each processing only the arguments it defines +and passes the rest to the next in chain. This is useful for defining arguments +that need to be processed out of order and before anything else. + +This is similar to chaining parsers via `.then(..)` but with additional setup: +- all parsers except the last will have: + - `.splitOptions` set to `false` + - `"-help"` set to `undefined` enabling `-help` pass-through + - `"-*"` and `"@*"` set to `undefined` enabling arguments pass-through +- the last parser will have all the options from the other parsers merged into + it for complete docs/`-help` + + +XXX the resulting `` object will only contain data from the last parser, +this may change in the future. + + ## Advanced parser API diff --git a/README.md b/README.md index e626027..c4e8d63 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ This code is an evolution of that parser. - `-version` – print version - `-quiet` – suppress printing - `-` – stop argument processing -- Extensible +- Extensible and self-applicable ### Planned diff --git a/argv.js b/argv.js index 1ab1f6f..ca90b0c 100644 --- a/argv.js +++ b/argv.js @@ -319,6 +319,44 @@ object.Constructor('Parser', { toggle: function(v, cur){ return !cur }, }, + // XXX this does not merge the parse results... (???) + // XXX EXPERIMENTAL... + chain: function(...parsers){ + var Parser = this + var [post, ...pre] = parsers.reverse() + pre.reverse() + + // prepare the final parser for merged doc... + // XXX object.deepKeys(..) ??? + var final = Parser(Object.assign({}, + // set attribute order... + // NOTE: this is here to set the attribute order according + // to priority... + ...pre, + // set the correct values... + post, + ...pre)) + + return pre + // setup the chain for arg pass-through... + .map(function(e){ + // XXX object.deepKeys(..) ??? + return Parser(Object.assign({}, + e, + { + splitOptions: false, + '-help': undefined, + '-*': undefined, + '@*': undefined, + })) }) + .concat([final]) + // chain... + .reduce(function(res, cur){ + return res ? + // NOTE: need to call .then(..) on each of the parsers, + // so we return cur to be next... + (res.then(cur), cur) + : cur }, null) }, }, { // config... // @@ -1096,8 +1134,10 @@ object.Constructor('Parser', { return res } // NOTE: if successful this needs to modify the arg, thus it // returns both the new first arg and the handler... - // XXX if no handler is found this should return the original - // input arg... + // NOTE: if the first letter is a fail the whole arg will get + // reported... + // XXX do we need to report the specific fail or the whole + // unsplit arg??? (see below) var splitArgs = function(arg, rest){ var [arg, value] = arg.split(/=/) // skip single letter unknown or '--' options... @@ -1111,9 +1151,18 @@ object.Constructor('Parser', { // push the value to the last arg... value !== undefined && r.push(r.pop() +'='+ value) - // push new options back to option "stack"... - rest.splice(0, 0, ...r) - return [ a, parsed.handler(a)[1] ] } + var h = parsed.handler(a)[1] + // XXX do we need to report the specific fail or the whole + // unsplit arg??? + // check the rest of the args... + //if(h && r.reduce(function(r, a){ + // return r && parsed.handler(a)[1] }, true)){ + if(h){ + // push new options back to option "stack"... + rest.splice(0, 0, ...r) + return [ a, h ] } + // no handler found -> return undefined + return [ arg, undefined ] } try{ // parse/interpret the arguments and call handlers... @@ -1139,6 +1188,7 @@ object.Constructor('Parser', { : parsed.isCommand(arg) ? ['cmd', COMMAND_PREFIX +'*'] : ['unhandled'] + // no handler is found... if(type == 'unhandled'){ unhandled.push(arg) continue } @@ -1156,9 +1206,6 @@ object.Constructor('Parser', { // dynamic or error... || parsed.handler(dfl)[1] // no handler found and '-*' or '@*' not defined... - // XXX if nether the whole arg nor it split are found - // we need to push the original to unhandled... - // ...or is setting .splitOptions to false enough??? if(handler == null){ unhandled.push(arg) continue } diff --git a/examples/chain.js b/examples/chain.js index d17815c..86b40f7 100644 --- a/examples/chain.js +++ b/examples/chain.js @@ -52,8 +52,34 @@ argv.Parser({ 'any other options']}, })) + +var parser2 = +exports.parser2 = +argv.Parser.chain({ + '-a': { + doc: [ + 'high priority option', + 'this will get processed before', + 'any other options'], + handler: function(){ + console.log('### high priority option') }}, +},{ + '-b': { + doc: 'medium priority option', + handler: function(){ + console.log('### normal priority option') }}, +},{ + '-c': { + doc: 'normal priority option', + handler: function(){ + console.log('### normal priority option') }}, +}) + + + // run the parser... __filename == (require.main || {}).filename - && parser(process.argv) + && parser2() + // vim:set ts=4 sw=4 spell : diff --git a/package.json b/package.json index eee3905..941268e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-argv", - "version": "2.14.0", + "version": "2.15.0", "description": "simple argv parser", "main": "argv.js", "scripts": {