Giter Club home page Giter Club logo

coa's Introduction

Command-Option-Argument

Yet another parser for command line options.

NPM Status Travis Status AppVeyor Status Coverage Status Dependency Status

What is it?

COA is a parser for command line options that aim to get maximum profit from formalization your program API. Once you write definition in terms of commands, options and arguments you automaticaly get:

  • Command line help text
  • Program API for use COA-based programs as modules
  • Shell completion

Other features

  • Rich types for options and arguments, such as arrays, boolean flags and required
  • Commands can be async throught using promising (powered by Q)
  • Easy submoduling some existing commands to new top-level one
  • Combined validation and complex parsing of values

TODO

  • Localization
  • Shell-mode
  • Configs
  • Aliases
  • Defaults

Examples

require('coa').Cmd() // main (top level) command declaration
    .name(process.argv[1]) // set top level command name from program name
    .title('My awesome command line util') // title for use in text messages
    .helpful() // make command "helpful", i.e. options -h --help with usage message
    .opt() // add some option
        .name('version') // name for use in API
        .title('Version') // title for use in text messages
        .short('v') // short key: -v
        .long('version') // long key: --version
        .flag() // for options without value
        .act(function(opts) { // add action for option
            // return message as result of action
            return JSON.parse(require('fs').readFileSync(__dirname + '/package.json'))
                .version;
        })
        .end() // end option chain and return to main command
    .cmd().name('subcommand').apply(require('./subcommand').COA).end() // load subcommand from module
    .cmd() // inplace subcommand declaration
        .name('othercommand').title('Awesome other subcommand').helpful()
        .opt()
            .name('input').title('input file, required')
            .short('i').long('input')
            .val(function(v) { // validator function, also for translate simple values
                return require('fs').createReadStream(v) })
            .req() // make option required
            .end() // end option chain and return to command
        .end() // end subcommand chain and return to parent command
    .run(process.argv.slice(2)); // parse and run on process.argv
// subcommand.js
exports.COA = function() {
    this
        .title('Awesome subcommand').helpful()
        .opt()
            .name('output').title('output file')
            .short('o').long('output')
            .output() // use default preset for "output" option declaration
            .end()
};

API reference

Cmd

Command is a top level entity. Commands may have options and arguments.

Cmd.api

Returns object containing all its subcommands as methods to use from other programs.
@returns {Object}

Cmd.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)

Cmd.title

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)

Cmd.cmd

Create new or add existing subcommand for current command.
@param COA.Cmd [cmd] existing command instance
@returns COA.Cmd new or added subcommand instance

Cmd.opt

Create option for current command.
@returns COA.Opt new option instance

Cmd.arg

Create argument for current command.
@returns COA.Opt new argument instance

Cmd.act

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)

Cmd.apply

Apply function with arguments in context of command instance.
@param Function fn
@param Array args
@returns COA.Cmd this instance (for chainability)

Cmd.comp

Set custom additional completion for current command.
@param Function fn 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)

Cmd.helpful

Make command "helpful", i.e. add -h --help flags for print usage.
@returns COA.Cmd this instance (for chainability)

Cmd.completable

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)

Cmd.usage

Build full usage text for current command instance.
@returns String usage text

Cmd.run

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)

Cmd.invoke

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

Cmd.reject

Return reject of actions results promise.
Use in .act() for return with error.
@param Object reason reject reason
You can customize toString() method and exitCode property of reason object.
@returns Q.promise rejected promise

Cmd.end

Finish chain for current subcommand and return parent command instance.
@returns COA.Cmd parent command

Opt

Option is a named entity. Options may have short and long keys for use from command line.
@namespace
@class Presents option

Opt.name

Set a canonical option identifier to be used anywhere in the API.
@param String _name option name
@returns COA.Opt this instance (for chainability)

Opt.title

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)

Opt.short

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)

Opt.long

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)

Opt.flag

Make an option boolean, i.e. option without value.
@returns COA.Opt this instance (for chainability)

Opt.arr

Makes an option accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns COA.Opt this instance (for chainability)

Opt.req

Makes an option req.
@returns COA.Opt this instance (for chainability)

Opt.only

