mirror of
https://github.com/flynx/argv.js.git
synced 2025-10-28 18:30:07 +00:00
refactored part of doc generation and output...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
622eadee96
commit
d5d8c7a9bb
@ -28,8 +28,9 @@ This code is an evolution of that parser.
|
|||||||
- Environment variable option/command values
|
- Environment variable option/command values
|
||||||
env can control option defaults
|
env can control option defaults
|
||||||
- Reasonable defaults
|
- Reasonable defaults
|
||||||
- `-help`
|
- `-help` – generate and print help,
|
||||||
- '-version'
|
- `-version` – print version,
|
||||||
|
- `-` – stop argument processing,
|
||||||
- common option aliases
|
- common option aliases
|
||||||
- Extensible:
|
- Extensible:
|
||||||
- Hooks for option value conversion _(XXX should this be implemented???)_
|
- Hooks for option value conversion _(XXX should this be implemented???)_
|
||||||
|
|||||||
133
argv.js
133
argv.js
@ -82,15 +82,16 @@ var afterCallback = function(name){
|
|||||||
// // the function handler above to same effect...
|
// // the function handler above to same effect...
|
||||||
// '-t': '-test',
|
// '-t': '-test',
|
||||||
// '-test': {
|
// '-test': {
|
||||||
// doc: 'test option.',
|
// doc: 'test option',
|
||||||
//
|
//
|
||||||
// arg: 'VALUE',
|
// arg: 'VALUE',
|
||||||
//
|
//
|
||||||
// env: 'VALUE',
|
// env: 'VALUE',
|
||||||
//
|
//
|
||||||
// // XXX
|
|
||||||
// default: 123,
|
// default: 123,
|
||||||
//
|
//
|
||||||
|
// required: true,
|
||||||
|
//
|
||||||
// handler: function(opts, key, value){
|
// handler: function(opts, key, value){
|
||||||
// ...
|
// ...
|
||||||
// }},
|
// }},
|
||||||
@ -133,10 +134,10 @@ var afterCallback = function(name){
|
|||||||
// yet know of any error or stop conditions triggered later in the argv.
|
// yet know of any error or stop conditions triggered later in the argv.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// XXX handle .required options...
|
// NOTE: essentially this parser is a very basic stack language...
|
||||||
// XXX handle .default value
|
// XXX can we implement the whole thing directly as a stack language???
|
||||||
|
//
|
||||||
// XXX handle option types???
|
// XXX handle option types???
|
||||||
// XXX add support for outputting strings instead of console.log(..)
|
|
||||||
// XXX --help should work for any command and not just for the nested
|
// XXX --help should work for any command and not just for the nested
|
||||||
// parser commands... (???)
|
// parser commands... (???)
|
||||||
// ...not sure how to implement this...
|
// ...not sure how to implement this...
|
||||||
@ -166,6 +167,16 @@ object.Constructor('Parser', {
|
|||||||
scriptPath: null,
|
scriptPath: null,
|
||||||
|
|
||||||
|
|
||||||
|
// output...
|
||||||
|
// XXX is this the right way to go???
|
||||||
|
print: function(){
|
||||||
|
console.log(...arguments)
|
||||||
|
return this },
|
||||||
|
printError: function(){
|
||||||
|
console.error(...arguments)
|
||||||
|
return this },
|
||||||
|
|
||||||
|
|
||||||
// Handler API...
|
// Handler API...
|
||||||
//
|
//
|
||||||
// Format:
|
// Format:
|
||||||
@ -174,6 +185,10 @@ object.Constructor('Parser', {
|
|||||||
// ...
|
// ...
|
||||||
// ]
|
// ]
|
||||||
//
|
//
|
||||||
|
// XXX do we need to output <doc> here???
|
||||||
|
// ...if it's used only in -help then it would be simpler to
|
||||||
|
// remove it from here and get everything in formDoc(..), same
|
||||||
|
// goes for <arg>...
|
||||||
options: function(...prefix){
|
options: function(...prefix){
|
||||||
var that = this
|
var that = this
|
||||||
prefix = prefix.length == 0 ?
|
prefix = prefix.length == 0 ?
|
||||||
@ -211,6 +226,10 @@ object.Constructor('Parser', {
|
|||||||
.filter(function([k, a, d, handler]){
|
.filter(function([k, a, d, handler]){
|
||||||
return !!handler.env
|
return !!handler.env
|
||||||
|| 'default' in handler }) },
|
|| 'default' in handler }) },
|
||||||
|
requiredOptions: function(){
|
||||||
|
return this.options()
|
||||||
|
.filter(function([k, a, d, handler]){
|
||||||
|
return handler.required }) },
|
||||||
commands: function(){
|
commands: function(){
|
||||||
return this.options(this.commandPrefix) },
|
return this.options(this.commandPrefix) },
|
||||||
isCommand: function(str){
|
isCommand: function(str){
|
||||||
@ -246,11 +265,13 @@ object.Constructor('Parser', {
|
|||||||
// XXX need to test option definitions... (???)
|
// XXX need to test option definitions... (???)
|
||||||
// i.e. report loops and dead ends...
|
// i.e. report loops and dead ends...
|
||||||
|
|
||||||
// doc stuff...
|
// doc config...
|
||||||
helpColumnOffset: 3,
|
helpColumnOffset: 3,
|
||||||
helpColumnPrefix: '- ',
|
helpColumnPrefix: '- ',
|
||||||
//helpOptionSeparator: ' | ',
|
//helpOptionSeparator: ' | ',
|
||||||
helpArgumentSeparator: ', ',
|
helpArgumentSeparator: ', ',
|
||||||
|
//helpValueSeparator: '=',
|
||||||
|
helpValueSeparator: ' ',
|
||||||
|
|
||||||
// doc sections...
|
// doc sections...
|
||||||
version: undefined,
|
version: undefined,
|
||||||
@ -288,10 +309,22 @@ object.Constructor('Parser', {
|
|||||||
var that = this
|
var that = this
|
||||||
var sep = this.helpArgumentSeparator
|
var sep = this.helpArgumentSeparator
|
||||||
var expandVars = this.expandTextVars.bind(this)
|
var expandVars = this.expandTextVars.bind(this)
|
||||||
var formDoc = function(doc, env){
|
var formDoc = function(doc, handler){
|
||||||
return [doc, ...(env ?
|
var info = [
|
||||||
[`(default value: \$${env})`]
|
...(handler.required ?
|
||||||
: [])] }
|
['Required']
|
||||||
|
: []),
|
||||||
|
...('default' in handler ?
|
||||||
|
[`Default: ${handler.default}`]
|
||||||
|
: []),
|
||||||
|
...(handler.env ?
|
||||||
|
[`Env: \$${handler.env}`]
|
||||||
|
: []),
|
||||||
|
].join(', ')
|
||||||
|
return [doc,
|
||||||
|
...(info.length > 0 ?
|
||||||
|
['('+ info +')']
|
||||||
|
: [])] }
|
||||||
var getValue = function(name){
|
var getValue = function(name){
|
||||||
return that[name] ?
|
return that[name] ?
|
||||||
['', typeof(that[name]) == 'function' ?
|
['', typeof(that[name]) == 'function' ?
|
||||||
@ -304,7 +337,7 @@ object.Constructor('Parser', {
|
|||||||
['', title +':', ...items]
|
['', title +':', ...items]
|
||||||
: [] }
|
: [] }
|
||||||
|
|
||||||
console.log(
|
this.print(
|
||||||
expandVars([
|
expandVars([
|
||||||
`Usage: ${ getValue('usage').join('') }`,
|
`Usage: ${ getValue('usage').join('') }`,
|
||||||
// doc (optional)...
|
// doc (optional)...
|
||||||
@ -317,7 +350,7 @@ object.Constructor('Parser', {
|
|||||||
return doc !== false })
|
return doc !== false })
|
||||||
.map(function([opts, arg, doc, handler]){
|
.map(function([opts, arg, doc, handler]){
|
||||||
return [
|
return [
|
||||||
opts
|
[opts
|
||||||
.sort(function(a, b){
|
.sort(function(a, b){
|
||||||
return a.length - b.length})
|
return a.length - b.length})
|
||||||
.map(function(o, i){
|
.map(function(o, i){
|
||||||
@ -328,9 +361,12 @@ object.Constructor('Parser', {
|
|||||||
' '.repeat(sep.length + 2) +'-'+ o
|
' '.repeat(sep.length + 2) +'-'+ o
|
||||||
// add extra '-' to long options...
|
// add extra '-' to long options...
|
||||||
: '-'+ o })
|
: '-'+ o })
|
||||||
.join(sep)
|
.join(sep),
|
||||||
+' '+ (arg || ''),
|
...(arg ?
|
||||||
...formDoc(doc, handler.env) ] })),
|
[arg]
|
||||||
|
: [])]
|
||||||
|
.join(that.helpValueSeparator),
|
||||||
|
...formDoc(doc, handler) ] })),
|
||||||
// dynamic options...
|
// dynamic options...
|
||||||
...section('Dynamic options',
|
...section('Dynamic options',
|
||||||
this.handleArgument ?
|
this.handleArgument ?
|
||||||
@ -341,11 +377,14 @@ object.Constructor('Parser', {
|
|||||||
this.commands()
|
this.commands()
|
||||||
.map(function([cmd, arg, doc, handler]){
|
.map(function([cmd, arg, doc, handler]){
|
||||||
return [
|
return [
|
||||||
cmd
|
[cmd
|
||||||
.map(function(cmd){ return cmd.slice(1)})
|
.map(function(cmd){ return cmd.slice(1)})
|
||||||
.join(sep)
|
.join(sep),
|
||||||
+' '+ (arg || ''),
|
...(arg ?
|
||||||
...formDoc(doc, handler.env) ] })),
|
[arg]
|
||||||
|
: [])]
|
||||||
|
.join(that.helpValueSeparator),
|
||||||
|
...formDoc(doc, handler) ] })),
|
||||||
// examples (optional)...
|
// examples (optional)...
|
||||||
...section('Examples',
|
...section('Examples',
|
||||||
this.examples instanceof Array ?
|
this.examples instanceof Array ?
|
||||||
@ -372,7 +411,7 @@ object.Constructor('Parser', {
|
|||||||
doc: 'show $SCRIPTNAME verion and exit',
|
doc: 'show $SCRIPTNAME verion and exit',
|
||||||
priority: 99,
|
priority: 99,
|
||||||
handler: function(){
|
handler: function(){
|
||||||
console.log(this.version || '0.0.0')
|
this.print(this.version || '0.0.0')
|
||||||
return module.STOP }, },
|
return module.STOP }, },
|
||||||
|
|
||||||
// Stop processing arguments and continue into .then(..) handlers...
|
// Stop processing arguments and continue into .then(..) handlers...
|
||||||
@ -382,7 +421,7 @@ object.Constructor('Parser', {
|
|||||||
// stopping the nested context and letting the parent continue.
|
// stopping the nested context and letting the parent continue.
|
||||||
//
|
//
|
||||||
// XXX should we be able to force the parent/root to also stop???
|
// XXX should we be able to force the parent/root to also stop???
|
||||||
// XXX do we actually need this???
|
// ...this can be done by pushing '-' to the rest's head...
|
||||||
'-': {
|
'-': {
|
||||||
doc: 'stop processing arguments after this point',
|
doc: 'stop processing arguments after this point',
|
||||||
handler: function(){
|
handler: function(){
|
||||||
@ -425,7 +464,7 @@ object.Constructor('Parser', {
|
|||||||
// doc handler...
|
// doc handler...
|
||||||
if(arguments.length == 1 && arguments[0] == 'doc'){
|
if(arguments.length == 1 && arguments[0] == 'doc'){
|
||||||
return undefined }
|
return undefined }
|
||||||
console.error('Unknown '+ (key.startsWith('-') ? 'option:' : 'command:'), key)
|
this.printError('Unknown '+ (key.startsWith('-') ? 'option:' : 'command:'), key)
|
||||||
return module.ERROR },
|
return module.ERROR },
|
||||||
|
|
||||||
// Handle argument value conversion...
|
// Handle argument value conversion...
|
||||||
@ -489,6 +528,7 @@ object.Constructor('Parser', {
|
|||||||
//
|
//
|
||||||
// NOTE: this (i.e. parser) can be used as a nested command/option
|
// NOTE: this (i.e. parser) can be used as a nested command/option
|
||||||
// handler...
|
// handler...
|
||||||
|
//
|
||||||
__call__: function(context, argv, main, root_value){
|
__call__: function(context, argv, main, root_value){
|
||||||
var that = this
|
var that = this
|
||||||
var nested = false
|
var nested = false
|
||||||
@ -522,6 +562,10 @@ object.Constructor('Parser', {
|
|||||||
var opt_pattern = this.optionInputPattern
|
var opt_pattern = this.optionInputPattern
|
||||||
|
|
||||||
// helpers...
|
// helpers...
|
||||||
|
var handleError = function(reason, arg, rest){
|
||||||
|
that.error(reason, arg, rest)
|
||||||
|
that.handleErrorExit
|
||||||
|
&& that.handleErrorExit(arg, reason) }
|
||||||
var runHandler = function(handler, arg, rest){
|
var runHandler = function(handler, arg, rest){
|
||||||
var [arg, value] = arg.split(/=/)
|
var [arg, value] = arg.split(/=/)
|
||||||
// get option value...
|
// get option value...
|
||||||
@ -549,13 +593,12 @@ object.Constructor('Parser', {
|
|||||||
[value]
|
[value]
|
||||||
: []))
|
: []))
|
||||||
// handle .STOP / .ERROR
|
// handle .STOP / .ERROR
|
||||||
if(res === module.STOP || res === module.ERROR){
|
res === module.STOP
|
||||||
that[res === module.STOP ?
|
&& that.stop(arg, rest)
|
||||||
'stop'
|
// XXX might be a good idea to use exceptions for this...
|
||||||
: 'error'](arg, rest)
|
res === module.ERROR
|
||||||
res === module.ERROR
|
// XXX is this the correct reason???
|
||||||
&& that.handleErrorExit
|
&& handleError('unknown', arg, rest)
|
||||||
&& that.handleErrorExit(arg) }
|
|
||||||
return res }
|
return res }
|
||||||
// NOTE: if successful this needs to modify the arg, thus it
|
// NOTE: if successful this needs to modify the arg, thus it
|
||||||
// returns both the new first arg and the handler...
|
// returns both the new first arg and the handler...
|
||||||
@ -579,6 +622,7 @@ object.Constructor('Parser', {
|
|||||||
&& [a, handler] }
|
&& [a, handler] }
|
||||||
|
|
||||||
var values = new Set()
|
var values = new Set()
|
||||||
|
var seen = new Set()
|
||||||
var unhandled = []
|
var unhandled = []
|
||||||
while(rest.length > 0){
|
while(rest.length > 0){
|
||||||
var arg = rest.shift()
|
var arg = rest.shift()
|
||||||
@ -607,6 +651,7 @@ object.Constructor('Parser', {
|
|||||||
;(handler.env
|
;(handler.env
|
||||||
|| 'default' in handler)
|
|| 'default' in handler)
|
||||||
&& values.add(handler)
|
&& values.add(handler)
|
||||||
|
seen.add(handler)
|
||||||
|
|
||||||
var res = runHandler(handler, arg, rest)
|
var res = runHandler(handler, arg, rest)
|
||||||
|
|
||||||
@ -623,13 +668,29 @@ object.Constructor('Parser', {
|
|||||||
// unhandled...
|
// unhandled...
|
||||||
arg
|
arg
|
||||||
&& unhandled.push(arg) }
|
&& unhandled.push(arg) }
|
||||||
// call value handlers that were not explicitly called yet...
|
// call value handlers with .env or .default values that were
|
||||||
typeof(process) != 'unhandled'
|
// not explicitly called yet...
|
||||||
&& this.optionsWithValue()
|
this.optionsWithValue()
|
||||||
.forEach(function([k, a, d, handler]){
|
.forEach(function([k, a, d, handler]){
|
||||||
values.has(handler)
|
values.has(handler)
|
||||||
|| ((handler.env in process.env || handler.default)
|
|| (((typeof(process) != 'undefined'
|
||||||
&& runHandler(handler, a || k[0], null, rest)) })
|
&& handler.env in process.env)
|
||||||
|
|| handler.default)
|
||||||
|
&& seen.add(handler)
|
||||||
|
&& runHandler(handler, a || k[0], null, rest)) })
|
||||||
|
|
||||||
|
// check required options...
|
||||||
|
var missing = this
|
||||||
|
.requiredOptions()
|
||||||
|
.filter(function([k, a, d, h]){
|
||||||
|
return !seen.has(h) })
|
||||||
|
.map(function([k, a, d, h]){
|
||||||
|
return k.pop() })
|
||||||
|
if(missing.length > 0){
|
||||||
|
handleError('required', missing, rest)
|
||||||
|
this.printError('Required but missing:', missing.join(', '))
|
||||||
|
return this }
|
||||||
|
|
||||||
// post handlers...
|
// post handlers...
|
||||||
root_value = root_value && this.handleArgumentValue ?
|
root_value = root_value && this.handleArgumentValue ?
|
||||||
this.handleArgumentValue(this, root_value)
|
this.handleArgumentValue(this, root_value)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ig-argv",
|
"name": "ig-argv",
|
||||||
"version": "2.0.6",
|
"version": "2.0.7",
|
||||||
"description": "simple argv parser",
|
"description": "simple argv parser",
|
||||||
"main": "argv.js",
|
"main": "argv.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
7
test.js
7
test.js
@ -40,8 +40,15 @@ argv.Parser({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'-r': '-required',
|
||||||
|
'-required': {
|
||||||
|
doc: 'Required option',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
'-test': argv.Parser({
|
'-test': argv.Parser({
|
||||||
env: 'TEST',
|
env: 'TEST',
|
||||||
|
arg: 'TEST',
|
||||||
default: 'moo',
|
default: 'moo',
|
||||||
}).then(function(){
|
}).then(function(){
|
||||||
console.log('TEST', ...arguments) }),
|
console.log('TEST', ...arguments) }),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user