uapte
This commit is contained in:
130
node_modules/coa/src/arg.coffee
generated
vendored
Normal file
130
node_modules/coa/src/arg.coffee
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
Color = require('./color').Color
|
||||
Cmd = require('./cmd').Cmd
|
||||
Opt = require('./opt').Opt
|
||||
|
||||
###*
|
||||
Argument
|
||||
|
||||
Unnamed entity. From command line arguments passed as list of unnamed values.
|
||||
@namespace
|
||||
@class Presents argument
|
||||
###
|
||||
exports.Arg = class Arg
|
||||
|
||||
###*
|
||||
@constructs
|
||||
@param {COA.Cmd} cmd parent command
|
||||
###
|
||||
constructor: (@_cmd) -> @_cmd._args.push @
|
||||
|
||||
###*
|
||||
Set a canonical argument identifier to be used anywhere in text messages.
|
||||
@param {String} _name argument name
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
name: Opt::name
|
||||
|
||||
###*
|
||||
Set a long description for argument to be used anywhere in text messages.
|
||||
@param {String} _title argument title
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
title: Cmd::title
|
||||
|
||||
###*
|
||||
Makes an argument accepts multiple values.
|
||||
Otherwise, the value will be used by the latter passed.
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
arr: Opt::arr
|
||||
|
||||
###*
|
||||
Makes an argument required.
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
req: Opt::req
|
||||
|
||||
###*
|
||||
Set a validation (or value) function for argument.
|
||||
Value from command line passes through before becoming available from API.
|
||||
Using for validation and convertion simple types to any values.
|
||||
@param {Function} _val validating function,
|
||||
invoked in the context of argument instance
|
||||
and has one parameter with value from command line
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
val: Opt::val
|
||||
|
||||
###*
|
||||
Set a default value for argument.
|
||||
Default value passed through validation function as ordinary value.
|
||||
@param {Object} _def
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
def: Opt::def
|
||||
|
||||
###*
|
||||
Set custom additional completion for current argument.
|
||||
@param {Function} completion generation function,
|
||||
invoked in the context of argument instance.
|
||||
Accepts parameters:
|
||||
- {Object} opts completion options
|
||||
It can return promise or any other value treated as result.
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
comp: Cmd::comp
|
||||
|
||||
###*
|
||||
Make argument value inputting stream.
|
||||
It's add useful validation and shortcut for STDIN.
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
input: Opt::input
|
||||
|
||||
###*
|
||||
Make argument value outputing stream.
|
||||
It's add useful validation and shortcut for STDOUT.
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
output: Opt::output
|
||||
|
||||
_parse: (arg, args) ->
|
||||
@_saveVal(args, arg)
|
||||
|
||||
_saveVal: Opt::_saveVal
|
||||
|
||||
_checkParsed: (opts, args) -> not args.hasOwnProperty(@_name)
|
||||
|
||||
_usage: ->
|
||||
res = []
|
||||
|
||||
res.push Color('lpurple', @_name.toUpperCase()), ' : ', @_title
|
||||
if @_req then res.push ' ', Color('lred', '(required)')
|
||||
|
||||
res.join ''
|
||||
|
||||
_requiredText: -> 'Missing required argument:\n ' + @_usage()
|
||||
|
||||
###*
|
||||
Return rejected promise with error code.
|
||||
Use in .val() for return with error.
|
||||
@param {Object} reject reason
|
||||
You can customize toString() method and exitCode property
|
||||
of reason object.
|
||||
@returns {Q.promise} rejected promise
|
||||
###
|
||||
reject: Cmd::reject
|
||||
|
||||
###*
|
||||
Finish chain for current option and return parent command instance.
|
||||
@returns {COA.Cmd} parent command
|
||||
###
|
||||
end: Cmd::end
|
||||
|
||||
###*
|
||||
Apply function with arguments in context of arg instance.
|
||||
@param {Function} fn
|
||||
@param {Array} args
|
||||
@returns {COA.Arg} this instance (for chainability)
|
||||
###
|
||||
apply: Cmd::apply
|
||||
456
node_modules/coa/src/cmd.coffee
generated
vendored
Normal file
456
node_modules/coa/src/cmd.coffee
generated
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
UTIL = require 'util'
|
||||
PATH = require 'path'
|
||||
Color = require('./color').Color
|
||||
Q = require('q')
|
||||
|
||||
#inspect = require('eyes').inspector { maxLength: 99999, stream: process.stderr }
|
||||
|
||||
###*
|
||||
Command
|
||||
|
||||
Top level entity. Commands may have options and arguments.
|
||||
@namespace
|
||||
@class Presents command
|
||||
###
|
||||
exports.Cmd = class Cmd
|
||||
|
||||
###*
|
||||
@constructs
|
||||
@param {COA.Cmd} [cmd] parent command
|
||||
###
|
||||
constructor: (cmd) ->
|
||||
if this not instanceof Cmd
|
||||
return new Cmd cmd
|
||||
|
||||
@_parent cmd
|
||||
|
||||
@_cmds = []
|
||||
@_cmdsByName = {}
|
||||
|
||||
@_opts = []
|
||||
@_optsByKey = {}
|
||||
|
||||
@_args = []
|
||||
|
||||
@_ext = false
|
||||
|
||||
@get: (propertyName, func) ->
|
||||
Object.defineProperty @::, propertyName,
|
||||
configurable: true
|
||||
enumerable: true
|
||||
get: func
|
||||
|
||||
###*
|
||||
Returns object containing all its subcommands as methods
|
||||
to use from other programs.
|
||||
@returns {Object}
|
||||
###
|
||||
@get 'api', () ->
|
||||
if not @_api
|
||||
@_api = => @invoke.apply @, arguments
|
||||
for c of @_cmdsByName
|
||||
do (c) =>
|
||||
@_api[c] = @_cmdsByName[c].api
|
||||
@_api
|
||||
|
||||
_parent: (cmd) ->
|
||||
@_cmd = cmd or this
|
||||
if cmd
|
||||
cmd._cmds.push @
|
||||
if @_name then @_cmd._cmdsByName[@_name] = @
|
||||
@
|
||||
|
||||
###*
|
||||
Set a canonical command identifier to be used anywhere in the API.
|
||||
@param {String} _name command name
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
name: (@_name) ->
|
||||
if @_cmd isnt @ then @_cmd._cmdsByName[_name] = @
|
||||
@
|
||||
|
||||
###*
|
||||
Set a long description for command to be used anywhere in text messages.
|
||||
@param {String} _title command title
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
title: (@_title) -> @
|
||||
|
||||
###*
|
||||
Create new or add existing subcommand for current command.
|
||||
@param {COA.Cmd} [cmd] existing command instance
|
||||
@returns {COA.Cmd} new subcommand instance
|
||||
###
|
||||
cmd: (cmd) ->
|
||||
if cmd then cmd._parent @
|
||||
else new Cmd @
|
||||
|
||||
###*
|
||||
Create option for current command.
|
||||
@returns {COA.Opt} new option instance
|
||||
###
|
||||
opt: -> new (require('./opt').Opt) @
|
||||
|
||||
###*
|
||||
Create argument for current command.
|
||||
@returns {COA.Opt} new argument instance
|
||||
###
|
||||
arg: -> new (require('./arg').Arg) @
|
||||
|
||||
###*
|
||||
Add (or set) action for current command.
|
||||
@param {Function} act action function,
|
||||
invoked in the context of command instance
|
||||
and has the parameters:
|
||||
- {Object} opts parsed options
|
||||
- {Array} args parsed arguments
|
||||
- {Object} res actions result accumulator
|
||||
It can return rejected promise by Cmd.reject (in case of error)
|
||||
or any other value treated as result.
|
||||
@param {Boolean} [force=false] flag for set action instead add to existings
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
act: (act, force) ->
|
||||
return @ unless act
|
||||
|
||||
if not force and @_act
|
||||
@_act.push act
|
||||
else
|
||||
@_act = [act]
|
||||
|
||||
@
|
||||
|
||||
###*
|
||||
Set custom additional completion for current command.
|
||||
@param {Function} completion generation function,
|
||||
invoked in the context of command instance.
|
||||
Accepts parameters:
|
||||
- {Object} opts completion options
|
||||
It can return promise or any other value treated as result.
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
comp: (@_comp) -> @
|
||||
|
||||
###*
|
||||
Apply function with arguments in context of command instance.
|
||||
@param {Function} fn
|
||||
@param {Array} args
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
apply: (fn, args...) ->
|
||||
fn.apply this, args
|
||||
@
|
||||
|
||||
###*
|
||||
Make command "helpful", i.e. add -h --help flags for print usage.
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
helpful: ->
|
||||
@opt()
|
||||
.name('help').title('Help')
|
||||
.short('h').long('help')
|
||||
.flag()
|
||||
.only()
|
||||
.act ->
|
||||
return @usage()
|
||||
.end()
|
||||
|
||||
###*
|
||||
Adds shell completion to command, adds "completion" subcommand,
|
||||
that makes all the magic.
|
||||
Must be called only on root command.
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
completable: ->
|
||||
@cmd()
|
||||
.name('completion')
|
||||
.apply(require './completion')
|
||||
.end()
|
||||
|
||||
###*
|
||||
Allow command to be extendable by external node.js modules.
|
||||
@param {String} [pattern] Pattern of node.js module to find subcommands at.
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
extendable: (pattern) ->
|
||||
@_ext = pattern or true
|
||||
@
|
||||
|
||||
_exit: (msg, code) ->
|
||||
process.once 'exit', ->
|
||||
if msg then console.error msg
|
||||
process.exit code or 0
|
||||
|
||||
###*
|
||||
Build full usage text for current command instance.
|
||||
@returns {String} usage text
|
||||
###
|
||||
usage: ->
|
||||
res = []
|
||||
|
||||
if @_title then res.push @_fullTitle()
|
||||
|
||||
res.push('', 'Usage:')
|
||||
|
||||
if @_cmds.length then res.push(['', '',
|
||||
Color('lred', @_fullName()),
|
||||
Color('lblue', 'COMMAND'),
|
||||
Color('lgreen', '[OPTIONS]'),
|
||||
Color('lpurple', '[ARGS]')].join ' ')
|
||||
|
||||
if @_opts.length + @_args.length then res.push(['', '',
|
||||
Color('lred', @_fullName()),
|
||||
Color('lgreen', '[OPTIONS]'),
|
||||
Color('lpurple', '[ARGS]')].join ' ')
|
||||
|
||||
res.push(
|
||||
@_usages(@_cmds, 'Commands'),
|
||||
@_usages(@_opts, 'Options'),
|
||||
@_usages(@_args, 'Arguments'))
|
||||
|
||||
res.join '\n'
|
||||
|
||||
_usage: ->
|
||||
Color('lblue', @_name) + ' : ' + @_title
|
||||
|
||||
_usages: (os, title) ->
|
||||
unless os.length then return
|
||||
res = ['', title + ':']
|
||||
for o in os
|
||||
res.push ' ' + o._usage()
|
||||
res.join '\n'
|
||||
|
||||
_fullTitle: ->
|
||||
(if @_cmd is this then '' else @_cmd._fullTitle() + '\n') + @_title
|
||||
|
||||
_fullName: ->
|
||||
(if this._cmd is this then '' else @_cmd._fullName() + ' ') + PATH.basename(@_name)
|
||||
|
||||
_ejectOpt: (opts, opt) ->
|
||||
if (pos = opts.indexOf(opt)) >= 0
|
||||
if opts[pos]._arr
|
||||
opts[pos]
|
||||
else
|
||||
opts.splice(pos, 1)[0]
|
||||
|
||||
_checkRequired: (opts, args) ->
|
||||
if not (@_opts.filter (o) -> o._only and o._name of opts).length
|
||||
all = @_opts.concat @_args
|
||||
while i = all.shift()
|
||||
if i._req and i._checkParsed opts, args
|
||||
return @reject i._requiredText()
|
||||
|
||||
_parseCmd: (argv, unparsed = []) ->
|
||||
argv = argv.concat()
|
||||
optSeen = false
|
||||
while i = argv.shift()
|
||||
if not i.indexOf '-'
|
||||
optSeen = true
|
||||
if not optSeen and /^\w[\w-_]*$/.test(i)
|
||||
cmd = @_cmdsByName[i]
|
||||
|
||||
if not cmd and @_ext
|
||||
# construct package name to require
|
||||
if typeof @_ext is 'string'
|
||||
if ~@_ext.indexOf('%s')
|
||||
# use formatted string
|
||||
pkg = UTIL.format(@_ext, i)
|
||||
else
|
||||
# just append subcommand name to the prefix
|
||||
pkg = @_ext + i
|
||||
else if @_ext is true
|
||||
# use default scheme: <command>-<subcommand>-<subcommand> and so on
|
||||
pkg = i
|
||||
c = @
|
||||
loop
|
||||
pkg = c._name + '-' + pkg
|
||||
if c._cmd is c then break
|
||||
c = c._cmd
|
||||
|
||||
try
|
||||
cmdDesc = require(pkg)
|
||||
catch e
|
||||
|
||||
if cmdDesc
|
||||
if typeof cmdDesc == 'function'
|
||||
# set create subcommand, set its name and apply imported function
|
||||
@cmd()
|
||||
.name(i)
|
||||
.apply(cmdDesc)
|
||||
.end()
|
||||
else if typeof cmdDesc == 'object'
|
||||
# register subcommand
|
||||
@cmd(cmdDesc)
|
||||
# set command name
|
||||
cmdDesc.name(i)
|
||||
else
|
||||
throw new Error 'Error: Unsupported command declaration type, ' +
|
||||
'should be function or COA.Cmd() object'
|
||||
cmd = @_cmdsByName[i]
|
||||
if cmd
|
||||
return cmd._parseCmd argv, unparsed
|
||||
|
||||
unparsed.push i
|
||||
|
||||
{ cmd: @, argv: unparsed }
|
||||
|
||||
_parseOptsAndArgs: (argv) ->
|
||||
opts = {}
|
||||
args = {}
|
||||
|
||||
nonParsedOpts = @_opts.concat()
|
||||
nonParsedArgs = @_args.concat()
|
||||
|
||||
while i = argv.shift()
|
||||
# opt
|
||||
if i isnt '--' and not i.indexOf '-'
|
||||
|
||||
if m = i.match /^(--\w[\w-_]*)=(.*)$/
|
||||
i = m[1]
|
||||
|
||||
# suppress 'unknown argument' error for flag options with values
|
||||
if not @_optsByKey[i]._flag
|
||||
argv.unshift m[2]
|
||||
|
||||
if opt = @_ejectOpt nonParsedOpts, @_optsByKey[i]
|
||||
if Q.isRejected(res = opt._parse argv, opts)
|
||||
return res
|
||||
else
|
||||
return @reject "Unknown option: #{ i }"
|
||||
|
||||
# arg
|
||||
else
|
||||
if i is '--'
|
||||
i = argv.splice(0)
|
||||
|
||||
i = if Array.isArray(i) then i else [i]
|
||||
|
||||
while a = i.shift()
|
||||
if arg = nonParsedArgs.shift()
|
||||
if arg._arr then nonParsedArgs.unshift arg
|
||||
if Q.isRejected(res = arg._parse a, args)
|
||||
return res
|
||||
else
|
||||
return @reject "Unknown argument: #{ a }"
|
||||
|
||||
# set defaults
|
||||
{
|
||||
opts: @_setDefaults(opts, nonParsedOpts),
|
||||
args: @_setDefaults(args, nonParsedArgs)
|
||||
}
|
||||
|
||||
_setDefaults: (params, desc) ->
|
||||
for i in desc
|
||||
if i._name not of params and '_def' of i
|
||||
i._saveVal params, i._def
|
||||
params
|
||||
|
||||
_processParams: (params, desc) ->
|
||||
notExists = []
|
||||
for i in desc
|
||||
n = i._name
|
||||
if n not of params
|
||||
notExists.push i
|
||||
continue
|
||||
|
||||
vals = params[n]
|
||||
delete params[n]
|
||||
if not Array.isArray vals
|
||||
vals = [vals]
|
||||
|
||||
for v in vals
|
||||
if Q.isRejected(res = i._saveVal(params, v))
|
||||
return res
|
||||
|
||||
# set defaults
|
||||
@_setDefaults params, notExists
|
||||
|
||||
_parseArr: (argv) ->
|
||||
Q.when @_parseCmd(argv), (p) ->
|
||||
Q.when p.cmd._parseOptsAndArgs(p.argv), (r) ->
|
||||
{ cmd: p.cmd, opts: r.opts, args: r.args }
|
||||
|
||||
_do: (input) ->
|
||||
Q.when input, (input) =>
|
||||
cmd = input.cmd
|
||||
[@_checkRequired].concat(cmd._act or []).reduce(
|
||||
(res, act) ->
|
||||
Q.when res, (res) ->
|
||||
act.call(
|
||||
cmd
|
||||
input.opts
|
||||
input.args
|
||||
res)
|
||||
undefined
|
||||
)
|
||||
|
||||
###*
|
||||
Parse arguments from simple format like NodeJS process.argv
|
||||
and run ahead current program, i.e. call process.exit when all actions done.
|
||||
@param {Array} argv
|
||||
@returns {COA.Cmd} this instance (for chainability)
|
||||
###
|
||||
run: (argv = process.argv.slice(2)) ->
|
||||
cb = (code) => (res) =>
|
||||
if res
|
||||
@_exit res.stack ? res.toString(), res.exitCode ? code
|
||||
else
|
||||
@_exit()
|
||||
Q.when(@do(argv), cb(0), cb(1)).done()
|
||||
@
|
||||
|
||||
###*
|
||||
Convenient function to run command from tests.
|
||||
@param {Array} argv
|
||||
@returns {Q.Promise}
|
||||
###
|
||||
do: (argv) ->
|
||||
@_do(@_parseArr argv || [])
|
||||
|
||||
###*
|
||||
Invoke specified (or current) command using provided
|
||||
options and arguments.
|
||||
@param {String|Array} cmds subcommand to invoke (optional)
|
||||
@param {Object} opts command options (optional)
|
||||
@param {Object} args command arguments (optional)
|
||||
@returns {Q.Promise}
|
||||
###
|
||||
invoke: (cmds = [], opts = {}, args = {}) ->
|
||||
if typeof cmds == 'string'
|
||||
cmds = cmds.split(' ')
|
||||
|
||||
if arguments.length < 3
|
||||
if not Array.isArray cmds
|
||||
args = opts
|
||||
opts = cmds
|
||||
cmds = []
|
||||
|
||||
Q.when @_parseCmd(cmds), (p) =>
|
||||
if p.argv.length
|
||||
return @reject "Unknown command: " + cmds.join ' '
|
||||
|
||||
Q.all([@_processParams(opts, @_opts), @_processParams(args, @_args)])
|
||||
.spread (opts, args) =>
|
||||
@_do({ cmd: p.cmd, opts: opts, args: args })
|
||||
# catch fails from .only() options
|
||||
.fail (res) =>
|
||||
if res and res.exitCode is 0
|
||||
res.toString()
|
||||
else
|
||||
@reject(res)
|
||||
|
||||
###*
|
||||
Return reject of actions results promise with error code.
|
||||
Use in .act() for return with error.
|
||||
@param {Object} reject reason
|
||||
You can customize toString() method and exitCode property
|
||||
of reason object.
|
||||
@returns {Q.promise} rejected promise
|
||||
###
|
||||
reject: (reason) -> Q.reject(reason)
|
||||
|
||||
###*
|
||||
Finish chain for current subcommand and return parent command instance.
|
||||
@returns {COA.Cmd} parent command
|
||||
###
|
||||
end: -> @_cmd
|
||||
25
node_modules/coa/src/color.coffee
generated
vendored
Normal file
25
node_modules/coa/src/color.coffee
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
colors =
|
||||
black: '30'
|
||||
dgray: '1;30'
|
||||
red: '31'
|
||||
lred: '1;31'
|
||||
green: '32'
|
||||
lgreen: '1;32'
|
||||
brown: '33'
|
||||
yellow: '1;33'
|
||||
blue: '34'
|
||||
lblue: '1;34'
|
||||
purple: '35'
|
||||
lpurple: '1;35'
|
||||
cyan: '36'
|
||||
lcyan: '1;36'
|
||||
lgray: '37'
|
||||
white: '1;37'
|
||||
|
||||
exports.Color = (c, str) ->
|
||||
# Use \x1B instead of \033 because of CoffeeScript 1.3.x compilation error
|
||||
[
|
||||
'\x1B[', colors[c], 'm'
|
||||
str
|
||||
'\x1B[m'
|
||||
].join ''
|
||||
156
node_modules/coa/src/completion.coffee
generated
vendored
Normal file
156
node_modules/coa/src/completion.coffee
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
###*
|
||||
Most of the code adopted from the npm package shell completion code.
|
||||
See https://github.com/isaacs/npm/blob/master/lib/completion.js
|
||||
###
|
||||
|
||||
Q = require 'q'
|
||||
escape = require('./shell').escape
|
||||
unescape = require('./shell').unescape
|
||||
|
||||
module.exports = ->
|
||||
@title('Shell completion')
|
||||
.helpful()
|
||||
.arg()
|
||||
.name('raw')
|
||||
.title('Completion words')
|
||||
.arr()
|
||||
.end()
|
||||
.act (opts, args) ->
|
||||
if process.platform == 'win32'
|
||||
e = new Error 'shell completion not supported on windows'
|
||||
e.code = 'ENOTSUP'
|
||||
e.errno = require('constants').ENOTSUP
|
||||
return @reject(e)
|
||||
|
||||
# if the COMP_* isn't in the env, then just dump the script
|
||||
if !process.env.COMP_CWORD? or !process.env.COMP_LINE? or !process.env.COMP_POINT?
|
||||
return dumpScript(@_cmd._name)
|
||||
|
||||
console.error 'COMP_LINE: %s', process.env.COMP_LINE
|
||||
console.error 'COMP_CWORD: %s', process.env.COMP_CWORD
|
||||
console.error 'COMP_POINT: %s', process.env.COMP_POINT
|
||||
console.error 'args: %j', args.raw
|
||||
|
||||
# completion opts
|
||||
opts = getOpts args.raw
|
||||
|
||||
# cmd
|
||||
{ cmd, argv } = @_cmd._parseCmd opts.partialWords
|
||||
Q.when complete(cmd, opts), (compls) ->
|
||||
console.error 'filtered: %j', compls
|
||||
console.log compls.map(escape).join('\n')
|
||||
|
||||
|
||||
dumpScript = (name) ->
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
defer = Q.defer()
|
||||
|
||||
fs.readFile path.resolve(__dirname, 'completion.sh'), 'utf8', (err, d) ->
|
||||
if err then return defer.reject err
|
||||
d = d.replace(/{{cmd}}/g, path.basename name).replace(/^\#\!.*?\n/, '')
|
||||
|
||||
onError = (err) ->
|
||||
# Darwin is a real dick sometimes.
|
||||
#
|
||||
# This is necessary because the "source" or "." program in
|
||||
# bash on OS X closes its file argument before reading
|
||||
# from it, meaning that you get exactly 1 write, which will
|
||||
# work most of the time, and will always raise an EPIPE.
|
||||
#
|
||||
# Really, one should not be tossing away EPIPE errors, or any
|
||||
# errors, so casually. But, without this, `. <(cmd completion)`
|
||||
# can never ever work on OS X.
|
||||
if err.errno == require('constants').EPIPE
|
||||
process.stdout.removeListener 'error', onError
|
||||
defer.resolve()
|
||||
else
|
||||
defer.reject(err)
|
||||
|
||||
process.stdout.on 'error', onError
|
||||
process.stdout.write d, -> defer.resolve()
|
||||
|
||||
defer.promise
|
||||
|
||||
|
||||
getOpts = (argv) ->
|
||||
# get the partial line and partial word, if the point isn't at the end
|
||||
# ie, tabbing at: cmd foo b|ar
|
||||
line = process.env.COMP_LINE
|
||||
w = +process.env.COMP_CWORD
|
||||
point = +process.env.COMP_POINT
|
||||
words = argv.map unescape
|
||||
word = words[w]
|
||||
partialLine = line.substr 0, point
|
||||
partialWords = words.slice 0, w
|
||||
|
||||
# figure out where in that last word the point is
|
||||
partialWord = argv[w] or ''
|
||||
i = partialWord.length
|
||||
while partialWord.substr(0, i) isnt partialLine.substr(-1 * i) and i > 0
|
||||
i--
|
||||
partialWord = unescape partialWord.substr 0, i
|
||||
if partialWord then partialWords.push partialWord
|
||||
|
||||
{
|
||||
line: line
|
||||
w: w
|
||||
point: point
|
||||
words: words
|
||||
word: word
|
||||
partialLine: partialLine
|
||||
partialWords: partialWords
|
||||
partialWord: partialWord
|
||||
}
|
||||
|
||||
|
||||
complete = (cmd, opts) ->
|
||||
compls = []
|
||||
|
||||
# complete on cmds
|
||||
if opts.partialWord.indexOf('-')
|
||||
compls = Object.keys(cmd._cmdsByName)
|
||||
# Complete on required opts without '-' in last partial word
|
||||
# (if required not already specified)
|
||||
#
|
||||
# Commented out because of uselessness:
|
||||
# -b, --block suggest results in '-' on cmd line;
|
||||
# next completion suggest all options, because of '-'
|
||||
#.concat Object.keys(cmd._optsByKey).filter (v) -> cmd._optsByKey[v]._req
|
||||
else
|
||||
# complete on opt values: --opt=| case
|
||||
if m = opts.partialWord.match /^(--\w[\w-_]*)=(.*)$/
|
||||
optWord = m[1]
|
||||
optPrefix = optWord + '='
|
||||
else
|
||||
# complete on opts
|
||||
# don't complete on opts in case of --opt=val completion
|
||||
# TODO: don't complete on opts in case of unknown arg after commands
|
||||
# TODO: complete only on opts with arr() or not already used
|
||||
# TODO: complete only on full opts?
|
||||
compls = Object.keys cmd._optsByKey
|
||||
|
||||
# complete on opt values: next arg case
|
||||
if not (o = opts.partialWords[opts.w - 1]).indexOf '-'
|
||||
optWord = o
|
||||
|
||||
# complete on opt values: completion
|
||||
if optWord and opt = cmd._optsByKey[optWord]
|
||||
if not opt._flag and opt._comp
|
||||
compls = Q.join compls, Q.when opt._comp(opts), (c, o) ->
|
||||
c.concat o.map (v) -> (optPrefix or '') + v
|
||||
|
||||
# TODO: complete on args values (context aware, custom completion?)
|
||||
|
||||
# custom completion on cmds
|
||||
if cmd._comp
|
||||
compls = Q.join compls, Q.when(cmd._comp(opts)), (c, o) ->
|
||||
c.concat o
|
||||
|
||||
# TODO: context aware custom completion on cmds, opts and args
|
||||
# (can depend on already entered values, especially options)
|
||||
|
||||
Q.when compls, (compls) ->
|
||||
console.error 'partialWord: %s', opts.partialWord
|
||||
console.error 'compls: %j', compls
|
||||
compls.filter (c) -> c.indexOf(opts.partialWord) is 0
|
||||
5
node_modules/coa/src/index.coffee
generated
vendored
Normal file
5
node_modules/coa/src/index.coffee
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
exports.Cmd = require('./cmd').Cmd
|
||||
exports.Opt = require('./cmd').Opt
|
||||
exports.Arg = require('./cmd').Arg
|
||||
exports.shell = require('./shell')
|
||||
exports.require = require;
|
||||
243
node_modules/coa/src/opt.coffee
generated
vendored
Normal file
243
node_modules/coa/src/opt.coffee
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
fs = require 'fs'
|
||||
Q = require 'q'
|
||||
Color = require('./color').Color
|
||||
Cmd = require('./cmd').Cmd
|
||||
|
||||
###*
|
||||
Option
|
||||
|
||||
Named entity. Options may have short and long keys for use from command line.
|
||||
@namespace
|
||||
@class Presents option
|
||||
###
|
||||
exports.Opt = class Opt
|
||||
|
||||
###*
|
||||
@constructs
|
||||
@param {COA.Cmd} cmd parent command
|
||||
###
|
||||
constructor: (@_cmd) -> @_cmd._opts.push @
|
||||
|
||||
###*
|
||||
Set a canonical option identifier to be used anywhere in the API.
|
||||
@param {String} _name option name
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
name: (@_name) -> @
|
||||
|
||||
###*
|
||||
Set a long description for option to be used anywhere in text messages.
|
||||
@param {String} _title option title
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
title: Cmd::title
|
||||
|
||||
###*
|
||||
Set a short key for option to be used with one hyphen from command line.
|
||||
@param {String} _short
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
short: (@_short) -> @_cmd._optsByKey['-' + _short] = @
|
||||
|
||||
###*
|
||||
Set a short key for option to be used with double hyphens from command line.
|
||||
@param {String} _long
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
long: (@_long) -> @_cmd._optsByKey['--' + _long] = @
|
||||
|
||||
###*
|
||||
Make an option boolean, i.e. option without value.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
flag: () ->
|
||||
@_flag = true
|
||||
@
|
||||
|
||||
###*
|
||||
Makes an option accepts multiple values.
|
||||
Otherwise, the value will be used by the latter passed.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
arr: ->
|
||||
@_arr = true
|
||||
@
|
||||
|
||||
###*
|
||||
Makes an option required.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
req: ->
|
||||
@_req = true
|
||||
@
|
||||
|
||||
###*
|
||||
Makes an option to act as a command,
|
||||
i.e. program will exit just after option action.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
only: ->
|
||||
@_only = true
|
||||
@
|
||||
|
||||
###*
|
||||
Set a validation (or value) function for option.
|
||||
Value from command line passes through before becoming available from API.
|
||||
Using for validation and convertion simple types to any values.
|
||||
@param {Function} _val validating function,
|
||||
invoked in the context of option instance
|
||||
and has one parameter with value from command line
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
val: (@_val) -> @
|
||||
|
||||
###*
|
||||
Set a default value for option.
|
||||
Default value passed through validation function as ordinary value.
|
||||
@param {Object} _def
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
def: (@_def) -> @
|
||||
|
||||
###*
|
||||
Make option value inputting stream.
|
||||
It's add useful validation and shortcut for STDIN.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
input: ->
|
||||
# XXX: hack to workaround a bug in node 0.6.x,
|
||||
# see https://github.com/joyent/node/issues/2130
|
||||
process.stdin.pause();
|
||||
|
||||
@
|
||||
.def(process.stdin)
|
||||
.val (v) ->
|
||||
if typeof v is 'string'
|
||||
if v is '-'
|
||||
process.stdin
|
||||
else
|
||||
s = fs.createReadStream v, { encoding: 'utf8' }
|
||||
s.pause()
|
||||
s
|
||||
else v
|
||||
|
||||
###*
|
||||
Make option value outputing stream.
|
||||
It's add useful validation and shortcut for STDOUT.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
output: ->
|
||||
@
|
||||
.def(process.stdout)
|
||||
.val (v) ->
|
||||
if typeof v is 'string'
|
||||
if v is '-'
|
||||
process.stdout
|
||||
else
|
||||
fs.createWriteStream v, { encoding: 'utf8' }
|
||||
else v
|
||||
|
||||
###*
|
||||
Add action for current option command.
|
||||
This action is performed if the current option
|
||||
is present in parsed options (with any value).
|
||||
@param {Function} act action function,
|
||||
invoked in the context of command instance
|
||||
and has the parameters:
|
||||
- {Object} opts parsed options
|
||||
- {Array} args parsed arguments
|
||||
- {Object} res actions result accumulator
|
||||
It can return rejected promise by Cmd.reject (in case of error)
|
||||
or any other value treated as result.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
act: (act) ->
|
||||
opt = @
|
||||
name = @_name
|
||||
@_cmd.act (opts) ->
|
||||
if name of opts
|
||||
res = act.apply @, arguments
|
||||
if opt._only
|
||||
Q.when res, (res) =>
|
||||
@reject {
|
||||
toString: -> res.toString()
|
||||
exitCode: 0
|
||||
}
|
||||
else
|
||||
res
|
||||
@
|
||||
|
||||
###*
|
||||
Set custom additional completion for current option.
|
||||
@param {Function} completion generation function,
|
||||
invoked in the context of option instance.
|
||||
Accepts parameters:
|
||||
- {Object} opts completion options
|
||||
It can return promise or any other value treated as result.
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
comp: Cmd::comp
|
||||
|
||||
_saveVal: (opts, val) ->
|
||||
if @_val then val = @_val val
|
||||
if @_arr
|
||||
(opts[@_name] or= []).push val
|
||||
else
|
||||
opts[@_name] = val
|
||||
val
|
||||
|
||||
_parse: (argv, opts) ->
|
||||
@_saveVal(
|
||||
opts,
|
||||
if @_flag
|
||||
true
|
||||
else
|
||||
argv.shift()
|
||||
)
|
||||
|
||||
_checkParsed: (opts, args) -> not opts.hasOwnProperty @_name
|
||||
|
||||
_usage: ->
|
||||
res = []
|
||||
nameStr = @_name.toUpperCase()
|
||||
|
||||
if @_short
|
||||
res.push '-', Color 'lgreen', @_short
|
||||
unless @_flag then res.push ' ' + nameStr
|
||||
res.push ', '
|
||||
|
||||
if @_long
|
||||
res.push '--', Color 'green', @_long
|
||||
unless @_flag then res.push '=' + nameStr
|
||||
|
||||
res.push ' : ', @_title
|
||||
|
||||
if @_req then res.push ' ', Color('lred', '(required)')
|
||||
|
||||
res.join ''
|
||||
|
||||
_requiredText: -> 'Missing required option:\n ' + @_usage()
|
||||
|
||||
###*
|
||||
Return rejected promise with error code.
|
||||
Use in .val() for return with error.
|
||||
@param {Object} reject reason
|
||||
You can customize toString() method and exitCode property
|
||||
of reason object.
|
||||
@returns {Q.promise} rejected promise
|
||||
###
|
||||
reject: Cmd::reject
|
||||
|
||||
###*
|
||||
Finish chain for current option and return parent command instance.
|
||||
@returns {COA.Cmd} parent command
|
||||
###
|
||||
end: Cmd::end
|
||||
|
||||
###*
|
||||
Apply function with arguments in context of option instance.
|
||||
@param {Function} fn
|
||||
@param {Array} args
|
||||
@returns {COA.Opt} this instance (for chainability)
|
||||
###
|
||||
apply: Cmd::apply
|
||||
10
node_modules/coa/src/shell.coffee
generated
vendored
Normal file
10
node_modules/coa/src/shell.coffee
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
exports.unescape = (w) ->
|
||||
w = if w.charAt(0) is '"'
|
||||
w.replace(/^"|([^\\])"$/g, '$1')
|
||||
else
|
||||
w.replace(/\\ /g, ' ')
|
||||
w.replace(/\\("|'|\$|`|\\)/g, '$1')
|
||||
|
||||
exports.escape = (w) ->
|
||||
w = w.replace(/(["'$`\\])/g,'\\$1')
|
||||
if w.match(/\s+/) then '"' + w + '"' else w
|
||||
Reference in New Issue
Block a user