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.
148 lines
3.1 KiB
148 lines
3.1 KiB
2 years ago
|
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;
|