Makes an option to act as a command, i.e. program will exit just after option action.
@returns COA.Opt this instance (for chainability)

Opt.val

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 option instance and has one parameter with value from command line
@returns COA.Opt this instance (for chainability)

Opt.def

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)

Opt.input

Make option value inputting stream. It's add useful validation and shortcut for STDIN. @returns {COA.Opt} this instance (for chainability)

Opt.output

Make option value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns COA.Opt this instance (for chainability)

Opt.act

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)

Opt.comp

Set custom additional completion for current option.
@param Function fn 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.Opt this instance (for chainability)

Opt.end

Finish chain for current option and return parent command instance.
@returns COA.Cmd parent command

Arg

Argument is a unnamed entity.
From command line arguments passed as list of unnamed values.

Arg.name

Set a canonical argument identifier to be used anywhere in text messages.
@param String _name argument name
@returns COA.Arg this instance (for chainability)

Arg.title

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)

Arg.arr

Makes an argument accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns COA.Arg this instance (for chainability)

Arg.req

Makes an argument req.
@returns COA.Arg this instance (for chainability)

Arg.val

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)

Arg.def

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)

Arg.output

Make argument value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns COA.Arg this instance (for chainability)

Arg.comp

Set custom additional completion for current argument.
@param Function fn 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.Arg this instance (for chainability)

Arg.end

Finish chain for current option and return parent command instance.
@returns COA.Cmd parent command

coa's People

Contributors

acandidmind avatar alexbadm avatar aliosv avatar arikon avatar canac avatar coderpuppy avatar gfranco avatar greli avatar h4 avatar holtjohnson avatar qfox avatar tadatuta avatar tunnckocore avatar veged avatar xpaw avatar yarastqt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

coa's Issues

Incorrect Termination Code

I dunno who's at fault for this one. SVGO depends on COA, but when you use COA 1.0.2 the termination code for svgo -v at the command line is incorrect, which breaks integration with other tools. When you use COA 1.0.1, it works just fine. I'm looping you in so somebody can figure out what's up.

See: svg/svgo#739

Shell completion code fails on win32 (in bash/msys/git env)

As per http://bem.info/tools/bem/installation/ I 'm running bem completion, which fails with

$ bem completion
Error: shell completion not supported on windows
    at Cmd.module.exports (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\node_modules\coa\lib\completion.js:19:11)
    at exports.Cmd.Cmd._do (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\node_modules\coa\lib\cmd.js:429:22)
From previous event:
    at Cmd.exports.Cmd.Cmd._do (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\node_modules\coa\lib\cmd.js:424:14)
    at Cmd.exports.Cmd.Cmd.do (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\node_modules\coa\lib\cmd.js:471:17)
    at Cmd.exports.Cmd.Cmd.run (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\node_modules\coa\lib\cmd.js:459:22)
    at Object.<anonymous> (c:\Users\leonids.maslovs\AppData\Roaming\npm\node_modules\bem\bin\bem:6:23)

I appears the problem is with ./blob/master/lib/completion.js#L18-23

If I comment it out - it works perfectly well and generated autocomplete code works for me (.bashrc). In my opinion - completion.js either should comment it out completely or should check for execution environment (e.g. bash, msys etc ..). What's you opinion on this? If the later is the case, I could try to comeup with pull-request (though I'm unfamiliar with nodejs thing and that might be a personal challenge to some degree ;) )

PS: following environment variables are available:

MACHTYPE=i686-pc-msys
OSTYPE=msys

Variadic options

I want to add variadic options support to COA.

What is a preferred solution?

  • add Cmd flag-method like allowAny(), that will translate all specified cmd options to opts regardless of there existence in the command description
  • add Opt flag-method like prefixed(), that will translate all the options with common prefix that equals to opt name to opts.<opt-name> object as properties

Use exitCode instead of exit()

To provide a better user experience we should not force event loop to stop since some library code can require additional things to do.

In other words we should set exitCode instead of calling exit() method.

coa/lib/cmd.js

Line 197 in fb1080e

process.exit(code || exitCode || 0);

Add a LICENSE

Hi there,

We were wondering if you might consider adding one so that we can use your package. Thanks!

