Comparing version 7.0.0 to 8.0.0
@@ -16,8 +16,12 @@ /*jshint laxcomma:true, smarttabs: true, node: true, esnext: true, unused: true */ | ||
* @requires clone | ||
* @requires debug | ||
* @requires strip-ansi | ||
* @requires mout/array/append | ||
* @requires mout/lang/toArray | ||
* @requires mout/object/hasOwn | ||
* @requires mout/lang/isObject | ||
* @requires seeli/lib/domain | ||
* @requires seeli/lib/usage | ||
* @requires seeli/lib/exceptions | ||
* @requires seeli/lib/conf | ||
* @requires seeli/lib/lang/object | ||
**/ | ||
@@ -34,2 +38,4 @@ const os = require( 'os' ) // native os | ||
, strip = require( 'strip-ansi') // function helper to remove ansi color | ||
, debug = require( 'debug') | ||
, ora = require('ora') | ||
, array_append = require( 'mout/array/append' ) // | ||
@@ -39,17 +45,14 @@ , toArray = require( 'mout/lang/toArray' ) // | ||
, isObject = require( 'mout/lang/isObject' ) | ||
, debug = require( 'debug')('seeli:command') | ||
, domain = require( './domain') | ||
, conf = require('./conf') | ||
, usage = require('./usage') | ||
, conf = require( './conf' ) | ||
, usage = require( './usage') | ||
, object = require( './lang/object' ) | ||
, typeOf = require('./usage/type-of') | ||
, exceptions = require( './exceptions') | ||
, typeOf = require( './usage/type-of' ) | ||
, exceptions = require( './exceptions' ) | ||
, stop_flags = ['help', 'interactive', 'skip','color'] | ||
, ARGV = 'argv' | ||
, on_exp = /^on[A-z]/ | ||
, noop = () => {} | ||
, on_exp = /^on([A-z])/ | ||
, noop = () => { return Promise.resolve(null); } | ||
; | ||
/** | ||
@@ -101,3 +104,3 @@ * Deep merge objects. except for path & url | ||
function removeOn( name ){ | ||
return name.replace(on_exp, function(full, first ){ | ||
return name.replace(on_exp, function(full, first){ | ||
return first.toLowerCase(); | ||
@@ -113,2 +116,4 @@ }); | ||
, interactive: true | ||
, name: 'command' | ||
, ui: 'dots' | ||
, flags: { | ||
@@ -139,2 +144,3 @@ interactive: { | ||
* @param {String|String[]} [options.usage=""] a string or array of string to describe command usage. If an array, items will be join with a new line character | ||
* @param {String} [options.ui="dots"] ui progress indicator | ||
* @param {Object} [options.flags] cli flags. top level keys will be used as the long hand flag | ||
@@ -144,37 +150,48 @@ * @param {Function} [options.run] A function that will be used as the primary drive of the command. It should perform what ever action the command was intended to do | ||
options:{ | ||
description:"diaplays a simple hello world command" | ||
,usage:[ | ||
"Usage: cli hello --interactive", | ||
"Usage: cli hello --name=john", | ||
"Usage: cli hello --name=john --name=marry --name=paul -v screaming" | ||
description:"diaplays a simple hello world command" | ||
, usage:[ | ||
"Usage: cli hello --interactive" | ||
, "Usage: cli hello --name=john" | ||
, "Usage: cli hello --name=john --name=marry --name=paul -v screaming" | ||
] | ||
,flags:{ | ||
, flags:{ | ||
name:{ | ||
type:[ String, Array ] | ||
,shorthand:'n' | ||
,description:"The name of the person to say hello to" | ||
, shorthand:'n' | ||
, description:"The name of the person to say hello to" | ||
} | ||
,excited: { | ||
, excited: { | ||
type:Boolean | ||
,shorthand: 'e' | ||
,description:"Say hello in a very excited manner" | ||
,default:false | ||
, shorthand: 'e' | ||
, description:"Say hello in a very excited manner" | ||
, default:false | ||
} | ||
,volume:{ | ||
, volume:{ | ||
type:String | ||
,choices:['normal', 'screaming'] | ||
,default:'normal' | ||
,shorthand:'v' | ||
, choices:['normal', 'screaming'] | ||
, default:'normal' | ||
, shorthand:'v' | ||
} | ||
} | ||
,run: function( cmd, data, cb ){ | ||
var out = []; | ||
for( var x =0; x< data.name.length; x++ ){ | ||
out.push( "Hello, " + data.name[x] + "!" ); | ||
, run: async ( cmd, data ) => { | ||
const out = []; | ||
this.ui.start('processing names'); | ||
var names = Array.isArray( data.name ) ? data.name : [ data.name ]; | ||
for( var x = 0; x < names.length; x++ ){ | ||
this.ui.text = (`processing ${names[x]}`) | ||
await new Promise((resolve) => { | ||
setTimeout(() => { | ||
let value = "Hello, " + names[x]; | ||
if( data.excited ){ | ||
value += '!'; | ||
} | ||
out.push( data.volume === 'screaming' ? value.toUpperCase() : value ); | ||
resolve(true); | ||
}, 1000 * x + 1); | ||
}); | ||
} | ||
out = out.join('\n'); | ||
out = data.value == 'screaming' ? out.toUpperCase() : out; | ||
callback( out ); | ||
ui.succeed('names processed successfully'); | ||
return out | ||
} | ||
@@ -189,3 +206,2 @@ } | ||
events.EventEmitter.call( this ); | ||
domain.add( this ); | ||
this._shcache = null; | ||
@@ -197,3 +213,12 @@ this._optcache = null; | ||
this.setOptions( defaults, ...options ); | ||
debug('strict mode: %s', this.options.strict); | ||
this.ui = ora({ | ||
color: conf.get('color') | ||
, spinner: this.options.ui | ||
, text: 'loading' | ||
, stream: process.stdout | ||
}); | ||
this.debug = debug(`${conf.get('name')}:${this.options.name}`); | ||
this.debug('strict mode: %s', this.options.strict); | ||
} | ||
@@ -207,3 +232,7 @@ | ||
get usage(){ | ||
var out = usage.from( this.options.flags ); | ||
const out = usage.from( | ||
this.options.flags | ||
, null | ||
, this.options.interactive | ||
); | ||
return array_append( | ||
@@ -213,3 +242,3 @@ toArray( this.options.usage ), | ||
'' | ||
, !!out ? 'Options:' : '' | ||
, !!out ? `Options:${os.EOL}` : '' | ||
, out | ||
@@ -334,4 +363,5 @@ ] | ||
for( var opt in options ){ | ||
if( typeof( options[ opt ] ) == 'function' || on_exp.test(opt) ) { | ||
if(on_exp.test(opt) && typeof( options[ opt ] ) == 'function' ) { | ||
this.addListener( removeOn( opt ), options[ opt ]); | ||
delete options[opt]; | ||
} | ||
@@ -368,3 +398,3 @@ } | ||
**/ | ||
interactive( cmd, done ){ | ||
async interactive( cmd ){ | ||
let that = this, flags; | ||
@@ -374,47 +404,31 @@ flags = Object | ||
.filter( function( flag ){ | ||
return stop_flags.indexOf( flag ) == -1; | ||
return !stop_flags.includes( flag ); | ||
}); | ||
const answers = Object.create(null); | ||
const series = (cb) => { | ||
const next = () => { | ||
if (!flags.length) return cb(null, answers); | ||
const flag = flags.shift(); | ||
const current = that.options.flags[flag]; | ||
if (Array.isArray(current.type)) { | ||
return ask(flag, cmd, current, (err, results) => { | ||
if(err) return done(err); | ||
answers[flag] = results; | ||
next(); | ||
}); | ||
} | ||
const arg = toQuestion(flag, cmd, current, answers); | ||
inquirer | ||
.prompt(arg) | ||
.then((answer) => { | ||
Object.assign(answers, answer); | ||
next(); | ||
}) | ||
.catch(done); | ||
}; | ||
next(); | ||
}; | ||
const args = []; | ||
while (flags.length) { | ||
const flag = flags.shift(); | ||
const current = that.options.flags[flag]; | ||
if (Array.isArray(current.type)) { | ||
answers[flag] = await ask(flag, cmd, current); | ||
continue; | ||
} | ||
const arg = toQuestion(flag, cmd, current, answers); | ||
Object.assign(answers, await inquirer.prompt(arg)); | ||
} | ||
series((err) => { | ||
if (err) return done(err); | ||
let args = []; | ||
that.parsed = Object.assign( that.parsed, answers ); | ||
this.parsed = Object.assign(this.parsed, answers); | ||
for( let answer in answers ){ | ||
args.push( `--${answer}=${answers[answer]}` ); | ||
object.set(that.parsed, answer, answers[answer]); | ||
} | ||
for( let answer in answers ){ | ||
args.push( `--${answer}=${answers[answer]}` ); | ||
object.set(this.parsed, answer, answers[answer]); | ||
} | ||
that.setOptions({ | ||
args: args | ||
}); | ||
that.validate(cmd); | ||
that.dispatch(); | ||
return that.options.run.call(that, cmd, that.argv, done); | ||
this.setOptions({ | ||
args: args | ||
}); | ||
this.validate(cmd); | ||
this.dispatch(); | ||
return this.options.run.call(this, cmd, this.argv); | ||
} | ||
@@ -445,21 +459,14 @@ | ||
**/ | ||
run( cmd, callback ){ | ||
var done // callback function for commadn | ||
, directive // the first non-flag directive passed to the command | ||
; | ||
async run( cmd ){ | ||
nopt.invalidHandler = this.invalidHandler.bind(this); | ||
nopt.invalidHandler = this.invalidHandler.bind(this); | ||
done = function( err, content ){ | ||
content = !!this.argv.color ? content : strip( content ); | ||
nopt.invalidHandler = null; | ||
if( err ){ | ||
const directive = this.argv.argv.remain[1]; | ||
cmd = cmd ? cmd : directive || null; | ||
if( this.argv.interactive ) { | ||
if( this.options.interactive ){ | ||
this.dispatch(); | ||
const result = await this.interactive.call( this, cmd ); | ||
const content = !!this.argv.color ? result : strip( result ); | ||
/** | ||
* dispatched when the command has failed in some way | ||
* @name command.Command#error | ||
* @event | ||
* @param {Error} e | ||
*/ | ||
this.emit('error', err ); | ||
} else{ | ||
/** | ||
* dispatched when the command has sucessfully completed | ||
@@ -471,15 +478,6 @@ * @name command.Command#content | ||
this.emit('content', content); | ||
} | ||
callback && callback( err, content ); | ||
}.bind( this ); | ||
directive = this.argv.argv.remain[1]; | ||
cmd = cmd ? cmd : directive || null; | ||
if( this.argv.interactive ) { | ||
if( this.options.interactive ){ | ||
this.dispatch(); | ||
return this.interactive.call( this, cmd, done ); | ||
return content; | ||
} else { | ||
console.error('interactive mode - not availible\n'.yellow ); | ||
console.error( chalk.yellow('interactive mode - not availible\n') ); | ||
return null; | ||
} | ||
@@ -490,4 +488,7 @@ } | ||
this.dispatch(); | ||
const result = this.options.run.call(this, cmd, this.argv, done ); | ||
return !!this.argv.color ? result : strip( result ); | ||
const result = await this.options.run.call(this, cmd, this.argv); | ||
this.ui.stop(); | ||
const content = !!this.argv.color ? result : strip( result ); | ||
this.emit('content', content); | ||
return content; | ||
} | ||
@@ -500,2 +501,3 @@ | ||
* @throws module:seeli/lib/exceptions/InvalidFieldException | ||
* @throws module:seeli/lib/exceptions/RequiredException | ||
**/ | ||
@@ -507,3 +509,3 @@ validate( cmd ){ | ||
.filter(( flag ) => { | ||
return stop_flags.indexOf( flag ) == -1; | ||
return !stop_flags.includes(flag); | ||
}) | ||
@@ -516,4 +518,4 @@ .forEach(( flag ) => { | ||
if( this._required.indexOf(flag) >= 0 && this.parsed[flag] === UNDEF ){ | ||
return this.emit('error', new exceptions.RequiredFieldException( flag ) ); | ||
if( this._required.includes(flag) && this.parsed[flag] === UNDEF ){ | ||
throw new exceptions.RequiredFieldException( flag ); | ||
} | ||
@@ -524,7 +526,7 @@ | ||
if(isValid === false ){ | ||
return this.emit('error', new exceptions.InvalidFieldException(`${flag} failed validation.`) ); | ||
throw new exceptions.InvalidFieldException(`${flag} failed validation.`); | ||
} | ||
if( typeof isValid == 'string'){ | ||
return this.emit('error', new exceptions.InvalidFieldException( `${flag} - ${isValid}` ) ); | ||
throw new exceptions.InvalidFieldException( `${flag} - ${isValid}` ); | ||
} | ||
@@ -535,2 +537,12 @@ }); | ||
/** | ||
* Pass through function to inquirer for prompting input at the terminal | ||
* @method module:seeli/lib/command#prompt | ||
* @param {Object} options Inquirer prompt options | ||
* @returns {Promise} Promise object representing the end user input from the question | ||
**/ | ||
prompt(opts) { | ||
return inquirer.prompt(opts); | ||
} | ||
/** | ||
* Colorizes a text blob | ||
@@ -550,24 +562,19 @@ * @method module:seeli/lib/command#colorize | ||
function ask(name, cmd, opts, cb) { | ||
async function ask(name, cmd, opts) { | ||
const results = []; | ||
const arg = toQuestion(name, cmd, opts); | ||
const prompt = () => { | ||
inquirer | ||
.prompt( arg ) | ||
.then(( answer ) => { | ||
if ( answer[name] === '' ) return cb(null, results); | ||
results.push(answer[name]); | ||
prompt(); | ||
}) | ||
.catch(cb); | ||
}; | ||
prompt(); | ||
while (true) { | ||
const answer = await inquirer.prompt( arg ); | ||
if ( answer[name] === '' ) break; | ||
results.push(answer[name]); | ||
} | ||
return results; | ||
} | ||
function toQuestion(flag, cmd, opts, answers) { | ||
const flag_display = flag.replace(':', ' '); | ||
const arg = { | ||
type: opts.type === Boolean ? 'confirm' : opts.mask ? 'password' : 'input' | ||
, name: flag | ||
, message: flag + ' : ' + opts.description || '(no description)' | ||
, name: flag_display | ||
, message: flag_display + ': ' + (opts.description || '(no description)') | ||
, default: opts.default || null | ||
@@ -587,3 +594,6 @@ }; | ||
} | ||
return arg; | ||
} | ||
@@ -22,5 +22,5 @@ /*jshint laxcomma:true, smarttabs: true, node: true, unused: true */ | ||
description:"displays information about available commands" | ||
,interactive:false | ||
,alias:['hlep'] | ||
,run: function( cmd, data, done ){ | ||
, interactive:false | ||
, alias:['hlep'] | ||
, run: async function(cmd){ | ||
var commands | ||
@@ -36,9 +36,7 @@ , cls | ||
if( cmd == "help" ){ | ||
done(null, "really?" ); | ||
return; | ||
return 'really?'; | ||
} | ||
if( !cmd ){ | ||
done(null, list() ); | ||
return; | ||
return list(); | ||
} | ||
@@ -61,6 +59,5 @@ | ||
} catch ( e ){ | ||
help = "no help found for " + cmd + " "+ e.message; | ||
help = `no help found for ${cmd}`; | ||
} | ||
done( null, help ); | ||
return; | ||
return help; | ||
} | ||
@@ -67,0 +64,0 @@ }); |
@@ -14,3 +14,2 @@ /*jshint laxcomma:true, smarttabs: true, unused: true, esnext: true, node: true */ | ||
* @requires seeli/lib/conf | ||
* @requires seeli/lib/domain | ||
**/ | ||
@@ -20,3 +19,2 @@ var nopt = require('nopt') | ||
, chalk = require('chalk') | ||
, clidomain = require('./domain') | ||
, command = require( './command' ) // this is command | ||
@@ -28,3 +26,2 @@ , commands = require('./commands') | ||
, parsed | ||
, clidomain | ||
, colors | ||
@@ -42,3 +39,5 @@ , help | ||
clidomain.on('error', function(err){ | ||
process.on('unhandledRejection', onError); | ||
function onError (err) { | ||
console.error( chalk.red( util.format( '%s:', err.name) ), chalk( err.message ) ); | ||
@@ -51,3 +50,3 @@ if( parsed && parsed.traceback ){ | ||
return conf.get('exitOnError') ? process.exit( err.code || 1 ) : null; | ||
}); | ||
} | ||
@@ -94,8 +93,2 @@ // primary flags | ||
/** | ||
* Overrides a named command unconditionally | ||
* @param {String} name The name of the command to set | ||
* @param {module:seel/lib/command} command A commadn to over ride | ||
**/ | ||
, cmd: cmd | ||
/** | ||
* starts the command line execution process | ||
@@ -106,2 +99,16 @@ **/ | ||
, reset: commands.reset.bind( commands ) | ||
/** | ||
* colorizes a given string using the chalk color found in local configuration | ||
* @param {String} text A string to wrap in an ansi color | ||
* @returns {String} The given string wrapped in the configued ansi color | ||
* @example | ||
* const seeli = require('seeli') | ||
* seeli.set('color', 'blue') | ||
* seeli.colorize('I am blue') | ||
**/ | ||
, colorize: (txt) => { | ||
const color = chalk[conf.get('color')] | ||
if (!color) return txt | ||
return color(txt) | ||
} | ||
}; | ||
@@ -127,12 +134,6 @@ | ||
function cmd( key, value ){ | ||
commands[ key ] = value; | ||
} | ||
function run( ){ | ||
parsed = nopt( opts, shorthands ); | ||
const cb = (err, content ) => { | ||
if (err) return; | ||
console.log( content || '' ); | ||
const cb = (content) => { | ||
typeof content === 'string' && console.log( content ); | ||
if( conf.get('exitOnContent') ){ | ||
@@ -150,12 +151,9 @@ process.exit(0); | ||
// allow for abbreviated commands | ||
return clidomain.run(function(){ | ||
commands.help.run( command, cb); | ||
}); | ||
return commands.help.run( command ).then(cb).catch(onError); | ||
} | ||
if(commands.hasOwnProperty( command ) ) { | ||
return clidomain.run(function(){ | ||
commands[command].run(null, cb); | ||
}); | ||
return commands[command].run(null).then(cb).catch(onError); | ||
} | ||
console.error('unknown command %s', command ); | ||
@@ -162,0 +160,0 @@ console.error('know commands: %s ', Object.keys( commands ).join(', ') ); |
@@ -19,4 +19,6 @@ /*jshint laxcomma: true, smarttabs: true, node: true, esnext: true*/ | ||
const os = require('os'); | ||
const cliui = require('cliui'); | ||
const util = require('util'); | ||
const chalk = require('chalk'); | ||
const width = require('string-width'); | ||
const typeOf = require('./type-of'); | ||
@@ -28,3 +30,4 @@ const conf = require('../conf'); | ||
function from( flags, plain ){ | ||
function from( flags, plain, interactive = true ){ | ||
const ui = cliui(); | ||
const return_value = ['']; | ||
@@ -34,16 +37,30 @@ const style = !!plain ? noop : chalk[conf.get('color')] || chalk.green; | ||
for( const flag in flags ) { | ||
const cols = []; | ||
if (flag === 'interactive' && !interactive) continue | ||
const config = flags[flag]; | ||
const type = typeOf( config.type ); | ||
return_value.push(util.format( | ||
'%s--%s%s <%s> %s %s' | ||
, config.shorthand ? `-${config.shorthand}, ` : '' | ||
, flag | ||
, type === 'boolean' ? `, --no-${flag}` : '' | ||
, style(type) | ||
, typeof config.default === 'undefined' ? '' : `[${chalk.bold( config.default )}]` | ||
, config.description || '' | ||
)); | ||
ui.div({ | ||
text: util.format( | ||
'%s--%s%s' | ||
, config.shorthand ? `-${config.shorthand}, ` : '' | ||
, flag | ||
, type === 'boolean' ? `, --no-${flag}` : '' | ||
) | ||
, padding: [0, 0, 0, 2] | ||
, width: 40 | ||
}, { | ||
text: `<${style(type)}>` | ||
, align: 'left', width: 10 | ||
}, { | ||
text: typeof config.default === 'undefined' ? '' : `[${chalk.bold( config.default )}]` | ||
, align: 'left' | ||
, width: 20 | ||
}, { | ||
text: config.description || '' | ||
, align: 'left' | ||
, width: Math.max(80, width((config.description || '').trim())) | ||
}); | ||
} | ||
return_value.push('', `${style('<...>')}: input type | ${style('*')}: repeatable flags | ${style("[...]")}: default values` ); | ||
return return_value.join( os.EOL + ' ' ); | ||
ui.div({text: `${style('<...>')}: input type | ${style('*')}: repeatable flags | ${style("[...]")}: default values`, padding: [2, 0, 0, 2] }); | ||
return ui.toString(); | ||
} |
{ | ||
"name": "seeli", | ||
"version": "7.0.0", | ||
"version": "8.0.0", | ||
"description": "Object oriented, flexible CLI tools", | ||
@@ -10,3 +10,3 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "tap -Rtap -J --cov test/", | ||
"test": "tap -Rtap -J --cov test", | ||
"docs": "jsdoc -r lib" | ||
@@ -47,3 +47,3 @@ }, | ||
"engines": { | ||
"node": ">=6.0.0" | ||
"node": ">=7.0.0" | ||
}, | ||
@@ -57,2 +57,3 @@ "bugs": { | ||
"chalk": "^2.1.0", | ||
"cliui": "^4.0.0", | ||
"clone": "^2.0.0", | ||
@@ -63,2 +64,4 @@ "debug": "^3.1.0", | ||
"nopt": "~4.0.0", | ||
"ora": "^1.3.0", | ||
"string-width": "^2.1.1", | ||
"strip-ansi": "^4.0.0" | ||
@@ -65,0 +68,0 @@ }, |
176
README.md
@@ -13,68 +13,89 @@ ![build image](https://travis-ci.org/esatterwhite/node-seeli.svg?branch=master) ![package dependancies](https://david-dm.org/esatterwhite/node-seeli.png) | ||
```js | ||
var cli = require("seeli") | ||
var Hello = new cli.Command({ | ||
const os = require('os') | ||
const cli = require('seeli') | ||
cli.set({ | ||
exitOnError: true | ||
, color: 'green' | ||
, name: 'example' | ||
}) | ||
const Hello new cli.Command({ | ||
description:"displays a simple hello world command" | ||
,usage:[ | ||
`${cli.bold("Usage:")} cli hello --interactive`, | ||
`${cli.bold("Usage:")} cli hello --name=john`, | ||
`${cli.bold("Usage:")} cli hello --name=john --name=marry --name=paul -v screaming` | ||
, name: 'hello' | ||
, ui: 'dots' | ||
, usage:[ | ||
`${cli.bold("Usage:")} ${cli.get('name')} hello --interactive` | ||
, `${cli.bold("Usage:")} ${cli.get('name')} hello --name=john` | ||
, `${cli.bold("Usage:")} ${cli.get('name')} hello --name=john --name=marry --name=paul -v screaming` | ||
] | ||
,flags:{ | ||
, flags:{ | ||
name:{ | ||
type:[ String, Array ] | ||
,shorthand:'n' | ||
,description:"The name of the person to say hello to" | ||
,required:true | ||
, shorthand:'n' | ||
, description:"The name of the person to say hello to" | ||
, required:true | ||
} | ||
,excited: { | ||
, 'nested:value' : { | ||
type: Number | ||
, shorthand: 'nv' | ||
, description: 'A newsted Value' | ||
, name: 'nested' | ||
} | ||
, excited: { | ||
type:Boolean | ||
,shorthand: 'e' | ||
,description:"Say hello in a very excited manner" | ||
,default:false | ||
, shorthand: 'e' | ||
, description:"Say hello in a very excited manner" | ||
, default:false | ||
} | ||
,volume:{ | ||
, volume:{ | ||
type:String | ||
,choices:['normal', 'screaming'] | ||
,description:"Will yell at each person" | ||
,default:'normal' | ||
,shorthand:'v' | ||
, choices:['normal', 'screaming'] | ||
, description:"Will yell at each person" | ||
, default:'normal' | ||
, shorthand:'v' | ||
} | ||
} | ||
, onContent: (content) => { | ||
// command success | ||
// content is the final output from run function | ||
// non string content is not written to stdout automatically | ||
// you could do it here | ||
,password: { | ||
type:String, | ||
mask:true, | ||
description:"unique password", | ||
shorthand:'p', | ||
required: false | ||
} | ||
console.log(content.join(os.EOL)) | ||
} | ||
,run: function( cmd, data, cb ){ | ||
, run: async function( cmd, data ){ | ||
const out = []; | ||
const names = Array.isArray( data.name ) ? data.name : [ data.name ] | ||
for(const name of names){ | ||
var value = `Hello ${name} | ||
if( data.excited ){ | ||
value += '!' | ||
} | ||
this.ui.start('processing names'); | ||
var names = Array.isArray( data.name ) ? data.name : [ data.name ]; | ||
for( var x = 0; x< names.length; x++ ){ | ||
this.ui.text = (`processing ${names[x]}`) | ||
await new Promise((resolve) => { | ||
setTimeout(() => { | ||
let value = "Hello, " + names[x]; | ||
if( data.excited ){ | ||
value += '!'; | ||
} | ||
out.push( value ); | ||
out.push( data.volume === 'screaming' ? value.toUpperCase() : value ); | ||
resolve(true); | ||
}, 1000 * x + 1); | ||
}); | ||
} | ||
if (data.password) { | ||
out.push('') | ||
out.push('your password was set.') | ||
} | ||
out = out.join('\n'); | ||
out = data.volume == 'screaming' ? out.toUpperCase() : out; | ||
cb( null, out ); | ||
this.ui.succeed('names processed successfully'); | ||
// anything returned from run | ||
// is emitted from the `content` event | ||
// strings will automatically be written to stdout | ||
return out | ||
} | ||
}); | ||
cli.set('exitOnError', true) | ||
cli.use('hello', Hello) | ||
cli.set('color','green'); | ||
cli.run(); | ||
@@ -86,6 +107,6 @@ ``` | ||
``` | ||
node cli.js help world | ||
node cli.js world --help | ||
node cli.js world --interactive | ||
node cli.js world --name=Mark --name=Sally --no-excited | ||
node ./cli help world | ||
node ./cli world --help | ||
node ./cli world --interactive | ||
node ./cli world --name=Mark --name=Sally --no-excited | ||
``` | ||
@@ -103,7 +124,2 @@ | ||
## Seeli.exitOnError `<Boolean>` | ||
If set to turn seeli will exit the process when it encouters an error. If false, it will leave error handling up to | ||
the parent application | ||
## Seeli.use( name `<string>`, cmd `<Command>` ) | ||
@@ -198,3 +214,4 @@ | ||
**flags** | `Object` | `{}` | key value pairs used to control the command where keys are the name of the flag and the values is a configuration object for the flag | ||
**run** | `Function` | `no-op` | A function used as the body of the command. it will be passed a `name`, a `data` object containing the processed values from the command input and a `done` function to be called when the command is done. | ||
**ui** | `String` | `dots` | The kind of [progress indicator](https://github.com/sindresorhus/cli-spinners/blob/master/spinners.jso) your command should use | ||
**run** | `Function` | `no-op` | An async function used as the body of the command. It will be passed a `subcommand` name if one was passed, and a `data` object containing the processed values from the command input. | ||
@@ -244,4 +261,9 @@ ### Flag Options | ||
Your defined `run` function will be passed a `done` function to be called when your command has finished. This allows you to do complex async operations and I/O. The `done` callback accepts an error, if there is one, and the final output to be displayed for your command. | ||
Your defined `run` function can be an async function, or a function that returns a `Promise`. This allows you to do complex async operations and I/O. If an error is thrown, it will be displayed. | ||
Otherwise, the content returned from your `run` function will be output to stdout ( if it returned a `String`). | ||
## Progress | ||
Your command's `run` function has access to an instance of [ora](https://www.npmjs.com/package/ora) allowing you to display progress indicators and helpful messages while you perform other work. | ||
## Events | ||
@@ -253,28 +275,28 @@ | ||
var EventCommand = new cli.Command({ | ||
args:[ '--one', '--no-two'] | ||
, flags:{ | ||
one:{ | ||
type:Boolean | ||
,event:true | ||
} | ||
,two:{ | ||
type:Boolean | ||
,event:true | ||
} | ||
args:[ '--one', '--no-two'] | ||
, flags:{ | ||
one:{ | ||
type:Boolean | ||
, event:true | ||
} | ||
, run: function( cmd, data, done ){ | ||
done( null, data.one && data.two ) | ||
, two:{ | ||
type:Boolean | ||
, event:true | ||
} | ||
} | ||
, run: async function( cmd, data ){ | ||
return data.one && data.two | ||
} | ||
}); | ||
EventCommand.on('one', function( value ){ | ||
assert.equal( true, value ); | ||
assert.equal( true, value ); | ||
}); | ||
EventCommand.on('two', function( value ){ | ||
assert.equal( false, value ) | ||
assert.equal( false, value ) | ||
}); | ||
EventCommand.on('content', function( value ){ | ||
assert.equal( false, value ); | ||
assert.equal( false, value ); | ||
}); | ||
@@ -285,15 +307,1 @@ | ||
## Errors | ||
Errors are handled by Node's error [domains](http://nodejs.org/api/domain.html). Each command will run inside of its own domain and will emit an error event if and error is passed to the `done` callback from the `run` method. Seeli will suppress trace messages by default. You can use the `--traceback` flag on any command to surface the full stack trace. If the error object emitted has a `code` property that is a non zero value, seeli will forcefully exit the process with the error code. | ||
```js | ||
var cli = require("seeli") | ||
var ErrCmd = new cli.Command({ | ||
run: function(){ | ||
var e = new Error("Invalid Command") | ||
e.code = 10; | ||
this.emit('error',e ) | ||
} | ||
}); | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
143769
20
4456
301
11
1
+ Addedcliui@^4.0.0
+ Addedora@^1.3.0
+ Addedstring-width@^2.1.1
+ Addedansi-regex@2.1.1(transitive)
+ Addedcli-spinners@1.3.1(transitive)
+ Addedcliui@4.1.0(transitive)
+ Addedcode-point-at@1.1.0(transitive)
+ Addedis-fullwidth-code-point@1.0.0(transitive)
+ Addedlog-symbols@2.2.0(transitive)
+ Addednumber-is-nan@1.0.1(transitive)
+ Addedora@1.4.0(transitive)
+ Addedstring-width@1.0.2(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedwrap-ansi@2.1.0(transitive)