UNPKG

3.22 kBJavaScriptView Raw
1const { InvalidArgumentError } = require('./error.js');
2
3class Argument {
4 /**
5 * Initialize a new command argument with the given name and description.
6 * The default is that the argument is required, and you can explicitly
7 * indicate this with <> around the name. Put [] around the name for an optional argument.
8 *
9 * @param {string} name
10 * @param {string} [description]
11 */
12
13 constructor(name, description) {
14 this.description = description || '';
15 this.variadic = false;
16 this.parseArg = undefined;
17 this.defaultValue = undefined;
18 this.defaultValueDescription = undefined;
19 this.argChoices = undefined;
20
21 switch (name[0]) {
22 case '<': // e.g. <required>
23 this.required = true;
24 this._name = name.slice(1, -1);
25 break;
26 case '[': // e.g. [optional]
27 this.required = false;
28 this._name = name.slice(1, -1);
29 break;
30 default:
31 this.required = true;
32 this._name = name;
33 break;
34 }
35
36 if (this._name.length > 3 && this._name.slice(-3) === '...') {
37 this.variadic = true;
38 this._name = this._name.slice(0, -3);
39 }
40 }
41
42 /**
43 * Return argument name.
44 *
45 * @return {string}
46 */
47
48 name() {
49 return this._name;
50 }
51
52 /**
53 * @package
54 */
55
56 _concatValue(value, previous) {
57 if (previous === this.defaultValue || !Array.isArray(previous)) {
58 return [value];
59 }
60
61 return previous.concat(value);
62 }
63
64 /**
65 * Set the default value, and optionally supply the description to be displayed in the help.
66 *
67 * @param {*} value
68 * @param {string} [description]
69 * @return {Argument}
70 */
71
72 default(value, description) {
73 this.defaultValue = value;
74 this.defaultValueDescription = description;
75 return this;
76 }
77
78 /**
79 * Set the custom handler for processing CLI command arguments into argument values.
80 *
81 * @param {Function} [fn]
82 * @return {Argument}
83 */
84
85 argParser(fn) {
86 this.parseArg = fn;
87 return this;
88 }
89
90 /**
91 * Only allow argument value to be one of choices.
92 *
93 * @param {string[]} values
94 * @return {Argument}
95 */
96
97 choices(values) {
98 this.argChoices = values.slice();
99 this.parseArg = (arg, previous) => {
100 if (!this.argChoices.includes(arg)) {
101 throw new InvalidArgumentError(
102 `Allowed choices are ${this.argChoices.join(', ')}.`,
103 );
104 }
105 if (this.variadic) {
106 return this._concatValue(arg, previous);
107 }
108 return arg;
109 };
110 return this;
111 }
112
113 /**
114 * Make argument required.
115 *
116 * @returns {Argument}
117 */
118 argRequired() {
119 this.required = true;
120 return this;
121 }
122
123 /**
124 * Make argument optional.
125 *
126 * @returns {Argument}
127 */
128 argOptional() {
129 this.required = false;
130 return this;
131 }
132}
133
134/**
135 * Takes an argument and returns its human readable equivalent for help usage.
136 *
137 * @param {Argument} arg
138 * @return {string}
139 * @private
140 */
141
142function humanReadableArgName(arg) {
143 const nameOutput = arg.name() + (arg.variadic === true ? '...' : '');
144
145 return arg.required ? '<' + nameOutput + '>' : '[' + nameOutput + ']';
146}
147
148exports.Argument = Argument;
149exports.humanReadableArgName = humanReadableArgName;