Utility helpers like helpful should not finalize itselves

It's a major change but it will add a way to extend basic behaviour of helpers.

E.g. .helpful().title('another title').act(function() { doSomething(); return this.__base.apply(); }).end().

As temporary (or persistent) alternative we can add an option like .helpful({ endless: true }).

Subcommands don't work at all

Минимальный пример, показывающий, что команды совсем не работают:

require('../lib/coa').Cmd()
    .name('cmd')
    .title('Command test')
    .helpful()
    .cmd()
        .name('command')
        .title('Do command work')
        .helpful()
        .act(function() {
            console.log('Doing command work...');
        })
        .end()
    .run(['command', '--help'])
    .run(['command']);

Bug in case of creating options via methods '.arr()' + '.def()'

Hi!

I'm using coa in my-script.js this way:

require('coa').Cmd()
    .name(process.argv[1]).title('Makes awesome things').helpful()
    .opt()
        .name('awesomeOpt')
        .title('My awesome option')
        .long('awesome-opt')
        .arr()
        .def(['1', '2'])
        .end()
    .act(opts => console.log(opts))
    .run();

Then I run my script:

$ node my-script.js

The output is:

{ awesomeOpt: [['1', '2']] }

but I expected:

{ awesomeOpt: ['1', '2'] }

API for command like options (--help, --version)

Нужна возможность реализовывать опции-комманды вроде --help и --version.
Выполнение act() для этих опций должно прерывать цепочку всех действий команды, но не завершать выполнение команды с ошибкой. Поэтому this.reject() не подходит.

Кроме прочего не хочется сильно усложнять существующий API и хочется попробовать вписать это в Promises API.

Варианты такие.

  1. Использоваться в качестве результата действий сложный объект, в который можно было бы вшивать код ошибки или флаг завершения выполнения цепочки. Проверять наличие этого флага при раскручивании цепочки действий.
  2. Использовать ещё один флаг на опцию, устанавливаемый как-то так: opt.last()
  3. ...?

Prettify JSDoc types

Atm there are few different ways of using namespaces: {COA.Cmd}, {Cmd}, etc. Need to choose one.

Fix completion script

Should you more precise one from karma for example.

###-begin-karma-completion-###
#
# karma command completion script
# This is stolen from NPM. Thanks @isaac!
#
# Installation: karma completion >> ~/.bashrc  (or ~/.zshrc)
# Or, maybe: karma completion > /usr/local/etc/bash_completion.d/karma
#

if type complete &>/dev/null; then
  __karma_completion () {
    local si="$IFS"
    IFS=$'\n' COMPREPLY=($(COMP_CWORD="$COMP_CWORD" \
                           COMP_LINE="$COMP_LINE" \
                           COMP_POINT="$COMP_POINT" \
                           karma completion -- "${COMP_WORDS[@]}" \
                           2>/dev/null)) || return $?
    IFS="$si"
  }
  complete -F __karma_completion karma
elif type compdef &>/dev/null; then
  __karma_completion() {
    si=$IFS
    compadd -- $(COMP_CWORD=$((CURRENT-1)) \
                 COMP_LINE=$BUFFER \
                 COMP_POINT=0 \
                 karma completion -- "${words[@]}" \
                 2>/dev/null)
    IFS=$si
  }
  compdef __karma_completion karma
elif type compctl &>/dev/null; then
  __karma_completion () {
    local cword line point words si
    read -Ac words
    read -cn cword
    let cword-=1
    read -l line
    read -ln point
    si="$IFS"
    IFS=$'\n' reply=($(COMP_CWORD="$cword" \
                       COMP_LINE="$line" \
                       COMP_POINT="$point" \
                       karma completion -- "${words[@]}" \
                       2>/dev/null)) || return $?
    IFS="$si"
  }
  compctl -K __karma_completion karma
fi
###-end-karma-completion-###

npm test fails

Deprecated mocha-as-promised package is not compatible with mocha ~1.12.0

Problem with Opt.req()

Если есть хотя бы одна из опций с req() и есть какой-нибудь --version, запуск ./cmd --version будет фейлиться из-за отсутствующей опции с req()

