[](https://travis-ci.org/tandrewnichols/simple-cli) [](https://npmjs.org/package/simple-cli) [](https://npmjs.org/package/simple-cli) [](https://codeclimate.com/github/tandrewnichols/simple-cli) [](https://codeclimate.com/github/tandrewnichols/simple-cli) [](https://david-dm.org/tandrewnichols/simple-cli)
# simple-cli
Gruntify command-line APIs with ease.
## Installation
```bash
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](#cmd-1).
`options` is an object that configures how simple-cli handles the executable. See [options](#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:
```js
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):
```js
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.
```js
grunt.initConfig({
blerg: {
shazzam: {
options: {
foo: 'bar'
}
}
}
});
```
This will run `blerg shazzam --foo bar`.
#### Multi-word options
Multi-word options work too.
```js
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.
```js
grunt.initConfig({
blerg: {
jazzhands: {
options: {
banana: true
}
}
}
});
```
This will run `blerg jazzhands --banana`.
#### Short options
You can also use short options.
```js
grunt.initConfig({
blerg: {
wasabi: {
options: {
a: 'foo'
}
}
}
});
```
This will run `blerg wasabi -a foo`.
#### Short boolean options
And short options as booleans.
```js
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.
```js
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.
```js
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](#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`).
```js
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`.
```js
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.
```js
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.
```js
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).
```js
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.
```js
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.
```js
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.
```js
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.
```js
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.
```js
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:
```js
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.
```js
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.
```js
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.
```js
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 `.
```js
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. `/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.
```js
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`
```js
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.
```js
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:
```js
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:
```js
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.
```js
const cli = require('simple-cli');
module.exports = cli('nyc', {
standalone: true,
flags: 'before'
});
```
That will allow users to do this:
```js
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](#cmd).