Socket
Socket
Sign inDemoInstall

cmdln

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.0.2 to 3.1.0

43

CHANGES.md
# node-cmdln Changelog
## 3.1.0
- [issue #5] Add `helpSubcmds` constructor option to allow control over the
output of the "Commands:" section of top-level help. For example, this code:
helpSubcmds: [
'help',
{ group: '' },
'in-empty-group',
{ group: 'Most Excellent Commands' },
'awesome',
{ group: 'Other Commands', unmatched: true }
]
yields help output something like:
...
Commands:
help (?) Help on a specific sub-command.
in-empty-group Do in-empty-group things.
Most Excellent Commands:
awesome Do awesome things.
Other Commands:
something-else Do something-else things.
By Josh Clulow.
- [issue #4] Add `Cmdln.prototype.helpFromSubcmd(subcmd)` to get the help string
for the given subcommand. This can be useful for tools that want to emit usage
information as part of a usage error. E.g.:
MyCLI.prototype.do_frob = do_frob(subcmd, opts, args, cb) {
if (!opts.frobber) {
return callback(new Error('you forgot the frobber\n' +
this.helpFromSubcmd(subcmd)));
}
// ...
};
## 3.0.2

@@ -4,0 +47,0 @@

189

lib/cmdln.js
/*
* Copyright (c) 2014, Trent Mick. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2015 Joyent, Inc.
*/
var DEBUG = false;

@@ -166,2 +167,16 @@ if (DEBUG) {

* help output.
* - @param helpSubcmds {Array} Control the output of the "Commands:"
* section of top-level help output. By default all commands are
* listed in the order defined in code. `helpSubcmds` allows one to
* specify the order and, optionally, groupings with group headers. E.g.:
* helpSubcmds: [
* 'help',
* { group: '' }, // an empty group, results in a blank line
* 'foo',
* 'bar',
* { group: 'Bling' }, // a group header
* 'bloom',
* // Use `unmatched: true` to include remaining subcmds.
* { group: 'Other Commands', unmatched: true }
* ]
* - @param options {Array} Custom options (in the format used by

@@ -188,2 +203,3 @@ * [dashdash](https://github.com/trentm/node-dashdash)). If not

assert.optionalString(config.helpBody, 'config.helpBody')
assert.optionalObject(config.helpSubcmds, 'config.helpSubcmds');

@@ -195,2 +211,3 @@ this.name = config.name || this.constructor.name.toLowerCase();

this.helpBody = config.helpBody;
this.helpSubcmds = config.helpSubcmds || null;
if (!this.helpOpts.indent)

@@ -200,2 +217,8 @@ this.helpOpts.indent = space(4);

this.helpOpts.indent = space(this.helpOpts.indent);
if (!this.helpOpts.groupIndent) {
var gilen = Math.round(this.helpOpts.indent.length / 2);
this.helpOpts.groupIndent = space(gilen);
} else if (typeof (this.helpOpts.groupIndent) === 'number') {
this.helpOpts.groupIndent = space(this.helpOpts.groupIndent);
}
if (!this.helpOpts.maxCol) this.helpOpts.maxCol = 80;

@@ -220,3 +243,3 @@ if (!this.helpOpts.minHelpCol) this.helpOpts.minHelpCol = 20;

// Load subcmds (do_* methods) and aliases (`do_*.aliases`).
this._subcmdOrder = [];
var enumOrder = [];
this._handlerFromName = {};

@@ -281,3 +304,3 @@ this._nameFromAlias = {};

}
self._subcmdOrder.push(name);
enumOrder.push(name);
self._nameFromAlias[name] = name;

@@ -289,2 +312,63 @@ (func.aliases || []).forEach(function (alias) {

});
if (self.helpSubcmds !== null) {
/*
* Reconcile the provided subcommand order (and group headings) with
* the discovered options.
*/
var unmatchedNames = [];
var matchedNames = [];
enumOrder.forEach(function (enumName) {
if (self.helpSubcmds.indexOf(enumName) === -1) {
unmatchedNames.push(enumName);
} else {
matchedNames.push(enumName);
}
});
var unmatchCount = 0;
self._subcmdOrder = [];
self.helpSubcmds.forEach(function (sc) {
if (typeof (sc) === 'object') {
assert.string(sc.group, 'helpSubcmds.*.group');
assert.optionalBool(sc.unmatched, 'helpSubcmds.*.unmatched');
self._subcmdOrder.push(sc);
if (sc.unmatched) {
if (++unmatchCount > 1) {
throw (new Error(format('"unmatched" directive used ' +
'more than once in "helpSubcmds" option: %j', sc)));
}
/*
* Include all of the unmatched names here:
*/
while (unmatchedNames.length > 0) {
self._subcmdOrder.push(unmatchedNames.shift());
}
}
return;
}
/*
* If this is not a group heading object, it must be the name
* of a handler to include in the output:
*/
assert.string(sc);
if (matchedNames.indexOf(sc) === -1) {
throw (new Error('command handler included in help order ' +
'but not found: ' + sc));
}
self._subcmdOrder.push(sc);
});
if (unmatchedNames.length > 0) {
throw (new Error('"helpSubcmds" error: unmatched command ' +
'handlers found: ' + unmatchedNames.join(', ') + '.'));
}
} else {
self._subcmdOrder = enumOrder;
}
// p('_subcmdOrder:', this._subcmdOrder);

@@ -416,2 +500,3 @@ // p('_handlerFromName: ', this._handlerFromName);

var indent = helpOpts.indent;
var gindent = helpOpts.groupIndent;

@@ -444,3 +529,25 @@ var lines = [];

indent, helpOpts.minHelpCol - indent.length - 2);
this._subcmdOrder.forEach(function (name) {
this._subcmdOrder.forEach(function (name, idx) {
if (typeof (name) === 'object') {
if (idx > 0) {
/*
* If this is not the first line, print a blank line to
* visually separate this group from previous lines.
*/
lines.push('');
}
/*
* If the group name is not blank, print the group heading.
* If it is blank, the caller only wants the separator line
* printed above.
*/
assert.string(name.group, 'name.group');
if (name.group) {
lines.push(format('%s%s:', gindent, name.group));
}
return;
}
assert.string(name, 'name');
var handler = self._handlerFromName[name];

@@ -454,5 +561,5 @@ if (handler.hidden) {

}
var summary = handler.desc
|| (typeof (handler.help) === 'string' && handler.help)
|| '';
var summary = handler.desc ||
(typeof (handler.help) === 'string' && handler.help) ||
'';
summary = summary.split('\n', 1)[0]; // just leading line

@@ -491,2 +598,42 @@ summary = summary.replace(/{{name}}/g, self.name);

/**
* Return the help content for the given sub-command string (aka the
* subcmd *name*).
*
* *Limitation*: If the command has a `.help` *function*, then the function is
* returned. It is up to the caller to call it, if they like. The `.help`
* function is defined to be async and is *not* defined to return the
* string, so running it isn't useful here.
*
* @param alias {String} The sub-command name or alias.
* @throws `UnknownCommandError` if there is no such sub-command.
* @returns The help string, `null` if no help, or the `.help` function if
* so defined for that sub-command.
*/
Cmdln.prototype.helpFromSubcmd = function helpFromSubcmd(alias) {
var handler = this.handlerFromSubcmd(alias);
if (!handler) {
throw new UnknownCommandError(alias);
}
if (!handler.help) {
return;
} else if (typeof (handler.help) === 'function') {
return handler.help;
} else {
var help = handler.help;
help = help.replace(/{{name}}/g, this.name);
if (~help.indexOf('{{options}}') && handler.options) {
var parser = new dashdash.Parser({options: handler.options});
var helpOpts = (handler.helpOpts
? objMerge(this.helpOpts, handler.helpOpts) : this.helpOpts);
help = help.replace('{{options}}',
'Options:\n' + parser.help(helpOpts));
}
help = help.trimRight();
return help;
}
};
/**
* Dispatch to the appropriate "do_SUBCMD" function.

@@ -518,3 +665,2 @@ */

Cmdln.prototype.do_help = function do_help(subcmd, opts, args, callback) {
var self = this;
if (args.length === 0) {

@@ -525,4 +671,4 @@ this.printHelp(callback);

var alias = args[0];
var name = this._nameFromAlias[alias];
if (!name) {
var handler = this.handlerFromSubcmd(alias);
if (!handler) {
callback(new UnknownCommandError(alias));

@@ -532,18 +678,13 @@ return;

var func = this._handlerFromName[name];
if (!func.help) {
try {
var help = this.helpFromSubcmd(alias);
} catch (e) {
callback(e);
}
if (!help) {
callback(new CmdlnError({message: format('no help for "%s"', alias)}));
} else if (typeof (func.help) === 'function') {
func.help(subcmd, opts, args, callback);
} else if (typeof (help) === 'function') {
help(subcmd, opts, args, callback);
} else {
var help = func.help;
help = help.replace(/{{name}}/g, self.name);
if (~help.indexOf('{{options}}') && func.options) {
var parser = new dashdash.Parser({options: func.options});
var helpOpts = (func.helpOpts
? objMerge(this.helpOpts, func.helpOpts) : this.helpOpts);
help = help.replace('{{options}}',
'Options:\n' + parser.help(helpOpts));
}
console.log(help.trimRight());
console.log(help);
callback();

@@ -550,0 +691,0 @@ }

2

package.json
{
"name": "cmdln",
"version": "3.0.2",
"version": "3.1.0",
"description": "helper lib for creating CLI tools with subcommands; think `git`, `svn`, `zfs`",

@@ -5,0 +5,0 @@ "author": "Trent Mick (http://trentm.com)",

@@ -203,3 +203,7 @@ `node-cmdln` is a node.js helper lib for creating CLI tools with subcommands

- `<Cmdln>.helpFromSubcmd(<subcmd>)` 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.
## `cmdln.main()`

@@ -206,0 +210,0 @@

# 2.1?
- support for sub-subcommands
- bash completion generation support

@@ -5,0 +4,0 @@ - docs: see below

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc