mirror of
https://github.com/flynx/object.js.git
synced 2025-10-29 18:40:08 +00:00
split out argv parsing into ig-argv + some tweaks...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
c700ec3812
commit
f7eb790f41
31
README.md
31
README.md
@ -835,13 +835,28 @@ normalizeTextIndent(..)
|
|||||||
|
|
||||||
This ignores `object.LEADING_TABS` and `leading_tabs` is 0 by default.
|
This ignores `object.LEADING_TABS` and `leading_tabs` is 0 by default.
|
||||||
|
|
||||||
|
### `deepKeys(..)`
|
||||||
|
|
||||||
|
```
|
||||||
|
deepKeys(<obj>)
|
||||||
|
-> <keys>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
deepKeys(<obj>, <stop>)
|
||||||
|
-> <keys>
|
||||||
|
```
|
||||||
|
|
||||||
|
This is like `Object.keys(..)` but will get the keys from the whole
|
||||||
|
prototype chain or until `<stop>` if given.
|
||||||
|
|
||||||
|
|
||||||
### `match(..)`
|
### `match(..)`
|
||||||
|
|
||||||
Test if the two objects match in attributes and attribute values
|
Test if the two objects match in attributes and attribute values
|
||||||
```
|
```
|
||||||
match(base, obj)
|
match(<base>, <obj>)
|
||||||
-> bool
|
-> <bool>
|
||||||
```
|
```
|
||||||
|
|
||||||
This relies on first level object structure to match the input object, for
|
This relies on first level object structure to match the input object, for
|
||||||
@ -857,8 +872,8 @@ or:
|
|||||||
|
|
||||||
Non-strict match
|
Non-strict match
|
||||||
```
|
```
|
||||||
match(base, obj, true)
|
match(<base., <obj>, true)
|
||||||
-> bool
|
-> <bool>
|
||||||
```
|
```
|
||||||
|
|
||||||
Like the default case but uses _equality_ instead of _identity_ to match
|
Like the default case but uses _equality_ instead of _identity_ to match
|
||||||
@ -868,12 +883,12 @@ values.
|
|||||||
### `matchPartial(..)`
|
### `matchPartial(..)`
|
||||||
|
|
||||||
```
|
```
|
||||||
match(base, obj)
|
matchPartial(<base>, <obj>)
|
||||||
-> bool
|
-> <bool>
|
||||||
|
|
||||||
// non-strict version...
|
// non-strict version...
|
||||||
match(base, obj, true)
|
matchPartial(<base>, <obj>, true)
|
||||||
-> bool
|
-> <bool>
|
||||||
```
|
```
|
||||||
|
|
||||||
Like `.match(..)` but will check for a partial match, i.e. when `obj` is
|
Like `.match(..)` but will check for a partial match, i.e. when `obj` is
|
||||||
|
|||||||
21
object.js
21
object.js
@ -103,6 +103,27 @@ function(text, tab_size, leading_tabs){
|
|||||||
return module.normalizeIndent(text, tab_size, leading_tabs || 0) }
|
return module.normalizeIndent(text, tab_size, leading_tabs || 0) }
|
||||||
|
|
||||||
|
|
||||||
|
// Get keys from prototype chain...
|
||||||
|
//
|
||||||
|
// deepKeys(obj)
|
||||||
|
// deepKeys(obj, stop)
|
||||||
|
// -> keys
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this is like Object.keys(..) but will get keys for all levels
|
||||||
|
// till stop if given...
|
||||||
|
//
|
||||||
|
// XXX should we add this to Object???
|
||||||
|
var deepKeys =
|
||||||
|
module.deepKeys =
|
||||||
|
function(obj, stop){
|
||||||
|
var res = []
|
||||||
|
while(obj !== stop && obj != null){
|
||||||
|
res.push(Object.keys(obj))
|
||||||
|
obj = obj.__proto__ }
|
||||||
|
return [...(new Set(res.flat()))] }
|
||||||
|
|
||||||
|
|
||||||
// Match two objects...
|
// Match two objects...
|
||||||
//
|
//
|
||||||
// match(a, b)
|
// match(a, b)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ig-object",
|
"name": "ig-object",
|
||||||
"version": "5.0.8",
|
"version": "5.0.9",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "object.js",
|
"main": "object.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -27,6 +27,7 @@
|
|||||||
"homepage": "https://github.com/flynx/object.js#readme",
|
"homepage": "https://github.com/flynx/object.js#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"c8": "*"
|
"c8": "*",
|
||||||
|
"ig-argv": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
253
test.js
253
test.js
@ -43,6 +43,7 @@
|
|||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
|
||||||
var colors = require('colors')
|
var colors = require('colors')
|
||||||
|
var argv = require('ig-argv')
|
||||||
|
|
||||||
var object = require('./object')
|
var object = require('./object')
|
||||||
|
|
||||||
@ -71,15 +72,6 @@ Object.defineProperty(String.prototype, 'raw', {
|
|||||||
return this.replace(/\x1b\[..?m/g, '') }, })
|
return this.replace(/\x1b\[..?m/g, '') }, })
|
||||||
|
|
||||||
|
|
||||||
// get all keys accessible from object...
|
|
||||||
var deepKeys = function(obj, stop){
|
|
||||||
var res = []
|
|
||||||
while(obj !== stop && obj != null){
|
|
||||||
res.push(Object.keys(obj))
|
|
||||||
obj = obj.__proto__ }
|
|
||||||
return [...(new Set(res.flat()))] }
|
|
||||||
|
|
||||||
|
|
||||||
// compare two arrays by items...
|
// compare two arrays by items...
|
||||||
var arrayCmp = function(a, b){
|
var arrayCmp = function(a, b){
|
||||||
var ka = Object.keys(a)
|
var ka = Object.keys(a)
|
||||||
@ -115,241 +107,6 @@ var instances = function(obj){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
module.OPTION_PATTERN = /^--?/
|
|
||||||
module.COMMAND_PATTERN = /^[a-zA-Z]/
|
|
||||||
|
|
||||||
// basic argv parser...
|
|
||||||
//
|
|
||||||
// Format:
|
|
||||||
// {
|
|
||||||
// // alias...
|
|
||||||
// v: 'verbose',
|
|
||||||
// // handler...
|
|
||||||
// verbose: function(opt, rest){
|
|
||||||
// ...
|
|
||||||
// },
|
|
||||||
//
|
|
||||||
// t: 'test',
|
|
||||||
// test: {
|
|
||||||
// doc: 'test option.',
|
|
||||||
// arg: 'VALUE',
|
|
||||||
// handler: function(value, opt, rest){
|
|
||||||
// ...
|
|
||||||
// }},
|
|
||||||
//
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// XXX add features:
|
|
||||||
// - option groups -- nested specs...
|
|
||||||
// - arg value type conversion???
|
|
||||||
// - make this a constructor???
|
|
||||||
// - extend this to support command calling...
|
|
||||||
// XXX do we handle = for options with values???
|
|
||||||
// XXX move this to it's own lib...
|
|
||||||
// argv-handler
|
|
||||||
// ig-argv
|
|
||||||
// ...
|
|
||||||
// XXX need better test processing:
|
|
||||||
// - line breaks
|
|
||||||
// - ...
|
|
||||||
var ArgvParser = function(spec){
|
|
||||||
// spec defaults...
|
|
||||||
// NOTE: this is intentionally not dynamic...
|
|
||||||
spec = Object.assign({
|
|
||||||
// builtin options...
|
|
||||||
'-h': '-help',
|
|
||||||
// XXX revise...
|
|
||||||
'-help': {
|
|
||||||
doc: 'print this message and exit.',
|
|
||||||
handler: function(){
|
|
||||||
var spec = this.spec
|
|
||||||
var that = this
|
|
||||||
var x
|
|
||||||
console.log([
|
|
||||||
`Usage: ${
|
|
||||||
typeof(spec.__usage__) == 'function' ?
|
|
||||||
spec.__usage__.call(this)
|
|
||||||
: spec.__usage__ }`,
|
|
||||||
// doc...
|
|
||||||
...(spec.__doc__ ?
|
|
||||||
['', typeof(spec.__doc__) == 'function' ?
|
|
||||||
spec.__doc__()
|
|
||||||
: spec.__doc__]
|
|
||||||
: []),
|
|
||||||
// options...
|
|
||||||
'',
|
|
||||||
'Options:',
|
|
||||||
...(spec.__getoptions__()
|
|
||||||
.map(function([opts, arg, doc]){
|
|
||||||
return [opts.join(' | -') +' '+ (arg || ''), doc] })),
|
|
||||||
// commands...
|
|
||||||
...(((x = spec.__getcommands__()) && x.length > 0) ?
|
|
||||||
['', 'Commands:',
|
|
||||||
...x.map(function([cmd, _, doc]){
|
|
||||||
return [cmd.join(' | '), doc] })]
|
|
||||||
: []),
|
|
||||||
// examples...
|
|
||||||
...(this.spec.__examples__ ?
|
|
||||||
['', 'Examples:', ...(
|
|
||||||
this.spec.__examples__ instanceof Array ?
|
|
||||||
spec.__examples__
|
|
||||||
.map(function(e){
|
|
||||||
return e instanceof Array ? e : [e] })
|
|
||||||
: spec.__examples__(this) )]
|
|
||||||
: []),
|
|
||||||
// footer...
|
|
||||||
...(this.spec.__footer__?
|
|
||||||
['', typeof(this.spec.__footer__) == 'function' ?
|
|
||||||
spec.__footer__(this)
|
|
||||||
: spec.__footer__]
|
|
||||||
: []) ]
|
|
||||||
.map(function(e){
|
|
||||||
return e instanceof Array ?
|
|
||||||
spec.__align__(...e
|
|
||||||
.map(function(s){
|
|
||||||
return s.replace(/\$scriptname/g, that.scriptname) }))
|
|
||||||
// indent lists...
|
|
||||||
.map(function(s){
|
|
||||||
return '\t'+ s })
|
|
||||||
: e })
|
|
||||||
.flat()
|
|
||||||
.join('\n')
|
|
||||||
.replace(/\$scriptname/g, this.scriptname))
|
|
||||||
|
|
||||||
process.exit() }},
|
|
||||||
|
|
||||||
// special values and methods...
|
|
||||||
__pre_check__: true,
|
|
||||||
__opt_pattern__: module.OPTION_PATTERN,
|
|
||||||
__cmd_pattern__: module.COMMAND_PATTERN,
|
|
||||||
__opts_width__: 3,
|
|
||||||
__doc_prefix__: '- ',
|
|
||||||
|
|
||||||
// these is run in the same context as the handlers... (XXX ???)
|
|
||||||
__align__: function(a, b, ...rest){
|
|
||||||
var opts_width = this.__opts_width__ || 4
|
|
||||||
var prefix = this.__doc_prefix__ || ''
|
|
||||||
b = [b, ...rest].join('\n'+ ('\t'.repeat(opts_width+1) + ' '.repeat(prefix.length)))
|
|
||||||
return b ?
|
|
||||||
(a.raw.length < opts_width*8 ?
|
|
||||||
[a +'\t'.repeat(opts_width - Math.floor(a.raw.length/8))+ prefix + b]
|
|
||||||
: [a, '\t'.repeat(opts_width)+ prefix + b])
|
|
||||||
: [a] },
|
|
||||||
|
|
||||||
__usage__: function(){
|
|
||||||
return `${ this.scriptname } [OPTIONS]` },
|
|
||||||
__doc__: undefined,
|
|
||||||
__examples__: undefined,
|
|
||||||
__footer__: undefined,
|
|
||||||
|
|
||||||
__unknown__: function(key){
|
|
||||||
console.error('Unknown option:', key)
|
|
||||||
process.exit(1) },
|
|
||||||
|
|
||||||
// these are run in the context of spec...
|
|
||||||
__getoptions__: function(...pattern){
|
|
||||||
var that = this
|
|
||||||
pattern = pattern.length == 0 ?
|
|
||||||
[this.__opt_pattern__
|
|
||||||
|| module.OPTION_PATTERN]
|
|
||||||
: pattern
|
|
||||||
return pattern
|
|
||||||
.map(function(pattern){
|
|
||||||
var handlers = {}
|
|
||||||
Object.keys(that)
|
|
||||||
.forEach(function(opt){
|
|
||||||
// skip special methods...
|
|
||||||
if(/^__.*__$/.test(opt)
|
|
||||||
|| !pattern.test(opt)){
|
|
||||||
return }
|
|
||||||
var [k, h] = that.__gethandler__(opt)
|
|
||||||
handlers[k] ?
|
|
||||||
handlers[k][0].push(opt)
|
|
||||||
: (handlers[k] = [[opt], h.arg, h.doc || k, h]) })
|
|
||||||
return Object.values(handlers) })
|
|
||||||
.flat(1) },
|
|
||||||
__iscommand__: function(str){
|
|
||||||
return (this.__cmd_pattern__
|
|
||||||
|| module.COMMAND_PATTERN)
|
|
||||||
.test(str)
|
|
||||||
&& str in this },
|
|
||||||
__getcommands__: function(){
|
|
||||||
return this.__getoptions__(
|
|
||||||
this.__cmd_pattern__
|
|
||||||
|| module.COMMAND_PATTERN) },
|
|
||||||
__gethandler__: function(key){
|
|
||||||
key = key.replace(
|
|
||||||
this.__opt_pattern__
|
|
||||||
|| module.OPTION_PATTERN,
|
|
||||||
'-')
|
|
||||||
var seen = new Set([key])
|
|
||||||
while(key in this
|
|
||||||
&& typeof(this[key]) == typeof('str')){
|
|
||||||
key = this[key]
|
|
||||||
// check for loops...
|
|
||||||
if(seen.has(key)){
|
|
||||||
throw Error('Option loop detected: '+ ([...seen, key].join(' -> '))) }
|
|
||||||
seen.add(key) }
|
|
||||||
return [key, this[key]] },
|
|
||||||
}, spec)
|
|
||||||
|
|
||||||
// sanity check -- this will detect argument loops for builtin opts
|
|
||||||
// and commands...
|
|
||||||
spec.__pre_check__
|
|
||||||
&& spec.__getoptions__(
|
|
||||||
spec.__opt_pattern__ || module.OPTION_PATTERN,
|
|
||||||
spec.__cmd_pattern__ || module.COMMAND_PATTERN)
|
|
||||||
|
|
||||||
return function(argv){
|
|
||||||
var opt_pattern = spec.__opt_pattern__
|
|
||||||
|| module.OPTION_PATTERN
|
|
||||||
argv = argv.slice()
|
|
||||||
var context = {
|
|
||||||
spec: spec,
|
|
||||||
argv: argv.slice(),
|
|
||||||
|
|
||||||
interpreter: argv.shift(),
|
|
||||||
script: argv[0],
|
|
||||||
scriptname: argv.shift().split(/[\\\/]/).pop(),
|
|
||||||
|
|
||||||
rest: argv,
|
|
||||||
}
|
|
||||||
var other = []
|
|
||||||
while(argv.length > 0){
|
|
||||||
var arg = argv.shift()
|
|
||||||
var type = opt_pattern.test(arg) ?
|
|
||||||
'opt'
|
|
||||||
: spec.__iscommand__(arg) ?
|
|
||||||
'cmd'
|
|
||||||
: 'other'
|
|
||||||
// options / commands...
|
|
||||||
if(type != 'other'){
|
|
||||||
// get handler...
|
|
||||||
var handler = spec.__gethandler__(arg).pop()
|
|
||||||
|| spec.__unknown__
|
|
||||||
// get option value...
|
|
||||||
var value = (handler.arg && !opt_pattern.test(argv[0])) ?
|
|
||||||
argv.shift()
|
|
||||||
: undefined
|
|
||||||
// run handler...
|
|
||||||
;(typeof(handler) == 'function' ?
|
|
||||||
handler
|
|
||||||
: handler.handler)
|
|
||||||
.call(context,
|
|
||||||
// pass value...
|
|
||||||
...(handler.arg ? [value] : []),
|
|
||||||
arg,
|
|
||||||
argv)
|
|
||||||
continue }
|
|
||||||
// other...
|
|
||||||
other.push(arg) }
|
|
||||||
return other } }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Tests...
|
// Tests...
|
||||||
|
|
||||||
@ -752,7 +509,7 @@ module.tests = {
|
|||||||
methods: function(assert, setup){
|
methods: function(assert, setup){
|
||||||
instances(setup)
|
instances(setup)
|
||||||
.forEach(function([k, o]){
|
.forEach(function([k, o]){
|
||||||
deepKeys(o)
|
object.deepKeys(o)
|
||||||
.forEach(function(m){
|
.forEach(function(m){
|
||||||
typeof(o[m]) == 'function'
|
typeof(o[m]) == 'function'
|
||||||
// skip special methods...
|
// skip special methods...
|
||||||
@ -762,7 +519,7 @@ module.tests = {
|
|||||||
constructor_methods: function(assert, setup){
|
constructor_methods: function(assert, setup){
|
||||||
constructors(setup)
|
constructors(setup)
|
||||||
.forEach(function([k, O]){
|
.forEach(function([k, O]){
|
||||||
deepKeys(O)
|
object.deepKeys(O)
|
||||||
.forEach(function(m){
|
.forEach(function(m){
|
||||||
typeof(O[m]) == 'function'
|
typeof(O[m]) == 'function'
|
||||||
// skip special methods...
|
// skip special methods...
|
||||||
@ -1052,7 +809,7 @@ if(typeof(__filename) != 'undefined'
|
|||||||
|
|
||||||
// parse args...
|
// parse args...
|
||||||
var chains =
|
var chains =
|
||||||
ArgvParser({
|
argv.ArgvParser({
|
||||||
// doc...
|
// doc...
|
||||||
__usage__: `$scriptname [OPTIONS] [CHAIN] ...`,
|
__usage__: `$scriptname [OPTIONS] [CHAIN] ...`,
|
||||||
__doc__: object.normalizeTextIndent(
|
__doc__: object.normalizeTextIndent(
|
||||||
@ -1136,7 +893,7 @@ if(typeof(__filename) != 'undefined'
|
|||||||
doc: 'test command...'
|
doc: 'test command...'
|
||||||
}),
|
}),
|
||||||
// XXX need to make this nestable...
|
// XXX need to make this nestable...
|
||||||
'nested': ArgvParser({ }),
|
'nested': argv.ArgvParser({ }),
|
||||||
})(process.argv)
|
})(process.argv)
|
||||||
|
|
||||||
// run the tests...
|
// run the tests...
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user