Skip to main content
Module

x/yargs/lib/yargs-factory.ts

yargs the modern, pirate-themed successor to optimist.
Very Popular
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397
/* eslint-disable @typescript-eslint/no-unused-vars */// Platform agnostic entrypoint for yargs, i.e., this factory is used to// create an instance of yargs for CJS, ESM, Deno.//// Works by accepting a shim which shims methods that contain platform// specific logic.import { command as Command, CommandInstance, CommandHandler, CommandBuilderDefinition, CommandBuilder, CommandHandlerCallback, CommandHandlerDefinition, DefinitionOrCommandName,} from './command.js';import type { Dictionary, KeyOf, DictionaryKeyof, ValueOf, RequireDirectoryOptions, PlatformShim, RequireType, nil,} from './typings/common-types.js';import { assertNotStrictEqual, assertSingleKey, objectKeys,} from './typings/common-types.js';import { ArgsOutput, DetailedArguments as ParserDetailedArguments, Configuration as ParserConfiguration, Options as ParserOptions, ConfigCallback, CoerceCallback,} from './typings/yargs-parser-types.js';import {YError} from './yerror.js';import {UsageInstance, FailureFunction, usage as Usage} from './usage.js';import {argsert} from './argsert.js';import { completion as Completion, CompletionInstance, CompletionFunction,} from './completion.js';import { validation as Validation, ValidationInstance, KeyOrPos,} from './validation.js';import {objFilter} from './utils/obj-filter.js';import {applyExtends} from './utils/apply-extends.js';import { applyMiddleware, GlobalMiddleware, MiddlewareCallback, Middleware,} from './middleware.js';import {isPromise} from './utils/is-promise.js';import {maybeAsyncResult} from './utils/maybe-async-result.js';import setBlocking from './utils/set-blocking.js';
export function YargsFactory(_shim: PlatformShim) { return ( processArgs: string | string[] = [], cwd = _shim.process.cwd(), parentRequire?: RequireType ): YargsInstance => { const yargs = new YargsInstance(processArgs, cwd, parentRequire, _shim); // Legacy yargs.argv interface, it's recommended that you use .parse(). Object.defineProperty(yargs, 'argv', { get: () => { return yargs.parse(); }, enumerable: true, }); // an app should almost always have --version and --help, // if you *really* want to disable this use .help(false)/.version(false). yargs.help(); yargs.version(); return yargs; };}
// Used to expose private methods to other module-level classes,// such as the command parser and usage printer.const kCopyDoubleDash = Symbol('copyDoubleDash');const kCreateLogger = Symbol('copyDoubleDash');const kDeleteFromParserHintObject = Symbol('deleteFromParserHintObject');const kEmitWarning = Symbol('emitWarning');const kFreeze = Symbol('freeze');const kGetDollarZero = Symbol('getDollarZero');const kGetParserConfiguration = Symbol('getParserConfiguration');const kGuessLocale = Symbol('guessLocale');const kGuessVersion = Symbol('guessVersion');const kParsePositionalNumbers = Symbol('parsePositionalNumbers');const kPkgUp = Symbol('pkgUp');const kPopulateParserHintArray = Symbol('populateParserHintArray');const kPopulateParserHintSingleValueDictionary = Symbol( 'populateParserHintSingleValueDictionary');const kPopulateParserHintArrayDictionary = Symbol( 'populateParserHintArrayDictionary');const kPopulateParserHintDictionary = Symbol('populateParserHintDictionary');const kSanitizeKey = Symbol('sanitizeKey');const kSetKey = Symbol('setKey');const kUnfreeze = Symbol('unfreeze');const kValidateAsync = Symbol('validateAsync');const kGetCommandInstance = Symbol('getCommandInstance');const kGetContext = Symbol('getContext');const kGetHasOutput = Symbol('getHasOutput');const kGetLoggerInstance = Symbol('getLoggerInstance');const kGetParseContext = Symbol('getParseContext');const kGetUsageInstance = Symbol('getUsageInstance');const kGetValidationInstance = Symbol('getValidationInstance');const kHasParseCallback = Symbol('hasParseCallback');const kIsGlobalContext = Symbol('isGlobalContext');const kPostProcess = Symbol('postProcess');const kRebase = Symbol('rebase');const kReset = Symbol('reset');const kRunYargsParserAndExecuteCommands = Symbol( 'runYargsParserAndExecuteCommands');const kRunValidation = Symbol('runValidation');const kSetHasOutput = Symbol('setHasOutput');const kTrackManuallySetKeys = Symbol('kTrackManuallySetKeys');export interface YargsInternalMethods { getCommandInstance(): CommandInstance; getContext(): Context; getHasOutput(): boolean; getLoggerInstance(): LoggerInstance; getParseContext(): Object; getParserConfiguration(): Configuration; getUsageInstance(): UsageInstance; getValidationInstance(): ValidationInstance; hasParseCallback(): boolean; isGlobalContext(): boolean; postProcess<T extends Arguments | Promise<Arguments>>( argv: Arguments | Promise<Arguments>, populateDoubleDash: boolean, calledFromCommand: boolean, runGlobalMiddleware: boolean ): any; reset(aliases?: Aliases): YargsInstance; runValidation( aliases: Dictionary<string[]>, positionalMap: Dictionary<string[]>, parseErrors: Error | null, isDefaultCommand?: boolean ): (argv: Arguments) => void; runYargsParserAndExecuteCommands( args: string | string[] | null, shortCircuit?: boolean | null, calledFromCommand?: boolean, commandIndex?: number, helpOnly?: boolean ): Arguments | Promise<Arguments>; setHasOutput(): void;}
export class YargsInstance { $0: string; argv?: Arguments; customScriptName = false; parsed: DetailedArguments | false = false;
#command: CommandInstance; #cwd: string; // use context object to keep track of resets, subcommand execution, etc., // submodules should modify and check the state of context as necessary: #context: Context = {commands: [], fullCommands: []}; #completion: CompletionInstance | null = null; #completionCommand: string | null = null; #defaultShowHiddenOpt = 'show-hidden'; #exitError: YError | string | nil = null; #detectLocale = true; #emittedWarnings: Dictionary<boolean> = {}; #exitProcess = true; #frozens: FrozenYargsInstance[] = []; #globalMiddleware: GlobalMiddleware; #groups: Dictionary<string[]> = {}; #hasOutput = false; #helpOpt: string | null = null; #isGlobalContext = true; #logger: LoggerInstance; #output = ''; #options: Options; #parentRequire?: RequireType; #parserConfig: Configuration = {}; #parseFn: ParseCallback | null = null; #parseContext: object | null = null; #pkgs: Dictionary<{[key: string]: string | {[key: string]: string}}> = {}; #preservedGroups: Dictionary<string[]> = {}; #processArgs: string | string[]; #recommendCommands = false; #shim: PlatformShim; #strict = false; #strictCommands = false; #strictOptions = false; #usage: UsageInstance; #versionOpt: string | null = null; #validation: ValidationInstance;
constructor( processArgs: string | string[] = [], cwd: string, parentRequire: RequireType | undefined, shim: PlatformShim ) { this.#shim = shim; this.#processArgs = processArgs; this.#cwd = cwd; this.#parentRequire = parentRequire; this.#globalMiddleware = new GlobalMiddleware(this); this.$0 = this[kGetDollarZero](); // #command, #validation, and #usage are initialized on first reset: this[kReset](); this.#command = this!.#command; this.#usage = this!.#usage; this.#validation = this!.#validation; this.#options = this!.#options; this.#options.showHiddenOpt = this.#defaultShowHiddenOpt; this.#logger = this[kCreateLogger](); } addHelpOpt(opt?: string | false, msg?: string): YargsInstance { const defaultHelpOpt = 'help'; argsert('[string|boolean] [string]', [opt, msg], arguments.length);
// nuke the key previously configured // to return help. if (this.#helpOpt) { this[kDeleteFromParserHintObject](this.#helpOpt); this.#helpOpt = null; }
if (opt === false && msg === undefined) return this;
// use arguments, fallback to defaults for opt and msg this.#helpOpt = typeof opt === 'string' ? opt : defaultHelpOpt; this.boolean(this.#helpOpt); this.describe( this.#helpOpt, msg || this.#usage.deferY18nLookup('Show help') ); return this; } help(opt?: string, msg?: string): YargsInstance { return this.addHelpOpt(opt, msg); }
addShowHiddenOpt(opt?: string | false, msg?: string): YargsInstance { argsert('[string|boolean] [string]', [opt, msg], arguments.length); if (opt === false && msg === undefined) return this; const showHiddenOpt = typeof opt === 'string' ? opt : this.#defaultShowHiddenOpt; this.boolean(showHiddenOpt); this.describe( showHiddenOpt, msg || this.#usage.deferY18nLookup('Show hidden options') ); this.#options.showHiddenOpt = showHiddenOpt; return this; } showHidden(opt?: string | false, msg?: string): YargsInstance { return this.addShowHiddenOpt(opt, msg); }
alias( key: string | string[] | Dictionary<string | string[]>, value?: string | string[] ): YargsInstance { argsert( '<object|string|array> [string|array]', [key, value], arguments.length ); this[kPopulateParserHintArrayDictionary]( this.alias.bind(this), 'alias', key, value ); return this; } array(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('array', keys); this[kTrackManuallySetKeys](keys); return this; } boolean(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('boolean', keys); this[kTrackManuallySetKeys](keys); return this; } check( f: (argv: Arguments, options: Options) => any, global?: boolean ): YargsInstance { argsert('<function> [boolean]', [f, global], arguments.length); this.middleware( ( argv: Arguments, _yargs: YargsInstance ): Partial<Arguments> | Promise<Partial<Arguments>> => { return maybeAsyncResult< Partial<Arguments> | Promise<Partial<Arguments>> | any >( () => { return f(argv, _yargs.getOptions()); }, (result: any): Partial<Arguments> | Promise<Partial<Arguments>> => { if (!result) { this.#usage.fail( this.#shim.y18n.__('Argument check failed: %s', f.toString()) ); } else if (typeof result === 'string' || result instanceof Error) { this.#usage.fail(result.toString(), result); } return argv; }, (err: Error): Partial<Arguments> | Promise<Partial<Arguments>> => { this.#usage.fail(err.message ? err.message : err.toString(), err); return argv; } ); }, false, global ); return this; } choices( key: string | string[] | Dictionary<string | string[]>, value?: string | string[] ): YargsInstance { argsert( '<object|string|array> [string|array]', [key, value], arguments.length ); this[kPopulateParserHintArrayDictionary]( this.choices.bind(this), 'choices', key, value ); return this; } coerce( keys: string | string[] | Dictionary<CoerceCallback>, value?: CoerceCallback ): YargsInstance { argsert( '<object|string|array> [function]', [keys, value], arguments.length ); if (Array.isArray(keys)) { if (!value) { throw new YError('coerce callback must be provided'); } for (const key of keys) { this.coerce(key, value); } return this; } else if (typeof keys === 'object') { for (const key of Object.keys(keys)) { this.coerce(key, keys[key]); } return this; } if (!value) { throw new YError('coerce callback must be provided'); } // This noop tells yargs-parser about the existence of the option // represented by "keys", so that it can apply camel case expansion // if needed: this.#options.key[keys] = true; this.#globalMiddleware.addCoerceMiddleware( ( argv: Arguments, yargs: YargsInstance ): Partial<Arguments> | Promise<Partial<Arguments>> => { let aliases: Dictionary<string[]>;
// Skip coerce logic if related arg was not provided const shouldCoerce = Object.prototype.hasOwnProperty.call(argv, keys); if (!shouldCoerce) { return argv; }
return maybeAsyncResult< Partial<Arguments> | Promise<Partial<Arguments>> | any >( () => { aliases = yargs.getAliases(); return value(argv[keys]); }, (result: any): Partial<Arguments> => { argv[keys] = result; const stripAliased = yargs .getInternalMethods() .getParserConfiguration()['strip-aliased']; if (aliases[keys] && stripAliased !== true) { for (const alias of aliases[keys]) { argv[alias] = result; } } return argv; }, (err: Error): Partial<Arguments> | Promise<Partial<Arguments>> => { throw new YError(err.message); } ); }, keys ); return this; } conflicts( key1: string | Dictionary<string | string[]>, key2?: string | string[] ): YargsInstance { argsert('<string|object> [string|array]', [key1, key2], arguments.length); this.#validation.conflicts(key1, key2); return this; } config( key: string | string[] | Dictionary = 'config', msg?: string | ConfigCallback, parseFn?: ConfigCallback ): YargsInstance { argsert( '[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length ); // allow a config object to be provided directly. if (typeof key === 'object' && !Array.isArray(key)) { key = applyExtends( key, this.#cwd, this[kGetParserConfiguration]()['deep-merge-config'] || false, this.#shim ); this.#options.configObjects = (this.#options.configObjects || []).concat( key ); return this; }
// allow for a custom parsing function. if (typeof msg === 'function') { parseFn = msg; msg = undefined; }
this.describe( key, msg || this.#usage.deferY18nLookup('Path to JSON config file') ); (Array.isArray(key) ? key : [key]).forEach(k => { this.#options.config[k] = parseFn || true; });
return this; } completion( cmd?: string, desc?: string | false | CompletionFunction, fn?: CompletionFunction ): YargsInstance { argsert( '[string] [string|boolean|function] [function]', [cmd, desc, fn], arguments.length );
// a function to execute when generating // completions can be provided as the second // or third argument to completion. if (typeof desc === 'function') { fn = desc; desc = undefined; }
// register the completion command. this.#completionCommand = cmd || this.#completionCommand || 'completion'; if (!desc && desc !== false) { desc = 'generate completion script'; } this.command(this.#completionCommand, desc);
// a function can be provided if (fn) this.#completion!.registerFunction(fn);
return this; } command( cmd: string | CommandHandlerDefinition | DefinitionOrCommandName[], description?: CommandHandler['description'], builder?: CommandBuilderDefinition | CommandBuilder, handler?: CommandHandlerCallback, middlewares?: Middleware[], deprecated?: boolean ): YargsInstance { argsert( '<string|array|object> [string|boolean] [function|object] [function] [array] [boolean|string]', [cmd, description, builder, handler, middlewares, deprecated], arguments.length ); this.#command.addHandler( cmd, description, builder, handler, middlewares, deprecated ); return this; } commands( cmd: string | CommandHandlerDefinition | DefinitionOrCommandName[], description?: CommandHandler['description'], builder?: CommandBuilderDefinition | CommandBuilder, handler?: CommandHandlerCallback, middlewares?: Middleware[], deprecated?: boolean ): YargsInstance { return this.command( cmd, description, builder, handler, middlewares, deprecated ); } commandDir(dir: string, opts?: RequireDirectoryOptions): YargsInstance { argsert('<string> [object]', [dir, opts], arguments.length); const req = this.#parentRequire || this.#shim.require; this.#command.addDirectory(dir, req, this.#shim.getCallerFile(), opts); return this; } count(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('count', keys); this[kTrackManuallySetKeys](keys); return this; } default( key: string | string[] | Dictionary<any>, value?: any, defaultDescription?: string ): YargsInstance { argsert( '<object|string|array> [*] [string]', [key, value, defaultDescription], arguments.length ); if (defaultDescription) { assertSingleKey(key, this.#shim); this.#options.defaultDescription[key] = defaultDescription; } if (typeof value === 'function') { assertSingleKey(key, this.#shim); if (!this.#options.defaultDescription[key]) this.#options.defaultDescription[key] = this.#usage.functionDescription(value); value = value.call(); } this[kPopulateParserHintSingleValueDictionary]<'default'>( this.default.bind(this), 'default', key, value ); return this; } defaults( key: string | string[] | Dictionary<any>, value?: any, defaultDescription?: string ): YargsInstance { return this.default(key, value, defaultDescription); } demandCommand( min = 1, max?: number | string, minMsg?: string | null, maxMsg?: string | null ): YargsInstance { argsert( '[number] [number|string] [string|null|undefined] [string|null|undefined]', [min, max, minMsg, maxMsg], arguments.length );
if (typeof max !== 'number') { minMsg = max; max = Infinity; }
this.global('_', false);
this.#options.demandedCommands._ = { min, max, minMsg, maxMsg, };
return this; } demand( keys: string | string[] | Dictionary<string | undefined> | number, max?: number | string[] | string | true, msg?: string | true ): YargsInstance { // you can optionally provide a 'max' key, // which will raise an exception if too many '_' // options are provided. if (Array.isArray(max)) { max.forEach(key => { assertNotStrictEqual(msg, true as const, this.#shim); this.demandOption(key, msg); }); max = Infinity; } else if (typeof max !== 'number') { msg = max; max = Infinity; }
if (typeof keys === 'number') { assertNotStrictEqual(msg, true as const, this.#shim); this.demandCommand(keys, max, msg, msg); } else if (Array.isArray(keys)) { keys.forEach(key => { assertNotStrictEqual(msg, true as const, this.#shim); this.demandOption(key, msg); }); } else { if (typeof msg === 'string') { this.demandOption(keys, msg); } else if (msg === true || typeof msg === 'undefined') { this.demandOption(keys); } }
return this; } demandOption( keys: string | string[] | Dictionary<string | undefined>, msg?: string ): YargsInstance { argsert('<object|string|array> [string]', [keys, msg], arguments.length); this[kPopulateParserHintSingleValueDictionary]( this.demandOption.bind(this), 'demandedOptions', keys, msg ); return this; } deprecateOption(option: string, message: string | boolean): YargsInstance { argsert('<string> [string|boolean]', [option, message], arguments.length); this.#options.deprecatedOptions[option] = message; return this; } describe( keys: string | string[] | Dictionary<string>, description?: string ): YargsInstance { argsert( '<object|string|array> [string]', [keys, description], arguments.length ); this[kSetKey](keys, true); this.#usage.describe(keys, description); return this; } detectLocale(detect: boolean): YargsInstance { argsert('<boolean>', [detect], arguments.length); this.#detectLocale = detect; return this; } // as long as options.envPrefix is not undefined, // parser will apply env vars matching prefix to argv env(prefix?: string | false): YargsInstance { argsert('[string|boolean]', [prefix], arguments.length); if (prefix === false) delete this.#options.envPrefix; else this.#options.envPrefix = prefix || ''; return this; } epilogue(msg: string): YargsInstance { argsert('<string>', [msg], arguments.length); this.#usage.epilog(msg); return this; } epilog(msg: string): YargsInstance { return this.epilogue(msg); } example( cmd: string | [string, string?][], description?: string ): YargsInstance { argsert('<string|array> [string]', [cmd, description], arguments.length);
if (Array.isArray(cmd)) { cmd.forEach(exampleParams => this.example(...exampleParams)); } else { this.#usage.example(cmd, description); }
return this; } // maybe exit, always capture context about why we wanted to exit: exit(code: number, err?: YError | string): void { this.#hasOutput = true; this.#exitError = err; if (this.#exitProcess) this.#shim.process.exit(code); } exitProcess(enabled = true): YargsInstance { argsert('[boolean]', [enabled], arguments.length); this.#exitProcess = enabled; return this; } fail(f: FailureFunction | boolean): YargsInstance { argsert('<function|boolean>', [f], arguments.length); if (typeof f === 'boolean' && f !== false) { throw new YError( "Invalid first argument. Expected function or boolean 'false'" ); } this.#usage.failFn(f); return this; } getAliases(): Dictionary<string[]> { return this.parsed ? this.parsed.aliases : {}; } async getCompletion( args: string[], done?: (err: Error | null, completions: string[] | undefined) => void ): Promise<string[] | void> { argsert('<array> [function]', [args, done], arguments.length); if (!done) { return new Promise((resolve, reject) => { this.#completion!.getCompletion(args, (err, completions) => { if (err) reject(err); else resolve(completions); }); }); } else { return this.#completion!.getCompletion(args, done); } } getDemandedOptions() { argsert([], 0); return this.#options.demandedOptions; } getDemandedCommands() { argsert([], 0); return this.#options.demandedCommands; } getDeprecatedOptions() { argsert([], 0); return this.#options.deprecatedOptions; } getDetectLocale(): boolean { return this.#detectLocale; } getExitProcess(): boolean { return this.#exitProcess; } // combine explicit and preserved groups. explicit groups should be first getGroups(): Dictionary<string[]> { return Object.assign({}, this.#groups, this.#preservedGroups); } getHelp(): Promise<string> { this.#hasOutput = true; if (!this.#usage.hasCachedHelpMessage()) { if (!this.parsed) { // Run the parser as if --help was passed to it (this is what // the last parameter `true` indicates). const parse = this[kRunYargsParserAndExecuteCommands]( this.#processArgs, undefined, undefined, 0, true ); if (isPromise(parse)) { return parse.then(() => { return this.#usage.help(); }); } } // Ensure top level options/positionals have been configured: const builderResponse = this.#command.runDefaultBuilderOn(this); if (isPromise(builderResponse)) { return builderResponse.then(() => { return this.#usage.help(); }); } } return Promise.resolve(this.#usage.help()); } getOptions(): Options { return this.#options; } getStrict(): boolean { return this.#strict; } getStrictCommands(): boolean { return this.#strictCommands; } getStrictOptions(): boolean { return this.#strictOptions; } global(globals: string | string[], global?: boolean): YargsInstance { argsert('<string|array> [boolean]', [globals, global], arguments.length); globals = ([] as string[]).concat(globals); if (global !== false) { this.#options.local = this.#options.local.filter( l => globals.indexOf(l) === -1 ); } else { globals.forEach(g => { if (!this.#options.local.includes(g)) this.#options.local.push(g); }); } return this; } group(opts: string | string[], groupName: string): YargsInstance { argsert('<string|array> <string>', [opts, groupName], arguments.length); const existing = this.#preservedGroups[groupName] || this.#groups[groupName]; if (this.#preservedGroups[groupName]) { // we now only need to track this group name in groups. delete this.#preservedGroups[groupName]; } const seen: Dictionary<boolean> = {}; this.#groups[groupName] = (existing || []).concat(opts).filter(key => { if (seen[key]) return false; return (seen[key] = true); }); return this; } hide(key: string): YargsInstance { argsert('<string>', [key], arguments.length); this.#options.hiddenOptions.push(key); return this; } implies( key: string | Dictionary<KeyOrPos | KeyOrPos[]>, value?: KeyOrPos | KeyOrPos[] ): YargsInstance { argsert( '<string|object> [number|string|array]', [key, value], arguments.length ); this.#validation.implies(key, value); return this; } locale(locale?: string): YargsInstance | string { argsert('[string]', [locale], arguments.length); if (locale === undefined) { this[kGuessLocale](); return this.#shim.y18n.getLocale(); } this.#detectLocale = false; this.#shim.y18n.setLocale(locale); return this; } middleware( callback: MiddlewareCallback | MiddlewareCallback[], applyBeforeValidation?: boolean, global?: boolean ): YargsInstance { return this.#globalMiddleware.addMiddleware( callback, !!applyBeforeValidation, global ); } nargs( key: string | string[] | Dictionary<number>, value?: number ): YargsInstance { argsert('<string|object|array> [number]', [key, value], arguments.length); this[kPopulateParserHintSingleValueDictionary]( this.nargs.bind(this), 'narg', key, value ); return this; } normalize(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('normalize', keys); return this; } number(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('number', keys); this[kTrackManuallySetKeys](keys); return this; } option( key: string | Dictionary<OptionDefinition>, opt?: OptionDefinition ): YargsInstance { argsert('<string|object> [object]', [key, opt], arguments.length); if (typeof key === 'object') { Object.keys(key).forEach(k => { this.options(k, key[k]); }); } else { if (typeof opt !== 'object') { opt = {}; }
this[kTrackManuallySetKeys](key);
// Warn about version name collision // Addresses: https://github.com/yargs/yargs/issues/1979 if (this.#versionOpt && (key === 'version' || opt?.alias === 'version')) { this[kEmitWarning]( [ '"version" is a reserved word.', 'Please do one of the following:', '- Disable version with `yargs.version(false)` if using "version" as an option', '- Use the built-in `yargs.version` method instead (if applicable)', '- Use a different option key', 'https://yargs.js.org/docs/#api-reference-version', ].join('\n'), undefined, 'versionWarning' // TODO: better dedupeId ); }
this.#options.key[key] = true; // track manually set keys.
if (opt.alias) this.alias(key, opt.alias);
const deprecate = opt.deprecate || opt.deprecated;
if (deprecate) { this.deprecateOption(key, deprecate); }
const demand = opt.demand || opt.required || opt.require;
// A required option can be specified via "demand: true". if (demand) { this.demand(key, demand); }
if (opt.demandOption) { this.demandOption( key, typeof opt.demandOption === 'string' ? opt.demandOption : undefined ); }
if (opt.conflicts) { this.conflicts(key, opt.conflicts); }
if ('default' in opt) { this.default(key, opt.default); }
if (opt.implies !== undefined) { this.implies(key, opt.implies); }
if (opt.nargs !== undefined) { this.nargs(key, opt.nargs); }
if (opt.config) { this.config(key, opt.configParser); }
if (opt.normalize) { this.normalize(key); }
if (opt.choices) { this.choices(key, opt.choices); }
if (opt.coerce) { this.coerce(key, opt.coerce); }
if (opt.group) { this.group(key, opt.group); }
if (opt.boolean || opt.type === 'boolean') { this.boolean(key); if (opt.alias) this.boolean(opt.alias); }
if (opt.array || opt.type === 'array') { this.array(key); if (opt.alias) this.array(opt.alias); }
if (opt.number || opt.type === 'number') { this.number(key); if (opt.alias) this.number(opt.alias); }
if (opt.string || opt.type === 'string') { this.string(key); if (opt.alias) this.string(opt.alias); }
if (opt.count || opt.type === 'count') { this.count(key); }
if (typeof opt.global === 'boolean') { this.global(key, opt.global); }
if (opt.defaultDescription) { this.#options.defaultDescription[key] = opt.defaultDescription; }
if (opt.skipValidation) { this.skipValidation(key); }
const desc = opt.describe || opt.description || opt.desc; this.describe(key, desc); if (opt.hidden) { this.hide(key); }
if (opt.requiresArg) { this.requiresArg(key); } }
return this; } options( key: string | Dictionary<OptionDefinition>, opt?: OptionDefinition ): YargsInstance { return this.option(key, opt); } parse( args?: string | string[], shortCircuit?: object | ParseCallback | boolean, _parseFn?: ParseCallback ): Arguments | Promise<Arguments> { argsert( '[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length ); this[kFreeze](); // Push current state of parser onto stack. if (typeof args === 'undefined') { args = this.#processArgs; }
// a context object can optionally be provided, this allows // additional information to be passed to a command handler. if (typeof shortCircuit === 'object') { this.#parseContext = shortCircuit; shortCircuit = _parseFn; }
// by providing a function as a second argument to // parse you can capture output that would otherwise // default to printing to stdout/stderr. if (typeof shortCircuit === 'function') { this.#parseFn = shortCircuit as ParseCallback; shortCircuit = false; } // completion short-circuits the parsing process, // skipping validation, etc. if (!shortCircuit) this.#processArgs = args;
if (this.#parseFn) this.#exitProcess = false;
const parsed = this[kRunYargsParserAndExecuteCommands]( args, !!shortCircuit ); const tmpParsed = this.parsed; this.#completion!.setParsed(this.parsed as DetailedArguments); if (isPromise(parsed)) { return parsed .then(argv => { if (this.#parseFn) this.#parseFn(this.#exitError, argv, this.#output); return argv; }) .catch(err => { if (this.#parseFn) { this.#parseFn!( err, (this.parsed as DetailedArguments).argv, this.#output ); } throw err; }) .finally(() => { this[kUnfreeze](); // Pop the stack. this.parsed = tmpParsed; }); } else { if (this.#parseFn) this.#parseFn(this.#exitError, parsed, this.#output); this[kUnfreeze](); // Pop the stack. this.parsed = tmpParsed; } return parsed; } parseAsync( args?: string | string[], shortCircuit?: object | ParseCallback | boolean, _parseFn?: ParseCallback ): Promise<Arguments> { const maybePromise = this.parse(args, shortCircuit, _parseFn); return !isPromise(maybePromise) ? Promise.resolve(maybePromise) : maybePromise; } parseSync( args?: string | string[], shortCircuit?: object | ParseCallback | boolean, _parseFn?: ParseCallback ): Arguments { const maybePromise = this.parse(args, shortCircuit, _parseFn); if (isPromise(maybePromise)) { throw new YError( '.parseSync() must not be used with asynchronous builders, handlers, or middleware' ); } return maybePromise; } parserConfiguration(config: Configuration) { argsert('<object>', [config], arguments.length); this.#parserConfig = config; return this; } pkgConf(key: string, rootPath?: string): YargsInstance { argsert('<string> [string]', [key, rootPath], arguments.length); let conf = null; // prefer cwd to require-main-filename in this method // since we're looking for e.g. "nyc" config in nyc consumer // rather than "yargs" config in nyc (where nyc is the main filename) const obj = this[kPkgUp](rootPath || this.#cwd);
// If an object exists in the key, add it to options.configObjects if (obj[key] && typeof obj[key] === 'object') { conf = applyExtends( obj[key] as {[key: string]: string}, rootPath || this.#cwd, this[kGetParserConfiguration]()['deep-merge-config'] || false, this.#shim ); this.#options.configObjects = (this.#options.configObjects || []).concat( conf ); } return this; } positional(key: string, opts: PositionalDefinition): YargsInstance { argsert('<string> <object>', [key, opts], arguments.length); // .positional() only supports a subset of the configuration // options available to .option(): const supportedOpts: (keyof PositionalDefinition)[] = [ 'default', 'defaultDescription', 'implies', 'normalize', 'choices', 'conflicts', 'coerce', 'type', 'describe', 'desc', 'description', 'alias', ]; opts = objFilter(opts, (k, v) => { // type can be one of string|number|boolean. if (k === 'type' && !['string', 'number', 'boolean'].includes(v)) return false; return supportedOpts.includes(k); });
// copy over any settings that can be inferred from the command string. const fullCommand = this.#context.fullCommands[this.#context.fullCommands.length - 1]; const parseOptions = fullCommand ? this.#command.cmdToParseOptions(fullCommand) : { array: [], alias: {}, default: {}, demand: {}, }; objectKeys(parseOptions).forEach(pk => { const parseOption = parseOptions[pk]; if (Array.isArray(parseOption)) { if (parseOption.indexOf(key) !== -1) opts[pk] = true; } else { if (parseOption[key] && !(pk in opts)) opts[pk] = parseOption[key]; } }); this.group(key, this.#usage.getPositionalGroupName()); return this.option(key, opts); } recommendCommands(recommend = true): YargsInstance { argsert('[boolean]', [recommend], arguments.length); this.#recommendCommands = recommend; return this; } required( keys: string | string[] | Dictionary<string | undefined> | number, max?: number | string[] | string | true, msg?: string | true ): YargsInstance { return this.demand(keys, max, msg); } require( keys: string | string[] | Dictionary<string | undefined> | number, max?: number | string[] | string | true, msg?: string | true ): YargsInstance { return this.demand(keys, max, msg); } requiresArg(keys: string | string[] | Dictionary): YargsInstance { // the 2nd paramter [number] in the argsert the assertion is mandatory // as populateParserHintSingleValueDictionary recursively calls requiresArg // with Nan as a 2nd parameter, although we ignore it argsert('<array|string|object> [number]', [keys], arguments.length); // If someone configures nargs at the same time as requiresArg, // nargs should take precedence, // see: https://github.com/yargs/yargs/pull/1572 // TODO: make this work with aliases, using a check similar to // checkAllAliases() in yargs-parser. if (typeof keys === 'string' && this.#options.narg[keys]) { return this; } else { this[kPopulateParserHintSingleValueDictionary]( this.requiresArg.bind(this), 'narg', keys, NaN ); } return this; } showCompletionScript($0?: string, cmd?: string): YargsInstance { argsert('[string] [string]', [$0, cmd], arguments.length); $0 = $0 || this.$0; this.#logger.log( this.#completion!.generateCompletionScript( $0, cmd || this.#completionCommand || 'completion' ) ); return this; } showHelp( level: 'error' | 'log' | ((message: string) => void) ): YargsInstance { argsert('[string|function]', [level], arguments.length); this.#hasOutput = true; if (!this.#usage.hasCachedHelpMessage()) { if (!this.parsed) { // Run the parser as if --help was passed to it (this is what // the last parameter `true` indicates). const parse = this[kRunYargsParserAndExecuteCommands]( this.#processArgs, undefined, undefined, 0, true ); if (isPromise(parse)) { parse.then(() => { this.#usage.showHelp(level); }); return this; } } // Ensure top level options/positionals have been configured: const builderResponse = this.#command.runDefaultBuilderOn(this); if (isPromise(builderResponse)) { builderResponse.then(() => { this.#usage.showHelp(level); }); return this; } } this.#usage.showHelp(level); return this; } scriptName(scriptName: string): YargsInstance { this.customScriptName = true; this.$0 = scriptName; return this; } showHelpOnFail(enabled?: string | boolean, message?: string): YargsInstance { argsert('[boolean|string] [string]', [enabled, message], arguments.length); this.#usage.showHelpOnFail(enabled, message); return this; } showVersion( level: 'error' | 'log' | ((message: string) => void) ): YargsInstance { argsert('[string|function]', [level], arguments.length); this.#usage.showVersion(level); return this; } skipValidation(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('skipValidation', keys); return this; } strict(enabled?: boolean): YargsInstance { argsert('[boolean]', [enabled], arguments.length); this.#strict = enabled !== false; return this; } strictCommands(enabled?: boolean): YargsInstance { argsert('[boolean]', [enabled], arguments.length); this.#strictCommands = enabled !== false; return this; } strictOptions(enabled?: boolean): YargsInstance { argsert('[boolean]', [enabled], arguments.length); this.#strictOptions = enabled !== false; return this; } string(keys: string | string[]): YargsInstance { argsert('<array|string>', [keys], arguments.length); this[kPopulateParserHintArray]('string', keys); this[kTrackManuallySetKeys](keys); return this; } terminalWidth(): number | null { argsert([], 0); return this.#shim.process.stdColumns; } updateLocale(obj: Dictionary<string>): YargsInstance { return this.updateStrings(obj); } updateStrings(obj: Dictionary<string>): YargsInstance { argsert('<object>', [obj], arguments.length); this.#detectLocale = false; this.#shim.y18n.updateLocale(obj); return this; } usage( msg: string | null, description?: CommandHandler['description'], builder?: CommandBuilderDefinition | CommandBuilder, handler?: CommandHandlerCallback ): YargsInstance { argsert( '<string|null|undefined> [string|boolean] [function|object] [function]', [msg, description, builder, handler], arguments.length );
if (description !== undefined) { assertNotStrictEqual(msg, null, this.#shim); // .usage() can be used as an alias for defining // a default command. if ((msg || '').match(/^\$0( |$)/)) { return this.command(msg, description, builder, handler); } else { throw new YError( '.usage() description must start with $0 if being used as alias for .command()' ); } } else { this.#usage.usage(msg); return this; } } version(opt?: string | false, msg?: string, ver?: string): YargsInstance { const defaultVersionOpt = 'version'; argsert( '[boolean|string] [string] [string]', [opt, msg, ver], arguments.length );
// nuke the key previously configured // to return version #. if (this.#versionOpt) { this[kDeleteFromParserHintObject](this.#versionOpt); this.#usage.version(undefined); this.#versionOpt = null; }
if (arguments.length === 0) { ver = this[kGuessVersion](); opt = defaultVersionOpt; } else if (arguments.length === 1) { if (opt === false) { // disable default 'version' key. return this; } ver = opt; opt = defaultVersionOpt; } else if (arguments.length === 2) { ver = msg; msg = undefined; }
this.#versionOpt = typeof opt === 'string' ? opt : defaultVersionOpt; msg = msg || this.#usage.deferY18nLookup('Show version number');
this.#usage.version(ver || undefined); this.boolean(this.#versionOpt); this.describe(this.#versionOpt, msg); return this; } wrap(cols: number | nil): YargsInstance { argsert('<number|null|undefined>', [cols], arguments.length); this.#usage.wrap(cols); return this; }
// to simplify the parsing of positionals in commands, // we temporarily populate '--' rather than _, with arguments // after the '--' directive. After the parse, we copy these back. [kCopyDoubleDash](argv: Arguments): any { if (!argv._ || !argv['--']) return argv; // eslint-disable-next-line prefer-spread argv._.push.apply(argv._, argv['--']);
// We catch an error here, in case someone has called Object.seal() // on the parsed object, see: https://github.com/babel/babel/pull/10733 try { delete argv['--']; // eslint-disable-next-line no-empty } catch (_err) {}
return argv; } [kCreateLogger](): LoggerInstance { return { log: (...args: any[]) => { if (!this[kHasParseCallback]()) console.log(...args); this.#hasOutput = true; if (this.#output.length) this.#output += '\n'; this.#output += args.join(' '); }, error: (...args: any[]) => { if (!this[kHasParseCallback]()) console.error(...args); this.#hasOutput = true; if (this.#output.length) this.#output += '\n'; this.#output += args.join(' '); }, }; } [kDeleteFromParserHintObject](optionKey: string) { // delete from all parsing hints: // boolean, array, key, alias, etc. objectKeys(this.#options).forEach((hintKey: keyof Options) => { // configObjects is not a parsing hint array if (((key): key is 'configObjects' => key === 'configObjects')(hintKey)) return; const hint = this.#options[hintKey]; if (Array.isArray(hint)) { if (hint.includes(optionKey)) hint.splice(hint.indexOf(optionKey), 1); } else if (typeof hint === 'object') { delete (hint as Dictionary)[optionKey]; } }); // now delete the description from usage.js. delete this.#usage.getDescriptions()[optionKey]; } [kEmitWarning]( warning: string, type: string | undefined, deduplicationId: string ) { // prevent duplicate warning emissions if (!this.#emittedWarnings[deduplicationId]) { this.#shim.process.emitWarning(warning, type); this.#emittedWarnings[deduplicationId] = true; } } [kFreeze]() { this.#frozens.push({ options: this.#options, configObjects: this.#options.configObjects.slice(0), exitProcess: this.#exitProcess, groups: this.#groups, strict: this.#strict, strictCommands: this.#strictCommands, strictOptions: this.#strictOptions, completionCommand: this.#completionCommand, output: this.#output, exitError: this.#exitError!, hasOutput: this.#hasOutput, parsed: this.parsed, parseFn: this.#parseFn!, parseContext: this.#parseContext, }); this.#usage.freeze(); this.#validation.freeze(); this.#command.freeze(); this.#globalMiddleware.freeze(); } [kGetDollarZero](): string { let $0 = ''; // ignore the node bin, specify this in your // bin file with #!/usr/bin/env node let default$0: string[]; if (/\b(node|iojs|electron)(\.exe)?$/.test(this.#shim.process.argv()[0])) { default$0 = this.#shim.process.argv().slice(1, 2); } else { default$0 = this.#shim.process.argv().slice(0, 1); }
$0 = default$0 .map(x => { const b = this[kRebase](this.#cwd, x); return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x; }) .join(' ') .trim();
if ( this.#shim.getEnv('_') && this.#shim.getProcessArgvBin() === this.#shim.getEnv('_') ) { $0 = this.#shim .getEnv('_')! .replace( `${this.#shim.path.dirname(this.#shim.process.execPath())}/`, '' ); } return $0; } [kGetParserConfiguration](): Configuration { return this.#parserConfig; } [kGuessLocale]() { if (!this.#detectLocale) return; const locale = this.#shim.getEnv('LC_ALL') || this.#shim.getEnv('LC_MESSAGES') || this.#shim.getEnv('LANG') || this.#shim.getEnv('LANGUAGE') || 'en_US'; this.locale(locale.replace(/[.:].*/, '')); } [kGuessVersion](): string { const obj = this[kPkgUp](); return (obj.version as string) || 'unknown'; } // We wait to coerce numbers for positionals until after the initial parse. // This allows commands to configure number parsing on a positional by // positional basis: [kParsePositionalNumbers](argv: Arguments): any { const args: (string | number)[] = argv['--'] ? argv['--'] : argv._;
for (let i = 0, arg; (arg = args[i]) !== undefined; i++) { if ( this.#shim.Parser.looksLikeNumber(arg) && Number.isSafeInteger(Math.floor(parseFloat(`${arg}`))) ) { args[i] = Number(arg); } } return argv; } [kPkgUp](rootPath?: string) { const npath = rootPath || '*'; if (this.#pkgs[npath]) return this.#pkgs[npath];
let obj = {}; try { let startDir = rootPath || this.#shim.mainFilename;
// When called in an environment that lacks require.main.filename, such as a jest test runner, // startDir is already process.cwd(), and should not be shortened. // Whether or not it is _actually_ a directory (e.g., extensionless bin) is irrelevant, find-up handles it. if (!rootPath && this.#shim.path.extname(startDir)) { startDir = this.#shim.path.dirname(startDir); }
const pkgJsonPath = this.#shim.findUp( startDir, (dir: string[], names: string[]) => { if (names.includes('package.json')) { return 'package.json'; } else { return undefined; } } ); assertNotStrictEqual(pkgJsonPath, undefined, this.#shim); obj = JSON.parse(this.#shim.readFileSync(pkgJsonPath, 'utf8')); // eslint-disable-next-line no-empty } catch (_noop) {}
this.#pkgs[npath] = obj || {}; return this.#pkgs[npath]; } [kPopulateParserHintArray]<T extends KeyOf<Options, string[]>>( type: T, keys: string | string[] ) { keys = ([] as string[]).concat(keys); keys.forEach(key => { key = this[kSanitizeKey](key); this.#options[type].push(key); }); } [kPopulateParserHintSingleValueDictionary]< T extends | Exclude<DictionaryKeyof<Options>, DictionaryKeyof<Options, any[]>> | 'default', K extends keyof Options[T] & string = keyof Options[T] & string, V extends ValueOf<Options[T]> = ValueOf<Options[T]> >( builder: (key: K, value: V, ...otherArgs: any[]) => YargsInstance, type: T, key: K | K[] | {[key in K]: V}, value?: V ) { this[kPopulateParserHintDictionary]<T, K, V>( builder, type, key, value, (type, key, value) => { this.#options[type][key] = value as ValueOf<Options[T]>; } ); } [kPopulateParserHintArrayDictionary]< T extends DictionaryKeyof<Options, any[]>, K extends keyof Options[T] & string = keyof Options[T] & string, V extends ValueOf<ValueOf<Options[T]>> | ValueOf<ValueOf<Options[T]>>[] = | ValueOf<ValueOf<Options[T]>> | ValueOf<ValueOf<Options[T]>>[] >( builder: (key: K, value: V, ...otherArgs: any[]) => YargsInstance, type: T, key: K | K[] | {[key in K]: V}, value?: V ) { this[kPopulateParserHintDictionary]<T, K, V>( builder, type, key, value, (type, key, value) => { this.#options[type][key] = ( this.#options[type][key] || ([] as Options[T][keyof Options[T]]) ).concat(value); } ); } [kPopulateParserHintDictionary]< T extends keyof Options, K extends keyof Options[T], V >( builder: (key: K, value: V, ...otherArgs: any[]) => YargsInstance, type: T, key: K | K[] | {[key in K]: V}, value: V | undefined, singleKeyHandler: (type: T, key: K, value?: V) => void ) { if (Array.isArray(key)) { // an array of keys with one value ['x', 'y', 'z'], function parse () {} key.forEach(k => { builder(k, value!); }); } else if ( ((key): key is {[key in K]: V} => typeof key === 'object')(key) ) { // an object of key value pairs: {'x': parse () {}, 'y': parse() {}} for (const k of objectKeys(key)) { builder(k, key[k]); } } else { singleKeyHandler(type, this[kSanitizeKey](key), value); } } [kSanitizeKey](key: any) { if (key === '__proto__') return '___proto___'; return key; } [kSetKey]( key: string | string[] | Dictionary<string | boolean>, set?: boolean | string ) { this[kPopulateParserHintSingleValueDictionary]( this[kSetKey].bind(this), 'key', key, set ); return this; } [kUnfreeze]() { const frozen = this.#frozens.pop(); assertNotStrictEqual(frozen, undefined, this.#shim); let configObjects: Dictionary[]; ({ options: this.#options, configObjects, exitProcess: this.#exitProcess, groups: this.#groups, output: this.#output, exitError: this.#exitError, hasOutput: this.#hasOutput, parsed: this.parsed, strict: this.#strict, strictCommands: this.#strictCommands, strictOptions: this.#strictOptions, completionCommand: this.#completionCommand, parseFn: this.#parseFn, parseContext: this.#parseContext, } = frozen); this.#options.configObjects = configObjects; this.#usage.unfreeze(); this.#validation.unfreeze(); this.#command.unfreeze(); this.#globalMiddleware.unfreeze(); } // If argv is a promise (which is possible if async middleware is used) // delay applying validation until the promise has resolved: [kValidateAsync]( validation: (argv: Arguments) => void, argv: Arguments | Promise<Arguments> ): Arguments | Promise<Arguments> { return maybeAsyncResult<Arguments>(argv, result => { validation(result); return result; }); }
// Note: these method names could change at any time, and should not be // depended upon externally: getInternalMethods(): YargsInternalMethods { return { getCommandInstance: this[kGetCommandInstance].bind(this), getContext: this[kGetContext].bind(this), getHasOutput: this[kGetHasOutput].bind(this), getLoggerInstance: this[kGetLoggerInstance].bind(this), getParseContext: this[kGetParseContext].bind(this), getParserConfiguration: this[kGetParserConfiguration].bind(this), getUsageInstance: this[kGetUsageInstance].bind(this), getValidationInstance: this[kGetValidationInstance].bind(this), hasParseCallback: this[kHasParseCallback].bind(this), isGlobalContext: this[kIsGlobalContext].bind(this), postProcess: this[kPostProcess].bind(this), reset: this[kReset].bind(this), runValidation: this[kRunValidation].bind(this), runYargsParserAndExecuteCommands: this[kRunYargsParserAndExecuteCommands].bind(this), setHasOutput: this[kSetHasOutput].bind(this), }; } [kGetCommandInstance](): CommandInstance { return this.#command; } [kGetContext](): Context { return this.#context; } [kGetHasOutput](): boolean { return this.#hasOutput; } [kGetLoggerInstance](): LoggerInstance { return this.#logger; } [kGetParseContext](): Object { return this.#parseContext || {}; } [kGetUsageInstance](): UsageInstance { return this.#usage; } [kGetValidationInstance](): ValidationInstance { return this.#validation; } [kHasParseCallback](): boolean { return !!this.#parseFn; } [kIsGlobalContext](): boolean { return this.#isGlobalContext; } [kPostProcess]<T extends Arguments | Promise<Arguments>>( argv: Arguments | Promise<Arguments>, populateDoubleDash: boolean, calledFromCommand: boolean, runGlobalMiddleware: boolean ): any { if (calledFromCommand) return argv; if (isPromise(argv)) return argv; if (!populateDoubleDash) { argv = this[kCopyDoubleDash](argv); } const parsePositionalNumbers = this[kGetParserConfiguration]()['parse-positional-numbers'] || this[kGetParserConfiguration]()['parse-positional-numbers'] === undefined; if (parsePositionalNumbers) { argv = this[kParsePositionalNumbers](argv as Arguments); } if (runGlobalMiddleware) { argv = applyMiddleware( argv, this, this.#globalMiddleware.getMiddleware(), false ); } return argv; } // put yargs back into an initial state; this is used mainly for running // commands in a breadth first manner: [kReset](aliases: Aliases = {}): YargsInstance { this.#options = this.#options || ({} as Options); const tmpOptions = {} as Options; tmpOptions.local = this.#options.local || []; tmpOptions.configObjects = this.#options.configObjects || [];
// if a key has been explicitly set as local, // we should reset it before passing options to command. const localLookup: Dictionary<boolean> = {}; tmpOptions.local.forEach(l => { localLookup[l] = true; (aliases[l] || []).forEach(a => { localLookup[a] = true; }); });
// add all groups not set to local to preserved groups Object.assign( this.#preservedGroups, Object.keys(this.#groups).reduce((acc, groupName) => { const keys = this.#groups[groupName].filter( key => !(key in localLookup) ); if (keys.length > 0) { acc[groupName] = keys; } return acc; }, {} as Dictionary<string[]>) ); // groups can now be reset this.#groups = {};
const arrayOptions: KeyOf<Options, string[]>[] = [ 'array', 'boolean', 'string', 'skipValidation', 'count', 'normalize', 'number', 'hiddenOptions', ];
const objectOptions: DictionaryKeyof<Options>[] = [ 'narg', 'key', 'alias', 'default', 'defaultDescription', 'config', 'choices', 'demandedOptions', 'demandedCommands', 'deprecatedOptions', ];
arrayOptions.forEach(k => { tmpOptions[k] = (this.#options[k] || []).filter( (k: string) => !localLookup[k] ); });
objectOptions.forEach(<K extends DictionaryKeyof<Options>>(k: K) => { tmpOptions[k] = objFilter( this.#options[k], k => !localLookup[k as string] ); });
tmpOptions.envPrefix = this.#options.envPrefix; this.#options = tmpOptions;
// if this is the first time being executed, create // instances of all our helpers -- otherwise just reset. this.#usage = this.#usage ? this.#usage.reset(localLookup) : Usage(this, this.#shim); this.#validation = this.#validation ? this.#validation.reset(localLookup) : Validation(this, this.#usage, this.#shim); this.#command = this.#command ? this.#command.reset() : Command( this.#usage, this.#validation, this.#globalMiddleware, this.#shim ); if (!this.#completion) this.#completion = Completion( this, this.#usage, this.#command, this.#shim ); this.#globalMiddleware.reset();
this.#completionCommand = null; this.#output = ''; this.#exitError = null; this.#hasOutput = false; this.parsed = false;
return this; } [kRebase](base: string, dir: string): string { return this.#shim.path.relative(base, dir); } [kRunYargsParserAndExecuteCommands]( args: string | string[] | null, shortCircuit?: boolean | null, calledFromCommand?: boolean, commandIndex = 0, helpOnly = false ): Arguments | Promise<Arguments> { let skipValidation = !!calledFromCommand || helpOnly; args = args || this.#processArgs;
this.#options.__ = this.#shim.y18n.__; this.#options.configuration = this[kGetParserConfiguration]();
const populateDoubleDash = !!this.#options.configuration['populate--']; const config = Object.assign({}, this.#options.configuration, { 'populate--': true, }); const parsed = this.#shim.Parser.detailed( args, Object.assign({}, this.#options, { configuration: {'parse-positional-numbers': false, ...config}, }) ) as DetailedArguments;
const argv: Arguments = Object.assign( parsed.argv, this.#parseContext ) as Arguments; let argvPromise: Arguments | Promise<Arguments> | undefined = undefined; const aliases = parsed.aliases;
let helpOptSet = false; let versionOptSet = false; Object.keys(argv).forEach(key => { if (key === this.#helpOpt && argv[key]) { helpOptSet = true; } else if (key === this.#versionOpt && argv[key]) { versionOptSet = true; } });
argv.$0 = this.$0; this.parsed = parsed;
// A single yargs instance may be used multiple times, e.g. // const y = yargs(); y.parse('foo --bar'); yargs.parse('bar --foo'). // When a prior parse has completed and a new parse is beginning, we // need to clear the cached help message from the previous parse: if (commandIndex === 0) { this.#usage.clearCachedHelpMessage(); }
try { this[kGuessLocale](); // guess locale lazily, so that it can be turned off in chain.
// while building up the argv object, there // are two passes through the parser. If completion // is being performed short-circuit on the first pass. if (shortCircuit) { return this[kPostProcess]( argv, populateDoubleDash, !!calledFromCommand, false // Don't run middleware when figuring out completion. ); }
// if there's a handler associated with a // command defer processing to it. if (this.#helpOpt) { // consider any multi-char helpOpt alias as a valid help command // unless all helpOpt aliases are single-char // note that parsed.aliases is a normalized bidirectional map :) const helpCmds = [this.#helpOpt] .concat(aliases[this.#helpOpt] || []) .filter(k => k.length > 1); // check if help should trigger and strip it from _. if (helpCmds.includes('' + argv._[argv._.length - 1])) { argv._.pop(); helpOptSet = true; } }
this.#isGlobalContext = false;
const handlerKeys = this.#command.getCommands(); const requestCompletions = this.#completion!.completionKey in argv; const skipRecommendation = helpOptSet || requestCompletions || helpOnly; if (argv._.length) { if (handlerKeys.length) { let firstUnknownCommand; for (let i = commandIndex || 0, cmd; argv._[i] !== undefined; i++) { cmd = String(argv._[i]); if (handlerKeys.includes(cmd) && cmd !== this.#completionCommand) { // commands are executed using a recursive algorithm that executes // the deepest command first; we keep track of the position in the // argv._ array that is currently being executed. const innerArgv = this.#command.runCommand( cmd, this, parsed, i + 1, // Don't run a handler, just figure out the help string: helpOnly, // Passed to builder so that expensive commands can be deferred: helpOptSet || versionOptSet || helpOnly ); return this[kPostProcess]( innerArgv, populateDoubleDash, !!calledFromCommand, false ); } else if ( !firstUnknownCommand && cmd !== this.#completionCommand ) { firstUnknownCommand = cmd; break; } } // recommend a command if recommendCommands() has // been enabled, and no commands were found to execute if ( !this.#command.hasDefaultCommand() && this.#recommendCommands && firstUnknownCommand && !skipRecommendation ) { this.#validation.recommendCommands( firstUnknownCommand, handlerKeys ); } }
// generate a completion script for adding to ~/.bashrc. if ( this.#completionCommand && argv._.includes(this.#completionCommand) && !requestCompletions ) { if (this.#exitProcess) setBlocking(true); this.showCompletionScript(); this.exit(0); } }
if (this.#command.hasDefaultCommand() && !skipRecommendation) { const innerArgv = this.#command.runCommand( null, this, parsed, 0, helpOnly, helpOptSet || versionOptSet || helpOnly ); return this[kPostProcess]( innerArgv, populateDoubleDash, !!calledFromCommand, false ); }
// we must run completions first, a user might // want to complete the --help or --version option. if (requestCompletions) { if (this.#exitProcess) setBlocking(true);
// we allow for asynchronous completions, // e.g., loading in a list of commands from an API. args = ([] as string[]).concat(args); const completionArgs = args.slice( args.indexOf(`--${this.#completion!.completionKey}`) + 1 ); this.#completion!.getCompletion(completionArgs, (err, completions) => { if (err) throw new YError(err.message); (completions || []).forEach(completion => { this.#logger.log(completion); }); this.exit(0); }); return this[kPostProcess]( argv, !populateDoubleDash, !!calledFromCommand, false // Don't run middleware when figuring out completion. ); }
// Handle 'help' and 'version' options // if we haven't already output help! if (!this.#hasOutput) { if (helpOptSet) { if (this.#exitProcess) setBlocking(true); skipValidation = true; this.showHelp('log'); this.exit(0); } else if (versionOptSet) { if (this.#exitProcess) setBlocking(true); skipValidation = true; this.#usage.showVersion('log'); this.exit(0); } }
// Check if any of the options to skip validation were provided if (!skipValidation && this.#options.skipValidation.length > 0) { skipValidation = Object.keys(argv).some( key => this.#options.skipValidation.indexOf(key) >= 0 && argv[key] === true ); }
// If the help or version options were used and exitProcess is false, // or if explicitly skipped, we won't run validations. if (!skipValidation) { if (parsed.error) throw new YError(parsed.error.message);
// if we're executed via bash completion, don't // bother with validation. if (!requestCompletions) { const validation = this[kRunValidation](aliases, {}, parsed.error); if (!calledFromCommand) { argvPromise = applyMiddleware( argv, this, this.#globalMiddleware.getMiddleware(), true ); } argvPromise = this[kValidateAsync](validation, argvPromise ?? argv); if (isPromise(argvPromise) && !calledFromCommand) { argvPromise = argvPromise.then(() => { return applyMiddleware( argv, this, this.#globalMiddleware.getMiddleware(), false ); }); } } } } catch (err) { if (err instanceof YError) this.#usage.fail(err.message, err); else throw err; }
return this[kPostProcess]( argvPromise ?? argv, populateDoubleDash, !!calledFromCommand, true ); } [kRunValidation]( aliases: Dictionary<string[]>, positionalMap: Dictionary<string[]>, parseErrors: Error | null, isDefaultCommand?: boolean ): (argv: Arguments) => void { const demandedOptions = {...this.getDemandedOptions()}; return (argv: Arguments) => { if (parseErrors) throw new YError(parseErrors.message); this.#validation.nonOptionCount(argv); this.#validation.requiredArguments(argv, demandedOptions); let failedStrictCommands = false; if (this.#strictCommands) { failedStrictCommands = this.#validation.unknownCommands(argv); } if (this.#strict && !failedStrictCommands) { this.#validation.unknownArguments( argv, aliases, positionalMap, !!isDefaultCommand ); } else if (this.#strictOptions) { this.#validation.unknownArguments(argv, aliases, {}, false, false); } this.#validation.limitedChoices(argv); this.#validation.implications(argv); this.#validation.conflicting(argv); }; } [kSetHasOutput]() { this.#hasOutput = true; } [kTrackManuallySetKeys](keys: string | string[]) { if (typeof keys === 'string') { this.#options.key[keys] = true; } else { for (const k of keys) { this.#options.key[k] = true; } } }}
export function isYargsInstance(y: YargsInstance | void): y is YargsInstance { return !!y && typeof y.getInternalMethods === 'function';}
/** Yargs' context. */export interface Context { commands: string[]; fullCommands: string[];}
interface LoggerInstance { error: Function; log: Function;}
export interface Options extends ParserOptions { __: (format: any, ...param: any[]) => string; alias: Dictionary<string[]>; array: string[]; boolean: string[]; choices: Dictionary<string[]>; config: Dictionary<ConfigCallback | boolean>; configObjects: Dictionary[]; configuration: Configuration; count: string[]; defaultDescription: Dictionary<string | undefined>; demandedCommands: Dictionary<{ min: number; max: number; minMsg?: string | null; maxMsg?: string | null; }>; demandedOptions: Dictionary<string | undefined>; deprecatedOptions: Dictionary<string | boolean | undefined>; hiddenOptions: string[]; /** Manually set keys */ key: Dictionary<boolean | string>; local: string[]; normalize: string[]; number: string[]; showHiddenOpt: string; skipValidation: string[]; string: string[];}
export interface Configuration extends Partial<ParserConfiguration> { /** Should a config object be deep-merged with the object config it extends? */ 'deep-merge-config'?: boolean; /** Should commands be sorted in help? */ 'sort-commands'?: boolean;}
export interface OptionDefinition { alias?: string | string[]; array?: boolean; boolean?: boolean; choices?: string | string[]; coerce?: CoerceCallback; config?: boolean; configParser?: ConfigCallback; conflicts?: string | string[]; count?: boolean; default?: any; defaultDescription?: string; deprecate?: string | boolean; deprecated?: OptionDefinition['deprecate']; desc?: string; describe?: OptionDefinition['desc']; description?: OptionDefinition['desc']; demand?: string | true; demandOption?: OptionDefinition['demand']; global?: boolean; group?: string; hidden?: boolean; implies?: string | number | KeyOrPos[]; nargs?: number; normalize?: boolean; number?: boolean; require?: OptionDefinition['demand']; required?: OptionDefinition['demand']; requiresArg?: boolean; skipValidation?: boolean; string?: boolean; type?: 'array' | 'boolean' | 'count' | 'number' | 'string';}
interface PositionalDefinition extends Pick< OptionDefinition, | 'alias' | 'array' | 'coerce' | 'choices' | 'conflicts' | 'default' | 'defaultDescription' | 'demand' | 'desc' | 'describe' | 'description' | 'implies' | 'normalize' > { type?: 'boolean' | 'number' | 'string';}
interface FrozenYargsInstance { options: Options; configObjects: Dictionary[]; exitProcess: boolean; groups: Dictionary<string[]>; strict: boolean; strictCommands: boolean; strictOptions: boolean; completionCommand: string | null; output: string; exitError: YError | string | nil; hasOutput: boolean; parsed: DetailedArguments | false; parseFn: ParseCallback | null; parseContext: object | null;}
interface ParseCallback { (err: YError | string | nil, argv: Arguments, output: string): void;}
interface Aliases { [key: string]: Array<string>;}
export interface Arguments { /** The script name or node command */ $0: string; /** Non-option arguments */ _: ArgsOutput; /** Arguments after the end-of-options flag `--` */ '--'?: ArgsOutput; /** All remaining options */ [argName: string]: any;}
export interface DetailedArguments extends ParserDetailedArguments { argv: Arguments; aliases: Dictionary<string[]>;}