You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
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 <value,...> 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.envVar = undefined; this.parseArg = undefined; this.hidden = false; this.argChoices = 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; };
/** * Set environment variable to check for option value. * Priority order of option values is default < env < cli * * @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; this.parseArg = (arg, previous) => { if (!values.includes(arg)) { throw new InvalidArgumentError(`Allowed choices are ${values.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; }; }
/** * 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 <value>' * * @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;
|