summaryrefslogtreecommitdiff
path: root/node_modules/commander/lib/help.js
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/commander/lib/help.js
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/commander/lib/help.js')
-rw-r--r--node_modules/commander/lib/help.js464
1 files changed, 464 insertions, 0 deletions
diff --git a/node_modules/commander/lib/help.js b/node_modules/commander/lib/help.js
new file mode 100644
index 0000000..14e0fb9
--- /dev/null
+++ b/node_modules/commander/lib/help.js
@@ -0,0 +1,464 @@
+const { humanReadableArgName } = require('./argument.js');
+
+/**
+ * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
+ * https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
+ * @typedef { import("./argument.js").Argument } Argument
+ * @typedef { import("./command.js").Command } Command
+ * @typedef { import("./option.js").Option } Option
+ */
+
+// @ts-check
+
+// Although this is a class, methods are static in style to allow override using subclass or just functions.
+class Help {
+ constructor() {
+ this.helpWidth = undefined;
+ this.sortSubcommands = false;
+ this.sortOptions = false;
+ this.showGlobalOptions = false;
+ }
+
+ /**
+ * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
+ *
+ * @param {Command} cmd
+ * @returns {Command[]}
+ */
+
+ visibleCommands(cmd) {
+ const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);
+ if (cmd._hasImplicitHelpCommand()) {
+ // Create a command matching the implicit help command.
+ const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);
+ const helpCommand = cmd.createCommand(helpName)
+ .helpOption(false);
+ helpCommand.description(cmd._helpCommandDescription);
+ if (helpArgs) helpCommand.arguments(helpArgs);
+ visibleCommands.push(helpCommand);
+ }
+ if (this.sortSubcommands) {
+ visibleCommands.sort((a, b) => {
+ // @ts-ignore: overloaded return type
+ return a.name().localeCompare(b.name());
+ });
+ }
+ return visibleCommands;
+ }
+
+ /**
+ * Compare options for sort.
+ *
+ * @param {Option} a
+ * @param {Option} b
+ * @returns number
+ */
+ compareOptions(a, b) {
+ const getSortKey = (option) => {
+ // WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
+ return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
+ };
+ return getSortKey(a).localeCompare(getSortKey(b));
+ }
+
+ /**
+ * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
+ *
+ * @param {Command} cmd
+ * @returns {Option[]}
+ */
+
+ visibleOptions(cmd) {
+ const visibleOptions = cmd.options.filter((option) => !option.hidden);
+ // Implicit help
+ const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);
+ const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);
+ if (showShortHelpFlag || showLongHelpFlag) {
+ let helpOption;
+ if (!showShortHelpFlag) {
+ helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);
+ } else if (!showLongHelpFlag) {
+ helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);
+ } else {
+ helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);
+ }
+ visibleOptions.push(helpOption);
+ }
+ if (this.sortOptions) {
+ visibleOptions.sort(this.compareOptions);
+ }
+ return visibleOptions;
+ }
+
+ /**
+ * Get an array of the visible global options. (Not including help.)
+ *
+ * @param {Command} cmd
+ * @returns {Option[]}
+ */
+
+ visibleGlobalOptions(cmd) {
+ if (!this.showGlobalOptions) return [];
+
+ const globalOptions = [];
+ for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
+ const visibleOptions = parentCmd.options.filter((option) => !option.hidden);
+ globalOptions.push(...visibleOptions);
+ }
+ if (this.sortOptions) {
+ globalOptions.sort(this.compareOptions);
+ }
+ return globalOptions;
+ }
+
+ /**
+ * Get an array of the arguments if any have a description.
+ *
+ * @param {Command} cmd
+ * @returns {Argument[]}
+ */
+
+ visibleArguments(cmd) {
+ // Side effect! Apply the legacy descriptions before the arguments are displayed.
+ if (cmd._argsDescription) {
+ cmd._args.forEach(argument => {
+ argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
+ });
+ }
+
+ // If there are any arguments with a description then return all the arguments.
+ if (cmd._args.find(argument => argument.description)) {
+ return cmd._args;
+ }
+ return [];
+ }
+
+ /**
+ * Get the command term to show in the list of subcommands.
+ *
+ * @param {Command} cmd
+ * @returns {string}
+ */
+
+ subcommandTerm(cmd) {
+ // 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] : '') +
+ (cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
+ (args ? ' ' + args : '');
+ }
+
+ /**
+ * Get the option term to show in the list of options.
+ *
+ * @param {Option} option
+ * @returns {string}
+ */
+
+ optionTerm(option) {
+ return option.flags;
+ }
+
+ /**
+ * Get the argument term to show in the list of arguments.
+ *
+ * @param {Argument} argument
+ * @returns {string}
+ */
+
+ argumentTerm(argument) {
+ return argument.name();
+ }
+
+ /**
+ * Get the longest command term length.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {number}
+ */
+
+ longestSubcommandTermLength(cmd, helper) {
+ return helper.visibleCommands(cmd).reduce((max, command) => {
+ return Math.max(max, helper.subcommandTerm(command).length);
+ }, 0);
+ }
+
+ /**
+ * Get the longest option term length.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {number}
+ */
+
+ longestOptionTermLength(cmd, helper) {
+ return helper.visibleOptions(cmd).reduce((max, option) => {
+ return Math.max(max, helper.optionTerm(option).length);
+ }, 0);
+ }
+
+ /**
+ * Get the longest global option term length.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {number}
+ */
+
+ longestGlobalOptionTermLength(cmd, helper) {
+ return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
+ return Math.max(max, helper.optionTerm(option).length);
+ }, 0);
+ }
+
+ /**
+ * Get the longest argument term length.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {number}
+ */
+
+ longestArgumentTermLength(cmd, helper) {
+ return helper.visibleArguments(cmd).reduce((max, argument) => {
+ return Math.max(max, helper.argumentTerm(argument).length);
+ }, 0);
+ }
+
+ /**
+ * Get the command usage to be displayed at the top of the built-in help.
+ *
+ * @param {Command} cmd
+ * @returns {string}
+ */
+
+ commandUsage(cmd) {
+ // Usage
+ let cmdName = cmd._name;
+ if (cmd._aliases[0]) {
+ cmdName = cmdName + '|' + cmd._aliases[0];
+ }
+ let parentCmdNames = '';
+ for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
+ parentCmdNames = parentCmd.name() + ' ' + parentCmdNames;
+ }
+ return parentCmdNames + cmdName + ' ' + cmd.usage();
+ }
+
+ /**
+ * Get the description for the command.
+ *
+ * @param {Command} cmd
+ * @returns {string}
+ */
+
+ commandDescription(cmd) {
+ // @ts-ignore: overloaded return type
+ return cmd.description();
+ }
+
+ /**
+ * Get the subcommand summary to show in the list of subcommands.
+ * (Fallback to description for backwards compatibility.)
+ *
+ * @param {Command} cmd
+ * @returns {string}
+ */
+
+ subcommandDescription(cmd) {
+ // @ts-ignore: overloaded return type
+ return cmd.summary() || cmd.description();
+ }
+
+ /**
+ * Get the option description to show in the list of options.
+ *
+ * @param {Option} option
+ * @return {string}
+ */
+
+ optionDescription(option) {
+ const extraInfo = [];
+
+ if (option.argChoices) {
+ extraInfo.push(
+ // use stringify to match the display of the default value
+ `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
+ }
+ if (option.defaultValue !== undefined) {
+ // default for boolean and negated more for programmer than end user,
+ // but show true/false for boolean option as may be for hand-rolled env or config processing.
+ const showDefault = option.required || option.optional ||
+ (option.isBoolean() && typeof option.defaultValue === 'boolean');
+ if (showDefault) {
+ extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
+ }
+ }
+ // preset for boolean and negated are more for programmer than end user
+ 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;
+ }
+
+ /**
+ * Get the argument description to show in the list of arguments.
+ *
+ * @param {Argument} argument
+ * @return {string}
+ */
+
+ argumentDescription(argument) {
+ const extraInfo = [];
+ if (argument.argChoices) {
+ extraInfo.push(
+ // use stringify to match the display of the default value
+ `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;
+ }
+
+ /**
+ * Generate the built-in help text.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {string}
+ */
+
+ formatHelp(cmd, helper) {
+ 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([helper.wrap(commandDescription, helpWidth, 0), '']);
+ }
+
+ // Arguments
+ 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), '']);
+ }
+
+ // Options
+ 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), '']);
+ }
+ }
+
+ // Commands
+ 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');
+ }
+
+ /**
+ * Calculate the pad width from the maximum term length.
+ *
+ * @param {Command} cmd
+ * @param {Help} helper
+ * @returns {number}
+ */
+
+ padWidth(cmd, helper) {
+ return Math.max(
+ helper.longestOptionTermLength(cmd, helper),
+ helper.longestGlobalOptionTermLength(cmd, helper),
+ helper.longestSubcommandTermLength(cmd, helper),
+ helper.longestArgumentTermLength(cmd, helper)
+ );
+ }
+
+ /**
+ * Wrap the given string to width characters per line, with lines after the first indented.
+ * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
+ *
+ * @param {string} str
+ * @param {number} width
+ * @param {number} indent
+ * @param {number} [minColumnWidth=40]
+ * @return {string}
+ *
+ */
+
+ wrap(str, width, indent, minColumnWidth = 40) {
+ // Full \s characters, minus the linefeeds.
+ const indents = ' \\f\\t\\v\u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff';
+ // Detect manually wrapped and indented strings by searching for line break followed by spaces.
+ const manualIndent = new RegExp(`[\\n][${indents}]+`);
+ if (str.match(manualIndent)) return str;
+ // Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
+ 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}`;
+ // Match line end (so empty lines don't collapse),
+ // or as much text as will fit in column, or excess text up to first break.
+ 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 ''; // preserve empty lines
+ return ((i > 0) ? indentString : '') + line.trimEnd();
+ }).join('\n');
+ }
+}
+
+exports.Help = Help;