simple-cli

5.0.5 • Public • Published

Build Status downloads npm Code Climate Test Coverage dependencies

simple-cli

Gruntify command-line APIs with ease.

Installation

npm install --save simple-cli

API

cli(task[, options])

task is a string that will be assigned as the name of the grunt task. It is also (by default) the name of the executable being wrapped, but you can override this with cmd.

options is an object that configures how simple-cli handles the executable. See options.

Usage

This module is intended to be used with grunt to make writing plugin wrappers for command line tools easier to do. In your grunt task declaration, require this module and invoke it as follows:

var cli = require('simple-cli');
 
// Or "npm" or "hg" or "bower" etc.
module.exports = cli('git');

Yes, that is all that is necessary to build a fully functioning git plugin for grunt.

This module allows any command on the executable to be invoked as a target with any options specified (camel-cased) under options. It basically makes it possible to do anything the executable can do in grunt. Even options not normally a part of the tool (i.e. from a branch or fork) can be invoked with simple-cli because simple-cli doesn't allow options from a list of known options like most plugins for executables do. It, instead, assumes that the end-user actually does know what he or she is doing and that he or she knows, or can look up, the available options. Below are the kinds of options that can be specified.

Options on the executable

Options provided to the executable are generated by opted, so check the documentation there. Practical examples follow:

Let's write a wrapper for the super-awesome (and totally made up) blerg executable. First, we write the wrapper and publish it as grunt-blerg (or maybe grunt-simple-blerg since grunt-blerg is probably taken):

var cli = require('simple-cli');
 
module.exports = cli('blerg');

Done. Now we can blerg on the command line via grunt! Let's see how an end-user would consume our new library.

Long options

You can specify any long option under options with a corresponding value.

grunt.initConfig({
  blerg: {
    shazzam: {
      options: {
        foo: 'bar'
      }
    }
  }
});

This will run blerg shazzam --foo bar.

Multi-word options

Multi-word options work too.

grunt.initConfig({
  blerg: {
    awesome: {
      options: {
        fooBar: 'baz'
      }
    }
  }
});

This will run blerg awesome --foo-bar baz. Note the camel-casing for options with hyphens.

Boolean options

But not all options have values. blerg, for example, has that super-user --banana option.

grunt.initConfig({
  blerg: {
    jazzhands: {
      options: {
        banana: true
      }
    }
  }
});

This will run blerg jazzhands --banana.

Short options

You can also use short options.

grunt.initConfig({
  blerg: {
    wasabi: {
      options: {
        a: 'foo'
      }
    }
  }
});

This will run blerg wasabi -a foo.

Short boolean options

And short options as booleans.

grunt.initConfig({
  blerg: {
    hashbang: {
      options: {
        a: true
      }
    }
  }
});

This will run blerg hashbang -a.

Options with equal signs

Some libraries have weird "="-style options. Like git. And blerg.

grunt.initConfig({
  blerg: {
    nafblat: {
      options: {
        'bloogs=': 'meep'
      }
    }
  }
});

This will run blerg nafblat --bloogs=meep.

Arrays of options

You can also specify the same option more than once by passing an array.

grunt.initConfig({
  blerg: {
    murica: {
      options: {
        a: ['foo', 'bar'],
        fruit: ['banana', 'kiwi']
      }
    }
  }
});

This will run blerg murica -a foo -a bar --fruit banana --fruit kiwi.

Simple cli options

There are also some library specific options. Options about how simple cli itself behaves are placed at the top level of a task target.

quiet

Set to true to prevent logging during the child process. Regardless of the value of this flag, all stdout and stderr will be collected and passed to onComplete. However, if it is not true, it will also be logged as the process runs (similar to how stdio: 'inherit' works with child_process.spawn).

grunt.initConfig({
  blerg: {
    lollipop: {
      options: {
        foo: 'bar'
      },
      quiet: true
    }
  }
});

env

