sub-cmd
Version:
A pass through sub command (git-style) runner.
147 lines (135 loc) • 4.16 kB
text/coffeescript
fs = require 'fs'
path = require 'path'
which = require 'which'
subproc = require 'child_process'
loglet = require 'loglet'
split = (program) ->
[ path.dirname(program), path.basename(program) ]
isOption = (arg) ->
first = arg[0]
first == '-' or first == '/'
normalize = (argv) ->
program = fs.realpathSync argv[1]
command = []
rest = []
done = false
for i in [2...argv.length]
if done
rest.push argv[i]
else if isOption(argv[i])
done = true
rest.push argv[i]
else
command.push argv[i]
if command.length > 0 and command[0] == 'help'
command.shift()
rest.push '--help'
return [ program, command , rest ]
isHelp = (command) ->
command[0] == 'help'
spawn = (program, argv, cb) ->
loglet.debug 'spawn.search', program, argv
search program, argv, (err, res) ->
loglet.debug 'spawn.search', err, res
if err
cb err
else if res?.program
child = subproc.spawn res.program, res.args, { stdio: 'inherit' }
child.on 'exit', (code) ->
process.exit code
cb null, child
else
cb {error: 'subcommand_not_found', args: res.args}
searchHelper = (dirPath, cb) ->
whichHelper =
if dirPath
(name, cb) ->
filePath = path.join dirPath, name
fs.stat filePath, (err, stat) ->
if err?.code == 'ENOENT'
cb {file_not_found: filePath}
else if err # not found...
cb err
else
cb null, filePath
else
(name, cb) ->
which name, (err, cmd) ->
loglet.debug 'which.result', name, err, cmd, JSON.stringify(err)
if err?.message.match /not found\:/
loglet.debug 'which.result.not_found'
cb {file_not_found: name}
else if err
cb err
else
cb err, cmd
helper = (program, rest, prev) ->
loglet.debug 'searchHelper.helper', program, rest, prev
if rest.length == 0
return cb null, {program: prev, args: rest}
seg = rest.shift()
name = [ program, seg].join('-')
whichHelper name, (err, cmd) ->
loglet.debug 'whichHelper', name, err, cmd
if err?.file_not_found
loglet.debug 'whichHelper.not_found', name, prev
if prev
rest.unshift(seg)
cb null, {program: prev, args: rest}
else
cb err
else if err
cb err
else
helper name, rest, cmd
helper
_whichPath = (program, commands, cb) ->
[ programDir, programName ] = split program
loglet.debug '_whichPath.split', programDir, programName
helper = searchHelper null, cb
helper programName, [].concat(commands), null
_localPath = (program, commands, cb) ->
[ programDir, programName ] = split program
loglet.debug '_localPath.split', programDir, programName
helper = searchHelper programDir, cb
helper programName, [].concat(commands), null
search = (program, argv, cb) ->
if arguments.length == 0
return cb {error: 'invalid_call_need_3_params'}
else if arguments.length == 1
cb = program
argv = process.argv
program = argv[1]
else if arguments.length == 2
cb = argv
argv = program
program = argv[1]
[ program, commands , rest ] = normalize argv
if commands.length == 0
cb {no_sub_command: program, args: rest}
_localPath program, commands, (err, result) ->
if err?.file_not_found
_whichPath program, commands, (err, result) ->
if err
cb err
else
cb null, {program: result.program, args: result.args.concat(rest)}
else if err
cb err
else
cb null, {program: result.program, args: result.args.concat(rest)}
getSubCommands = (programPath, cb) ->
dirname = path.dirname(programPath)
program = path.basename programPath
fs.readdir dirname, (err, files) ->
if err
cb err
else
commands = {}
for file in files
if file != program and file.indexOf(program + '-') == 0
commands[file.substring(program.length + 1)] = path.join dirname, file
cb null, commands
module.exports =
which: search
spawn: spawn