| const { humanReadableArgName } = require('./argument.js');
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
| class Help {
|
| constructor() {
|
| this.helpWidth = undefined;
|
| this.sortSubcommands = false;
|
| this.sortOptions = false;
|
| this.showGlobalOptions = false;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| visibleCommands(cmd) {
|
| const visibleCommands = cmd.commands.filter((cmd) => !cmd._hidden);
|
| const helpCommand = cmd._getHelpCommand();
|
| if (helpCommand && !helpCommand._hidden) {
|
| visibleCommands.push(helpCommand);
|
| }
|
| if (this.sortSubcommands) {
|
| visibleCommands.sort((a, b) => {
|
|
|
| return a.name().localeCompare(b.name());
|
| });
|
| }
|
| return visibleCommands;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
| compareOptions(a, b) {
|
| const getSortKey = (option) => {
|
|
|
| return option.short
|
| ? option.short.replace(/^-/, '')
|
| : option.long.replace(/^--/, '');
|
| };
|
| return getSortKey(a).localeCompare(getSortKey(b));
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| visibleOptions(cmd) {
|
| const visibleOptions = cmd.options.filter((option) => !option.hidden);
|
|
|
| const helpOption = cmd._getHelpOption();
|
| if (helpOption && !helpOption.hidden) {
|
|
|
| const removeShort = helpOption.short && cmd._findOption(helpOption.short);
|
| const removeLong = helpOption.long && cmd._findOption(helpOption.long);
|
| if (!removeShort && !removeLong) {
|
| visibleOptions.push(helpOption);
|
| } else if (helpOption.long && !removeLong) {
|
| visibleOptions.push(
|
| cmd.createOption(helpOption.long, helpOption.description),
|
| );
|
| } else if (helpOption.short && !removeShort) {
|
| visibleOptions.push(
|
| cmd.createOption(helpOption.short, helpOption.description),
|
| );
|
| }
|
| }
|
| if (this.sortOptions) {
|
| visibleOptions.sort(this.compareOptions);
|
| }
|
| return visibleOptions;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| visibleGlobalOptions(cmd) {
|
| if (!this.showGlobalOptions) return [];
|
|
|
| const globalOptions = [];
|
| for (
|
| let ancestorCmd = cmd.parent;
|
| ancestorCmd;
|
| ancestorCmd = ancestorCmd.parent
|
| ) {
|
| const visibleOptions = ancestorCmd.options.filter(
|
| (option) => !option.hidden,
|
| );
|
| globalOptions.push(...visibleOptions);
|
| }
|
| if (this.sortOptions) {
|
| globalOptions.sort(this.compareOptions);
|
| }
|
| return globalOptions;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| visibleArguments(cmd) {
|
|
|
| if (cmd._argsDescription) {
|
| cmd.registeredArguments.forEach((argument) => {
|
| argument.description =
|
| argument.description || cmd._argsDescription[argument.name()] || '';
|
| });
|
| }
|
|
|
|
|
| if (cmd.registeredArguments.find((argument) => argument.description)) {
|
| return cmd.registeredArguments;
|
| }
|
| return [];
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| subcommandTerm(cmd) {
|
|
|
| const args = cmd.registeredArguments
|
| .map((arg) => humanReadableArgName(arg))
|
| .join(' ');
|
| return (
|
| cmd._name +
|
| (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
|
| (cmd.options.length ? ' [options]' : '') +
|
| (args ? ' ' + args : '')
|
| );
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| optionTerm(option) {
|
| return option.flags;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| argumentTerm(argument) {
|
| return argument.name();
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| longestSubcommandTermLength(cmd, helper) {
|
| return helper.visibleCommands(cmd).reduce((max, command) => {
|
| return Math.max(max, helper.subcommandTerm(command).length);
|
| }, 0);
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| longestOptionTermLength(cmd, helper) {
|
| return helper.visibleOptions(cmd).reduce((max, option) => {
|
| return Math.max(max, helper.optionTerm(option).length);
|
| }, 0);
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| longestGlobalOptionTermLength(cmd, helper) {
|
| return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
| return Math.max(max, helper.optionTerm(option).length);
|
| }, 0);
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| longestArgumentTermLength(cmd, helper) {
|
| return helper.visibleArguments(cmd).reduce((max, argument) => {
|
| return Math.max(max, helper.argumentTerm(argument).length);
|
| }, 0);
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| commandUsage(cmd) {
|
|
|
| let cmdName = cmd._name;
|
| if (cmd._aliases[0]) {
|
| cmdName = cmdName + '|' + cmd._aliases[0];
|
| }
|
| let ancestorCmdNames = '';
|
| for (
|
| let ancestorCmd = cmd.parent;
|
| ancestorCmd;
|
| ancestorCmd = ancestorCmd.parent
|
| ) {
|
| ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames;
|
| }
|
| return ancestorCmdNames + cmdName + ' ' + cmd.usage();
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| commandDescription(cmd) {
|
|
|
| return cmd.description();
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| subcommandDescription(cmd) {
|
|
|
| return cmd.summary() || cmd.description();
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| optionDescription(option) {
|
| const extraInfo = [];
|
|
|
| if (option.argChoices) {
|
| extraInfo.push(
|
|
|
| `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
|
| );
|
| }
|
| if (option.defaultValue !== undefined) {
|
|
|
|
|
| const showDefault =
|
| option.required ||
|
| option.optional ||
|
| (option.isBoolean() && typeof option.defaultValue === 'boolean');
|
| if (showDefault) {
|
| extraInfo.push(
|
| `default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`,
|
| );
|
| }
|
| }
|
|
|
| if (option.presetArg !== undefined && option.optional) {
|
| extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
|
| }
|
| if (option.envVar !== undefined) {
|
| extraInfo.push(`env: ${option.envVar}`);
|
| }
|
| if (extraInfo.length > 0) {
|
| return `${option.description} (${extraInfo.join(', ')})`;
|
| }
|
|
|
| return option.description;
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
|
|
| argumentDescription(argument) {
|
| const extraInfo = [];
|
| if (argument.argChoices) {
|
| extraInfo.push(
|
|
|
| `choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
|
| );
|
| }
|
| if (argument.defaultValue !== undefined) {
|
| extraInfo.push(
|
| `default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`,
|
| );
|
| }
|
| if (extraInfo.length > 0) {
|
| const extraDescripton = `(${extraInfo.join(', ')})`;
|
| if (argument.description) {
|
| return `${argument.description} ${extraDescripton}`;
|
| }
|
| return extraDescripton;
|
| }
|
| return argument.description;
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| formatHelp(cmd, helper) {
|
| const termWidth = helper.padWidth(cmd, helper);
|
| const helpWidth = helper.helpWidth || 80;
|
| const itemIndentWidth = 2;
|
| const itemSeparatorWidth = 2;
|
| 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));
|
| }
|
|
|
|
|
| let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
|
|
|
|
|
| const commandDescription = helper.commandDescription(cmd);
|
| if (commandDescription.length > 0) {
|
| output = output.concat([
|
| helper.wrap(commandDescription, helpWidth, 0),
|
| '',
|
| ]);
|
| }
|
|
|
|
|
| const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
| return formatItem(
|
| helper.argumentTerm(argument),
|
| helper.argumentDescription(argument),
|
| );
|
| });
|
| if (argumentList.length > 0) {
|
| output = output.concat(['Arguments:', formatList(argumentList), '']);
|
| }
|
|
|
|
|
| const optionList = helper.visibleOptions(cmd).map((option) => {
|
| return formatItem(
|
| helper.optionTerm(option),
|
| helper.optionDescription(option),
|
| );
|
| });
|
| if (optionList.length > 0) {
|
| output = output.concat(['Options:', formatList(optionList), '']);
|
| }
|
|
|
| if (this.showGlobalOptions) {
|
| const globalOptionList = helper
|
| .visibleGlobalOptions(cmd)
|
| .map((option) => {
|
| return formatItem(
|
| helper.optionTerm(option),
|
| helper.optionDescription(option),
|
| );
|
| });
|
| if (globalOptionList.length > 0) {
|
| output = output.concat([
|
| 'Global Options:',
|
| formatList(globalOptionList),
|
| '',
|
| ]);
|
| }
|
| }
|
|
|
|
|
| const commandList = helper.visibleCommands(cmd).map((cmd) => {
|
| return formatItem(
|
| helper.subcommandTerm(cmd),
|
| helper.subcommandDescription(cmd),
|
| );
|
| });
|
| if (commandList.length > 0) {
|
| output = output.concat(['Commands:', formatList(commandList), '']);
|
| }
|
|
|
| return output.join('\n');
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| padWidth(cmd, helper) {
|
| return Math.max(
|
| helper.longestOptionTermLength(cmd, helper),
|
| helper.longestGlobalOptionTermLength(cmd, helper),
|
| helper.longestSubcommandTermLength(cmd, helper),
|
| helper.longestArgumentTermLength(cmd, helper),
|
| );
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| wrap(str, width, indent, minColumnWidth = 40) {
|
|
|
| const indents =
|
| ' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff';
|
|
|
| const manualIndent = new RegExp(`[\\n][${indents}]+`);
|
| if (str.match(manualIndent)) return str;
|
|
|
| const columnWidth = width - indent;
|
| if (columnWidth < minColumnWidth) return str;
|
|
|
| const leadingStr = str.slice(0, indent);
|
| const columnText = str.slice(indent).replace('\r\n', '\n');
|
| const indentString = ' '.repeat(indent);
|
| const zeroWidthSpace = '\u200B';
|
| const breaks = `\\s${zeroWidthSpace}`;
|
|
|
|
|
| const regex = new RegExp(
|
| `\n|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`,
|
| 'g',
|
| );
|
| const lines = columnText.match(regex) || [];
|
| return (
|
| leadingStr +
|
| lines
|
| .map((line, i) => {
|
| if (line === '\n') return '';
|
| return (i > 0 ? indentString : '') + line.trimEnd();
|
| })
|
| .join('\n')
|
| );
|
| }
|
| }
|
|
|
| exports.Help = Help;
|
|
|