Supply additional environment variables to the child process. These variables are merged with process.env.

grunt.initConfig({
  blerg: {
    hoodoo: {
      options: {
        foo: 'bar'
      },
      env: {
        BANANA: 'yellow'
      }
    }
  }
});

Like running BANANA=yellow blerg hoodoo --foo bar.

cwd

Set the current working directory for the child process.

grunt.initConfig({
  blerg: {
    jackwagon: {
      options: {
        foo: 'bar'
      },
      cwd: './test'
    }
  }
});

Runs blerg jackwagon --foo bar, but in the ./test directory.

force

If the task fails, don't halt the entire task chain. Note that this is different that grunt's own force option. Really all this does is consume any error thrown . . . and simply ignore it.

grunt.initConfig({
  blerg: {
    muncher: {
      force: true
    }
  }
});

onComplete

A callback to handle the stdout and stderr streams. simple-cli aggregates the stdout and stderr data output and will supply the final strings to the onComplete function. This function should have the signature function(err, stdout, callback) where err is an error object containing the stderr stream (if any errors were reported) and the code returned by the child process (as err.code), stdout is a string, and callback is a function. The callback must be called with a falsy value to complete the task (calling it with a truthy value - e.g. 1 - will fail the task).

grunt.initConfig({
  blerg: {
    portmanteau: {
      onComplete: function(err, stdout, callback) {
        if (err) {
          grunt.fail.fatal(err.message, err.code);
        } else {
          grunt.config.set('portmanteau', stdout);
          callback();
        }
      }
    }
  }
});

cmd

An alternative sub-command to call on the cli. This is useful when you want to create multiple targets that call the same command with different options/parameters. If this value is present, it will be used instead of the grunt target as the first argument to the executable.

grunt.initConfig({
  // Using git as a real example
  git: {
    pushOrigin: {
      cmd: 'push',
      args: ['origin', 'master']
    },
    pushHeroku: {
      cmd: 'push',
      args: 'heroku master'
    }
  }
});

Running grunt git:pushOrigin will run git push origin master and running grunt git:pushHeroku will run git push heroku master.

Some executables do some default action when run without a subcommand (but still support subcommands for other things). To run a default action for an executable of this type, pass cmd: false to tell simple-cli to skip the subcommand.

grunt.initConfig({
  mocha: {
    test: {
      cmd: false
    },
    init: {}
  }
});

Here, grunt mocha:test will run just mocha, while grunt mocha:init will run mocha init.

args

Additional, non-flag arguments to pass to the executable. These can be passed as an array (as in git:pushOrigin above) or as a single string with arguments separated by a space (as in git:pushHeroku above). Note that, if you need to use spaces inside an argument, you will need to use the array syntax, since simple-cli will split a string on spaces.

rawArgs

rawArgs is a catch all for any arguments to the executable that can't be handled (for whatever reason) with the options above (e.g. the path arguments in some git commands: git checkout master -- config/production.json). Anything in rawArgs will be concatenated to the end of all the normal args. It can be a string or an array of strings.

grunt.initConfig({
  git: {
    checkout: {
      args: ['master'],
      rawArgs: '-- config/production.json'
    }
  }
});

debug

Similar to --dry-run in many executables. This will log the command that will be spawned in a child process without actually spawning it. Additionally, if you have an onComplete handler, fake stderr and stdout will be passed to this handler, simulating the real task. If you want to use specific stderr/stdout messages, debug can also be an object with stderr and stdout properties that will be passed to the onComplete handler.

grunt.initConfig({
  blerg: {
    'waffle-iron': {
      // Invoked with default fake stderr/stdout
      onComplete: function(err, stdout, callback) {
        console.log(err.message, stdout);
        callback();
      },
      debug: true
    },
    'wilty-salad': {
      onComplete: function(err, stdout, callback) {
        console.log(err.message, stdout); // Logs 'foo bar'
        callback();
      },
      debug: {
        stderr: 'foo',
        stdout: 'bar'
      }
    }
  }
});

