UNPKG

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