| const { InvalidArgumentError } = require('./error.js'); |
| |
| // @ts-check |
| |
| class Argument { |
| /** |
| * Initialize a new command argument with the given name and description. |
| * The default is that the argument is required, and you can explicitly |
| * indicate this with <> around the name. Put [] around the name for an optional argument. |
| * |
| * @param {string} name |
| * @param {string} [description] |
| */ |
| |
| constructor(name, description) { |
| this.description = description || ''; |
| this.variadic = false; |
| this.parseArg = undefined; |
| this.defaultValue = undefined; |
| this.defaultValueDescription = undefined; |
| this.argChoices = undefined; |
| |
| switch (name[0]) { |
| case '<': // e.g. <required> |
| this.required = true; |
| this._name = name.slice(1, -1); |
| break; |
| case '[': // e.g. [optional] |
| this.required = false; |
| this._name = name.slice(1, -1); |
| break; |
| default: |
| this.required = true; |
| this._name = name; |
| break; |
| } |
| |
| if (this._name.length > 3 && this._name.slice(-3) === '...') { |
| this.variadic = true; |
| this._name = this._name.slice(0, -3); |
| } |
| } |
| |
| /** |
| * Return argument name. |
| * |
| * @return {string} |
| */ |
| |
| name() { |
| return this._name; |
| } |
| |
| /** |
| * @api private |
| */ |
| |
| _concatValue(value, previous) { |
| if (previous === this.defaultValue || !Array.isArray(previous)) { |
| return [value]; |
| } |
| |
| return previous.concat(value); |
| } |
| |
| /** |
| * Set the default value, and optionally supply the description to be displayed in the help. |
| * |
| * @param {any} value |
| * @param {string} [description] |
| * @return {Argument} |
| */ |
| |
| default(value, description) { |
| this.defaultValue = value; |
| this.defaultValueDescription = description; |
| return this; |
| } |
| |
| /** |
| * Set the custom handler for processing CLI command arguments into argument values. |
| * |
| * @param {Function} [fn] |
| * @return {Argument} |
| */ |
| |
| argParser(fn) { |
| this.parseArg = fn; |
| return this; |
| } |
| |
| /** |
| * Only allow argument value to be one of choices. |
| * |
| * @param {string[]} values |
| * @return {Argument} |
| */ |
| |
| 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; |
| } |
| |
| /** |
| * Make argument required. |
| */ |
| argRequired() { |
| this.required = true; |
| return this; |
| } |
| |
| /** |
| * Make argument optional. |
| */ |
| argOptional() { |
| this.required = false; |
| return this; |
| } |
| } |
| |
| /** |
| * Takes an argument and returns its human readable equivalent for help usage. |
| * |
| * @param {Argument} arg |
| * @return {string} |
| * @api private |
| */ |
| |
| function humanReadableArgName(arg) { |
| const nameOutput = arg.name() + (arg.variadic === true ? '...' : ''); |
| |
| return arg.required |
| ? '<' + nameOutput + '>' |
| : '[' + nameOutput + ']'; |
| } |
| |
| exports.Argument = Argument; |
| exports.humanReadableArgName = humanReadableArgName; |