source: trip-planner-front/node_modules/yargs-parser/build/lib/yargs-parser.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 45.3 KB
Line 
1/**
2 * @license
3 * Copyright (c) 2016, Contributors
4 * SPDX-License-Identifier: ISC
5 */
6import { tokenizeArgString } from './tokenize-arg-string.js';
7import { DefaultValuesForTypeKey } from './yargs-parser-types.js';
8import { camelCase, decamelize, looksLikeNumber } from './string-utils.js';
9let mixin;
10export class YargsParser {
11 constructor(_mixin) {
12 mixin = _mixin;
13 }
14 parse(argsInput, options) {
15 const opts = Object.assign({
16 alias: undefined,
17 array: undefined,
18 boolean: undefined,
19 config: undefined,
20 configObjects: undefined,
21 configuration: undefined,
22 coerce: undefined,
23 count: undefined,
24 default: undefined,
25 envPrefix: undefined,
26 narg: undefined,
27 normalize: undefined,
28 string: undefined,
29 number: undefined,
30 __: undefined,
31 key: undefined
32 }, options);
33 // allow a string argument to be passed in rather
34 // than an argv array.
35 const args = tokenizeArgString(argsInput);
36 // aliases might have transitive relationships, normalize this.
37 const aliases = combineAliases(Object.assign(Object.create(null), opts.alias));
38 const configuration = Object.assign({
39 'boolean-negation': true,
40 'camel-case-expansion': true,
41 'combine-arrays': false,
42 'dot-notation': true,
43 'duplicate-arguments-array': true,
44 'flatten-duplicate-arrays': true,
45 'greedy-arrays': true,
46 'halt-at-non-option': false,
47 'nargs-eats-options': false,
48 'negation-prefix': 'no-',
49 'parse-numbers': true,
50 'parse-positional-numbers': true,
51 'populate--': false,
52 'set-placeholder-key': false,
53 'short-option-groups': true,
54 'strip-aliased': false,
55 'strip-dashed': false,
56 'unknown-options-as-args': false
57 }, opts.configuration);
58 const defaults = Object.assign(Object.create(null), opts.default);
59 const configObjects = opts.configObjects || [];
60 const envPrefix = opts.envPrefix;
61 const notFlagsOption = configuration['populate--'];
62 const notFlagsArgv = notFlagsOption ? '--' : '_';
63 const newAliases = Object.create(null);
64 const defaulted = Object.create(null);
65 // allow a i18n handler to be passed in, default to a fake one (util.format).
66 const __ = opts.__ || mixin.format;
67 const flags = {
68 aliases: Object.create(null),
69 arrays: Object.create(null),
70 bools: Object.create(null),
71 strings: Object.create(null),
72 numbers: Object.create(null),
73 counts: Object.create(null),
74 normalize: Object.create(null),
75 configs: Object.create(null),
76 nargs: Object.create(null),
77 coercions: Object.create(null),
78 keys: []
79 };
80 const negative = /^-([0-9]+(\.[0-9]+)?|\.[0-9]+)$/;
81 const negatedBoolean = new RegExp('^--' + configuration['negation-prefix'] + '(.+)');
82 [].concat(opts.array || []).filter(Boolean).forEach(function (opt) {
83 const key = typeof opt === 'object' ? opt.key : opt;
84 // assign to flags[bools|strings|numbers]
85 const assignment = Object.keys(opt).map(function (key) {
86 const arrayFlagKeys = {
87 boolean: 'bools',
88 string: 'strings',
89 number: 'numbers'
90 };
91 return arrayFlagKeys[key];
92 }).filter(Boolean).pop();
93 // assign key to be coerced
94 if (assignment) {
95 flags[assignment][key] = true;
96 }
97 flags.arrays[key] = true;
98 flags.keys.push(key);
99 });
100 [].concat(opts.boolean || []).filter(Boolean).forEach(function (key) {
101 flags.bools[key] = true;
102 flags.keys.push(key);
103 });
104 [].concat(opts.string || []).filter(Boolean).forEach(function (key) {
105 flags.strings[key] = true;
106 flags.keys.push(key);
107 });
108 [].concat(opts.number || []).filter(Boolean).forEach(function (key) {
109 flags.numbers[key] = true;
110 flags.keys.push(key);
111 });
112 [].concat(opts.count || []).filter(Boolean).forEach(function (key) {
113 flags.counts[key] = true;
114 flags.keys.push(key);
115 });
116 [].concat(opts.normalize || []).filter(Boolean).forEach(function (key) {
117 flags.normalize[key] = true;
118 flags.keys.push(key);
119 });
120 if (typeof opts.narg === 'object') {
121 Object.entries(opts.narg).forEach(([key, value]) => {
122 if (typeof value === 'number') {
123 flags.nargs[key] = value;
124 flags.keys.push(key);
125 }
126 });
127 }
128 if (typeof opts.coerce === 'object') {
129 Object.entries(opts.coerce).forEach(([key, value]) => {
130 if (typeof value === 'function') {
131 flags.coercions[key] = value;
132 flags.keys.push(key);
133 }
134 });
135 }
136 if (typeof opts.config !== 'undefined') {
137 if (Array.isArray(opts.config) || typeof opts.config === 'string') {
138 ;
139 [].concat(opts.config).filter(Boolean).forEach(function (key) {
140 flags.configs[key] = true;
141 });
142 }
143 else if (typeof opts.config === 'object') {
144 Object.entries(opts.config).forEach(([key, value]) => {
145 if (typeof value === 'boolean' || typeof value === 'function') {
146 flags.configs[key] = value;
147 }
148 });
149 }
150 }
151 // create a lookup table that takes into account all
152 // combinations of aliases: {f: ['foo'], foo: ['f']}
153 extendAliases(opts.key, aliases, opts.default, flags.arrays);
154 // apply default values to all aliases.
155 Object.keys(defaults).forEach(function (key) {
156 (flags.aliases[key] || []).forEach(function (alias) {
157 defaults[alias] = defaults[key];
158 });
159 });
160 let error = null;
161 checkConfiguration();
162 let notFlags = [];
163 const argv = Object.assign(Object.create(null), { _: [] });
164 // TODO(bcoe): for the first pass at removing object prototype we didn't
165 // remove all prototypes from objects returned by this API, we might want
166 // to gradually move towards doing so.
167 const argvReturn = {};
168 for (let i = 0; i < args.length; i++) {
169 const arg = args[i];
170 const truncatedArg = arg.replace(/^-{3,}/, '---');
171 let broken;
172 let key;
173 let letters;
174 let m;
175 let next;
176 let value;
177 // any unknown option (except for end-of-options, "--")
178 if (arg !== '--' && isUnknownOptionAsArg(arg)) {
179 pushPositional(arg);
180 // ---, ---=, ----, etc,
181 }
182 else if (truncatedArg.match(/---+(=|$)/)) {
183 // options without key name are invalid.
184 pushPositional(arg);
185 continue;
186 // -- separated by =
187 }
188 else if (arg.match(/^--.+=/) || (!configuration['short-option-groups'] && arg.match(/^-.+=/))) {
189 // Using [\s\S] instead of . because js doesn't support the
190 // 'dotall' regex modifier. See:
191 // http://stackoverflow.com/a/1068308/13216
192 m = arg.match(/^--?([^=]+)=([\s\S]*)$/);
193 // arrays format = '--f=a b c'
194 if (m !== null && Array.isArray(m) && m.length >= 3) {
195 if (checkAllAliases(m[1], flags.arrays)) {
196 i = eatArray(i, m[1], args, m[2]);
197 }
198 else if (checkAllAliases(m[1], flags.nargs) !== false) {
199 // nargs format = '--f=monkey washing cat'
200 i = eatNargs(i, m[1], args, m[2]);
201 }
202 else {
203 setArg(m[1], m[2]);
204 }
205 }
206 }
207 else if (arg.match(negatedBoolean) && configuration['boolean-negation']) {
208 m = arg.match(negatedBoolean);
209 if (m !== null && Array.isArray(m) && m.length >= 2) {
210 key = m[1];
211 setArg(key, checkAllAliases(key, flags.arrays) ? [false] : false);
212 }
213 // -- separated by space.
214 }
215 else if (arg.match(/^--.+/) || (!configuration['short-option-groups'] && arg.match(/^-[^-]+/))) {
216 m = arg.match(/^--?(.+)/);
217 if (m !== null && Array.isArray(m) && m.length >= 2) {
218 key = m[1];
219 if (checkAllAliases(key, flags.arrays)) {
220 // array format = '--foo a b c'
221 i = eatArray(i, key, args);
222 }
223 else if (checkAllAliases(key, flags.nargs) !== false) {
224 // nargs format = '--foo a b c'
225 // should be truthy even if: flags.nargs[key] === 0
226 i = eatNargs(i, key, args);
227 }
228 else {
229 next = args[i + 1];
230 if (next !== undefined && (!next.match(/^-/) ||
231 next.match(negative)) &&
232 !checkAllAliases(key, flags.bools) &&
233 !checkAllAliases(key, flags.counts)) {
234 setArg(key, next);
235 i++;
236 }
237 else if (/^(true|false)$/.test(next)) {
238 setArg(key, next);
239 i++;
240 }
241 else {
242 setArg(key, defaultValue(key));
243 }
244 }
245 }
246 // dot-notation flag separated by '='.
247 }
248 else if (arg.match(/^-.\..+=/)) {
249 m = arg.match(/^-([^=]+)=([\s\S]*)$/);
250 if (m !== null && Array.isArray(m) && m.length >= 3) {
251 setArg(m[1], m[2]);
252 }
253 // dot-notation flag separated by space.
254 }
255 else if (arg.match(/^-.\..+/) && !arg.match(negative)) {
256 next = args[i + 1];
257 m = arg.match(/^-(.\..+)/);
258 if (m !== null && Array.isArray(m) && m.length >= 2) {
259 key = m[1];
260 if (next !== undefined && !next.match(/^-/) &&
261 !checkAllAliases(key, flags.bools) &&
262 !checkAllAliases(key, flags.counts)) {
263 setArg(key, next);
264 i++;
265 }
266 else {
267 setArg(key, defaultValue(key));
268 }
269 }
270 }
271 else if (arg.match(/^-[^-]+/) && !arg.match(negative)) {
272 letters = arg.slice(1, -1).split('');
273 broken = false;
274 for (let j = 0; j < letters.length; j++) {
275 next = arg.slice(j + 2);
276 if (letters[j + 1] && letters[j + 1] === '=') {
277 value = arg.slice(j + 3);
278 key = letters[j];
279 if (checkAllAliases(key, flags.arrays)) {
280 // array format = '-f=a b c'
281 i = eatArray(i, key, args, value);
282 }
283 else if (checkAllAliases(key, flags.nargs) !== false) {
284 // nargs format = '-f=monkey washing cat'
285 i = eatNargs(i, key, args, value);
286 }
287 else {
288 setArg(key, value);
289 }
290 broken = true;
291 break;
292 }
293 if (next === '-') {
294 setArg(letters[j], next);
295 continue;
296 }
297 // current letter is an alphabetic character and next value is a number
298 if (/[A-Za-z]/.test(letters[j]) &&
299 /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next) &&
300 checkAllAliases(next, flags.bools) === false) {
301 setArg(letters[j], next);
302 broken = true;
303 break;
304 }
305 if (letters[j + 1] && letters[j + 1].match(/\W/)) {
306 setArg(letters[j], next);
307 broken = true;
308 break;
309 }
310 else {
311 setArg(letters[j], defaultValue(letters[j]));
312 }
313 }
314 key = arg.slice(-1)[0];
315 if (!broken && key !== '-') {
316 if (checkAllAliases(key, flags.arrays)) {
317 // array format = '-f a b c'
318 i = eatArray(i, key, args);
319 }
320 else if (checkAllAliases(key, flags.nargs) !== false) {
321 // nargs format = '-f a b c'
322 // should be truthy even if: flags.nargs[key] === 0
323 i = eatNargs(i, key, args);
324 }
325 else {
326 next = args[i + 1];
327 if (next !== undefined && (!/^(-|--)[^-]/.test(next) ||
328 next.match(negative)) &&
329 !checkAllAliases(key, flags.bools) &&
330 !checkAllAliases(key, flags.counts)) {
331 setArg(key, next);
332 i++;
333 }
334 else if (/^(true|false)$/.test(next)) {
335 setArg(key, next);
336 i++;
337 }
338 else {
339 setArg(key, defaultValue(key));
340 }
341 }
342 }
343 }
344 else if (arg.match(/^-[0-9]$/) &&
345 arg.match(negative) &&
346 checkAllAliases(arg.slice(1), flags.bools)) {
347 // single-digit boolean alias, e.g: xargs -0
348 key = arg.slice(1);
349 setArg(key, defaultValue(key));
350 }
351 else if (arg === '--') {
352 notFlags = args.slice(i + 1);
353 break;
354 }
355 else if (configuration['halt-at-non-option']) {
356 notFlags = args.slice(i);
357 break;
358 }
359 else {
360 pushPositional(arg);
361 }
362 }
363 // order of precedence:
364 // 1. command line arg
365 // 2. value from env var
366 // 3. value from config file
367 // 4. value from config objects
368 // 5. configured default value
369 applyEnvVars(argv, true); // special case: check env vars that point to config file
370 applyEnvVars(argv, false);
371 setConfig(argv);
372 setConfigObjects();
373 applyDefaultsAndAliases(argv, flags.aliases, defaults, true);
374 applyCoercions(argv);
375 if (configuration['set-placeholder-key'])
376 setPlaceholderKeys(argv);
377 // for any counts either not in args or without an explicit default, set to 0
378 Object.keys(flags.counts).forEach(function (key) {
379 if (!hasKey(argv, key.split('.')))
380 setArg(key, 0);
381 });
382 // '--' defaults to undefined.
383 if (notFlagsOption && notFlags.length)
384 argv[notFlagsArgv] = [];
385 notFlags.forEach(function (key) {
386 argv[notFlagsArgv].push(key);
387 });
388 if (configuration['camel-case-expansion'] && configuration['strip-dashed']) {
389 Object.keys(argv).filter(key => key !== '--' && key.includes('-')).forEach(key => {
390 delete argv[key];
391 });
392 }
393 if (configuration['strip-aliased']) {
394 ;
395 [].concat(...Object.keys(aliases).map(k => aliases[k])).forEach(alias => {
396 if (configuration['camel-case-expansion'] && alias.includes('-')) {
397 delete argv[alias.split('.').map(prop => camelCase(prop)).join('.')];
398 }
399 delete argv[alias];
400 });
401 }
402 // Push argument into positional array, applying numeric coercion:
403 function pushPositional(arg) {
404 const maybeCoercedNumber = maybeCoerceNumber('_', arg);
405 if (typeof maybeCoercedNumber === 'string' || typeof maybeCoercedNumber === 'number') {
406 argv._.push(maybeCoercedNumber);
407 }
408 }
409 // how many arguments should we consume, based
410 // on the nargs option?
411 function eatNargs(i, key, args, argAfterEqualSign) {
412 let ii;
413 let toEat = checkAllAliases(key, flags.nargs);
414 // NaN has a special meaning for the array type, indicating that one or
415 // more values are expected.
416 toEat = typeof toEat !== 'number' || isNaN(toEat) ? 1 : toEat;
417 if (toEat === 0) {
418 if (!isUndefined(argAfterEqualSign)) {
419 error = Error(__('Argument unexpected for: %s', key));
420 }
421 setArg(key, defaultValue(key));
422 return i;
423 }
424 let available = isUndefined(argAfterEqualSign) ? 0 : 1;
425 if (configuration['nargs-eats-options']) {
426 // classic behavior, yargs eats positional and dash arguments.
427 if (args.length - (i + 1) + available < toEat) {
428 error = Error(__('Not enough arguments following: %s', key));
429 }
430 available = toEat;
431 }
432 else {
433 // nargs will not consume flag arguments, e.g., -abc, --foo,
434 // and terminates when one is observed.
435 for (ii = i + 1; ii < args.length; ii++) {
436 if (!args[ii].match(/^-[^0-9]/) || args[ii].match(negative) || isUnknownOptionAsArg(args[ii]))
437 available++;
438 else
439 break;
440 }
441 if (available < toEat)
442 error = Error(__('Not enough arguments following: %s', key));
443 }
444 let consumed = Math.min(available, toEat);
445 if (!isUndefined(argAfterEqualSign) && consumed > 0) {
446 setArg(key, argAfterEqualSign);
447 consumed--;
448 }
449 for (ii = i + 1; ii < (consumed + i + 1); ii++) {
450 setArg(key, args[ii]);
451 }
452 return (i + consumed);
453 }
454 // if an option is an array, eat all non-hyphenated arguments
455 // following it... YUM!
456 // e.g., --foo apple banana cat becomes ["apple", "banana", "cat"]
457 function eatArray(i, key, args, argAfterEqualSign) {
458 let argsToSet = [];
459 let next = argAfterEqualSign || args[i + 1];
460 // If both array and nargs are configured, enforce the nargs count:
461 const nargsCount = checkAllAliases(key, flags.nargs);
462 if (checkAllAliases(key, flags.bools) && !(/^(true|false)$/.test(next))) {
463 argsToSet.push(true);
464 }
465 else if (isUndefined(next) ||
466 (isUndefined(argAfterEqualSign) && /^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next))) {
467 // for keys without value ==> argsToSet remains an empty []
468 // set user default value, if available
469 if (defaults[key] !== undefined) {
470 const defVal = defaults[key];
471 argsToSet = Array.isArray(defVal) ? defVal : [defVal];
472 }
473 }
474 else {
475 // value in --option=value is eaten as is
476 if (!isUndefined(argAfterEqualSign)) {
477 argsToSet.push(processValue(key, argAfterEqualSign));
478 }
479 for (let ii = i + 1; ii < args.length; ii++) {
480 if ((!configuration['greedy-arrays'] && argsToSet.length > 0) ||
481 (nargsCount && typeof nargsCount === 'number' && argsToSet.length >= nargsCount))
482 break;
483 next = args[ii];
484 if (/^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next))
485 break;
486 i = ii;
487 argsToSet.push(processValue(key, next));
488 }
489 }
490 // If both array and nargs are configured, create an error if less than
491 // nargs positionals were found. NaN has special meaning, indicating
492 // that at least one value is required (more are okay).
493 if (typeof nargsCount === 'number' && ((nargsCount && argsToSet.length < nargsCount) ||
494 (isNaN(nargsCount) && argsToSet.length === 0))) {
495 error = Error(__('Not enough arguments following: %s', key));
496 }
497 setArg(key, argsToSet);
498 return i;
499 }
500 function setArg(key, val) {
501 if (/-/.test(key) && configuration['camel-case-expansion']) {
502 const alias = key.split('.').map(function (prop) {
503 return camelCase(prop);
504 }).join('.');
505 addNewAlias(key, alias);
506 }
507 const value = processValue(key, val);
508 const splitKey = key.split('.');
509 setKey(argv, splitKey, value);
510 // handle populating aliases of the full key
511 if (flags.aliases[key]) {
512 flags.aliases[key].forEach(function (x) {
513 const keyProperties = x.split('.');
514 setKey(argv, keyProperties, value);
515 });
516 }
517 // handle populating aliases of the first element of the dot-notation key
518 if (splitKey.length > 1 && configuration['dot-notation']) {
519 ;
520 (flags.aliases[splitKey[0]] || []).forEach(function (x) {
521 let keyProperties = x.split('.');
522 // expand alias with nested objects in key
523 const a = [].concat(splitKey);
524 a.shift(); // nuke the old key.
525 keyProperties = keyProperties.concat(a);
526 // populate alias only if is not already an alias of the full key
527 // (already populated above)
528 if (!(flags.aliases[key] || []).includes(keyProperties.join('.'))) {
529 setKey(argv, keyProperties, value);
530 }
531 });
532 }
533 // Set normalize getter and setter when key is in 'normalize' but isn't an array
534 if (checkAllAliases(key, flags.normalize) && !checkAllAliases(key, flags.arrays)) {
535 const keys = [key].concat(flags.aliases[key] || []);
536 keys.forEach(function (key) {
537 Object.defineProperty(argvReturn, key, {
538 enumerable: true,
539 get() {
540 return val;
541 },
542 set(value) {
543 val = typeof value === 'string' ? mixin.normalize(value) : value;
544 }
545 });
546 });
547 }
548 }
549 function addNewAlias(key, alias) {
550 if (!(flags.aliases[key] && flags.aliases[key].length)) {
551 flags.aliases[key] = [alias];
552 newAliases[alias] = true;
553 }
554 if (!(flags.aliases[alias] && flags.aliases[alias].length)) {
555 addNewAlias(alias, key);
556 }
557 }
558 function processValue(key, val) {
559 // strings may be quoted, clean this up as we assign values.
560 if (typeof val === 'string' &&
561 (val[0] === "'" || val[0] === '"') &&
562 val[val.length - 1] === val[0]) {
563 val = val.substring(1, val.length - 1);
564 }
565 // handle parsing boolean arguments --foo=true --bar false.
566 if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
567 if (typeof val === 'string')
568 val = val === 'true';
569 }
570 let value = Array.isArray(val)
571 ? val.map(function (v) { return maybeCoerceNumber(key, v); })
572 : maybeCoerceNumber(key, val);
573 // increment a count given as arg (either no value or value parsed as boolean)
574 if (checkAllAliases(key, flags.counts) && (isUndefined(value) || typeof value === 'boolean')) {
575 value = increment();
576 }
577 // Set normalized value when key is in 'normalize' and in 'arrays'
578 if (checkAllAliases(key, flags.normalize) && checkAllAliases(key, flags.arrays)) {
579 if (Array.isArray(val))
580 value = val.map((val) => { return mixin.normalize(val); });
581 else
582 value = mixin.normalize(val);
583 }
584 return value;
585 }
586 function maybeCoerceNumber(key, value) {
587 if (!configuration['parse-positional-numbers'] && key === '_')
588 return value;
589 if (!checkAllAliases(key, flags.strings) && !checkAllAliases(key, flags.bools) && !Array.isArray(value)) {
590 const shouldCoerceNumber = looksLikeNumber(value) && configuration['parse-numbers'] && (Number.isSafeInteger(Math.floor(parseFloat(`${value}`))));
591 if (shouldCoerceNumber || (!isUndefined(value) && checkAllAliases(key, flags.numbers))) {
592 value = Number(value);
593 }
594 }
595 return value;
596 }
597 // set args from config.json file, this should be
598 // applied last so that defaults can be applied.
599 function setConfig(argv) {
600 const configLookup = Object.create(null);
601 // expand defaults/aliases, in-case any happen to reference
602 // the config.json file.
603 applyDefaultsAndAliases(configLookup, flags.aliases, defaults);
604 Object.keys(flags.configs).forEach(function (configKey) {
605 const configPath = argv[configKey] || configLookup[configKey];
606 if (configPath) {
607 try {
608 let config = null;
609 const resolvedConfigPath = mixin.resolve(mixin.cwd(), configPath);
610 const resolveConfig = flags.configs[configKey];
611 if (typeof resolveConfig === 'function') {
612 try {
613 config = resolveConfig(resolvedConfigPath);
614 }
615 catch (e) {
616 config = e;
617 }
618 if (config instanceof Error) {
619 error = config;
620 return;
621 }
622 }
623 else {
624 config = mixin.require(resolvedConfigPath);
625 }
626 setConfigObject(config);
627 }
628 catch (ex) {
629 // Deno will receive a PermissionDenied error if an attempt is
630 // made to load config without the --allow-read flag:
631 if (ex.name === 'PermissionDenied')
632 error = ex;
633 else if (argv[configKey])
634 error = Error(__('Invalid JSON config file: %s', configPath));
635 }
636 }
637 });
638 }
639 // set args from config object.
640 // it recursively checks nested objects.
641 function setConfigObject(config, prev) {
642 Object.keys(config).forEach(function (key) {
643 const value = config[key];
644 const fullKey = prev ? prev + '.' + key : key;
645 // if the value is an inner object and we have dot-notation
646 // enabled, treat inner objects in config the same as
647 // heavily nested dot notations (foo.bar.apple).
648 if (typeof value === 'object' && value !== null && !Array.isArray(value) && configuration['dot-notation']) {
649 // if the value is an object but not an array, check nested object
650 setConfigObject(value, fullKey);
651 }
652 else {
653 // setting arguments via CLI takes precedence over
654 // values within the config file.
655 if (!hasKey(argv, fullKey.split('.')) || (checkAllAliases(fullKey, flags.arrays) && configuration['combine-arrays'])) {
656 setArg(fullKey, value);
657 }
658 }
659 });
660 }
661 // set all config objects passed in opts
662 function setConfigObjects() {
663 if (typeof configObjects !== 'undefined') {
664 configObjects.forEach(function (configObject) {
665 setConfigObject(configObject);
666 });
667 }
668 }
669 function applyEnvVars(argv, configOnly) {
670 if (typeof envPrefix === 'undefined')
671 return;
672 const prefix = typeof envPrefix === 'string' ? envPrefix : '';
673 const env = mixin.env();
674 Object.keys(env).forEach(function (envVar) {
675 if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) {
676 // get array of nested keys and convert them to camel case
677 const keys = envVar.split('__').map(function (key, i) {
678 if (i === 0) {
679 key = key.substring(prefix.length);
680 }
681 return camelCase(key);
682 });
683 if (((configOnly && flags.configs[keys.join('.')]) || !configOnly) && !hasKey(argv, keys)) {
684 setArg(keys.join('.'), env[envVar]);
685 }
686 }
687 });
688 }
689 function applyCoercions(argv) {
690 let coerce;
691 const applied = new Set();
692 Object.keys(argv).forEach(function (key) {
693 if (!applied.has(key)) { // If we haven't already coerced this option via one of its aliases
694 coerce = checkAllAliases(key, flags.coercions);
695 if (typeof coerce === 'function') {
696 try {
697 const value = maybeCoerceNumber(key, coerce(argv[key]));
698 ([].concat(flags.aliases[key] || [], key)).forEach(ali => {
699 applied.add(ali);
700 argv[ali] = value;
701 });
702 }
703 catch (err) {
704 error = err;
705 }
706 }
707 }
708 });
709 }
710 function setPlaceholderKeys(argv) {
711 flags.keys.forEach((key) => {
712 // don't set placeholder keys for dot notation options 'foo.bar'.
713 if (~key.indexOf('.'))
714 return;
715 if (typeof argv[key] === 'undefined')
716 argv[key] = undefined;
717 });
718 return argv;
719 }
720 function applyDefaultsAndAliases(obj, aliases, defaults, canLog = false) {
721 Object.keys(defaults).forEach(function (key) {
722 if (!hasKey(obj, key.split('.'))) {
723 setKey(obj, key.split('.'), defaults[key]);
724 if (canLog)
725 defaulted[key] = true;
726 (aliases[key] || []).forEach(function (x) {
727 if (hasKey(obj, x.split('.')))
728 return;
729 setKey(obj, x.split('.'), defaults[key]);
730 });
731 }
732 });
733 }
734 function hasKey(obj, keys) {
735 let o = obj;
736 if (!configuration['dot-notation'])
737 keys = [keys.join('.')];
738 keys.slice(0, -1).forEach(function (key) {
739 o = (o[key] || {});
740 });
741 const key = keys[keys.length - 1];
742 if (typeof o !== 'object')
743 return false;
744 else
745 return key in o;
746 }
747 function setKey(obj, keys, value) {
748 let o = obj;
749 if (!configuration['dot-notation'])
750 keys = [keys.join('.')];
751 keys.slice(0, -1).forEach(function (key) {
752 // TODO(bcoe): in the next major version of yargs, switch to
753 // Object.create(null) for dot notation:
754 key = sanitizeKey(key);
755 if (typeof o === 'object' && o[key] === undefined) {
756 o[key] = {};
757 }
758 if (typeof o[key] !== 'object' || Array.isArray(o[key])) {
759 // ensure that o[key] is an array, and that the last item is an empty object.
760 if (Array.isArray(o[key])) {
761 o[key].push({});
762 }
763 else {
764 o[key] = [o[key], {}];
765 }
766 // we want to update the empty object at the end of the o[key] array, so set o to that object
767 o = o[key][o[key].length - 1];
768 }
769 else {
770 o = o[key];
771 }
772 });
773 // TODO(bcoe): in the next major version of yargs, switch to
774 // Object.create(null) for dot notation:
775 const key = sanitizeKey(keys[keys.length - 1]);
776 const isTypeArray = checkAllAliases(keys.join('.'), flags.arrays);
777 const isValueArray = Array.isArray(value);
778 let duplicate = configuration['duplicate-arguments-array'];
779 // nargs has higher priority than duplicate
780 if (!duplicate && checkAllAliases(key, flags.nargs)) {
781 duplicate = true;
782 if ((!isUndefined(o[key]) && flags.nargs[key] === 1) || (Array.isArray(o[key]) && o[key].length === flags.nargs[key])) {
783 o[key] = undefined;
784 }
785 }
786 if (value === increment()) {
787 o[key] = increment(o[key]);
788 }
789 else if (Array.isArray(o[key])) {
790 if (duplicate && isTypeArray && isValueArray) {
791 o[key] = configuration['flatten-duplicate-arrays'] ? o[key].concat(value) : (Array.isArray(o[key][0]) ? o[key] : [o[key]]).concat([value]);
792 }
793 else if (!duplicate && Boolean(isTypeArray) === Boolean(isValueArray)) {
794 o[key] = value;
795 }
796 else {
797 o[key] = o[key].concat([value]);
798 }
799 }
800 else if (o[key] === undefined && isTypeArray) {
801 o[key] = isValueArray ? value : [value];
802 }
803 else if (duplicate && !(o[key] === undefined ||
804 checkAllAliases(key, flags.counts) ||
805 checkAllAliases(key, flags.bools))) {
806 o[key] = [o[key], value];
807 }
808 else {
809 o[key] = value;
810 }
811 }
812 // extend the aliases list with inferred aliases.
813 function extendAliases(...args) {
814 args.forEach(function (obj) {
815 Object.keys(obj || {}).forEach(function (key) {
816 // short-circuit if we've already added a key
817 // to the aliases array, for example it might
818 // exist in both 'opts.default' and 'opts.key'.
819 if (flags.aliases[key])
820 return;
821 flags.aliases[key] = [].concat(aliases[key] || []);
822 // For "--option-name", also set argv.optionName
823 flags.aliases[key].concat(key).forEach(function (x) {
824 if (/-/.test(x) && configuration['camel-case-expansion']) {
825 const c = camelCase(x);
826 if (c !== key && flags.aliases[key].indexOf(c) === -1) {
827 flags.aliases[key].push(c);
828 newAliases[c] = true;
829 }
830 }
831 });
832 // For "--optionName", also set argv['option-name']
833 flags.aliases[key].concat(key).forEach(function (x) {
834 if (x.length > 1 && /[A-Z]/.test(x) && configuration['camel-case-expansion']) {
835 const c = decamelize(x, '-');
836 if (c !== key && flags.aliases[key].indexOf(c) === -1) {
837 flags.aliases[key].push(c);
838 newAliases[c] = true;
839 }
840 }
841 });
842 flags.aliases[key].forEach(function (x) {
843 flags.aliases[x] = [key].concat(flags.aliases[key].filter(function (y) {
844 return x !== y;
845 }));
846 });
847 });
848 });
849 }
850 function checkAllAliases(key, flag) {
851 const toCheck = [].concat(flags.aliases[key] || [], key);
852 const keys = Object.keys(flag);
853 const setAlias = toCheck.find(key => keys.includes(key));
854 return setAlias ? flag[setAlias] : false;
855 }
856 function hasAnyFlag(key) {
857 const flagsKeys = Object.keys(flags);
858 const toCheck = [].concat(flagsKeys.map(k => flags[k]));
859 return toCheck.some(function (flag) {
860 return Array.isArray(flag) ? flag.includes(key) : flag[key];
861 });
862 }
863 function hasFlagsMatching(arg, ...patterns) {
864 const toCheck = [].concat(...patterns);
865 return toCheck.some(function (pattern) {
866 const match = arg.match(pattern);
867 return match && hasAnyFlag(match[1]);
868 });
869 }
870 // based on a simplified version of the short flag group parsing logic
871 function hasAllShortFlags(arg) {
872 // if this is a negative number, or doesn't start with a single hyphen, it's not a short flag group
873 if (arg.match(negative) || !arg.match(/^-[^-]+/)) {
874 return false;
875 }
876 let hasAllFlags = true;
877 let next;
878 const letters = arg.slice(1).split('');
879 for (let j = 0; j < letters.length; j++) {
880 next = arg.slice(j + 2);
881 if (!hasAnyFlag(letters[j])) {
882 hasAllFlags = false;
883 break;
884 }
885 if ((letters[j + 1] && letters[j + 1] === '=') ||
886 next === '-' ||
887 (/[A-Za-z]/.test(letters[j]) && /^-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) ||
888 (letters[j + 1] && letters[j + 1].match(/\W/))) {
889 break;
890 }
891 }
892 return hasAllFlags;
893 }
894 function isUnknownOptionAsArg(arg) {
895 return configuration['unknown-options-as-args'] && isUnknownOption(arg);
896 }
897 function isUnknownOption(arg) {
898 arg = arg.replace(/^-{3,}/, '--');
899 // ignore negative numbers
900 if (arg.match(negative)) {
901 return false;
902 }
903 // if this is a short option group and all of them are configured, it isn't unknown
904 if (hasAllShortFlags(arg)) {
905 return false;
906 }
907 // e.g. '--count=2'
908 const flagWithEquals = /^-+([^=]+?)=[\s\S]*$/;
909 // e.g. '-a' or '--arg'
910 const normalFlag = /^-+([^=]+?)$/;
911 // e.g. '-a-'
912 const flagEndingInHyphen = /^-+([^=]+?)-$/;
913 // e.g. '-abc123'
914 const flagEndingInDigits = /^-+([^=]+?\d+)$/;
915 // e.g. '-a/usr/local'
916 const flagEndingInNonWordCharacters = /^-+([^=]+?)\W+.*$/;
917 // check the different types of flag styles, including negatedBoolean, a pattern defined near the start of the parse method
918 return !hasFlagsMatching(arg, flagWithEquals, negatedBoolean, normalFlag, flagEndingInHyphen, flagEndingInDigits, flagEndingInNonWordCharacters);
919 }
920 // make a best effort to pick a default value
921 // for an option based on name and type.
922 function defaultValue(key) {
923 if (!checkAllAliases(key, flags.bools) &&
924 !checkAllAliases(key, flags.counts) &&
925 `${key}` in defaults) {
926 return defaults[key];
927 }
928 else {
929 return defaultForType(guessType(key));
930 }
931 }
932 // return a default value, given the type of a flag.,
933 function defaultForType(type) {
934 const def = {
935 [DefaultValuesForTypeKey.BOOLEAN]: true,
936 [DefaultValuesForTypeKey.STRING]: '',
937 [DefaultValuesForTypeKey.NUMBER]: undefined,
938 [DefaultValuesForTypeKey.ARRAY]: []
939 };
940 return def[type];
941 }
942 // given a flag, enforce a default type.
943 function guessType(key) {
944 let type = DefaultValuesForTypeKey.BOOLEAN;
945 if (checkAllAliases(key, flags.strings))
946 type = DefaultValuesForTypeKey.STRING;
947 else if (checkAllAliases(key, flags.numbers))
948 type = DefaultValuesForTypeKey.NUMBER;
949 else if (checkAllAliases(key, flags.bools))
950 type = DefaultValuesForTypeKey.BOOLEAN;
951 else if (checkAllAliases(key, flags.arrays))
952 type = DefaultValuesForTypeKey.ARRAY;
953 return type;
954 }
955 function isUndefined(num) {
956 return num === undefined;
957 }
958 // check user configuration settings for inconsistencies
959 function checkConfiguration() {
960 // count keys should not be set as array/narg
961 Object.keys(flags.counts).find(key => {
962 if (checkAllAliases(key, flags.arrays)) {
963 error = Error(__('Invalid configuration: %s, opts.count excludes opts.array.', key));
964 return true;
965 }
966 else if (checkAllAliases(key, flags.nargs)) {
967 error = Error(__('Invalid configuration: %s, opts.count excludes opts.narg.', key));
968 return true;
969 }
970 return false;
971 });
972 }
973 return {
974 aliases: Object.assign({}, flags.aliases),
975 argv: Object.assign(argvReturn, argv),
976 configuration: configuration,
977 defaulted: Object.assign({}, defaulted),
978 error: error,
979 newAliases: Object.assign({}, newAliases)
980 };
981 }
982}
983// if any aliases reference each other, we should
984// merge them together.
985function combineAliases(aliases) {
986 const aliasArrays = [];
987 const combined = Object.create(null);
988 let change = true;
989 // turn alias lookup hash {key: ['alias1', 'alias2']} into
990 // a simple array ['key', 'alias1', 'alias2']
991 Object.keys(aliases).forEach(function (key) {
992 aliasArrays.push([].concat(aliases[key], key));
993 });
994 // combine arrays until zero changes are
995 // made in an iteration.
996 while (change) {
997 change = false;
998 for (let i = 0; i < aliasArrays.length; i++) {
999 for (let ii = i + 1; ii < aliasArrays.length; ii++) {
1000 const intersect = aliasArrays[i].filter(function (v) {
1001 return aliasArrays[ii].indexOf(v) !== -1;
1002 });
1003 if (intersect.length) {
1004 aliasArrays[i] = aliasArrays[i].concat(aliasArrays[ii]);
1005 aliasArrays.splice(ii, 1);
1006 change = true;
1007 break;
1008 }
1009 }
1010 }
1011 }
1012 // map arrays back to the hash-lookup (de-dupe while
1013 // we're at it).
1014 aliasArrays.forEach(function (aliasArray) {
1015 aliasArray = aliasArray.filter(function (v, i, self) {
1016 return self.indexOf(v) === i;
1017 });
1018 const lastAlias = aliasArray.pop();
1019 if (lastAlias !== undefined && typeof lastAlias === 'string') {
1020 combined[lastAlias] = aliasArray;
1021 }
1022 });
1023 return combined;
1024}
1025// this function should only be called when a count is given as an arg
1026// it is NOT called to set a default value
1027// thus we can start the count at 1 instead of 0
1028function increment(orig) {
1029 return orig !== undefined ? orig + 1 : 1;
1030}
1031// TODO(bcoe): in the next major version of yargs, switch to
1032// Object.create(null) for dot notation:
1033function sanitizeKey(key) {
1034 if (key === '__proto__')
1035 return '___proto___';
1036 return key;
1037}
Note: See TracBrowser for help on using the repository browser.