From 7a52ddeba2a68388b544f529d2d92104420f77b0 Mon Sep 17 00:00:00 2001 From: Shipwreckt Date: Fri, 31 Oct 2025 20:02:14 +0000 Subject: Changed from static to 11ty! --- node_modules/commander/lib/option.js | 331 +++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 node_modules/commander/lib/option.js (limited to 'node_modules/commander/lib/option.js') diff --git a/node_modules/commander/lib/option.js b/node_modules/commander/lib/option.js new file mode 100644 index 0000000..d61fc5f --- /dev/null +++ b/node_modules/commander/lib/option.js @@ -0,0 +1,331 @@ +const { InvalidArgumentError } = require('./error.js'); + +// @ts-check + +class Option { + /** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {string} flags + * @param {string} [description] + */ + + constructor(flags, description) { + this.flags = flags; + this.description = description || ''; + + this.required = flags.includes('<'); // A value must be supplied when the option is specified. + this.optional = flags.includes('['); // A value is optional when the option is specified. + // variadic test ignores et al which might be used to describe custom splitting of single argument + this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values. + this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line. + const optionFlags = splitOptionFlags(flags); + this.short = optionFlags.shortFlag; + this.long = optionFlags.longFlag; + this.negate = false; + if (this.long) { + this.negate = this.long.startsWith('--no-'); + } + this.defaultValue = undefined; + this.defaultValueDescription = undefined; + this.presetArg = undefined; + this.envVar = undefined; + this.parseArg = undefined; + this.hidden = false; + this.argChoices = undefined; + this.conflictsWith = []; + this.implied = undefined; + } + + /** + * Set the default value, and optionally supply the description to be displayed in the help. + * + * @param {any} value + * @param {string} [description] + * @return {Option} + */ + + default(value, description) { + this.defaultValue = value; + this.defaultValueDescription = description; + return this; + } + + /** + * Preset to use when option used without option-argument, especially optional but also boolean and negated. + * The custom processing (parseArg) is called. + * + * @example + * new Option('--color').default('GREYSCALE').preset('RGB'); + * new Option('--donate [amount]').preset('20').argParser(parseFloat); + * + * @param {any} arg + * @return {Option} + */ + + preset(arg) { + this.presetArg = arg; + return this; + } + + /** + * Add option name(s) that conflict with this option. + * An error will be displayed if conflicting options are found during parsing. + * + * @example + * new Option('--rgb').conflicts('cmyk'); + * new Option('--js').conflicts(['ts', 'jsx']); + * + * @param {string | string[]} names + * @return {Option} + */ + + conflicts(names) { + this.conflictsWith = this.conflictsWith.concat(names); + return this; + } + + /** + * Specify implied option values for when this option is set and the implied options are not. + * + * The custom processing (parseArg) is not called on the implied values. + * + * @example + * program + * .addOption(new Option('--log', 'write logging information to file')) + * .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' })); + * + * @param {Object} impliedOptionValues + * @return {Option} + */ + implies(impliedOptionValues) { + let newImplied = impliedOptionValues; + if (typeof impliedOptionValues === 'string') { + // string is not documented, but easy mistake and we can do what user probably intended. + newImplied = { [impliedOptionValues]: true }; + } + this.implied = Object.assign(this.implied || {}, newImplied); + return this; + } + + /** + * Set environment variable to check for option value. + * + * An environment variable is only used if when processed the current option value is + * undefined, or the source of the current value is 'default' or 'config' or 'env'. + * + * @param {string} name + * @return {Option} + */ + + env(name) { + this.envVar = name; + return this; + } + + /** + * Set the custom handler for processing CLI option arguments into option values. + * + * @param {Function} [fn] + * @return {Option} + */ + + argParser(fn) { + this.parseArg = fn; + return this; + } + + /** + * Whether the option is mandatory and must have a value after parsing. + * + * @param {boolean} [mandatory=true] + * @return {Option} + */ + + makeOptionMandatory(mandatory = true) { + this.mandatory = !!mandatory; + return this; + } + + /** + * Hide option in help. + * + * @param {boolean} [hide=true] + * @return {Option} + */ + + hideHelp(hide = true) { + this.hidden = !!hide; + return this; + } + + /** + * @api private + */ + + _concatValue(value, previous) { + if (previous === this.defaultValue || !Array.isArray(previous)) { + return [value]; + } + + return previous.concat(value); + } + + /** + * Only allow option value to be one of choices. + * + * @param {string[]} values + * @return {Option} + */ + + choices(values) { + this.argChoices = values.slice(); + this.parseArg = (arg, previous) => { + if (!this.argChoices.includes(arg)) { + throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(', ')}.`); + } + if (this.variadic) { + return this._concatValue(arg, previous); + } + return arg; + }; + return this; + } + + /** + * Return option name. + * + * @return {string} + */ + + name() { + if (this.long) { + return this.long.replace(/^--/, ''); + } + return this.short.replace(/^-/, ''); + } + + /** + * Return option name, in a camelcase format that can be used + * as a object attribute key. + * + * @return {string} + * @api private + */ + + attributeName() { + return camelcase(this.name().replace(/^no-/, '')); + } + + /** + * Check if `arg` matches the short or long flag. + * + * @param {string} arg + * @return {boolean} + * @api private + */ + + is(arg) { + return this.short === arg || this.long === arg; + } + + /** + * Return whether a boolean option. + * + * Options are one of boolean, negated, required argument, or optional argument. + * + * @return {boolean} + * @api private + */ + + isBoolean() { + return !this.required && !this.optional && !this.negate; + } +} + +/** + * This class is to make it easier to work with dual options, without changing the existing + * implementation. We support separate dual options for separate positive and negative options, + * like `--build` and `--no-build`, which share a single option value. This works nicely for some + * use cases, but is tricky for others where we want separate behaviours despite + * the single shared option value. + */ +class DualOptions { + /** + * @param {Option[]} options + */ + constructor(options) { + this.positiveOptions = new Map(); + this.negativeOptions = new Map(); + this.dualOptions = new Set(); + options.forEach(option => { + if (option.negate) { + this.negativeOptions.set(option.attributeName(), option); + } else { + this.positiveOptions.set(option.attributeName(), option); + } + }); + this.negativeOptions.forEach((value, key) => { + if (this.positiveOptions.has(key)) { + this.dualOptions.add(key); + } + }); + } + + /** + * Did the value come from the option, and not from possible matching dual option? + * + * @param {any} value + * @param {Option} option + * @returns {boolean} + */ + valueFromOption(value, option) { + const optionKey = option.attributeName(); + if (!this.dualOptions.has(optionKey)) return true; + + // Use the value to deduce if (probably) came from the option. + const preset = this.negativeOptions.get(optionKey).presetArg; + const negativeValue = (preset !== undefined) ? preset : false; + return option.negate === (negativeValue === value); + } +} + +/** + * Convert string from kebab-case to camelCase. + * + * @param {string} str + * @return {string} + * @api private + */ + +function camelcase(str) { + return str.split('-').reduce((str, word) => { + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Split the short and long flag out of something like '-m,--mixed ' + * + * @api private + */ + +function splitOptionFlags(flags) { + let shortFlag; + let longFlag; + // Use original very loose parsing to maintain backwards compatibility for now, + // which allowed for example unintended `-sw, --short-word` [sic]. + const flagParts = flags.split(/[ |,]+/); + if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1])) shortFlag = flagParts.shift(); + longFlag = flagParts.shift(); + // Add support for lone short flag without significantly changing parsing! + if (!shortFlag && /^-[^-]$/.test(longFlag)) { + shortFlag = longFlag; + longFlag = undefined; + } + return { shortFlag, longFlag }; +} + +exports.Option = Option; +exports.splitOptionFlags = splitOptionFlags; +exports.DualOptions = DualOptions; -- cgit v1.2.3