lib/command.ts

876 LOC · 876 covered · 0 uncovered · 195 ranges · 1119 concepts · 93 introducers · 788 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > import { yargs-factory.ts ×124
2 > Dictionary,
3 > assertNotStrictEqual,
4 > RequireDirectoryOptions,
5 > PlatformShim,
6 > } from './typings/common-types.js';
7 > import {isPromise} from './utils/is-promise.js';
8 > import {
9 > applyMiddleware,
10 > commandMiddlewareFactory,
11 > GlobalMiddleware,
12 > Middleware,
13 > } from './middleware.js';
14 > import {parseCommand, Positional} from './parse-command.js';
15 > import {UsageInstance} from './usage.js';
16 > import {ValidationInstance} from './validation.js';
17 > import {
18 > YargsInstance,
19 > isYargsInstance,
20 > Options,
21 > OptionDefinition,
22 > Context,
23 > Configuration,
24 > Arguments,
25 > DetailedArguments,
26 > } from './yargs-factory.js';
27 > import {maybeAsyncResult} from './utils/maybe-async-result.js';
28 >
29 > const DEFAULT_MARKER = /(^\*)|(^\$0)/;
30 > export type DefinitionOrCommandName = string | CommandHandlerDefinition;
31 >
32 > export class CommandInstance {
33 > shim: PlatformShim;
34 > requireCache: Set<string> = new Set();
35 > handlers: Dictionary<CommandHandler> = {};
36 > aliasMap: Dictionary<string> = {};
37 > defaultCommand?: CommandHandler;
38 > usage: UsageInstance;
39 > globalMiddleware: GlobalMiddleware;
40 > validation: ValidationInstance;
41 > // Used to cache state from prior invocations of commands.
42 > // This allows the parser to push and pop state when running
43 > // a nested command:
44 > frozens: FrozenCommandInstance[] = [];
45 > constructor(
46 > usage: UsageInstance, yargs-factory.ts ×35
47 > validation: ValidationInstance,
48 > globalMiddleware: GlobalMiddleware,
49 > shim: PlatformShim
50 > ) {
51 > this.shim = shim;
52 > this.usage = usage;
53 > this.globalMiddleware = globalMiddleware;
54 > this.validation = validation;
55 > }
56 > addDirectory( yargs-factory.ts ×124
57 > dir: string, command.ts ×4
58 > req: Function,
59 > callerFile: string,
60 > opts?: RequireDirectoryOptions
61 > ): void {
62 > opts = opts || {};
63 > this.requireCache.add(callerFile);
64 > const fullDirPath = this.shim.path.resolve(
65 > this.shim.path.dirname(callerFile),
66 > dir
67 > );
68 > const files = this.shim.readdirSync(fullDirPath, {
69 > recursive: opts.recurse ? true : false,
70 > });
71 > // exclude 'json', 'coffee' from require-directory defaults
72 > if (!Array.isArray(opts.extensions)) opts.extensions = ['js'];
73 > // allow consumer to define their own visitor function
74 > const visit = typeof opts.visit === 'function' ? opts.visit : (o: any) => o;
75 > for (const fileb of files) {
76 > const file = fileb.toString();
77 >
78 > // Support include / exclude logic from require-directory.
79 > if (opts.exclude) {
80 > let exclude = false; command.ts ×3
81 > if (typeof opts.exclude === 'function') {
82 > exclude = opts.exclude(file); command.ts ×1
83 > } else { command.ts ×3
84 > exclude = opts.exclude.test(file); command.ts ×1
85 > }
86 > if (exclude) continue; command.ts ×3
87 > }
88 > if (opts.include) { command.ts ×4
89 > let include = false; command.ts ×3
90 > if (typeof opts.include === 'function') {
91 > include = opts.include(file); command.ts ×1
92 > } else { command.ts ×3
93 > include = opts.include.test(file); command.ts ×1
94 > }
95 > if (!include) continue; command.ts ×3
96 > }
98 > let supportedExtension = false;
99 > for (const ext of opts.extensions) {
100 > if (file.endsWith(ext)) supportedExtension = true;
101 > }
102 > if (supportedExtension) {
103 > const joined = this.shim.path.join(fullDirPath, file);
104 > const module = req(joined);
105 > const extendableModule = Object.create(
106 > null,
107 > Object.getOwnPropertyDescriptors({...module})
108 > );
109 > const visited = visit(extendableModule, joined, file);
110 > if (visited) {
111 > if (this.requireCache.has(joined)) continue; command.ts ×2
112 > else this.requireCache.add(joined);
113 > // Infer command from directory structure if none is given:
114 > if (!extendableModule.command) {
115 > extendableModule.command = this.shim.path.basename( command.ts ×1
116 > joined,
117 > this.shim.path.extname(joined)
118 > );
119 > }
120 > this.addHandler(extendableModule); command.ts ×2
121 > }
122 > } command.ts ×4
123 > }
124 > }
125 > addHandler( yargs-factory.ts ×124
126 > cmd: string | CommandHandlerDefinition | DefinitionOrCommandName[], command.ts ×3
127 > description?: CommandHandler['description'],
128 > builder?: CommandBuilderDefinition | CommandBuilder,
129 > handler?: CommandHandlerCallback,
130 > commandMiddleware?: Middleware[],
131 > deprecated?: boolean
132 > ): void {
133 > let aliases: string[] = [];
134 > const middlewares = commandMiddlewareFactory(commandMiddleware);
135 > handler = handler || (() => {});
136 >
137 > // If an array is provided that is all CommandHandlerDefinitions, add
138 > // each handler individually:
139 > if (Array.isArray(cmd)) {
140 > if (isCommandAndAliases(cmd)) { command.ts ×3
141 > [cmd, ...aliases] = cmd; command.ts ×1
142 > } else { command.ts ×3
143 > for (const command of cmd) { command.ts ×1
144 > this.addHandler(command);
145 > }
146 > }
147 > } else if (isCommandHandlerDefinition(cmd)) { command.ts ×3
148 > let command = command.ts ×1
149 > Array.isArray(cmd.command) || typeof cmd.command === 'string'
150 > ? cmd.command
151 > : null;
152 > if (command === null) {
153 > throw new Error( command.ts ×1
154 > `No command name given for module: ${this.shim.inspect(cmd)}`
155 > );
156 > }
157 > if (cmd.aliases) command.ts ×2
158 > command = ([] as string[]).concat(command).concat(cmd.aliases);
159 > this.addHandler(
160 > command,
161 > this.extractDesc(cmd),
162 > cmd.builder,
163 > cmd.handler,
164 > cmd.middlewares,
165 > cmd.deprecated
166 > );
167 > return;
168 > } else if (isCommandBuilderDefinition(builder)) { command.ts ×1
169 > // Allow a module to be provided as builder, rather than function: command.ts ×2
170 > this.addHandler(
171 > [cmd].concat(aliases),
172 > description,
173 > builder.builder,
174 > builder.handler,
175 > builder.middlewares,
176 > builder.deprecated
177 > );
178 > return;
179 > }
181 > // The 'cmd' provided was a string, we apply the command DSL:
182 > // https://github.com/yargs/yargs/blob/main/docs/advanced.md#advanced-topics
183 > if (typeof cmd === 'string') {
184 > // parse positionals out of cmd string
185 > const parsedCommand = parseCommand(cmd);
186 >
187 > // remove positional args from aliases only
188 > aliases = aliases.map(alias => parseCommand(alias).cmd);
189 >
190 > // check for default and filter out '*'
191 > let isDefault = false;
192 > const parsedAliases = [parsedCommand.cmd].concat(aliases).filter(c => {
193 > if (DEFAULT_MARKER.test(c)) {
194 > isDefault = true; command.ts ×2
195 > return false;
196 > }
197 > return true; command.ts ×1
198 > }); command.ts ×5
199 >
200 > // standardize on $0 for default command.
201 > if (parsedAliases.length === 0 && isDefault) parsedAliases.push('$0');
202 >
203 > // shift cmd and aliases after filtering out '*'
204 > if (isDefault) {
205 > parsedCommand.cmd = parsedAliases[0]; command.ts ×2
206 > aliases = parsedAliases.slice(1);
207 > cmd = cmd.replace(DEFAULT_MARKER, parsedCommand.cmd);
208 > }
210 > // populate aliasMap
211 > aliases.forEach(alias => {
212 > this.aliasMap[alias] = parsedCommand.cmd; command.ts ×1
213 > }); command.ts ×5
214 >
215 > if (description !== false) {
216 > this.usage.command(cmd, description, isDefault, aliases, deprecated); usage.ts ×2
217 > }
219 > this.handlers[parsedCommand.cmd] = {
220 > original: cmd,
221 > description,
222 > handler,
223 > builder: (builder as CommandBuilder) || {},
224 > middlewares,
225 > deprecated,
226 > demanded: parsedCommand.demanded,
227 > optional: parsedCommand.optional,
228 > };
229 >
230 > if (isDefault) this.defaultCommand = this.handlers[parsedCommand.cmd];
231 > }
232 > } command.ts ×3
233 > getCommandHandlers(): Dictionary<CommandHandler> { yargs-factory.ts ×124
234 > return this.handlers; command.ts ×1
235 > }
236 > getCommands(): string[] { yargs-factory.ts ×124
237 > return Object.keys(this.handlers).concat(Object.keys(this.aliasMap)); command.ts ×1
238 > }
239 > hasDefaultCommand(): boolean { yargs-factory.ts ×124
240 > return !!this.defaultCommand; yargs-factory.ts ×5
241 > }
242 > runCommand( yargs-factory.ts ×124
243 > command: string | null, command.ts ×12
244 > yargs: YargsInstance,
245 > parsed: DetailedArguments,
246 > commandIndex: number,
247 > helpOnly: boolean,
248 > helpOrVersionSet: boolean
249 > ): Arguments | Promise<Arguments> {
250 > const commandHandler =
251 > this.handlers[command!] ||
252 > this.handlers[this.aliasMap[command!]] ||
253 > this.defaultCommand;
254 > const currentContext = yargs.getInternalMethods().getContext();
255 > const parentCommands = currentContext.commands.slice();
256 > const isDefaultCommand = !command;
257 > if (command) {
258 > currentContext.commands.push(command); command.ts ×1
259 > currentContext.fullCommands.push(commandHandler.original);
260 > }
261 > const builderResult = this.applyBuilderUpdateUsageAndParse( command.ts ×12
262 > isDefaultCommand,
263 > commandHandler,
264 > yargs,
265 > parsed.aliases,
266 > parentCommands,
267 > commandIndex,
268 > helpOnly,
269 > helpOrVersionSet
270 > );
271 > return isPromise(builderResult)
272 > ? builderResult.then(result =>
273 > this.applyMiddlewareAndGetResult( command.ts ×1
274 > isDefaultCommand,
275 > commandHandler,
276 > result.innerArgv,
277 > currentContext,
278 > helpOnly,
279 > result.aliases,
280 > yargs
281 > ) command.ts ×1
282 > ) command.ts ×12
283 > : this.applyMiddlewareAndGetResult(
284 > isDefaultCommand, command.ts ×2
285 > commandHandler,
286 > builderResult.innerArgv,
287 > currentContext,
288 > helpOnly,
289 > builderResult.aliases,
290 > yargs
291 > ); command.ts ×12
292 > }
293 > private applyBuilderUpdateUsageAndParse( yargs-factory.ts ×124
294 > isDefaultCommand: boolean, command.ts ×12
295 > commandHandler: CommandHandler,
296 > yargs: YargsInstance,
297 > aliases: Dictionary<string[]>,
298 > parentCommands: string[],
299 > commandIndex: number,
300 > helpOnly: boolean,
301 > helpOrVersionSet: boolean
302 > ):
303 > | {aliases: Dictionary<string[]>; innerArgv: Arguments}
304 > | Promise<{aliases: Dictionary<string[]>; innerArgv: Arguments}> {
305 > const builder = commandHandler.builder;
306 > let innerYargs: YargsInstance = yargs;
307 > if (isCommandBuilderCallback(builder)) {
308 > // A function can be provided, which builds command.ts ×1
309 > // up a yargs chain and possibly returns it.
310 > yargs.getInternalMethods().getUsageInstance().freeze();
311 > const builderOutput = builder(
312 > yargs.getInternalMethods().reset(aliases),
313 > helpOrVersionSet
314 > );
315 > // Support the use-case of async builders:
316 > if (isPromise(builderOutput)) {
317 > return builderOutput.then(output => { command.ts ×1
318 > innerYargs = isYargsInstance(output) ? output : yargs;
319 > return this.parseAndUpdateUsage(
320 > isDefaultCommand,
321 > commandHandler,
322 > innerYargs,
323 > parentCommands,
324 > commandIndex,
325 > helpOnly
326 > );
327 > });
328 > }
329 > } else if (isCommandBuilderOptionDefinitions(builder)) { command.ts ×12
330 > // as a short hand, an object can instead be provided, specifying command.ts ×3
331 > // the options that a command takes.
332 > yargs.getInternalMethods().getUsageInstance().freeze();
333 > innerYargs = yargs.getInternalMethods().reset(aliases);
334 > Object.keys(commandHandler.builder).forEach(key => {
335 > innerYargs.option(key, builder[key]); command.ts ×1
336 > }); command.ts ×3
337 > }
338 > return this.parseAndUpdateUsage( command.ts ×2
339 > isDefaultCommand,
340 > commandHandler,
341 > innerYargs,
342 > parentCommands,
343 > commandIndex,
344 > helpOnly
345 > );
346 > }
347 > private parseAndUpdateUsage( yargs-factory.ts ×124
348 > isDefaultCommand: boolean, command.ts ×12
349 > commandHandler: CommandHandler,
350 > innerYargs: YargsInstance,
351 > parentCommands: string[],
352 > commandIndex: number,
353 > helpOnly: boolean
354 > ):
355 > | {aliases: Dictionary<string[]>; innerArgv: Arguments}
356 > | Promise<{aliases: Dictionary<string[]>; innerArgv: Arguments}> {
357 > // A null command indicates we are running the default command,
358 > // if this is the case, we should show the root usage instructions
359 > // rather than the usage instructions for the nested default command:
360 > if (isDefaultCommand)
361 > innerYargs.getInternalMethods().getUsageInstance().unfreeze(true);
362 > if (this.shouldUpdateUsage(innerYargs)) {
363 > innerYargs command.ts ×3
364 > .getInternalMethods()
365 > .getUsageInstance()
366 > .usage(
367 > this.usageFromParentCommandsCommandHandler(
368 > parentCommands,
369 > commandHandler
370 > ),
371 > commandHandler.description
372 > );
373 > }
374 > const innerArgv = innerYargs command.ts ×12
375 > .getInternalMethods()
376 > .runYargsParserAndExecuteCommands(
377 > null,
378 > undefined,
379 > true,
380 > commandIndex,
381 > helpOnly
382 > );
383 >
384 > return isPromise(innerArgv)
385 > ? innerArgv.then(argv => ({
386 > aliases: (innerYargs.parsed as DetailedArguments).aliases, command.ts ×1
387 > innerArgv: argv,
388 > })) command.ts ×1
389 > : { command.ts ×12
390 > aliases: (innerYargs.parsed as DetailedArguments).aliases,
391 > innerArgv: innerArgv,
392 > };
393 > }
394 > private shouldUpdateUsage(yargs: YargsInstance) { yargs-factory.ts ×124
395 > return ( command.ts ×2
396 > !yargs.getInternalMethods().getUsageInstance().getUsageDisabled() &&
397 > yargs.getInternalMethods().getUsageInstance().getUsage().length === 0 command.ts ×1
398 > ); command.ts ×2
399 > }
400 > private usageFromParentCommandsCommandHandler( yargs-factory.ts ×124
401 > parentCommands: string[], command.ts ×3
402 > commandHandler: CommandHandler
403 > ) {
404 > const c = DEFAULT_MARKER.test(commandHandler.original)
405 > ? commandHandler.original.replace(DEFAULT_MARKER, '').trim()
406 > : commandHandler.original;
407 > const pc = parentCommands.filter(c => {
408 > return !DEFAULT_MARKER.test(c); command.ts ×1
409 > }); command.ts ×3
410 > pc.push(c);
411 > return `$0 ${pc.join(' ')}`;
412 > }
413 > private handleValidationAndGetResult( yargs-factory.ts ×124
414 > isDefaultCommand: boolean, command.ts ×2
415 > commandHandler: CommandHandler,
416 > innerArgv: Arguments | Promise<Arguments>,
417 > currentContext: Context,
418 > aliases: Dictionary<string[]>,
419 > yargs: YargsInstance,
420 > middlewares: Middleware[],
421 > positionalMap: Dictionary<string[]>
422 > ) {
423 > // we apply validation post-hoc, so that custom
424 > // checks get passed populated positional arguments.
425 > if (!yargs.getInternalMethods().getHasOutput()) {
426 > const validation = yargs command.ts ×1
427 > .getInternalMethods()
428 > .runValidation(
429 > aliases,
430 > positionalMap,
431 > (yargs.parsed as DetailedArguments).error,
432 > isDefaultCommand
433 > );
434 > innerArgv = maybeAsyncResult<Arguments>(innerArgv, result => {
435 > validation(result);
436 > return result;
437 > });
438 > }
440 > if (commandHandler.handler && !yargs.getInternalMethods().getHasOutput()) { command.ts ×2
441 > yargs.getInternalMethods().setHasOutput(); command.ts ×4
442 > // to simplify the parsing of positionals in commands,
443 > // we temporarily populate '--' rather than _, with arguments
444 > const populateDoubleDash =
445 > !!yargs.getOptions().configuration['populate--'];
446 > yargs
447 > .getInternalMethods()
448 > .postProcess(innerArgv, populateDoubleDash, false, false);
449 >
450 > innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false);
451 > innerArgv = maybeAsyncResult<Arguments>(innerArgv, result => {
452 > const handlerResult = commandHandler.handler(result as Arguments); command.ts ×1
453 > return isPromise(handlerResult)
454 > ? handlerResult.then(() => result)
455 > : result;
456 > }); command.ts ×4
457 >
458 > if (!isDefaultCommand) {
459 > yargs.getInternalMethods().getUsageInstance().cacheHelpMessage(); command.ts ×1
460 > }
462 > if (
463 > isPromise(innerArgv) &&
464 > !yargs.getInternalMethods().hasParseCallback() command.ts ×1
465 > ) { command.ts ×4
466 > innerArgv.catch(error => { command.ts ×2
467 > try { command.ts ×1
468 > yargs.getInternalMethods().getUsageInstance().fail(null, error);
469 > } catch (_err) {
470 > // If .fail(false) is not set, and no parse cb() has been command.ts ×1
471 > // registered, run usage's default fail method.
472 > }
473 > }); command.ts ×2
474 > }
475 > } command.ts ×4
477 > if (!isDefaultCommand) {
478 > currentContext.commands.pop(); command.ts ×1
479 > currentContext.fullCommands.pop();
480 > }
482 > return innerArgv;
483 > }
484 > private applyMiddlewareAndGetResult( yargs-factory.ts ×124
485 > isDefaultCommand: boolean, command.ts ×12
486 > commandHandler: CommandHandler,
487 > innerArgv: Arguments,
488 > currentContext: Context,
489 > helpOnly: boolean,
490 > aliases: Dictionary<string[]>,
491 > yargs: YargsInstance
492 > ): Arguments | Promise<Arguments> {
493 > let positionalMap: Dictionary<string[]> = {};
494 > // If showHelp() or getHelp() is being run, we should not
495 > // execute middleware or handlers (these may perform expensive operations
496 > // like creating a DB connection).
497 > if (helpOnly) return innerArgv;
498 > if (!yargs.getInternalMethods().getHasOutput()) { command.ts ×1
499 > positionalMap = this.populatePositionals( command.ts ×8
500 > commandHandler,
501 > innerArgv as Arguments,
502 > currentContext,
503 > yargs
504 > );
505 > }
506 > const middlewares = this.globalMiddleware command.ts ×1
507 > .getMiddleware()
508 > .slice(0)
509 > .concat(commandHandler.middlewares);
510 >
511 > const maybePromiseArgv = applyMiddleware(
512 > innerArgv,
513 > yargs,
514 > middlewares,
515 > true
516 > );
517 >
518 > return isPromise(maybePromiseArgv)
519 > ? maybePromiseArgv.then(resolvedInnerArgv =>
520 > this.handleValidationAndGetResult( command.ts ×1
521 > isDefaultCommand,
522 > commandHandler,
523 > resolvedInnerArgv,
524 > currentContext,
525 > aliases,
526 > yargs,
527 > middlewares,
528 > positionalMap
529 > )
530 > ) command.ts ×12
531 > : this.handleValidationAndGetResult(
532 > isDefaultCommand, command.ts ×1
533 > commandHandler,
534 > maybePromiseArgv,
535 > currentContext,
536 > aliases,
537 > yargs,
538 > middlewares,
539 > positionalMap
540 > ); command.ts ×12
541 > }
542 > // transcribe all positional arguments "command <foo> <bar> [apple]" yargs-factory.ts ×124
543 > // onto argv.
544 > private populatePositionals(
545 > commandHandler: CommandHandler, command.ts ×8
546 > argv: Arguments,
547 > context: Context,
548 > yargs: YargsInstance
549 > ) {
550 > argv._ = argv._.slice(context.commands.length); // nuke the current commands
551 > const demanded = commandHandler.demanded.slice(0);
552 > const optional = commandHandler.optional.slice(0);
553 > const positionalMap: Dictionary<string[]> = {};
554 >
555 > this.validation.positionalCount(demanded.length, argv._.length);
556 >
557 > while (demanded.length) {
558 > const demand = demanded.shift()!; command.ts ×1
559 > this.populatePositional(demand, argv, positionalMap);
560 > }
562 > while (optional.length) {
563 > const maybe = optional.shift()!; command.ts ×1
564 > this.populatePositional(maybe, argv, positionalMap);
565 > }
567 > argv._ = context.commands.concat(argv._.map(a => '' + a));
568 >
569 > this.postProcessPositionals(
570 > argv,
571 > positionalMap,
572 > this.cmdToParseOptions(commandHandler.original),
573 > yargs
574 > );
575 >
576 > return positionalMap;
577 > }
579 > private populatePositional(
580 > positional: Positional, command.ts ×4
581 > argv: Arguments,
582 > positionalMap: Dictionary<string[]>
583 > ) {
584 > const cmd = positional.cmd[0];
585 > if (positional.variadic) {
586 > positionalMap[cmd] = argv._.splice(0).map(String); command.ts ×1
587 > } else { command.ts ×4
588 > if (argv._.length) positionalMap[cmd] = [String(argv._.shift())]; command.ts ×1
589 > }
590 > } command.ts ×4
592 > // Based on parsing variadic markers '...', demand syntax '<foo>', etc.,
593 > // populate parser hints:
594 > public cmdToParseOptions(cmdString: string): Positionals {
595 > const parseOptions: Positionals = { command.ts ×3
596 > array: [],
597 > default: {},
598 > alias: {},
599 > demand: {},
600 > };
601 >
602 > const parsed = parseCommand(cmdString);
603 > parsed.demanded.forEach(d => {
604 > const [cmd, ...aliases] = d.cmd; command.ts ×2
605 > if (d.variadic) {
606 > parseOptions.array.push(cmd); command.ts ×1
607 > parseOptions.default[cmd] = [];
608 > }
609 > parseOptions.alias[cmd] = aliases; command.ts ×2
610 > parseOptions.demand[cmd] = true;
611 > }); command.ts ×3
612 >
613 > parsed.optional.forEach(o => {
614 > const [cmd, ...aliases] = o.cmd; command.ts ×2
615 > if (o.variadic) {
616 > parseOptions.array.push(cmd); command.ts ×1
617 > parseOptions.default[cmd] = [];
618 > }
619 > parseOptions.alias[cmd] = aliases; command.ts ×2
620 > }); command.ts ×3
621 >
622 > return parseOptions;
623 > }
625 > // we run yargs-parser against the positional arguments
626 > // applying the same parsing logic used for flags.
627 > private postProcessPositionals(
628 > argv: Arguments, command.ts ×8
629 > positionalMap: Dictionary<string[]>,
630 > parseOptions: Positionals,
631 > yargs: YargsInstance
632 > ) {
633 > // combine the parsing hints we've inferred from the command
634 > // string with explicitly configured parsing hints.
635 > const options = Object.assign({}, yargs.getOptions());
636 > options.default = Object.assign(parseOptions.default, options.default);
637 > for (const key of Object.keys(parseOptions.alias)) {
638 > options.alias[key] = (options.alias[key] || []).concat( command.ts ×4
639 > parseOptions.alias[key]
640 > );
641 > }
642 > options.array = options.array.concat(parseOptions.array); command.ts ×8
643 > options.config = {}; // don't load config when processing positionals.
644 >
645 > const unparsed: string[] = [];
646 > Object.keys(positionalMap).forEach(key => {
647 > positionalMap[key].map(value => { command.ts ×2
648 > if (options.configuration['unknown-options-as-args']) command.ts ×3
649 > options.key[key] = true;
650 > unparsed.push(`--${key}`);
651 > unparsed.push(value);
652 > }); command.ts ×2
653 > }); command.ts ×8
654 >
655 > // short-circuit parse.
656 > if (!unparsed.length) return;
658 > const config: Configuration = Object.assign({}, options.configuration, {
659 > 'populate--': false,
660 > });
661 >
662 > const parsed = this.shim.Parser.detailed(
663 > unparsed,
664 > Object.assign({}, options, {
665 > configuration: config,
666 > })
667 > );
668 >
669 > if (parsed.error) {
670 > yargs command.ts ×1
671 > .getInternalMethods()
672 > .getUsageInstance()
673 > .fail(parsed.error.message, parsed.error);
674 > } else { command.ts ×3
675 > // only copy over positional keys (don't overwrite command.ts ×8
676 > // flag arguments that were already parsed).
677 > const positionalKeys = Object.keys(positionalMap);
678 > Object.keys(positionalMap).forEach(key => {
679 > positionalKeys.push(...parsed.aliases[key]);
680 > });
681 >
682 > Object.keys(parsed.argv).forEach(key => {
683 > if (positionalKeys.includes(key)) {
684 > // any new aliases need to be placed in positionalMap, which
685 > // is used for validation.
686 > if (!positionalMap[key]) positionalMap[key] = parsed.argv[key];
687 > // Addresses: https://github.com/yargs/yargs/issues/1637
688 > // If both positionals/options provided,
689 > // and no default or config values were set for that key,
690 > // and if at least one is an array: don't overwrite, combine.
691 > if (
692 > !this.isInConfigs(yargs, key) &&
693 > !this.isDefaulted(yargs, key) &&
694 > Object.prototype.hasOwnProperty.call(argv, key) &&
695 > Object.prototype.hasOwnProperty.call(parsed.argv, key) &&
696 > (Array.isArray(argv[key]) || Array.isArray(parsed.argv[key])) command.ts ×2
697 > ) { command.ts ×8
698 > argv[key] = ([] as string[]).concat(argv[key], parsed.argv[key]); command.ts ×2
699 > } else { command.ts ×8
700 > argv[key] = parsed.argv[key]; command.ts ×1
701 > }
702 > } command.ts ×8
703 > });
704 > }
705 > } command.ts ×8
706 > // Check defaults for key (and camel case version of key) yargs-factory.ts ×124
707 > isDefaulted(yargs: YargsInstance, key: string): boolean {
708 > const {default: defaults} = yargs.getOptions(); command.ts ×8
709 > return (
710 > Object.prototype.hasOwnProperty.call(defaults, key) ||
711 > Object.prototype.hasOwnProperty.call( command.ts ×1
712 > defaults,
713 > this.shim.Parser.camelCase(key)
714 > ) command.ts ×8
715 > );
716 > }
717 > // Check each config for key (and camel case version of key) yargs-factory.ts ×124
718 > isInConfigs(yargs: YargsInstance, key: string): boolean {
719 > const {configObjects} = yargs.getOptions(); command.ts ×8
720 > return (
721 > configObjects.some(c => Object.prototype.hasOwnProperty.call(c, key)) ||
722 > configObjects.some(c =>
723 > Object.prototype.hasOwnProperty.call(c, this.shim.Parser.camelCase(key)) command.ts ×1
724 > ) command.ts ×8
725 > );
726 > }
727 > runDefaultBuilderOn(yargs: YargsInstance): unknown | Promise<unknown> { yargs-factory.ts ×124
728 > if (!this.defaultCommand) return; command.ts ×1
729 > if (this.shouldUpdateUsage(yargs)) { command.ts ×3
730 > // build the root-level command string from the default string. command.ts ×1
731 > const commandString = DEFAULT_MARKER.test(this.defaultCommand.original)
732 > ? this.defaultCommand.original
733 > : this.defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ');
734 > yargs
735 > .getInternalMethods()
736 > .getUsageInstance()
737 > .usage(commandString, this.defaultCommand.description);
738 > }
739 > const builder = this.defaultCommand.builder; command.ts ×3
740 > if (isCommandBuilderCallback(builder)) {
741 > return builder(yargs, true); command.ts ×1
742 > } else if (!isCommandBuilderDefinition(builder)) { command.ts ×3
743 > Object.keys(builder).forEach(key => { command.ts ×2
744 > yargs.option(key, builder[key]); command.ts ×1
745 > }); command.ts ×2
746 > }
747 > return undefined;
748 > }
750 > private extractDesc({describe, description, desc}: CommandHandlerDefinition) {
751 > for (const test of [describe, description, desc]) { command.ts ×2
752 > if (typeof test === 'string' || test === false) return test;
753 > assertNotStrictEqual(test, true as const, this.shim); command.ts ×1
754 > }
755 > return false; command.ts ×1
756 > }
757 > // Push/pop the current command configuration: yargs-factory.ts ×124
758 > freeze() {
759 > this.frozens.push({ yargs-factory.ts ×5
760 > handlers: this.handlers,
761 > aliasMap: this.aliasMap,
762 > defaultCommand: this.defaultCommand,
763 > });
764 > }
765 > unfreeze() { yargs-factory.ts ×124
766 > const frozen = this.frozens.pop(); yargs-factory.ts ×2
767 > assertNotStrictEqual(frozen, undefined, this.shim);
768 > ({
769 > handlers: this.handlers,
770 > aliasMap: this.aliasMap,
771 > defaultCommand: this.defaultCommand,
772 > } = frozen);
773 > }
774 > // Revert to initial state: yargs-factory.ts ×124
775 > reset(): CommandInstance {
776 > this.handlers = {}; command.ts ×1
777 > this.aliasMap = {};
778 > this.defaultCommand = undefined;
779 > this.requireCache = new Set();
780 > return this;
781 > }
783 >
784 > // Adds support to yargs for lazy loading a hierarchy of commands:
785 > export function command(
786 > usage: UsageInstance, yargs-factory.ts ×35
787 > validation: ValidationInstance,
788 > globalMiddleware: GlobalMiddleware,
789 > shim: PlatformShim
790 > ) {
791 > return new CommandInstance(usage, validation, globalMiddleware, shim);
792 > }
794 > export interface CommandHandlerDefinition extends Partial<
795 > Pick<CommandHandler, 'deprecated' | 'description' | 'handler' | 'middlewares'>
796 > > {
797 > aliases?: string[];
798 > builder?: CommandBuilder | CommandBuilderDefinition;
799 > command?: string | string[];
800 > desc?: CommandHandler['description'];
801 > describe?: CommandHandler['description'];
802 > }
803 >
804 > export interface CommandBuilderDefinition {
805 > builder?: CommandBuilder;
806 > deprecated?: boolean;
807 > handler: CommandHandlerCallback;
808 > middlewares?: Middleware[];
809 > }
810 >
811 > export function isCommandBuilderDefinition(
812 > builder?: CommandBuilder | CommandBuilderDefinition command.ts ×2
813 > ): builder is CommandBuilderDefinition {
814 > return (
815 > typeof builder === 'object' &&
816 > !!(builder as CommandBuilderDefinition).builder &&
817 > typeof (builder as CommandBuilderDefinition).handler === 'function' command.ts ×2
818 > ); command.ts ×2
819 > }
821 > export interface CommandHandlerCallback {
822 > (argv: Arguments): any;
823 > }
824 >
825 > export interface CommandHandler {
826 > builder: CommandBuilder;
827 > demanded: Positional[];
828 > deprecated?: boolean;
829 > description?: string | false;
830 > handler: CommandHandlerCallback;
831 > middlewares: Middleware[];
832 > optional: Positional[];
833 > original: string;
834 > }
835 >
836 > // To be completed later with other CommandBuilder flavours
837 > export type CommandBuilder =
838 > CommandBuilderCallback | Dictionary<OptionDefinition>;
839 >
840 > interface CommandBuilderCallback {
841 > (y: YargsInstance, helpOrVersionSet: boolean): YargsInstance | void;
842 > }
843 >
844 > function isCommandAndAliases( command.ts ×3
845 > cmd: DefinitionOrCommandName[]
846 > ): cmd is [CommandHandlerDefinition, ...string[]] {
847 > return cmd.every(c => typeof c === 'string');
848 > }
850 > export function isCommandBuilderCallback(
851 > builder: CommandBuilder command.ts ×1
852 > ): builder is CommandBuilderCallback {
853 > return typeof builder === 'function';
854 > }
856 > function isCommandBuilderOptionDefinitions( command.ts ×3
857 > builder: CommandBuilder
858 > ): builder is Dictionary<OptionDefinition> {
859 > return typeof builder === 'object';
860 > }
862 > export function isCommandHandlerDefinition(
863 > cmd: DefinitionOrCommandName | [DefinitionOrCommandName, ...string[]] command.ts ×1
864 > ): cmd is CommandHandlerDefinition {
865 > return typeof cmd === 'object' && !Array.isArray(cmd);
866 > }
868 > interface Positionals extends Pick<Options, 'alias' | 'array' | 'default'> {
869 > demand: Dictionary<boolean>;
870 > }
871 >
872 > type FrozenCommandInstance = {
873 > handlers: Dictionary<CommandHandler>;
874 > aliasMap: Dictionary<string>;
875 > defaultCommand: CommandHandler | undefined;
876 > };