You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.3 KiB
JavaScript
142 lines
4.3 KiB
JavaScript
'use strict';
|
|
|
|
// override commander help formatting functions
|
|
// to include color styling in the output
|
|
// so the CLI looks nice
|
|
|
|
const { Command, Help } = require('commander');
|
|
|
|
const colors = [
|
|
// depth = 0, top-level command
|
|
{ command: 'yellow', option: 'cyan', arg: 'magenta' },
|
|
// depth = 1, second-level commands
|
|
{ command: 'green', option: 'blue', arg: 'red' },
|
|
// depth = 2, third-level commands
|
|
{ command: 'yellow', option: 'cyan', arg: 'magenta' },
|
|
// depth = 3 fourth-level commands
|
|
{ command: 'green', option: 'blue', arg: 'red' },
|
|
];
|
|
|
|
function humanReadableArgName(arg) {
|
|
const nameOutput = arg.name + (arg.variadic === true ? '...' : '');
|
|
|
|
return arg.required ? `<${nameOutput}>` : `[${nameOutput}]`;
|
|
}
|
|
|
|
// get depth of command
|
|
// 0 = top, 1 = subcommand of top, etc
|
|
Command.prototype.depth = function () {
|
|
if (this._depth === undefined) {
|
|
let depth = 0;
|
|
let { parent } = this;
|
|
while (parent) { depth += 1; parent = parent.parent; }
|
|
|
|
this._depth = depth;
|
|
}
|
|
return this._depth;
|
|
};
|
|
|
|
module.exports = {
|
|
commandUsage(cmd) {
|
|
const depth = cmd.depth();
|
|
|
|
// Usage
|
|
let cmdName = cmd._name;
|
|
if (cmd._aliases[0]) {
|
|
cmdName = `${cmdName}|${cmd._aliases[0]}`;
|
|
}
|
|
let parentCmdNames = '';
|
|
let parentCmd = cmd.parent;
|
|
let parentDepth = depth - 1;
|
|
while (parentCmd) {
|
|
parentCmdNames = `${parentCmd.name()[colors[parentDepth].command]} ${parentCmdNames}`;
|
|
|
|
parentCmd = parentCmd.parent;
|
|
parentDepth -= 1;
|
|
}
|
|
|
|
// from Command.prototype.usage()
|
|
const args = cmd._args.map(arg => humanReadableArgName(arg)[colors[depth].arg]);
|
|
const cmdUsage = [].concat(
|
|
(cmd.options.length || cmd._hasHelpOption ? '[options]'[colors[depth].option] : []),
|
|
(cmd.commands.length ? '[command]'[colors[depth + 1].command] : []),
|
|
(cmd._args.length ? args : [])
|
|
).join(' ');
|
|
|
|
return `${parentCmdNames}${cmdName[colors[depth].command]} ${cmdUsage}`;
|
|
},
|
|
subcommandTerm(cmd) {
|
|
const depth = cmd.depth();
|
|
|
|
// Legacy. Ignores custom usage string, and nested commands.
|
|
const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
|
|
return (cmd._name + (
|
|
cmd._aliases[0] ? `|${cmd._aliases[0]}` : ''
|
|
))[colors[depth].command] +
|
|
(cmd.options.length ? ' [options]' : '')[colors[depth].option] + // simplistic check for non-help option
|
|
(args ? ` ${args}` : '')[colors[depth].arg];
|
|
},
|
|
longestOptionTermLength(cmd, helper) {
|
|
return Help.prototype.longestOptionTermLength.call(this, cmd, helper) + ''.red.length;
|
|
},
|
|
longestArgumentTermLength(cmd, helper) {
|
|
return Help.prototype.longestArgumentTermLength.call(this, cmd, helper) + ''.red.length;
|
|
},
|
|
formatHelp(cmd, helper) {
|
|
const depth = cmd.depth();
|
|
|
|
const termWidth = helper.padWidth(cmd, helper);
|
|
const helpWidth = helper.helpWidth || 80;
|
|
const itemIndentWidth = 2;
|
|
const itemSeparatorWidth = 2; // between term and description
|
|
function formatItem(term, description) {
|
|
if (description) {
|
|
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
|
|
return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);
|
|
}
|
|
return term;
|
|
}
|
|
function formatList(textArray) {
|
|
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
|
|
}
|
|
|
|
// Usage
|
|
let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
|
|
|
|
// Description
|
|
const commandDescription = helper.commandDescription(cmd);
|
|
if (commandDescription.length > 0) {
|
|
output = output.concat([commandDescription, '']);
|
|
}
|
|
|
|
// Arguments
|
|
const argumentList = helper.visibleArguments(cmd).map(argument => formatItem(
|
|
argument.term[colors[depth].arg],
|
|
argument.description
|
|
));
|
|
if (argumentList.length > 0) {
|
|
output = output.concat(['Arguments:', formatList(argumentList), '']);
|
|
}
|
|
|
|
// Options
|
|
const optionList = helper.visibleOptions(cmd).map(option => formatItem(
|
|
helper.optionTerm(option)[colors[depth].option],
|
|
helper.optionDescription(option)
|
|
));
|
|
if (optionList.length > 0) {
|
|
output = output.concat(['Options:', formatList(optionList), '']);
|
|
}
|
|
|
|
// Commands
|
|
const commandList = helper.visibleCommands(cmd).map(cmd => formatItem(
|
|
helper.subcommandTerm(cmd),
|
|
helper.subcommandDescription(cmd)
|
|
));
|
|
if (commandList.length > 0) {
|
|
output = output.concat(['Commands:', formatList(commandList), '']);
|
|
}
|
|
|
|
return output.join('\n');
|
|
},
|
|
};
|