Additionally, you can pass the --debug option to grunt itself to enable the above behavior in an ad hoc manner (e.g. grunt blerg:wilty-salad --debug).

Dynamic values

Sometimes you just don't know what values you want to supply to an executable until you're ready to use it. That makes it hard to put into a task. simple-cli supports dynamical values (via interpolation) which can be supplied in any of three ways:

via command line options to grunt (e.g. grunt.option)

Supply the value when you call the task itself.

grunt.initConfig({
  git: {
    push: {
      // You can also do this as a string, but note that simple-cli splits
      // string args on space, so you wouldn't be able to put space INSIDE
      // the interpolation. You'd have to say args: '{{remote}} master'
      args: ['origin', '{{ branch }}']
    }
  }
});

If the above was invoked with grunt git:push --branch master the final command would be git push origin master.

via grunt.config

This is primarily useful if you want the result of another task to determine the value of an argument. For instance, maybe in another task you say grunt.config.set('branch', 'new-feature'), then the task above would run git push origin new-feature.

via prompt

If simple-cli can't find an interpolation value via grunt.option or grunt.config, it will prompt you for one on the terminal. Thus you could do something like:

grunt.initConfig({
  git: {
    commit: {
      options: {
        message: '{{ message }}'
      }
    }
  }
});

and automate commits, while still supplying an accurate commit message.

Shortcut configurations

For very simple tasks, you can define the task body as an array or string, rather than as an object, as all the above examples have been.

grunt.initConfig({
  git: {
    // will invoke "git push origin master"
    push: ['origin', 'master'],
 
    // will invoke "git pull upstream master"
    pull: 'upstream master'
  }
});

Note that this only works if the target name is the command you want to run. If you need, for example, multiple push targets, you'll have to use the longer syntax with cmd and args.

Options

To setup the wrapper for an executable, require simple-cli and invoke the returned function.

var cli = require('simple-cli');
 
module.exports = cli('executable');

If you need finer controller, you can pass a configuration object as the second parameter. E.g.

const cli = require('simple-cli');
 
module.exports = cli('task', {
  cmd: 'something-else',
  standalone: true
});

The available options are below.

description

Optional.

A description to pass to grunt.registerMultiTask. If none is provided, simple-cli will build one for you that looks like A simple grunt wrapper for <executable name>.

var cli = require('simple-cli');
 
module.exports = cli('foo', {
  description: 'Do some foo! With authority.'
});

cmd

Optional.

Prior to version 4.1.0: The executable to run if different from the task. This can be useful for wrapping node binaries that you want to include as dependencies. Just set cmd equal to path to the local executable, e.g. <absolute_path>/node_modules/.bin/blah. Alternatively, you could use this as an alias if the executable is long and tedious to type (like "codeclimate-test-reporter").

In 4.1.0 and later: simple-cli adds the node_modules/.bin to the front of the PATH environment variable prior to calling the executable (similar to how npm run foo works), so anything installed locally should work fine out of the box. It uses require.resolve to figure out the path for this, which allows you (meaning, someone writing a grunt wrapper using simple-cli) to either include the executable as a dependency or as a peer dependency, as require.resolve will figure out where the module is located in the dependency tree.

var cli = require('simple-cli');
var path = require('path');
 
// v4.0.0 and earlier
module.exports = cli('foo', {
  cmd: path.resolve(__dirname, '../node_modules/.bin/foo')
});
 
// v4.1.0 and later
module.exports = cli('foo'); // If node_modules/.bin/foo exists, this will run it.

Note that the cmd option still exists in v4.1.0 and later, but you only need it if, for some reason, you want to name the task something different than the executable.

singleDash

Optional.

Set to true for executables that use a find style syntax, i.e. a single dash prefix for parameters: find . -name foo

var cli = require('simple-cli');
 
module.exports = cli('foo', {
  singleDash: true
});

callback

Optional.

A function to call after executing the child process. The child process code will be passed to this function. If omitted, this simply calls grunt's this.async() method to trigger the task completion. If you supply this options, you will have to call that method yourself. It will be set on the context within the function as done. Call this function with false or an Error to fail the task.

var cli = require('simple-cil');
 
module.exports = cli('bar', {
  callback: function(code) {
    // Do whatever...
    this.done(code === 0);
  }
});

Other properties available on the this object within this method are:

  • this.grunt -> the grunt object
  • this.context -> the grunt task context
  • this.cmd -> the command executed via child process
  • this.options -> the task options
  • this.config -> the task configuration (e.g. cmd, args, rawArgs, env, etc.)
  • this.custom -> custom options parsers provided by your wrapper
  • this.env -> environment variables to supply to the child process
  • this.target -> the command to run on the executable (e.g. "commit" in "git commit")
  • this.args -> the full array of command line args supplied to the executable
  • this.debugOn -> whether the task is running debug mode

custom

Optional.

The options object is actually just a way to extend the simple-cli API. Keys in the object are options allowed as part of the task configuration data and the values are the handlers for those options. So if you need more cowbell in your cli wrapper, you can do that:

var cli = require('simple-cil');
 
module.exports = cli('foo', {
  custom: {
    moreCowbell: function(val, cb) {
      // val is the user-assigned config value of "moreCowbell," e.g.
      // grunt.initConfig({
      //   foo: {
      //     bar: {
      //       options: {
      //         moreCowbell: 'blah'
      //       }
      //     }
      //   }
      // });
 
      // Now "this.target" is "halb" . . . probably not that useful, but it's just an example
      this.target = val.split('').reverse().join('');
      cb();
    }
  }
});

The handlers for custom opts are called immediately before the child process is spawned (so all the arguments have already been aggregated and put in the right form). The parameters passed to the handler are the value supplied by the user and a callback. The context within the function is the simple-cli context, the same as in the callback option above.

flags

Optional.

By default, any options (things with -- and - at the front) will be passed after any args. So for example:

grunt.initConfig({
  git: {
    push: {
      options: {
        f: true
      },
      args: ['origin', 'master']
    }
  }
});

will generate the command git push origin master -f. Most of the time, placement of flags doesn't affect the command, but sometimes it does, like in the case of nyc which has flags of its own that should be passed to the nyc executable itself, followed by args, which is the executable nyc should attempt to run, followed by that executables flags. If the executable you're wrapping works like this, pass flags: 'before' in options. E.g.

const cli = require('simple-cli');
 
module.exports = cli('nyc', {
  standalone: true,
  flags: 'before'
});

That will allow users to do this:

grunt.initConfig({
  nyc: {
    mocha: {
      options: {
        cwd: 'app'
      },
      args: ['mocha', '--require', 'should']
    }
  }
});

Now, when grunt nyc:mocha is run, the command it will generate will be nyc --cwd app mocha --require should. Note how options to the sub-executable here are passed under args (you could alternatively pass them as rawArgs since those are added last). If they were placed under options, they would be passed as flags to nyc instead of mocha.

standalone

Optional.

Some executables don't have subcommands, like mkdir for example. If you're wrapping an executable of this type, pass standalone: true in the options and simple-cli will not include a subcommand in the shell command it generates. Note that this option is only for executables that always and only standalone. Some executables do some default thing when no subcommand is passed, but still support subcommands. mocha, for example, runs tests when run by itself, but you can also say mocha init to setup a project. For this kind of executable, see cmd: false above.

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 5.0.5
    17,143
    • latest

Version History

Package Sidebar

Install

npm i simple-cli

Weekly Downloads

13,880

Version

5.0.5

License

MIT

Unpacked Size

68.6 kB

Total Files

26

Last publish

Collaborators

  • tandrewnichols