mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-17 14:00:29 +01:00
feat: upgrade commander, get rid of custom color & wrapping code
This commit is contained in:
@@ -54,7 +54,7 @@
|
|||||||
"cli-graph": "3.2.2",
|
"cli-graph": "3.2.2",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"colors": "1.4.0",
|
"colors": "1.4.0",
|
||||||
"commander": "12.1.0",
|
"commander": "13.1.0",
|
||||||
"compare-versions": "6.1.1",
|
"compare-versions": "6.1.1",
|
||||||
"compression": "1.8.0",
|
"compression": "1.8.0",
|
||||||
"connect-flash": "0.1.1",
|
"connect-flash": "0.1.1",
|
||||||
|
|||||||
@@ -1,160 +1,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// override commander help formatting functions
|
// todo: replace with styleText from node:util in node 20+
|
||||||
// to include color styling in the output
|
|
||||||
// so the CLI looks nice
|
|
||||||
|
|
||||||
const { Command } = require('commander');
|
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
|
|
||||||
const colors = [
|
// https://github.com/tj/commander.js/blob/master/examples/color-help.mjs
|
||||||
// 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}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getControlCharacterSpaces(term) {
|
|
||||||
const matches = term.match(/.\[\d+m/g);
|
|
||||||
return matches ? matches.length * 5 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = {
|
module.exports = {
|
||||||
commandUsage(cmd) {
|
styleTitle: str => chalk.bold(str),
|
||||||
const depth = cmd.depth();
|
styleCommandText: str => chalk.cyan(str),
|
||||||
|
styleCommandDescription: str => chalk.magenta(str),
|
||||||
// Usage
|
styleDescriptionText: str => chalk.italic(str),
|
||||||
let cmdName = cmd._name;
|
styleOptionText: str => chalk.green(str),
|
||||||
if (cmd._aliases[0]) {
|
styleArgumentText: str => chalk.yellow(str),
|
||||||
cmdName = `${cmdName}|${cmd._aliases[0]}`;
|
styleSubcommandText: str => chalk.blue(str),
|
||||||
}
|
|
||||||
let parentCmdNames = '';
|
|
||||||
let parentCmd = cmd.parent;
|
|
||||||
let parentDepth = depth - 1;
|
|
||||||
while (parentCmd) {
|
|
||||||
parentCmdNames = `${chalk[colors[parentDepth].command](parentCmd.name())} ${parentCmdNames}`;
|
|
||||||
|
|
||||||
parentCmd = parentCmd.parent;
|
|
||||||
parentDepth -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// from Command.prototype.usage()
|
|
||||||
const args = cmd._args.map(arg => chalk[colors[depth].arg](humanReadableArgName(arg)));
|
|
||||||
const cmdUsage = [].concat(
|
|
||||||
(cmd.options.length || cmd._hasHelpOption ? chalk[colors[depth].option]('[options]') : []),
|
|
||||||
(cmd.commands.length ? chalk[colors[depth + 1].command]('[command]') : []),
|
|
||||||
(cmd._args.length ? args : [])
|
|
||||||
).join(' ');
|
|
||||||
|
|
||||||
return `${parentCmdNames}${chalk[colors[depth].command](cmdName)} ${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 chalk[colors[depth].command](cmd._name + (
|
|
||||||
cmd._aliases[0] ? `|${cmd._aliases[0]}` : ''
|
|
||||||
)) +
|
|
||||||
chalk[colors[depth].option](cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
|
|
||||||
chalk[colors[depth].arg](args ? ` ${args}` : '');
|
|
||||||
},
|
|
||||||
longestOptionTermLength(cmd, helper) {
|
|
||||||
return helper.visibleOptions(cmd).reduce((max, option) => Math.max(
|
|
||||||
max,
|
|
||||||
helper.optionTerm(option).length - getControlCharacterSpaces(helper.optionTerm(option))
|
|
||||||
), 0);
|
|
||||||
},
|
|
||||||
longestSubcommandTermLength(cmd, helper) {
|
|
||||||
return helper.visibleCommands(cmd).reduce((max, command) => Math.max(
|
|
||||||
max,
|
|
||||||
helper.subcommandTerm(command).length - getControlCharacterSpaces(helper.subcommandTerm(command))
|
|
||||||
), 0);
|
|
||||||
},
|
|
||||||
longestArgumentTermLength(cmd, helper) {
|
|
||||||
return helper.visibleArguments(cmd).reduce((max, argument) => Math.max(
|
|
||||||
max,
|
|
||||||
helper.argumentTerm(argument).length - getControlCharacterSpaces(helper.argumentTerm(argument))
|
|
||||||
), 0);
|
|
||||||
},
|
|
||||||
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) {
|
|
||||||
const padding = ' '.repeat((termWidth + itemSeparatorWidth) - (term.length - getControlCharacterSpaces(term)));
|
|
||||||
if (description) {
|
|
||||||
const fullText = `${term}${padding}${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(
|
|
||||||
chalk[colors[depth].arg](argument.term),
|
|
||||||
argument.description
|
|
||||||
));
|
|
||||||
if (argumentList.length > 0) {
|
|
||||||
output = output.concat(['Arguments:', formatList(argumentList), '']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options
|
|
||||||
const optionList = helper.visibleOptions(cmd).map(option => formatItem(
|
|
||||||
chalk[colors[depth].option](helper.optionTerm(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');
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user