Setting the file. One moment.
Option · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page vendor/commander/lib/option.js
vendor/commander/lib/ option.js
JavaScript · 377 lines · 10 KB
) {
12 this .flags = flags;
13 this .description = description || '' ;
14
15 this .required = flags. includes ( '<' ); // A value must be supplied when the option is specified.
16 this .optional = flags. includes ( '[' ); // A value is optional when the option is specified.
17 // variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument
18 this .variadic = / \w \.\.\. [> \] ] $ / . test (flags); // The option can take multiple values.
19 this .mandatory = false ; // The option must have a value after parsing, which usually means it must be specified on command line.
20 const optionFlags = splitOptionFlags (flags);
21 this .short = optionFlags.shortFlag; // May be a short flag, undefined, or even a long flag (if option has two long flags).
22 this .long = optionFlags.longFlag;
23 this .negate = false ;
24 if ( this .long) {
25 this .negate = this .long. startsWith ( '--no-' );
26 }
27 this .defaultValue = undefined ;
28 this .defaultValueDescription = undefined ;
29 this .presetArg = undefined ;
30 this .envVar = undefined ;
31 this .parseArg = undefined ;
32 this .hidden = false ;
33 this .argChoices = undefined ;
34 this .conflictsWith = [];
35 this .implied = undefined ;
36 this .helpGroupHeading = undefined ; // soft initialised when option added to command
37 }
38
39 /**
40 * Set the default value, and optionally supply the description to be displayed in the help.
41 *
42 * @param {*} value
43 * @param {string} [description]
44 * @return {Option}
45 */
46
47 default ( value , description ) {
48 this .defaultValue = value;
49 this .defaultValueDescription = description;
50 return this ;
51 }
52
53 /**
54 * Preset to use when option used without option-argument, especially optional but also boolean and negated.
55 * The custom processing (parseArg) is called.
56 *
57 * @example
58 * new Option('--color').default('GREYSCALE').preset('RGB');
59 * new Option('--donate [amount]').preset('20').argParser(parseFloat);
60 *
61 * @param {*} arg
62 * @return {Option}
63 */
64
65 preset ( arg ) {
66 this .presetArg = arg;
67 return this ;
68 }
69
70 /**
71 * Add option name(s) that conflict with this option.
72 * An error will be displayed if conflicting options are found during parsing.
73 *
74 * @example
75 * new Option('--rgb').conflicts('cmyk');
76 * new Option('--js').conflicts(['ts', 'jsx']);
77 *
78 * @param {(string | string[])} names
79 * @return {Option}
80 */
81
82 conflicts ( names ) {
83 this .conflictsWith = this .conflictsWith. concat (names);
84 return this ;
85 }
86
87 /**
88 * Specify implied option values for when this option is set and the implied options are not.
89 *
90 * The custom processing (parseArg) is not called on the implied values.
91 *
92 * @example
93 * program
94 * .addOption(new Option('--log', 'write logging information to file'))
95 * .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' }));
96 *
97 * @param {object} impliedOptionValues
98 * @return {Option}
99 */
100 implies ( impliedOptionValues ) {
101 let newImplied = impliedOptionValues;
102 if ( typeof impliedOptionValues === 'string' ) {
103 // string is not documented, but easy mistake and we can do what user probably intended.
104 newImplied = { [impliedOptionValues]: true };
105 }
106 this .implied = Object. assign ( this .implied || {}, newImplied);
107 return this ;
108 }
109
110 /**
111 * Set environment variable to check for option value.
112 *
113 * An environment variable is only used if when processed the current option value is
114 * undefined, or the source of the current value is 'default' or 'config' or 'env'.
115 *
116 * @param {string} name
117 * @return {Option}
118 */
119
120 env ( name ) {
121 this .envVar = name;
122 return this ;
123 }
124
125 /**
126 * Set the custom handler for processing CLI option arguments into option values.
127 *
128 * @param {Function} [fn]
129 * @return {Option}
130 */
131
132 argParser ( fn ) {
133 this .parseArg = fn;
134 return this ;
135 }
136
137 /**
138 * Whether the option is mandatory and must have a value after parsing.
139 *
140 * @param {boolean} [mandatory = true]
141 * @return {Option}
142 */
143
144 makeOptionMandatory ( mandatory = true ) {
145 this .mandatory = !! mandatory;
146 return this ;
147 }
148
149 /**
150 * Hide option in help.
151 *
152 * @param {boolean} [hide = true]
153 * @return {Option}
154 */
155
156 hideHelp ( hide = true ) {
157 this .hidden = !! hide;
158 return this ;
159 }
160
161 /**
162 * @package
163 */
164
165 _collectValue ( value , previous ) {
166 if (previous === this .defaultValue || ! Array. isArray (previous)) {
167 return [value];
168 }
169
170 previous. push (value);
171 return previous;
172 }
173
174 /**
175 * Only allow option value to be one of choices.
176 *
177 * @param {string[]} values
178 * @return {Option}
179 */
180
181 choices ( values ) {
182 this .argChoices = values. slice ();
183 this . parseArg = ( arg , previous ) => {
184 if ( ! this .argChoices. includes (arg)) {
185 throw new InvalidArgumentError (
186 `Allowed choices are ${ this . argChoices . join ( ', ' ) }.` ,
187 );
188 }
189 if ( this .variadic) {
190 return this . _collectValue (arg, previous);
191 }
192 return arg;
193 };
194 return this ;
195 }
196
197 /**
198 * Return option name.
199 *
200 * @return {string}
201 */
202
203 name () {
204 if ( this .long) {
205 return this .long. replace ( / ^ --/ , '' );
206 }
207 return this .short. replace ( / ^ -/ , '' );
208 }
209
210 /**
211 * Return option name, in a camelcase format that can be used
212 * as an object attribute key.
213 *
214 * @return {string}
215 */
216
217 attributeName () {
218 if ( this .negate) {
219 return camelcase ( this . name (). replace ( / ^ no-/ , '' ));
220 }
221 return camelcase ( this . name ());
222 }
223
224 /**
225 * Set the help group heading.
226 *
227 * @param {string} heading
228 * @return {Option}
229 */
230 helpGroup ( heading ) {
231 this .helpGroupHeading = heading;
232 return this ;
233 }
234
235 /**
236 * Check if `arg` matches the short or long flag.
237 *
238 * @param {string} arg
239 * @return {boolean}
240 * @package
241 */
242
243 is ( arg ) {
244 return this .short === arg || this .long === arg;
245 }
246
247 /**
248 * Return whether a boolean option.
249 *
250 * Options are one of boolean, negated, required argument, or optional argument.
251 *
252 * @return {boolean}
253 * @package
254 */
255
256 isBoolean () {
257 return ! this .required && ! this .optional && ! this .negate;
258 }
259 }
260
261 /**
262 * This class is to make it easier to work with dual options, without changing the existing
263 * implementation. We support separate dual options for separate positive and negative options,
264 * like `--build` and `--no-build`, which share a single option value. This works nicely for some
265 * use cases, but is tricky for others where we want separate behaviours despite
266 * the single shared option value.
267 */
268 export class DualOptions {
269 /**
270 * @param {Option[]} options
271 */
272 constructor ( options ) {
273 this .positiveOptions = new Map ();
274 this .negativeOptions = new Map ();
275 this .dualOptions = new Set ();
276 options. forEach (( option ) => {
277 if (option.negate) {
278 this .negativeOptions. set (option. attributeName (), option);
279 } else {
280 this .positiveOptions. set (option. attributeName (), option);
281 }
282 });
283 this .negativeOptions. forEach (( value , key ) => {
284 if ( this .positiveOptions. has (key)) {
285 this .dualOptions. add (key);
286 }
287 });
288 }
289
290 /**
291 * Did the value come from the option, and not from possible matching dual option?
292 *
293 * @param {*} value
294 * @param {Option} option
295 * @returns {boolean}
296 */
297 valueFromOption ( value , option ) {
298 const optionKey = option. attributeName ();
299 if ( ! this .dualOptions. has (optionKey)) return true ;
300
301 // Use the value to deduce if (probably) came from the option.
302 const preset = this .negativeOptions. get (optionKey).presetArg;
303 const negativeValue = preset !== undefined ? preset : false ;
304 return option.negate === (negativeValue === value);
305 }
306 }
307
308 /**
309 * Convert string from kebab-case to camelCase.
310 *
311 * @param {string} str
312 * @return {string}
313 * @private
314 */
315
316 function camelcase ( str ) {
317 return str. split ( '-' ). reduce (( str , word ) => {
318 return str + word[ 0 ]. toUpperCase () + word. slice ( 1 );
319 });
320 }
321
322 /**
323 * Split the short and long flag out of something like '-m,--mixed <value>'
324 *
325 * @private
326 */
327
328 function splitOptionFlags ( flags ) {
329 let shortFlag;
330 let longFlag;
331 // short flag, single dash and single character
332 const shortFlagExp = / ^ - [ ^ -] $ / ;
333 // long flag, double dash and at least one character
334 const longFlagExp = / ^ -- [ ^ -] / ;
335
336 const flagParts = flags. split ( / [ |,] + / ). concat ( 'guard' );
337 // Normal is short and/or long.
338 if (shortFlagExp. test (flagParts[ 0 ])) shortFlag = flagParts. shift ();
339 if (longFlagExp. test (flagParts[ 0 ])) longFlag = flagParts. shift ();
340 // Long then short. Rarely used but fine.
341 if ( ! shortFlag && shortFlagExp. test (flagParts[ 0 ]))
342 shortFlag = flagParts. shift ();
343 // Allow two long flags, like '--ws, --workspace'
344 // This is the supported way to have a shortish option flag.
345 if ( ! shortFlag && longFlagExp. test (flagParts[ 0 ])) {
346 shortFlag = longFlag;
347 longFlag = flagParts. shift ();
348 }
349
350 // Check for unprocessed flag. Fail noisily rather than silently ignore.
351 if (flagParts[ 0 ]. startsWith ( '-' )) {
352 const unsupportedFlag = flagParts[ 0 ];
353 const baseError = `option creation failed due to '${ unsupportedFlag }' in option flags '${ flags }'` ;
354 if ( / ^ - [ ^ -][ ^ -] / . test (unsupportedFlag))
355 throw new Error (
356 `${ baseError }
357 - a short flag is a single dash and a single character
358 - either use a single dash and a single character (for a short flag)
359 - or use a double dash for a long option (and can have two, like '--ws, --workspace')` ,
360 );
361 if (shortFlagExp. test (unsupportedFlag))
362 throw new Error ( `${ baseError }
363 - too many short flags` );
364 if (longFlagExp. test (unsupportedFlag))
365 throw new Error ( `${ baseError }
366 - too many long flags` );
367
368 throw new Error ( `${ baseError }
369 - unrecognised flag format` );
370 }
371 if (shortFlag === undefined && longFlag === undefined )
372 throw new Error (
373 `option creation failed due to no flags found in '${ flags }'.` ,
374 );
375
376 return { shortFlag, longFlag };
377 }