fix: ./nodebb help with commander@7 ()

hopefully this one last as long as the last one did
v1.18.x
Peter Jaszkowiak committed by GitHub
parent dfdb005099
commit 2a03012e2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,58 +1,25 @@
'use strict';
// override commander functions
// override commander help formatting functions
// to include color styling in the output
// so the CLI looks nice
const { Command } = require('commander');
const commandColor = 'yellow';
const optionColor = 'cyan';
const argColor = 'magenta';
const subCommandColor = 'green';
const subOptionColor = 'blue';
const subArgColor = 'red';
Command.prototype.helpInformation = function () {
let desc = [];
if (this._description) {
desc = [
` ${this._description}`,
'',
];
}
let cmdName = this._name;
if (this._alias) {
cmdName = `${cmdName} | ${this._alias}`;
}
const usage = [
'',
` Usage: ${cmdName[commandColor]}${' '.reset}${this.usage()}`,
'',
];
let cmds = [];
const commandHelp = this.commandHelp();
if (commandHelp) {
cmds = [commandHelp];
}
const options = [
'',
' Options:',
'',
`${this.optionHelp().replace(/^/gm, ' ')}`,
'',
];
return usage
.concat(desc)
.concat(options)
.concat(cmds)
.join('\n'.reset);
};
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',
},
];
function humanReadableArgName(arg) {
const nameOutput = arg.name + (arg.variadic === true ? '...' : '');
@ -60,58 +27,119 @@ function humanReadableArgName(arg) {
return arg.required ? `<${nameOutput}>` : `[${nameOutput}]`;
}
Command.prototype.usage = function () {
const args = this._args.map(arg => humanReadableArgName(arg));
// 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; }
const usage = '[options]'[optionColor] +
(this.commands.length ? ' [command]' : '')[subCommandColor] +
(this._args.length ? ` ${args.join(' ')}` : '')[argColor];
return usage;
};
function pad(str, width) {
const len = Math.max(0, width - str.length);
return str + Array(len + 1).join(' ');
}
Command.prototype.commandHelp = function () {
if (!this.commands.length) {
return '';
this._depth = depth;
}
const commands = this.commands.filter(cmd => !cmd._noHelp).map((cmd) => {
const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
return [
`${cmd._name[subCommandColor] +
(cmd._alias ? ` | ${cmd._alias}` : '')[subCommandColor] +
(cmd.options.length ? ' [options]' : '')[subOptionColor]
} ${args[subArgColor]}`,
cmd._description,
];
});
const width = commands.reduce((max, command) => Math.max(max, command[0].length), 0);
return [
'',
' Commands:',
'',
commands.map((cmd) => {
const desc = cmd[1] ? ` ${cmd[1]}` : '';
return pad(cmd[0], width) + desc;
}).join('\n').replace(/^/gm, ' '),
'',
].join('\n');
return this._depth;
};
Command.prototype.optionHelp = function () {
const width = this.largestOptionLength();
// Append the help information
return this.options
.map(option => `${pad(option.flags, width)[optionColor]} ${option.description}`)
.concat([`${pad('-h, --help', width)[optionColor]} output usage information`])
.join('\n');
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');
},
};

@ -77,6 +77,8 @@ const pkg = require('../../package.json');
const file = require('../file');
const prestart = require('../prestart');
program.configureHelp(require('./colors'));
program
.name('./nodebb')
.description('Welcome to NodeBB')
@ -305,8 +307,6 @@ program
}
});
require('./colors');
if (process.argv.length === 2) {
program.help();
}

Loading…
Cancel
Save