Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/trentm/node-cmdln
node.js helper lib for creating CLI tools with subcommands; think `git`, `svn`, `zfs`
https://github.com/trentm/node-cmdln
Last synced: about 1 month ago
JSON representation
node.js helper lib for creating CLI tools with subcommands; think `git`, `svn`, `zfs`
- Host: GitHub
- URL: https://github.com/trentm/node-cmdln
- Owner: trentm
- License: other
- Created: 2013-02-08T07:39:06.000Z (almost 12 years ago)
- Default Branch: master
- Last Pushed: 2023-03-04T04:31:51.000Z (almost 2 years ago)
- Last Synced: 2024-04-14T22:24:38.530Z (9 months ago)
- Language: JavaScript
- Size: 543 KB
- Stars: 31
- Watchers: 3
- Forks: 7
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGES.md
- License: LICENSE.txt
Awesome Lists containing this project
README
`node-cmdln` is a node.js helper lib for creating CLI tools with subcommands
(think `git`, `svn`, `zfs`, `brew`, etc.). It is a sister of my earlier
[Python lib for this](https://github.com/trentm/cmdln).# Usage
You define a subclass of `Cmdln` and subcommands as `do_NAME` methods.
Minimally you could have a "conan.js" as follows:```javascript
#!/usr/bin/env node
var util = require('util');
var cmdln = require('cmdln');function Conan() {
cmdln.Cmdln.call(this, {
name: 'conan',
desc: 'What is best in life?'
});
}
util.inherits(Conan, cmdln.Cmdln);Conan.prototype.do_crush = function do_crush(subcmd, opts, args, cb) {
console.log('Yargh!');
cb();
};
Conan.prototype.do_crush.help = 'Crush your enemies.';cmdln.main(new Conan()); // mainline
```With this, you get the following behaviour:
```bash
$ node examples/conan.js
What is best in life?Usage:
conan [OPTIONS] COMMAND [ARGS...]
conan help COMMANDOptions:
-h, --help Show this help message and exit.Commands:
help (?) Help on a specific sub-command.
crush Crush your enemies.$ node examples/conan.js help crush
Crush your enemies.$ node examples/conan.js crush
Yargh!
```# Option processing
Option processing (using [dashdash](https://github.com/trentm/node-dashdash))
is integrated. `do_crush` above could be replaced with:```javascript
Conan.prototype.do_crush = function (subcmd, opts, args, cb) {
if (opts.help) {
this.do_help('help', {}, [subcmd], cb);
return;
}
if (!args.length) {
console.log('No enemies? Yarg!');
} else {
args.forEach(function (enemy) {
console.log('Smite %s with a %s!', enemy, opts.weapon);
});
}
cb();
};
Conan.prototype.do_crush.options = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Show this help.'
},
{
names: ['weapon', 'w'],
helpArg: 'WEAPON',
type: 'string',
default: 'sword',
help: 'Weapon with which to smite.'
}
];
Conan.prototype.do_crush.help = (
'Crush your enemies.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} {{cmd}} [OPTIONS] [ENEMIES...]\n'
+ '\n'
+ '{{options}}'
);
```Then we get this behaviour:
```bash
$ node examples/conan.js crush Bob
Smite Bob with a sword!$ node examples/conan.js crush Bob Linda --weapon mattock
Smite Bob with a mattock!
Smite Linda with a mattock!$ node examples/conan.js crush -h
Crush your enemies.Usage:
conan crush [OPTIONS] [ENEMIES...]Options:
-h, --help Show this help.
-w WEAPON, --weapon=WEAPON Weapon with which to smite.
```See [examples/conan.js](examples/conan.js) for the complete example. Run
`node example/conan.js ...` to try it out.# Bash completion
One can generate Bash completion code for a `Cmdln` subclass via
cli.bashCompletion()
One possible usage is to add a `completion` subcmd to your CLI:
CLI.prototype.do_completion = function (subcmd, opts, args, cb) {
console.log( this.bashCompletion() );
cb();
};and get users to use that to setup Bash completion:
$ alias conan="node examples/conan.js"
$ conan completion > conan.completion
$ source conan.completion$ conan
crush hear help pulverize see smash
$ conan -
--help --verbose --version -h -v -x
$ conan crush --weapon # custom 'weapon' completion type
bow-and-array mattock spear sword
$ conan crush --weapon spear # custom 'enemy' completion type
King-Osric Subotai Thulsa-Doom _mbsetupuser trentmSee the `do_completion` subcommand on "examples/conan.js" for a complete example
of this. See the equivalent in the larger `triton` tool for another example:
.Another potential usage could be to pre-generate a completion file and
distribute it with your tool.# Reference
In general, also please read the comments in [the source](./lib/cmdln.js) and
[browse the examples](./examples/).## `cmdln.Cmdln`
To use this module you create a class that inherits from `cmdln.Cmdln`; add
some methods to that class that define the tool's commands, options, etc.;
then pass an instance to `cmdln.main()`. Roughly like this:function CLI() {
cmdln.Cmdln.call(this, {});
}
util.inherits(CLI, cmdln.Cmdln);
...
var cli = new CLI();
cmdln.main(cli);We'll use the `CLI` and `cli` names as used above in the following reference:
- `new Cmdln()` Create a Cmdln subclass instance. See the block comment
in the code for full documentation on the `config` options.- `CLI.prototype.do_ = function (subcmd, opts, args, cb)` is how a
subcommand is defined. How the subcmd is handled can be customize with some
properties (e.g. `options`, `help`) on the handler function.- `CLI.prototype.do_ = ;` Instead of a function handler for a
subcommand, a `do_` can be set to another Cmdln subclass to support
sub-subcommands, like `git remote add|remove|rename|...`. See
["examples/fauxgit.js"](./examples/fauxgit.js) for an example.- `CLI.prototype.do_.aliases = ;` to define one or
more aliases for a command. These aliases are shown in the "Commands:"
section of the generated help output.- `CLI.prototype.do_.hiddenAliases = ;` to define one
or more aliases for a command **that are not shown in the generated help
output**. This can be useful when renaming a subcommand in a new version of
a tool and still support the old name.- `CLI.prototype.do_.options = ;` is how to set the options
(in [dashdash](https://github.com/trentm/node-dashdash) format) for that
subcommand.- `CLI.prototype.do_.synopses = ;`
Set to the synopsis string(s) for this command, i.e. the part typically
in the "SYNOPSIS" section of a man page. See
[examples/conan.js](examples/conan.js). This supports some template variables:- `{{name}}` becomes `cli.name` (i.e. the tool name).
- `{{cmd}}` becomes the sub-command name.Setting `synopses` can be used (a) for the `{{usage}}` template var in
subcmd help (see below) and (b) for `errHelp` for `UsageError`s (see below).- `CLI.prototype.do_.helpOpts = ;` to override
formatting settings for `options` help output for this command. By default
the `helpOpts` passed into the CLI constructor are used. The set of supported
helpOpts are defined by
[dashdash](https://github.com/trentm/node-dashdash#help-config).- `CLI.prototype.do_.help = ;` to set the help string for a
subcommand. This supports some template variables:- `{{name}}` becomes `cli.name` (i.e. the tool name).
- `{{cmd}}` becomes the sub-command name.
- `{{usage}}` becomes a "Usage:\n $synopses" block if `synopses` are
defined (see above).
- `{{options}}` becomes a "Options:\n $option-help" block if `options`
are provided for the subcmd (see above).- `CLI.prototype.do_.help = function (subcmd, opts, args, cb)` is
an alternate method to handle help for a subcommand. The given function
will be run when `tool help ` is called.- `CLI.prototype.do_.desc = ;` can be set to a short string
to be used in the `tool help` output to summarize subcmd. If not provided,
then the first line of `do_.help` will be used.- `CLI.prototype.do_.hidden = ;` Set to false to have
`tool help` output *not* list this subcmd.- `CLI.prototype.do_.interspersedOptions = ;` Set to
false to have `tool ...` not allow interspersed options
(i.e. options after the first argument).- `CLI.prototype.do_.allowUnknownOptions = ;` Set to
true to have `tool ...` allow unknown options.- `CLI.prototype.do_.completionArgtypes = ;` Set to an array
of strings to define the [Bash completion](#bash-completion) type for the
corresponding positional arg. For example, the following:
MyCLI.prototype.do_foo.completionArgtypes = ['fruit', 'file'];
would mean that `mycli foo ` would complete "fruit" (using a
`complete_fruit` bash function, typically provided via the `specExtra`
arg to `.bashCompletion()`) and the second and subsequent positional
args -- `mycli foo banana ` -- would use filename completion.- `CLI.prototype.init(opts, args, cb)` Hook run after option processing
(`this.opts` is set), but before the subcommand handler is run.- `CLI.prototype.fini(subcmd, err, cb)` Hook run after the subcommand handler is
run. Here `err` is the error returned by the invocation of the CLI. This allows
a `fini` method to use or deal with that error, if necessary. To just
pass that err on (to the calling `main`) do this:CLI.prototype.fini = function fini(subcmd, err, cb) {
// Whatever finalization you want to do here (possibly with a
// `finiErr`) ...
cb(finiErr || err, subcmd);
};(Note: The call signature to `fini` changed in cmdln v3. See the changelog
in CHANGES.md.)- `CLI.prototype.defaultHandler(subcmd, opts, args, cb)` This is a hook
function to handle an unknown option. By default it will callback with
`UnknownCommandError(subcmd)`. It can be overriden, for example as
follows:/*
* Provide the `jirash KEY-1` shortcut for `jirash issue get KEY-1`.
*/
JirashCli.prototype.defaultHandler = function defaultHandler(
subcmd, opts, args, cb) {
var keyRe = /^[A-Z]+-\d+$/;
if (keyRe.test(subcmd)) {
this.handlerFromSubcmd('issue').dispatch({
subcmd: 'get',
opts: {'short': true},
args: [subcmd]
}, cb);
} else {
Cmdln.prototype.defaultHandler.call(this, subcmd, opts, args, cb);
}
};- `cli.showErrStack` boolean. Set to true to have `cmdln.main()`, if used,
print a full stack on a shown error. A common pattern of mine is to set
this in the `.init()` method if a top-level `-v,--verbose` option is given.- `cli.handlerFromSubcmd()` will return the appropriate
`do_` method that handles the given sub-command. This resolves
sub-command aliases.- `cli.helpFromSubcmd()` will return the help string for
that subcmd *or*, if defined, the help function defined for that subcmd.
This is used by the default `do_help` implementation.- `cli.bashCompletion()` generates and returns bash completion for
the CLI.## `cmdln.main()`
This is a convenience method for driving the mainline of your script using
the your defined `Cmdln` subclass. There are a number of options to control
how it works. Read the block comment on that function in "lib/cmdln.js" for
the best docs.## `errHelp` and Errors
cmdln v4 introduced subcmd *synopses*, *`errHelp`*, and some related
functionality to help provide brief automatic command help for some usage
errors. `errHelp` is a brief message after a printed error, giving potentially
helpful info. Some examples from familiar commands (marked here with `>`):$ ls -D
ls: illegal option -- D
> usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]$ git foo
git: 'foo' is not a git command. See 'git --help'.> Did you mean this?
> foUse the following suggestions to get this kind of error-help for your commands:
1. Optionally set `synopses` on your subcmd handlers. E.g.:
do_list.synopses = ['{{name}} list [OPTIONS] FILTERS...'];
Doing so allows two things: (a) the use of the `{{usage}}` template var
in your command help, and (b) use of those synopses for `errHelp`.2. Optionally use the `{{usage}}` template var in your command help. E.g.:
do_list.help = [
'List instances.',
'',
'{{usage}}',
'',
'{{options}}'
].join('\n');3. Optionally use the `cmdln.UsageError` error class for usage errors in
your subcmds. E.g.:function do_list(subcmd, opts, args, callback) {
// ...
} else if (args.length < 1) {
callback(new cmdln.UsageError('missing FILTER args'));
return;
}4. Use `cmdln.main()` for your mainline
This will now attempt to determine `errHelp` from any returned error and
print it on stderr -- use `options.showErrHelp=false` to disable. Or if
you are not using `cmdln.main()`, then you can use
`cmdln.errHelpFromErr(err)` to get errHelp to print, if you like.Error help is determined by calling `err.cmdlnErrHelpFromErr()`, which is
implemented for cmdln's error classes:- `cmdln.OptionError`: Show a synopsis of the command's options.
- `cmdln.UsageError`: Show the command's synopses, if available.
- `cmdln.UnknownCommandError`: List possible fuzzy matches.You can implement that method for custom error classes if you like.
## `cmdln.dashdash`
This is a re-export of the [dashdash](https://github.com/trentm/node-dashdash)
option processing module that cmdln is using. This is exported so that calling
code can add option *types* if wanted, via `cmdln.dashdash.addOptionType`. E.g.,var cmdln = require('cmdln');
function parseCommaSepStringNoEmpties(option, optstr, arg) {
return arg.trim().split(/\s*,\s*/g)
.filter(function (part) { return part; });
}cmdln.dashdash.addOptionType({
name: 'commaSepString',
takesArg: true,
helpArg: 'STRING',
parseArg: parseCommaSepStringNoEmpties
});// ...
See [the node-dashdash documentation](https://github.com/trentm/node-dashdash#custom-option-types)
for details.# License
MIT. See LICENSE.txt