mirror of
https://github.com/flynx/argv.js.git
synced 2025-10-29 02:40:07 +00:00
reworked error handling...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
742e1c51bc
commit
dd74fb1444
225
argv.js
225
argv.js
@ -49,7 +49,14 @@ module.ParserError =
|
|||||||
// NOTE: I do not get why JavaScript's Error implements this
|
// NOTE: I do not get why JavaScript's Error implements this
|
||||||
// statically...
|
// statically...
|
||||||
get name(){
|
get name(){
|
||||||
return this.constructor.name }, })
|
return this.constructor.name },
|
||||||
|
|
||||||
|
// NOTE: msg is handled by Error(..)
|
||||||
|
__init__: function(msg, arg, rest){
|
||||||
|
this.arg = arg
|
||||||
|
this.rest = rest
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
module.ParserTypeError =
|
module.ParserTypeError =
|
||||||
object.Constructor('ParserTypeError', module.ParserError, {})
|
object.Constructor('ParserTypeError', module.ParserError, {})
|
||||||
@ -712,7 +719,7 @@ object.Constructor('Parser', {
|
|||||||
//section_doc: ...,
|
//section_doc: ...,
|
||||||
handler: function(_, key){
|
handler: function(_, key){
|
||||||
throw module.ParserError(
|
throw module.ParserError(
|
||||||
`Unknown ${key.startsWith('-') ? 'option:' : 'command:'} ${ key }`) } },
|
`Unknown ${key.startsWith('-') ? 'option:' : 'command:'} $ARG`) } },
|
||||||
'@*': '-*',
|
'@*': '-*',
|
||||||
|
|
||||||
|
|
||||||
@ -731,11 +738,20 @@ object.Constructor('Parser', {
|
|||||||
// .printError(error, ...)
|
// .printError(error, ...)
|
||||||
// -> error
|
// -> error
|
||||||
//
|
//
|
||||||
|
// NOTE: this handles $ARG in error.message.
|
||||||
printError: afterCallback('print_error', null, function(...args){
|
printError: afterCallback('print_error', null, function(...args){
|
||||||
if(args[0] instanceof module.ParserError){
|
if(args[0] instanceof module.ParserError){
|
||||||
|
var err = args[0]
|
||||||
console.error(
|
console.error(
|
||||||
this.scriptName+':', args[0].name+':', args[0].message, ...args.slice(1))
|
this.scriptName+':',
|
||||||
return args[0] }
|
err.name+':',
|
||||||
|
err.message
|
||||||
|
// XXX this should be done in ParserError but there
|
||||||
|
// we have to fight Error's implementation of
|
||||||
|
// .message and its use...
|
||||||
|
.replace(/\$ARG/, err.arg),
|
||||||
|
...args.slice(1))
|
||||||
|
return err }
|
||||||
console.error(this.scriptName+': Error:', ...args)
|
console.error(this.scriptName+': Error:', ...args)
|
||||||
return this }),
|
return this }),
|
||||||
|
|
||||||
@ -820,7 +836,8 @@ object.Constructor('Parser', {
|
|||||||
// error...
|
// error...
|
||||||
handleErrorExit: function(arg, reason){
|
handleErrorExit: function(arg, reason){
|
||||||
typeof(process) != 'unhandled'
|
typeof(process) != 'unhandled'
|
||||||
&& process.exit(1) },
|
&& process.exit(1)
|
||||||
|
return this },
|
||||||
|
|
||||||
|
|
||||||
// Post parsing callbacks...
|
// Post parsing callbacks...
|
||||||
@ -859,11 +876,6 @@ object.Constructor('Parser', {
|
|||||||
// all the parse data...
|
// all the parse data...
|
||||||
// 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...
|
||||||
// NOTE: we can't throw ParserError(..) from outside the try/catch
|
|
||||||
// block in here as it will not be handled locally...
|
|
||||||
// XXX this may need a rethink -- should the try/catch block
|
|
||||||
// include the rest of the cases where reportError(..) is
|
|
||||||
// used or be on a level above runHandler(..)
|
|
||||||
__call__: function(context, argv, main, root_value){
|
__call__: function(context, argv, main, root_value){
|
||||||
var parsed = Object.create(this)
|
var parsed = Object.create(this)
|
||||||
var opt_pattern = parsed.optionInputPattern
|
var opt_pattern = parsed.optionInputPattern
|
||||||
@ -896,17 +908,14 @@ object.Constructor('Parser', {
|
|||||||
|
|
||||||
// helpers...
|
// helpers...
|
||||||
var handleError = function(reason, arg, rest){
|
var handleError = function(reason, arg, rest){
|
||||||
|
arg = arg || reason.arg
|
||||||
|
rest = rest || reason.rest
|
||||||
reason = reason instanceof Error ?
|
reason = reason instanceof Error ?
|
||||||
[reason.name, reason.message].join(': ')
|
[reason.name, reason.message].join(': ')
|
||||||
: reason
|
: reason
|
||||||
parsed.error(reason, arg, rest)
|
parsed.error(reason, arg, rest)
|
||||||
parsed.handleErrorExit
|
parsed.handleErrorExit
|
||||||
&& parsed.handleErrorExit(arg, reason) }
|
&& parsed.handleErrorExit(arg, reason) }
|
||||||
var reportError = function(message, arg, rest){
|
|
||||||
message = message
|
|
||||||
.replace(/\$ARG/g, arg)
|
|
||||||
parsed.printError(module.ParserError(message))
|
|
||||||
return handleError(message, arg, rest) }
|
|
||||||
var runHandler = function(handler, arg, rest){
|
var runHandler = function(handler, arg, rest){
|
||||||
var [arg, value] = arg instanceof Array ?
|
var [arg, value] = arg instanceof Array ?
|
||||||
arg
|
arg
|
||||||
@ -927,26 +936,24 @@ object.Constructor('Parser', {
|
|||||||
: value
|
: value
|
||||||
// required value check...
|
// required value check...
|
||||||
if(handler.valueRequired && value == null){
|
if(handler.valueRequired && value == null){
|
||||||
return reportError('Value missing: $ARG=?', arg, rest) }
|
throw module.ParserValueError('Value missing: ${ arg }=?') }
|
||||||
|
|
||||||
|
// run handler...
|
||||||
try {
|
try {
|
||||||
// run handler...
|
|
||||||
var res = parsed.handle(handler, rest, arg, value)
|
var res = parsed.handle(handler, rest, arg, value)
|
||||||
|
|
||||||
|
// update error object with current context's arg and rest...
|
||||||
} catch(err){
|
} catch(err){
|
||||||
// re-throw the error...
|
if(err instanceof module.ParserError){
|
||||||
// NOTE: do not like that this can mask the location of
|
err.arg = err.arg || arg
|
||||||
// the original error.
|
err.rest = err.rest || rest }
|
||||||
if(!(err instanceof module.ParserError)){
|
throw err }
|
||||||
throw err }
|
|
||||||
// XXX should we report an error here???
|
|
||||||
parsed.printError(err)
|
|
||||||
res = err }
|
|
||||||
|
|
||||||
// NOTE: we also need to handle the errors passed to us from
|
// NOTE: we also need to handle the errors passed to us from
|
||||||
// nested parsers...
|
// nested parsers...
|
||||||
res === module.STOP
|
res === module.STOP
|
||||||
&& parsed.stop(arg, rest)
|
&& parsed.stop(arg, rest)
|
||||||
|
// XXX revise -- do we need to re-handle errors???
|
||||||
res instanceof module.ParserError
|
res instanceof module.ParserError
|
||||||
&& handleError(res, arg, rest)
|
&& handleError(res, arg, rest)
|
||||||
return res }
|
return res }
|
||||||
@ -969,88 +976,100 @@ object.Constructor('Parser', {
|
|||||||
rest.splice(0, 0, ...r)
|
rest.splice(0, 0, ...r)
|
||||||
return [ a, parsed.handler(a)[1] ] }
|
return [ a, parsed.handler(a)[1] ] }
|
||||||
|
|
||||||
// parse/interpret the arguments and call handlers...
|
try{
|
||||||
var values = new Set()
|
// parse/interpret the arguments and call handlers...
|
||||||
var seen = new Set()
|
var values = new Set()
|
||||||
var unhandled = parsed.unhandled = []
|
var seen = new Set()
|
||||||
while(rest.length > 0){
|
var unhandled = parsed.unhandled = []
|
||||||
var arg = rest.shift()
|
while(rest.length > 0){
|
||||||
// non-string stuff in arg list...
|
var arg = rest.shift()
|
||||||
if(typeof(arg) != typeof('str')){
|
// non-string stuff in arg list...
|
||||||
unhandled.push(arg)
|
if(typeof(arg) != typeof('str')){
|
||||||
continue }
|
unhandled.push(arg)
|
||||||
// NOTE: opts and commands do not follow the same path here
|
continue }
|
||||||
// because options if unidentified need to be split into
|
// NOTE: opts and commands do not follow the same path here
|
||||||
// single letter options and commands to not...
|
// because options if unidentified need to be split into
|
||||||
var [type, dfl] = opt_pattern.test(arg) ?
|
// single letter options and commands to not...
|
||||||
['opt', OPTION_PREFIX +'*']
|
var [type, dfl] = opt_pattern.test(arg) ?
|
||||||
: parsed.isCommand(arg) ?
|
['opt', OPTION_PREFIX +'*']
|
||||||
['cmd', COMMAND_PREFIX +'*']
|
: parsed.isCommand(arg) ?
|
||||||
: ['unhandled']
|
['cmd', COMMAND_PREFIX +'*']
|
||||||
// options / commands...
|
: ['unhandled']
|
||||||
if(type != 'unhandled'){
|
// options / commands...
|
||||||
// quote '-*' / '@*'...
|
if(type != 'unhandled'){
|
||||||
arg = arg.replace(/^(.)\*$/, '$1\\*')
|
// quote '-*' / '@*'...
|
||||||
// get handler...
|
arg = arg.replace(/^(.)\*$/, '$1\\*')
|
||||||
var handler = parsed.handler(arg)[1]
|
// get handler...
|
||||||
// handle merged options...
|
var handler = parsed.handler(arg)[1]
|
||||||
|| (type == 'opt'
|
// handle merged options...
|
||||||
&& parsed.splitOptions
|
|| (type == 'opt'
|
||||||
// NOTE: we set arg here...
|
&& parsed.splitOptions
|
||||||
&& ([arg, handler] = splitArgs(arg, rest))[1] )
|
// NOTE: we set arg here...
|
||||||
// dynamic or error...
|
&& ([arg, handler] = splitArgs(arg, rest))[1] )
|
||||||
|| parsed.handler(dfl)[1]
|
// dynamic or error...
|
||||||
// no handler found and '-*' or '@*' not defined...
|
|| parsed.handler(dfl)[1]
|
||||||
if(handler == null){
|
// no handler found and '-*' or '@*' not defined...
|
||||||
return reportError(`Unknown ${ type == 'opt' ? 'option' : 'command:' } $ARG`, arg, rest) }
|
if(handler == null){
|
||||||
|
throw ParserError(`Unknown ${ type == 'opt' ? 'option' : 'command:' } $ARG`, arg) }
|
||||||
|
|
||||||
// mark handler...
|
// mark handler...
|
||||||
;(handler.env || 'default' in handler)
|
;(handler.env || 'default' in handler)
|
||||||
&& values.add(handler)
|
&& values.add(handler)
|
||||||
seen.add(handler)
|
seen.add(handler)
|
||||||
|
|
||||||
var res = runHandler(handler, arg, rest)
|
var res = runHandler(handler, arg, rest)
|
||||||
|
|
||||||
// handle stop conditions...
|
// handle stop conditions...
|
||||||
if(res === module.STOP
|
if(res === module.STOP
|
||||||
|| res instanceof module.ParserError){
|
|| res instanceof module.ParserError){
|
||||||
return nested ?
|
return nested ?
|
||||||
res
|
res
|
||||||
: parsed }
|
: parsed }
|
||||||
// finish arg processing now...
|
// finish arg processing now...
|
||||||
if(res === module.THEN){
|
if(res === module.THEN){
|
||||||
break }
|
break }
|
||||||
continue }
|
continue }
|
||||||
// unhandled...
|
// unhandled...
|
||||||
unhandled.push(arg) }
|
unhandled.push(arg) }
|
||||||
// call value handlers with .env or .default values that were
|
// call value handlers with .env or .default values that were
|
||||||
// not explicitly called yet...
|
// not explicitly called yet...
|
||||||
// XXX an error, THEN or STOP returned from runHandler(..) in here will
|
// XXX THEN or STOP returned from runHandler(..) in here will
|
||||||
// not stop execution -- should it???
|
// not stop execution -- should it???
|
||||||
// XXX a ParserError thrown here will not be handled correctly
|
parsed.optionsWithValue()
|
||||||
// in the root parser...
|
.forEach(function([k, a, d, handler]){
|
||||||
parsed.optionsWithValue()
|
values.has(handler)
|
||||||
.forEach(function([k, a, d, handler]){
|
|| (((typeof(process) != 'undefined'
|
||||||
values.has(handler)
|
&& handler.env in process.env)
|
||||||
|| (((typeof(process) != 'undefined'
|
|| handler.default)
|
||||||
&& handler.env in process.env)
|
&& seen.add(handler)
|
||||||
|| handler.default)
|
&& runHandler(handler,
|
||||||
&& seen.add(handler)
|
[k[0], handler.default],
|
||||||
// XXX should we handle STOP / ParserError here???
|
rest)) })
|
||||||
&& runHandler(handler,
|
|
||||||
[k[0], handler.default],
|
|
||||||
rest)) })
|
|
||||||
|
|
||||||
// check and report required options...
|
// check and report required options...
|
||||||
var missing = parsed
|
var missing = parsed
|
||||||
.requiredOptions()
|
.requiredOptions()
|
||||||
.filter(function([k, a, d, h]){
|
.filter(function([k, a, d, h]){
|
||||||
return !seen.has(h) })
|
return !seen.has(h) })
|
||||||
.map(function([k, a, d, h]){
|
.map(function([k, a, d, h]){
|
||||||
return k.pop() })
|
return k.pop() })
|
||||||
if(missing.length > 0){
|
if(missing.length > 0){
|
||||||
reportError('required but missing: $ARG', missing.join(', '), rest)
|
throw module.ParserError(`required but missing: $ARG`, missing.join(', ')) }
|
||||||
return parsed }
|
|
||||||
|
// handle ParserError...
|
||||||
|
} catch(err){
|
||||||
|
// re-throw the error...
|
||||||
|
if(!(err instanceof module.ParserError)){
|
||||||
|
throw err }
|
||||||
|
|
||||||
|
// report local errors...
|
||||||
|
// NOTE: non-local errors are threaded as return values...
|
||||||
|
parsed.printError(err)
|
||||||
|
handleError(err, err.arg, rest)
|
||||||
|
|
||||||
|
return nested ?
|
||||||
|
err
|
||||||
|
: parsed }
|
||||||
|
|
||||||
// handle root value...
|
// handle root value...
|
||||||
root_value =
|
root_value =
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
// compatible with both node's and RequireJS' require(..)
|
// compatible with both node's and RequireJS' require(..)
|
||||||
var argv = require('../argv')
|
var argv = require('../argv')
|
||||||
|
|
||||||
var parser = argv.Parser({
|
var parser =
|
||||||
|
exports.parser =
|
||||||
|
argv.Parser({
|
||||||
// option definitions...
|
// option definitions...
|
||||||
// ...
|
// ...
|
||||||
})
|
})
|
||||||
@ -13,7 +15,7 @@ var parser = argv.Parser({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// run the parser...
|
// run the parser...
|
||||||
__filename == require.main.filename
|
__filename == (require.main || {}).filename
|
||||||
&& parser(process.argv)
|
&& parser(process.argv)
|
||||||
|
|
||||||
// vim:set ts=4 sw=4 spell :
|
// vim:set ts=4 sw=4 spell :
|
||||||
|
|||||||
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
var argv = require('../argv')
|
var argv = require('../argv')
|
||||||
|
|
||||||
var parser = argv.Parser({
|
var parser =
|
||||||
|
exports.parser =
|
||||||
|
argv.Parser({
|
||||||
doc: 'Example script options',
|
doc: 'Example script options',
|
||||||
|
|
||||||
// to make things consistent we'll take the version from package.json
|
// to make things consistent we'll take the version from package.json
|
||||||
@ -71,6 +73,7 @@ var parser = argv.Parser({
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// XXX this is misbehaving -- setting true instead of $HOME
|
||||||
'-home': {
|
'-home': {
|
||||||
doc: 'set home path',
|
doc: 'set home path',
|
||||||
arg: 'HOME | home',
|
arg: 'HOME | home',
|
||||||
@ -145,7 +148,7 @@ var parser = argv.Parser({
|
|||||||
|
|
||||||
|
|
||||||
// run the parser...
|
// run the parser...
|
||||||
__filename == require.main.filename
|
__filename == (require.main || {}).filename
|
||||||
&& parser()
|
&& parser()
|
||||||
|
|
||||||
// vim:set ts=4 sw=4 spell :
|
// vim:set ts=4 sw=4 spell :
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ig-argv",
|
"name": "ig-argv",
|
||||||
"version": "2.8.1",
|
"version": "2.9.0",
|
||||||
"description": "simple argv parser",
|
"description": "simple argv parser",
|
||||||
"main": "argv.js",
|
"main": "argv.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
17
test.js
17
test.js
@ -14,6 +14,11 @@ var object = require('ig-object')
|
|||||||
var argv = require('./argv')
|
var argv = require('./argv')
|
||||||
|
|
||||||
|
|
||||||
|
var bare = module.bare = require('./examples/bare').parser
|
||||||
|
var options = module.options = require('./examples/options').parser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -90,7 +95,7 @@ argv.Parser({
|
|||||||
'-error': {
|
'-error': {
|
||||||
doc: 'throw an error',
|
doc: 'throw an error',
|
||||||
handler: function(){
|
handler: function(){
|
||||||
throw argv.ParserError('error') }},
|
throw argv.ParserError('error: $ARG') }},
|
||||||
'-passive-error': {
|
'-passive-error': {
|
||||||
doc: 'throw an error',
|
doc: 'throw an error',
|
||||||
handler: function(){
|
handler: function(){
|
||||||
@ -148,6 +153,16 @@ argv.Parser({
|
|||||||
'-k': '-l',
|
'-k': '-l',
|
||||||
'-l': '-m',
|
'-l': '-m',
|
||||||
'-m': '-k',
|
'-m': '-k',
|
||||||
|
|
||||||
|
|
||||||
|
'@bare': bare,
|
||||||
|
'@opts': options,
|
||||||
|
|
||||||
|
|
||||||
|
// collision test...
|
||||||
|
// NOTE: values of these will shadow the API...
|
||||||
|
'@options': {},
|
||||||
|
'-handler': {},
|
||||||
})
|
})
|
||||||
//.print(function(...args){
|
//.print(function(...args){
|
||||||
// console.log('----\n', ...args)
|
// console.log('----\n', ...args)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user