Aliases support (inspired by git)

One could define alias as cb = create -b and invoke bem cb block. COA should resolve cb alias and invoke bem create -b block command.

Also think of shell completion for aliases.

Suppressed errors in async functions

The program ends without errors if errors throws in async functions.

Example:

cmd()
  .act((options) => {
    setTimeout(() => {
      throw new Error('catch me if you can')
    }, 100)
  })
  .run()

Postpone `process.exit()` call to 'natural' node.js exit stage

Demo of problem, see comment inside code block:

var FS = require('fs'),
    output = process.stdout
    data = getData();

output = FS.createWriteStream('exit-test.txt');

output.on('end', function() {
    console.log('output stream end');
});

output.write(data);
if (output !== process.stdout) output.end();

process.once('exit', function(code) {
    code = code || 0;
    console.log('about to exit with code %s', code);
    process.exit(code);
});

// Uncomment, and exit-test.txt wiil be empty
//process.exit(1);

function getData() {
    var data = '';
    for (var i = 0; i < 5000000; i++) {
        data += 'a';
    }
    return data;
}

API for empty option value checks

        cmd.opt()
            .name('format')
            .title('Output format: html, bemjson. Defaults to html')
            .short('f')
            .long('format')
            .def('html')
            .val(function(value) {
                // FIXME: don't use private API _usage()
                !value && this.end().errorExit("Missing required option value\n" + this._usage());
                (['html', 'bemjson'].indexOf(value) == -1) && this.end()
                    .errorExit('Wrong output format "' + value + '" specified, must be one of "html" or "bemjson"');
                return value;
            })
            .end()

Don't include test directory in production

There's no reason to have the test and tests directories in production through npm install. It's wasteful! You can ignore them easily through an .npmignore file or whitelisting files in the package.json. I can do both as a PR if you want.

Bump up node version deps

bem-tools can now work with node 0.5+, but coa strictly depends on node ~0.4.0, that prevents bem-tools from installing on node 0.5.x.

Need to bump up node version dependence to:

{
    "node" : ">=0.4.0 <0.7.0"
}

Custom commands support (inspired by git)

One could run bem my and COA should try to search for bem-cmd-my module and if not found for binary bem-my in PATH and run the found one.

Also think of shell completion for custom commands.

req() option doesn't affect help sheet

Consider such code:

require('coa').Cmd()
    .name('myscript')
    .helpful()
    .opt()
        .name('target')
        .title('Target')
        .short('t').long('target')
        .req()
        .end()
    .opt()
        .name('path')
        .title('Path')
        .short('p').long('path')
        .def('~/bin')
        .end()
    .run()

When I add the "req()" method to an option, I expect to get a kind of hint on the help sheet, that the option is required. However, I get the same descriptions for different kinds of options.

£ ./myscript -h

Usage:
myscript [OPTIONS] [ARGS]

Options:
-h, --help : Help
-t TARGET, --target=TARGET : Target
-p PATH, --path=PATH : Path

Empty string can't be passed as a required argument

Prerequisites:

.arg()
    .name('name')
    .title('some description')
    .req()
    .end()

Bug:
Pass "" as an argument -> error:

Missing required argument:
  NAME : some description (required)

UPD: Empty string can't be passed as argument at all. Remove .req() from the example above, pass "" as an argument and there will be no arguments at all, but should be argument with name name

Change interface of .act() callback functions

My proposal it to change API of .act() callbacks from

function(opts, args) {
}

to

function(options) {
   // options = opts + args
}

Motivation

  1. The difference between opts and args in semantical only and only make sense when command is run from the command line

  2. It is hard to explain to develepor what the difference between opts and args when it uses COA API to run command

  3. Division into two different arguments makes sense in case when opts is a hash and args is an array

  4. Compare

    var BEM = require('bem');
    BEM.api.create.block({ tech: ['css', 'js'] }, { names: ['b1', 'b2'] });

    and

    var BEM = require('bem');
    BEM.api.create.block({ names: ['b1', 'b2'], tech: ['css', 'js'] });

/cc @veged

Problem with parsing opts

For example:

program --opt="--one --two"

here we have one option
but coa thinks there are three of them

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.