Skip to main content
Module

x/cliffy/command/command.ts

Command line framework for deno 🦕 Including Commandline-Interfaces, Prompts, CLI-Table, Arguments Parser and more...
Extremely Popular
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366
// deno-lint-ignore-file no-explicit-anyimport { UnknownTypeError, ValidationError as FlagsValidationError,} from "../flags/_errors.ts";import { MissingRequiredEnvVarError } from "./_errors.ts";import { parseFlags } from "../flags/flags.ts";import type { ParseFlagsContext } from "../flags/types.ts";import { getDescription, parseArgumentsDefinition, splitArguments,} from "./_utils.ts";import { blue, bold, red, yellow } from "./deps.ts";import { CommandExecutableNotFoundError, CommandNotFoundError, DefaultCommandNotFoundError, DuplicateCommandAliasError, DuplicateCommandNameError, DuplicateCompletionError, DuplicateEnvVarError, DuplicateExampleError, DuplicateOptionNameError, DuplicateTypeError, MissingArgumentError, MissingArgumentsError, MissingCommandNameError, NoArgumentsAllowedError, TooManyArgumentsError, TooManyEnvVarValuesError, UnexpectedOptionalEnvVarValueError, UnexpectedVariadicEnvVarValueError, UnknownCommandError, ValidationError,} from "./_errors.ts";import { DefaultValue, OptionValueHandler } from "./types.ts";import { BooleanType } from "./types/boolean.ts";import { FileType } from "./types/file.ts";import { NumberType } from "./types/number.ts";import { StringType } from "./types/string.ts";import { Type } from "./type.ts";import { HelpGenerator } from "./help/_help_generator.ts";import type { HelpOptions } from "./help/_help_generator.ts";import type { ActionHandler, Argument, ArgumentValue, CommandResult, CompleteHandler, CompleteOptions, Completion, Description, EnvVar, EnvVarOptions, EnvVarValueHandler, Example, GlobalEnvVarOptions, GlobalOptionOptions, HelpHandler, MapTypes, Option, OptionOptions, TypeDef, TypeOptions, TypeOrTypeHandler, VersionHandler,} from "./types.ts";import { IntegerType } from "./types/integer.ts";import { underscoreToCamelCase } from "../flags/_utils.ts";
export class Command< TParentCommandGlobals extends Record<string, unknown> | void = void, TParentCommandTypes extends Record<string, unknown> | void = TParentCommandGlobals extends number ? any : void, TCommandOptions extends Record<string, unknown> | void = TParentCommandGlobals extends number ? any : void, TCommandArguments extends Array<unknown> = TParentCommandGlobals extends number ? any : [], TCommandGlobals extends Record<string, unknown> | void = TParentCommandGlobals extends number ? any : void, TCommandTypes extends Record<string, unknown> | void = TParentCommandGlobals extends number ? any : { number: number; integer: number; string: string; boolean: boolean; file: string; }, TCommandGlobalTypes extends Record<string, unknown> | void = TParentCommandGlobals extends number ? any : void, TParentCommand extends Command<any> | undefined = TParentCommandGlobals extends number ? any : undefined,> { private types: Map<string, TypeDef> = new Map(); private rawArgs: Array<string> = []; private literalArgs: Array<string> = []; // @TODO: get script name: https://github.com/denoland/deno/pull/5034 // private name: string = location.pathname.split( '/' ).pop() as string; private _name = "COMMAND"; private _parent?: TParentCommand; private _globalParent?: Command<any>; private ver?: VersionHandler; private desc: Description = ""; private _usage?: string; private fn?: ActionHandler; private options: Array<Option> = []; private commands: Map<string, Command<any>> = new Map(); private examples: Array<Example> = []; private envVars: Array<EnvVar> = []; private aliases: Array<string> = []; private completions: Map<string, Completion> = new Map(); private cmd: Command<any> = this; private argsDefinition?: string; private isExecutable = false; private throwOnError = false; private _allowEmpty = false; private _stopEarly = false; private defaultCommand?: string; private _useRawArgs = false; private args: Array<Argument> = []; private isHidden = false; private isGlobal = false; private hasDefaults = false; private _versionOptions?: DefaultOption | false; private _helpOptions?: DefaultOption | false; private _versionOption?: Option; private _helpOption?: Option; private _help?: HelpHandler; private _shouldExit?: boolean; private _meta: Record<string, string> = {}; private _groupName?: string; private _noGlobals = false;
/** Disable version option. */ public versionOption(enable: false): this;
/** * Set global version option. * @param flags The flags of the version option. * @param desc The description of the version option. * @param opts Version option options. */ public versionOption( flags: string, desc?: string, opts?: & OptionOptions< Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > & { global: true; }, ): this;
/** * Set version option. * @param flags The flags of the version option. * @param desc The description of the version option. * @param opts Version option options. */ public versionOption( flags: string, desc?: string, opts?: OptionOptions< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this;
/** * Set version option. * @param flags The flags of the version option. * @param desc The description of the version option. * @param opts The action of the version option. */ public versionOption( flags: string, desc?: string, opts?: ActionHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this;
public versionOption( flags: string | false, desc?: string, opts?: | ActionHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > | OptionOptions< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > | OptionOptions< Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > & { global: true; }, ): this { this._versionOptions = flags === false ? flags : { flags, desc, opts: typeof opts === "function" ? { action: opts } : opts, }; return this; }
/** Disable help option. */ public helpOption(enable: false): this;
/** * Set global help option. * @param flags The flags of the help option. * @param desc The description of the help option. * @param opts Help option options. */ public helpOption( flags: string, desc?: string, opts?: & OptionOptions< Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > & { global: true; }, ): this;
/** * Set help option. * @param flags The flags of the help option. * @param desc The description of the help option. * @param opts Help option options. */ public helpOption( flags: string, desc?: string, opts?: OptionOptions< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this;
/** * Set help option. * @param flags The flags of the help option. * @param desc The description of the help option. * @param opts The action of the help option. */ public helpOption( flags: string, desc?: string, opts?: ActionHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this;
public helpOption( flags: string | false, desc?: string, opts?: | ActionHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > | OptionOptions< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > | OptionOptions< Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > & { global: true; }, ): this { this._helpOptions = flags === false ? flags : { flags, desc, opts: typeof opts === "function" ? { action: opts } : opts, }; return this; }
/** * Add new sub-command. * @param name Command definition. E.g: `my-command <input-file:string> <output-file:string>` * @param cmd The new child command to register. * @param override Override existing child command. */ public command< TCommand extends Command< (TGlobalOptions & Record<string, unknown>) | void | undefined, TGlobalTypes | void | undefined, Record<string, unknown> | void, Array<unknown>, Record<string, unknown> | void, Record<string, unknown> | void, Record<string, unknown> | void, Command< TGlobalOptions | void | undefined, TGlobalTypes | void | undefined, Record<string, unknown> | void, Array<unknown>, Record<string, unknown> | void, Record<string, unknown> | void, Record<string, unknown> | void, undefined > >, TGlobalOptions extends (TParentCommand extends Command<any> ? TParentCommandGlobals : Merge<TParentCommandGlobals, TCommandGlobals>), TGlobalTypes extends (TParentCommand extends Command<any> ? TParentCommandTypes : Merge<TParentCommandTypes, TCommandTypes>), >( name: string, cmd: TCommand, override?: boolean, ): ReturnType<TCommand["reset"]> extends Command< Record<string, unknown> | void, Record<string, unknown> | void, infer Options, infer Arguments, infer GlobalOptions, infer Types, infer GlobalTypes, undefined > ? Command< TGlobalOptions, TGlobalTypes, Options, Arguments, GlobalOptions, Types, GlobalTypes, OneOf<TParentCommand, this> > : never;
/** * Add new sub-command. * @param name Command definition. E.g: `my-command <input-file:string> <output-file:string>` * @param cmd The new child command to register. * @param override Override existing child command. */ public command< TCommand extends Command< TGlobalOptions | void | undefined, TGlobalTypes | void | undefined, Record<string, unknown> | void, Array<unknown>, Record<string, unknown> | void, Record<string, unknown> | void, Record<string, unknown> | void, OneOf<TParentCommand, this> | undefined >, TGlobalOptions extends (TParentCommand extends Command<any> ? TParentCommandGlobals : Merge<TParentCommandGlobals, TCommandGlobals>), TGlobalTypes extends (TParentCommand extends Command<any> ? TParentCommandTypes : Merge<TParentCommandTypes, TCommandTypes>), >( name: string, cmd: TCommand, override?: boolean, ): TCommand extends Command< Record<string, unknown> | void, Record<string, unknown> | void, infer Options, infer Arguments, infer GlobalOptions, infer Types, infer GlobalTypes, OneOf<TParentCommand, this> | undefined > ? Command< TGlobalOptions, TGlobalTypes, Options, Arguments, GlobalOptions, Types, GlobalTypes, OneOf<TParentCommand, this> > : never;
/** * Add new sub-command. * @param nameAndArguments Command definition. E.g: `my-command <input-file:string> <output-file:string>` * @param desc The description of the new child command. * @param override Override existing child command. */ public command< TNameAndArguments extends string, TArguments extends TypedCommandArguments< TNameAndArguments, TParentCommand extends Command<any> ? TParentCommandTypes : Merge<TParentCommandTypes, TCommandGlobalTypes> >, >( nameAndArguments: TNameAndArguments, desc?: string, override?: boolean, ): TParentCommandGlobals extends number ? Command<any> : Command< TParentCommand extends Command<any> ? TParentCommandGlobals : Merge<TParentCommandGlobals, TCommandGlobals>, TParentCommand extends Command<any> ? TParentCommandTypes : Merge<TParentCommandTypes, TCommandGlobalTypes>, void, TArguments, void, void, void, OneOf<TParentCommand, this> >;
/** * Add new sub-command. * @param nameAndArguments Command definition. E.g: `my-command <input-file:string> <output-file:string>` * @param cmdOrDescription The description of the new child command. * @param override Override existing child command. */ command( nameAndArguments: string, cmdOrDescription?: Command<any> | string, override?: boolean, ): Command<any> { this.reset();
const result = splitArguments(nameAndArguments);
const name: string | undefined = result.flags.shift(); const aliases: string[] = result.flags;
if (!name) { throw new MissingCommandNameError(); }
if (this.getBaseCommand(name, true)) { if (!override) { throw new DuplicateCommandNameError(name); } this.removeCommand(name); }
let description: string | undefined; let cmd: Command<any>;
if (typeof cmdOrDescription === "string") { description = cmdOrDescription; }
if (cmdOrDescription instanceof Command) { cmd = cmdOrDescription.reset(); } else { cmd = new Command(); }
cmd._name = name; cmd._parent = this;
if (description) { cmd.description(description); }
if (result.typeDefinition) { cmd.arguments(result.typeDefinition); }
aliases.forEach((alias: string) => cmd.alias(alias));
this.commands.set(name, cmd);
this.select(name);
return this; }
/** * Add new command alias. * @param alias Tha name of the alias. */ public alias(alias: string): this { if (this.cmd._name === alias || this.cmd.aliases.includes(alias)) { throw new DuplicateCommandAliasError(alias); }
this.cmd.aliases.push(alias);
return this; }
/** Reset internal command reference to main command. */ public reset(): OneOf<TParentCommand, this> { this._groupName = undefined; this.cmd = this; return this as OneOf<TParentCommand, this>; }
/** * Set internal command pointer to child command with given name. * @param name The name of the command to select. */ public select< TOptions extends Record<string, unknown> | void = any, TArguments extends Array<unknown> = any, TGlobalOptions extends Record<string, unknown> | void = any, >( name: string, ): Command< TParentCommandGlobals, TParentCommandTypes, TOptions, TArguments, TGlobalOptions, TCommandTypes, TCommandGlobalTypes, TParentCommand > { const cmd = this.getBaseCommand(name, true);
if (!cmd) { throw new CommandNotFoundError(name, this.getBaseCommands(true)); }
this.cmd = cmd;
return this as Command<any>; }
/***************************************************************************** **** SUB HANDLER ************************************************************ *****************************************************************************/
/** Set command name. */ public name(name: string): this { this.cmd._name = name; return this; }
/** * Set command version. * @param version Semantic version string string or method that returns the version string. */ public version( version: | string | VersionHandler< Partial<TCommandOptions>, Partial<TCommandArguments>, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this { if (typeof version === "string") { this.cmd.ver = () => version; } else if (typeof version === "function") { this.cmd.ver = version; } return this; }
public meta(name: string, value: string): this { this.cmd._meta[name] = value; return this; }
public getMeta(): Record<string, string>; public getMeta(name: string): string; public getMeta(name?: string): Record<string, string> | string { return typeof name === "undefined" ? this._meta : this._meta[name]; }
/** * Set command help. * @param help Help string, method, or config for generator that returns the help string. */ public help( help: | string | HelpHandler< Partial<TCommandOptions>, Partial<TCommandArguments>, TCommandGlobals, TParentCommandGlobals > | HelpOptions, ): this { if (typeof help === "string") { this.cmd._help = () => help; } else if (typeof help === "function") { this.cmd._help = help; } else { this.cmd._help = (cmd: Command, options: HelpOptions): string => HelpGenerator.generate(cmd, { ...help, ...options }); } return this; }
/** * Set the long command description. * @param description The command description. */ public description( description: Description< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this { this.cmd.desc = description; return this; }
/** * Set the command usage. Defaults to arguments. * @param usage The command usage. */ public usage(usage: string): this { this.cmd._usage = usage; return this; }
/** * Hide command from help, completions, etc. */ public hidden(): this { this.cmd.isHidden = true; return this; }
/** Make command globally available. */ public global(): this { this.cmd.isGlobal = true; return this; }
/** Make command executable. */ public executable(): this { this.cmd.isExecutable = true; return this; }
/** * Set command arguments: * * <requiredArg:string> [optionalArg: number] [...restArgs:string] */ public arguments< TArguments extends TypedArguments< TArgs, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>> >, TArgs extends string = string, >( args: TArgs, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TArguments, TCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommand > { this.cmd.argsDefinition = args; return this as Command<any>; }
/** * Set command callback method. * @param fn Command action handler. */ public action( fn: ActionHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, ): this { this.cmd.fn = fn; return this; }
/** * Don't throw an error if the command was called without arguments. * @param allowEmpty Enable/disable allow empty. */ // public allowEmpty<TAllowEmpty extends boolean | undefined = undefined>( // allowEmpty?: TAllowEmpty, // ): false extends TAllowEmpty ? this // : Command< // Partial<TParentCommandGlobals>, // TParentCommandTypes, // Partial<TCommandOptions>, // TCommandArguments, // TCommandGlobals, // TCommandTypes, // TCommandGlobalTypes, // TParentCommand // > { // this.cmd._allowEmpty = allowEmpty !== false; // return this; // }
public allowEmpty<TAllowEmpty extends boolean | undefined = undefined>( allowEmpty?: TAllowEmpty, ): false extends TAllowEmpty ? this : Command< Partial<TParentCommandGlobals>, TParentCommandTypes, Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommand > { this.cmd._allowEmpty = allowEmpty !== false; return this as false extends TAllowEmpty ? this : Command< Partial<TParentCommandGlobals>, TParentCommandTypes, Partial<TCommandOptions>, TCommandArguments, TCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommand >; }
/** * Enable stop early. If enabled, all arguments starting from the first non * option argument will be passed as arguments with type string to the command * action handler. * * For example: * `command --debug-level warning server --port 80` * * Will result in: * - options: `{debugLevel: 'warning'}` * - args: `['server', '--port', '80']` * * @param stopEarly Enable/disable stop early. */ public stopEarly(stopEarly = true): this { this.cmd._stopEarly = stopEarly; return this; }
/** * Disable parsing arguments. If enabled the raw arguments will be passed to * the action handler. This has no effect for parent or child commands. Only * for the command on which this method was called. * @param useRawArgs Enable/disable raw arguments. */ public useRawArgs( useRawArgs = true, ): Command< void, void, void, Array<string>, void, void, void, TParentCommand > { this.cmd._useRawArgs = useRawArgs; return this as Command<any>; }
/** * Set default command. The default command is executed when the program * was called without any argument and if no action handler is registered. * @param name Name of the default command. */ public default(name: string): this { this.cmd.defaultCommand = name; return this; }
public globalType< THandler extends TypeOrTypeHandler<unknown>, TName extends string = string, >( name: TName, handler: THandler, options?: Omit<TypeOptions, "global">, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, TCommandGlobals, TCommandTypes, Merge<TCommandGlobalTypes, TypedType<TName, THandler>>, TParentCommand > { return this.type(name, handler, { ...options, global: true }); }
/** * Register custom type. * @param name The name of the type. * @param handler The callback method to parse the type. * @param options Type options. */ public type< THandler extends TypeOrTypeHandler<unknown>, TName extends string = string, >( name: TName, handler: THandler, options?: TypeOptions, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, TCommandGlobals, Merge<TCommandTypes, TypedType<TName, THandler>>, TCommandGlobalTypes, TParentCommand > { if (this.cmd.types.get(name) && !options?.override) { throw new DuplicateTypeError(name); }
this.cmd.types.set(name, { ...options, name, handler: handler as TypeOrTypeHandler<unknown>, });
if ( handler instanceof Type && (typeof handler.complete !== "undefined" || typeof handler.values !== "undefined") ) { const completeHandler: CompleteHandler = ( cmd: Command, parent?: Command, ) => handler.complete?.(cmd, parent) || []; this.complete(name, completeHandler, options); }
return this as Command<any>; }
public globalComplete( name: string, complete: CompleteHandler, options?: Omit<CompleteOptions, "global">, ): this { return this.complete(name, complete, { ...options, global: true }); }
/** * Register command specific custom type. * @param name The name of the completion. * @param complete The callback method to complete the type. * @param options Complete options. */ public complete( name: string, complete: CompleteHandler< Partial<TCommandOptions>, Partial<TCommandArguments>, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, any >, options: CompleteOptions & { global: boolean }, ): this; public complete( name: string, complete: CompleteHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, options?: CompleteOptions, ): this;
public complete( name: string, complete: | CompleteHandler< TCommandOptions, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > | CompleteHandler< Partial<TCommandOptions>, Partial<TCommandArguments>, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, any >, options?: CompleteOptions, ): this { if (this.cmd.completions.has(name) && !options?.override) { throw new DuplicateCompletionError(name); }
this.cmd.completions.set(name, { name, complete, ...options, });
return this; }
/** * Throw validation errors instead of calling `Deno.exit()` to handle * validation errors manually. * * A validation error is thrown when the command is wrongly used by the user. * For example: If the user passes some invalid options or arguments to the * command. * * This has no effect for parent commands. Only for the command on which this * method was called and all child commands. * * **Example:** * * ``` * try { * cmd.parse(); * } catch(error) { * if (error instanceof ValidationError) { * cmd.showHelp(); * Deno.exit(1); * } * throw error; * } * ``` * * @see ValidationError */ public throwErrors(): this { this.cmd.throwOnError = true; return this; }
/** * Same as `.throwErrors()` but also prevents calling `Deno.exit` after * printing help or version with the --help and --version option. */ public noExit(): this { this.cmd._shouldExit = false; this.throwErrors(); return this; }
/** * Disable inheriting global commands, options and environment variables from * parent commands. */ public noGlobals(): this { this.cmd._noGlobals = true; return this; }
/** Check whether the command should throw errors or exit. */ protected shouldThrowErrors(): boolean { return this.throwOnError || !!this._parent?.shouldThrowErrors(); }
/** Check whether the command should exit after printing help or version. */ protected shouldExit(): boolean { return this._shouldExit ?? this._parent?.shouldExit() ?? true; }
public globalOption< TFlags extends string, TGlobalOptions extends TypedOption< TFlags, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, undefined extends TConflicts ? TRequired : false, TDefaultValue >, TMappedGlobalOptions extends MapValue< TGlobalOptions, TMappedValue, TCollect >, TRequired extends OptionOptions["required"] = undefined, TCollect extends OptionOptions["collect"] = undefined, TConflicts extends OptionOptions["conflicts"] = undefined, TDefaultValue = undefined, TMappedValue = undefined, >( flags: TFlags, desc: string, opts?: | Omit< GlobalOptionOptions< Partial<TCommandOptions>, TCommandArguments, MergeOptions<TFlags, TCommandGlobals, TGlobalOptions>, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, "value" > & { default?: DefaultValue<TDefaultValue>; required?: TRequired; collect?: TCollect; value?: OptionValueHandler< MapTypes<ValueOf<TGlobalOptions>>, TMappedValue >; } | OptionValueHandler<MapTypes<ValueOf<TGlobalOptions>>, TMappedValue>, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, MergeOptions<TFlags, TCommandGlobals, TMappedGlobalOptions>, TCommandTypes, TCommandGlobalTypes, TParentCommand > { if (typeof opts === "function") { return this.option( flags, desc, { value: opts, global: true } as OptionOptions, ) as Command<any>; } return this.option( flags, desc, { ...opts, global: true } as OptionOptions, ) as Command<any>; }
/** * Enable grouping of options and set the name of the group. * All option which are added after calling the `.group()` method will be * grouped in the help output. If the `.group()` method can be use multiple * times to create more groups. * * @param name The name of the option group. */ public group(name: string): this { this.cmd._groupName = name; return this; }
/** * Add a new option. * @param flags Flags string e.g: -h, --help, --manual <requiredArg:string> [optionalArg:number] [...restArgs:string] * @param desc Flag description. * @param opts Flag options or custom handler for processing flag value. */ public option< TFlags extends string, TGlobalOptions extends TypedOption< TFlags, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, undefined extends TConflicts ? TRequired : false, TDefault >, TMappedGlobalOptions extends MapValue< TGlobalOptions, TMappedValue, TCollect >, TRequired extends OptionOptions["required"] = undefined, TCollect extends OptionOptions["collect"] = undefined, TConflicts extends OptionOptions["conflicts"] = undefined, TDefault = undefined, TMappedValue = undefined, >( flags: TFlags, desc: string, opts: | Omit< OptionOptions< Partial<TCommandOptions>, TCommandArguments, MergeOptions<TFlags, TCommandGlobals, TGlobalOptions>, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, "value" > & { global: true; default?: DefaultValue<TDefault>; required?: TRequired; collect?: TCollect; value?: OptionValueHandler< MapTypes<ValueOf<TGlobalOptions>>, TMappedValue >; } | OptionValueHandler<MapTypes<ValueOf<TGlobalOptions>>, TMappedValue>, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, MergeOptions<TFlags, TCommandGlobals, TMappedGlobalOptions>, TCommandTypes, TCommandGlobalTypes, TParentCommand >;
public option< TFlags extends string, TOptions extends TypedOption< TFlags, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, undefined extends TConflicts ? TRequired : false, TDefaultValue >, TMappedOptions extends MapValue<TOptions, TMappedValue, TCollect>, TRequired extends OptionOptions["required"] = undefined, TCollect extends OptionOptions["collect"] = undefined, TConflicts extends OptionOptions["conflicts"] = undefined, TDefaultValue = undefined, TMappedValue = undefined, >( flags: TFlags, desc: string, opts?: | Omit< OptionOptions< MergeOptions<TFlags, TCommandOptions, TMappedOptions>, TCommandArguments, TCommandGlobals, TParentCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand >, "value" > & { default?: DefaultValue<TDefaultValue>; required?: TRequired; collect?: TCollect; conflicts?: TConflicts; value?: OptionValueHandler<MapTypes<ValueOf<TOptions>>, TMappedValue>; } | OptionValueHandler<MapTypes<ValueOf<TOptions>>, TMappedValue>, ): Command< TParentCommandGlobals, TParentCommandTypes, MergeOptions<TFlags, TCommandOptions, TMappedOptions>, TCommandArguments, TCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommand >;
public option( flags: string, desc: string, opts?: OptionOptions | OptionValueHandler, ): Command<any> { if (typeof opts === "function") { return this.option(flags, desc, { value: opts }); }
const result = splitArguments(flags);
const args: Argument[] = result.typeDefinition ? parseArgumentsDefinition(result.typeDefinition) : [];
const option: Option = { ...opts, name: "", description: desc, args, flags: result.flags, equalsSign: result.equalsSign, typeDefinition: result.typeDefinition, groupName: this._groupName, };
if (option.separator) { for (const arg of args) { if (arg.list) { arg.separator = option.separator; } } }
for (const part of option.flags) { const arg = part.trim(); const isLong = /^--/.test(arg); const name = isLong ? arg.slice(2) : arg.slice(1);
if (this.cmd.getBaseOption(name, true)) { if (opts?.override) { this.removeOption(name); } else { throw new DuplicateOptionNameError(name); } }
if (!option.name && isLong) { option.name = name; } else if (!option.aliases) { option.aliases = [name]; } else { option.aliases.push(name); } }
if (option.prepend) { this.cmd.options.unshift(option); } else { this.cmd.options.push(option); }
return this; }
/** * Add new command example. * @param name Name of the example. * @param description The content of the example. */ public example(name: string, description: string): this { if (this.cmd.hasExample(name)) { throw new DuplicateExampleError(name); }
this.cmd.examples.push({ name, description });
return this; }
public globalEnv< TNameAndValue extends string, TGlobalEnvVars extends TypedEnv< TNameAndValue, TPrefix, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, TRequired >, TMappedGlobalEnvVars extends MapValue<TGlobalEnvVars, TMappedValue>, TRequired extends EnvVarOptions["required"] = undefined, TPrefix extends EnvVarOptions["prefix"] = undefined, TMappedValue = undefined, >( name: TNameAndValue, description: string, options?: Omit<GlobalEnvVarOptions, "value"> & { required?: TRequired; prefix?: TPrefix; value?: EnvVarValueHandler< MapTypes<ValueOf<TGlobalEnvVars>>, TMappedValue >; }, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, Merge<TCommandGlobals, TMappedGlobalEnvVars>, TCommandTypes, TCommandGlobalTypes, TParentCommand > { return this.env( name, description, { ...options, global: true } as EnvVarOptions, ) as Command<any>; }
/** * Add new environment variable. * @param name Name of the environment variable. * @param description The description of the environment variable. * @param options Environment variable options. */ public env< N extends string, G extends TypedEnv< N, P, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, R >, MG extends MapValue<G, V>, R extends EnvVarOptions["required"] = undefined, P extends EnvVarOptions["prefix"] = undefined, V = undefined, >( name: N, description: string, options: Omit<EnvVarOptions, "value"> & { global: true; required?: R; prefix?: P; value?: EnvVarValueHandler<MapTypes<ValueOf<G>>, V>; }, ): Command< TParentCommandGlobals, TParentCommandTypes, TCommandOptions, TCommandArguments, Merge<TCommandGlobals, MG>, TCommandTypes, TCommandGlobalTypes, TParentCommand >;
public env< TNameAndValue extends string, TEnvVar extends TypedEnv< TNameAndValue, TPrefix, TCommandOptions, Merge<TParentCommandTypes, Merge<TCommandGlobalTypes, TCommandTypes>>, TRequired >, TMappedEnvVar extends MapValue<TEnvVar, TMappedValue>, TRequired extends EnvVarOptions["required"] = undefined, TPrefix extends EnvVarOptions["prefix"] = undefined, TMappedValue = undefined, >( name: TNameAndValue, description: string, options?: Omit<EnvVarOptions, "value"> & { required?: TRequired; prefix?: TPrefix; value?: EnvVarValueHandler<MapTypes<ValueOf<TEnvVar>>, TMappedValue>; }, ): Command< TParentCommandGlobals, TParentCommandTypes, Merge<TCommandOptions, TMappedEnvVar>, TCommandArguments, TCommandGlobals, TCommandTypes, TCommandGlobalTypes, TParentCommand >;
public env( name: string, description: string, options?: EnvVarOptions, ): Command<any> { const result = splitArguments(name);
if (!result.typeDefinition) { result.typeDefinition = "<value:boolean>"; }
if (result.flags.some((envName) => this.cmd.getBaseEnvVar(envName, true))) { throw new DuplicateEnvVarError(name); }
const details: Argument[] = parseArgumentsDefinition( result.typeDefinition, );
if (details.length > 1) { throw new TooManyEnvVarValuesError(name); } else if (details.length && details[0].optionalValue) { throw new UnexpectedOptionalEnvVarValueError(name); } else if (details.length && details[0].variadic) { throw new UnexpectedVariadicEnvVarValueError(name); }
this.cmd.envVars.push({ name: result.flags[0], names: result.flags, description, type: details[0].type, details: details.shift() as Argument, ...options, });
return this; }
/***************************************************************************** **** MAIN HANDLER *********************************************************** *****************************************************************************/
/** * Parse command line arguments and execute matched command. * @param args Command line args to parse. Ex: `cmd.parse( Deno.args )` */ public parse( args: string[] = Deno.args, ): Promise< TParentCommand extends Command<any> ? CommandResult< Record<string, unknown>, Array<unknown>, Record<string, unknown>, Record<string, unknown>, Record<string, unknown>, Record<string, unknown>, Record<string, unknown>, undefined > : CommandResult< MapTypes<TCommandOptions>, MapTypes<TCommandArguments>, MapTypes<TCommandGlobals>, MapTypes<TParentCommandGlobals>, TCommandTypes, TCommandGlobalTypes, TParentCommandTypes, TParentCommand > > { const ctx: ParseContext = { unknown: args.slice(), flags: {}, env: {}, literal: [], stopEarly: false, stopOnUnknown: false, }; return this.parseCommand(ctx) as any; }
private async parseCommand(ctx: ParseContext): Promise<CommandResult> { try { this.reset(); this.registerDefaults(); this.rawArgs = ctx.unknown.slice();
if (this.isExecutable) { await this.executeExecutable(ctx.unknown); return { options: {}, args: [], cmd: this, literal: [] } as any; } else if (this._useRawArgs) { await this.parseEnvVars(ctx, this.envVars); return this.execute(ctx.env, ...ctx.unknown) as any; }
let preParseGlobals = false; let subCommand: Command<any> | undefined;
// Pre parse globals to support: cmd --global-option sub-command --option if (ctx.unknown.length > 0) { // Detect sub command. subCommand = this.getSubCommand(ctx);
if (!subCommand) { // Only pre parse globals if first arg ist a global option. const optionName = ctx.unknown[0].replace(/^-+/, ""); const option = this.getOption(optionName, true);
if (option?.global) { preParseGlobals = true; await this.parseGlobalOptionsAndEnvVars(ctx); } } }
if (subCommand || ctx.unknown.length > 0) { subCommand ??= this.getSubCommand(ctx);
if (subCommand) { subCommand._globalParent = this; return subCommand.parseCommand(ctx); } }
// Parse rest options & env vars. await this.parseOptionsAndEnvVars(ctx, preParseGlobals); const options = { ...ctx.env, ...ctx.flags }; const args = this.parseArguments(ctx, options); this.literalArgs = ctx.literal;
// Execute option action. if (ctx.action) { await ctx.action.action.call(this, options, ...args);
if (ctx.action.standalone) { return { options, args, cmd: this, literal: this.literalArgs, } as any; } }
return this.execute(options, ...args) as any; } catch (error: unknown) { this.throw( error instanceof FlagsValidationError ? new ValidationError(error.message) : error instanceof Error ? error : new Error(`[non-error-thrown] ${error}`), ); } }
private getSubCommand(ctx: ParseContext) { const subCommand = this.getCommand(ctx.unknown[0], true);
if (subCommand) { ctx.unknown.shift(); }
return subCommand; }
private async parseGlobalOptionsAndEnvVars( ctx: ParseContext, ): Promise<void> { const isHelpOption = this.getHelpOption()?.flags.includes(ctx.unknown[0]);
// Parse global env vars. const envVars = [ ...this.envVars.filter((envVar) => envVar.global), ...this.getGlobalEnvVars(true), ];
await this.parseEnvVars(ctx, envVars, !isHelpOption);
// Parse global options. const options = [ ...this.options.filter((option) => option.global), ...this.getGlobalOptions(true), ];
this.parseOptions(ctx, options, { stopEarly: true, stopOnUnknown: true, dotted: false, }); }
private async parseOptionsAndEnvVars( ctx: ParseContext, preParseGlobals: boolean, ): Promise<void> { const helpOption = this.getHelpOption(); const isVersionOption = this._versionOption?.flags.includes(ctx.unknown[0]); const isHelpOption = helpOption && ctx.flags?.[helpOption.name] === true;
// Parse env vars. const envVars = preParseGlobals ? this.envVars.filter((envVar) => !envVar.global) : this.getEnvVars(true);
await this.parseEnvVars( ctx, envVars, !isHelpOption && !isVersionOption, );
// Parse options. const options = this.getOptions(true);
this.parseOptions(ctx, options); }
/** Register default options like `--version` and `--help`. */ private registerDefaults(): this { if (this.hasDefaults || this.getParent()) { return this; } this.hasDefaults = true;
this.reset();
!this.types.has("string") && this.type("string", new StringType(), { global: true }); !this.types.has("number") && this.type("number", new NumberType(), { global: true }); !this.types.has("integer") && this.type("integer", new IntegerType(), { global: true }); !this.types.has("boolean") && this.type("boolean", new BooleanType(), { global: true }); !this.types.has("file") && this.type("file", new FileType(), { global: true });
if (!this._help) { this.help({ hints: true, types: false, }); }
if (this._versionOptions !== false && (this._versionOptions || this.ver)) { this.option( this._versionOptions?.flags || "-V, --version", this._versionOptions?.desc || "Show the version number for this program.", { standalone: true, prepend: true, action: async function () { const long = this.getRawArgs().includes( `--${this._versionOption?.name}`, ); if (long) { await this.checkVersion(); this.showLongVersion(); } else { this.showVersion(); } this.exit(); }, ...(this._versionOptions?.opts ?? {}), }, ); this._versionOption = this.options[0]; }
if (this._helpOptions !== false) { this.option( this._helpOptions?.flags || "-h, --help", this._helpOptions?.desc || "Show this help.", { standalone: true, global: true, prepend: true, action: async function () { const long = this.getRawArgs().includes( `--${this.getHelpOption()?.name}`, ); await this.checkVersion(); this.showHelp({ long }); this.exit(); }, ...(this._helpOptions?.opts ?? {}), }, ); this._helpOption = this.options[0]; }
return this; }
/** * Execute command. * @param options A map of options. * @param args Command arguments. */ protected async execute( options: Record<string, unknown>, ...args: Array<unknown> ): Promise<CommandResult> { if (this.fn) { await this.fn(options, ...args); } else if (this.defaultCommand) { const cmd = this.getCommand(this.defaultCommand, true);
if (!cmd) { throw new DefaultCommandNotFoundError( this.defaultCommand, this.getCommands(), ); } cmd._globalParent = this;
return cmd.execute(options, ...args); }
return { options, args, cmd: this, literal: this.literalArgs, }; }
/** * Execute external sub-command. * @param args Raw command line arguments. */ protected async executeExecutable(args: string[]) { const command = this.getPath().replace(/\s+/g, "-");
await Deno.permissions.request({ name: "run", command });
try { const process: Deno.Process = Deno.run({ cmd: [command, ...args], }); const status: Deno.ProcessStatus = await process.status();
if (!status.success) { Deno.exit(status.code); } } catch (error) { if (error instanceof Deno.errors.NotFound) { throw new CommandExecutableNotFoundError(command); } throw error; } }
/** Parse raw command line arguments. */ protected parseOptions( ctx: ParseContext, options: Option[], { stopEarly = this._stopEarly, stopOnUnknown = false, dotted = true, }: ParseOptionsOptions = {}, ): void { parseFlags(ctx, { stopEarly, stopOnUnknown, dotted, allowEmpty: this._allowEmpty, flags: options, ignoreDefaults: ctx.env, parse: (type: ArgumentValue) => this.parseType(type), option: (option: Option) => { if (!ctx.action && option.action) { ctx.action = option as ActionOption; } }, }); }
/** Parse argument type. */ protected parseType(type: ArgumentValue): unknown { const typeSettings: TypeDef | undefined = this.getType(type.type);
if (!typeSettings) { throw new UnknownTypeError( type.type, this.getTypes().map((type) => type.name), ); }
return typeSettings.handler instanceof Type ? typeSettings.handler.parse(type) : typeSettings.handler(type); }
/** * Read and validate environment variables. * @param ctx Parse context. * @param envVars env vars defined by the command. * @param validate when true, throws an error if a required env var is missing. */ protected async parseEnvVars( ctx: ParseContext, envVars: Array<EnvVar>, validate = true, ): Promise<void> { for (const envVar of envVars) { const env = await this.findEnvVar(envVar.names);
if (env) { const parseType = (value: string) => { return this.parseType({ label: "Environment variable", type: envVar.type, name: env.name, value, }); };
const propertyName = underscoreToCamelCase( envVar.prefix ? envVar.names[0].replace(new RegExp(`^${envVar.prefix}`), "") : envVar.names[0], );
if (envVar.details.list) { ctx.env[propertyName] = env.value .split(envVar.details.separator ?? ",") .map(parseType); } else { ctx.env[propertyName] = parseType(env.value); }
if (envVar.value && typeof ctx.env[propertyName] !== "undefined") { ctx.env[propertyName] = envVar.value(ctx.env[propertyName]); } } else if (envVar.required && validate) { throw new MissingRequiredEnvVarError(envVar); } } }
protected async findEnvVar( names: readonly string[], ): Promise<{ name: string; value: string } | undefined> { for (const name of names) { const status = await Deno.permissions.query({ name: "env", variable: name, });
if (status.state === "granted") { const value = Deno.env.get(name);
if (value) { return { name, value }; } } }
return undefined; }
/** * Parse command-line arguments. * @param ctx Parse context. * @param options Parsed command line options. */ protected parseArguments( ctx: ParseContext, options: Record<string, unknown>, ): TCommandArguments { const params: Array<unknown> = []; const args = ctx.unknown.slice();
if (!this.hasArguments()) { if (args.length) { if (this.hasCommands(true)) { if (this.hasCommand(args[0], true)) { // e.g: command --global-foo --foo sub-command throw new TooManyArgumentsError(args); } else { throw new UnknownCommandError(args[0], this.getCommands()); } } else { throw new NoArgumentsAllowedError(this.getPath()); } } } else { if (!args.length) { const required = this.getArguments() .filter((expectedArg) => !expectedArg.optionalValue) .map((expectedArg) => expectedArg.name);
if (required.length) { const optionNames: string[] = Object.keys(options); const hasStandaloneOption = !!optionNames.find((name) => this.getOption(name, true)?.standalone );
if (!hasStandaloneOption) { throw new MissingArgumentsError(required); } } } else { for (const expectedArg of this.getArguments()) { if (!args.length) { if (expectedArg.optionalValue) { break; } throw new MissingArgumentError(expectedArg.name); }
let arg: unknown;
const parseArgValue = (value: string) => { return expectedArg.list ? value.split(",").map((value) => parseArgType(value)) : parseArgType(value); };
const parseArgType = (value: string) => { return this.parseType({ label: "Argument", type: expectedArg.type, name: expectedArg.name, value, }); };
if (expectedArg.variadic) { arg = args.splice(0, args.length).map((value) => parseArgValue(value) ); } else { arg = parseArgValue(args.shift() as string); }
if (expectedArg.variadic && Array.isArray(arg)) { params.push(...arg); } else if (typeof arg !== "undefined") { params.push(arg); } }
if (args.length) { throw new TooManyArgumentsError(args); } } }
return params as TCommandArguments; }
/** * Handle error. If `throwErrors` is enabled the error will be returned, * otherwise a formatted error message will be printed and `Deno.exit(1)` * will be called. * @param error Error to handle. */ protected throw(error: Error): never { if (this.shouldThrowErrors() || !(error instanceof ValidationError)) { throw error; } this.showHelp();
console.error(red(` ${bold("error")}: ${error.message}\n`));
Deno.exit(error instanceof ValidationError ? error.exitCode : 1); }
/***************************************************************************** **** GETTER ***************************************************************** *****************************************************************************/
/** Get command name. */ public getName(): string { return this._name; }
/** Get parent command. */ public getParent(): TParentCommand { return this._parent as TParentCommand; }
/** * Get parent command from global executed command. * Be sure, to call this method only inside an action handler. Unless this or any child command was executed, * this method returns always undefined. */ public getGlobalParent(): Command<any> | undefined { return this._globalParent; }
/** Get main command. */ public getMainCommand(): Command<any> { return this._parent?.getMainCommand() ?? this; }
/** Get command name aliases. */ public getAliases(): string[] { return this.aliases; }
/** Get full command path. */ public getPath(): string { return this._parent ? this._parent.getPath() + " " + this._name : this._name; }
/** Get arguments definition. E.g: <input-file:string> <output-file:string> */ public getArgsDefinition(): string | undefined { return this.argsDefinition; }
/** * Get argument by name. * @param name Name of the argument. */ public getArgument(name: string): Argument | undefined { return this.getArguments().find((arg) => arg.name === name); }
/** Get arguments. */ public getArguments(): Argument[] { if (!this.args.length && this.argsDefinition) { this.args = parseArgumentsDefinition(this.argsDefinition); }
return this.args; }
/** Check if command has arguments. */ public hasArguments() { return !!this.argsDefinition; }
/** Get command version. */ public getVersion(): string | undefined { return this.getVersionHandler()?.call(this, this); }
/** Get help handler method. */ private getVersionHandler(): VersionHandler | undefined { return this.ver ?? this._parent?.getVersionHandler(); }
/** Get command description. */ public getDescription(): string { // call description method only once return typeof this.desc === "function" ? this.desc = this.desc() : this.desc; }
public getUsage() { return this._usage ?? this.getArgsDefinition(); }
/** Get short command description. This is the first line of the description. */ public getShortDescription(): string { return getDescription(this.getDescription(), true); }
/** Get original command-line arguments. */ public getRawArgs(): string[] { return this.rawArgs; }
/** Get all arguments defined after the double dash. */ public getLiteralArgs(): string[] { return this.literalArgs; }
/** Output generated help without exiting. */ public showVersion(): void { console.log(this.getVersion()); }
/** Returns command name, version and meta data. */ public getLongVersion(): string { return `${bold(this.getMainCommand().getName())} ${ blue(this.getVersion() ?? "") }` + Object.entries(this.getMeta()).map( ([k, v]) => `\n${bold(k)} ${blue(v)}`, ).join(""); }
/** Outputs command name, version and meta data. */ public showLongVersion(): void { console.log(this.getLongVersion()); }
/** Output generated help without exiting. */ public showHelp(options?: HelpOptions): void { console.log(this.getHelp(options)); }
/** Get generated help. */ public getHelp(options?: HelpOptions): string { this.registerDefaults(); return this.getHelpHandler().call(this, this, options ?? {}); }
/** Get help handler method. */ private getHelpHandler(): HelpHandler { return this._help ?? this._parent?.getHelpHandler() as HelpHandler; }
private exit(code = 0) { if (this.shouldExit()) { Deno.exit(code); } }
/** Check if new version is available and add hint to version. */ public async checkVersion(): Promise<void> { const mainCommand = this.getMainCommand(); const upgradeCommand = mainCommand.getCommand("upgrade");
if (!isUpgradeCommand(upgradeCommand)) { return; } const latestVersion = await upgradeCommand.getLatestVersion(); const currentVersion = mainCommand.getVersion();
if (currentVersion === latestVersion) { return; } const versionHelpText = `(New version available: ${latestVersion}. Run '${mainCommand.getName()} upgrade' to upgrade to the latest version!)`;
mainCommand.version(`${currentVersion} ${bold(yellow(versionHelpText))}`); }
/***************************************************************************** **** Options GETTER ********************************************************* *****************************************************************************/
/** * Checks whether the command has options or not. * @param hidden Include hidden options. */ public hasOptions(hidden?: boolean): boolean { return this.getOptions(hidden).length > 0; }
/** * Get options. * @param hidden Include hidden options. */ public getOptions(hidden?: boolean): Option[] { return this.getGlobalOptions(hidden).concat(this.getBaseOptions(hidden)); }
/** * Get base options. * @param hidden Include hidden options. */ public getBaseOptions(hidden?: boolean): Option[] { if (!this.options.length) { return []; }
return hidden ? this.options.slice(0) : this.options.filter((opt) => !opt.hidden); }
/** * Get global options. * @param hidden Include hidden options. */ public getGlobalOptions(hidden?: boolean): Option[] { const helpOption = this.getHelpOption(); const getGlobals = ( cmd: Command<any>, noGlobals: boolean, options: Option[] = [], names: string[] = [], ): Option[] => { if (cmd.options.length) { for (const option of cmd.options) { if ( option.global && !this.options.find((opt) => opt.name === option.name) && names.indexOf(option.name) === -1 && (hidden || !option.hidden) ) { if (noGlobals && option !== helpOption) { continue; }
names.push(option.name); options.push(option); } } }
return cmd._parent ? getGlobals( cmd._parent, noGlobals || cmd._noGlobals, options, names, ) : options; };
return this._parent ? getGlobals(this._parent, this._noGlobals) : []; }
/** * Checks whether the command has an option with given name or not. * @param name Name of the option. Must be in param-case. * @param hidden Include hidden options. */ public hasOption(name: string, hidden?: boolean): boolean { return !!this.getOption(name, hidden); }
/** * Get option by name. * @param name Name of the option. Must be in param-case. * @param hidden Include hidden options. */ public getOption(name: string, hidden?: boolean): Option | undefined { return this.getBaseOption(name, hidden) ?? this.getGlobalOption(name, hidden); }
/** * Get base option by name. * @param name Name of the option. Must be in param-case. * @param hidden Include hidden options. */ public getBaseOption(name: string, hidden?: boolean): Option | undefined { const option = this.options.find((option) => option.name === name || option.aliases?.includes(name) );
return option && (hidden || !option.hidden) ? option : undefined; }
/** * Get global option from parent commands by name. * @param name Name of the option. Must be in param-case. * @param hidden Include hidden options. */ public getGlobalOption(name: string, hidden?: boolean): Option | undefined { const helpOption = this.getHelpOption(); const getGlobalOption = ( parent: Command, noGlobals: boolean, ): Option | undefined => { const option: Option | undefined = parent.getBaseOption( name, hidden, );
if (!option?.global) { return parent._parent && getGlobalOption( parent._parent, noGlobals || parent._noGlobals, ); } if (noGlobals && option !== helpOption) { return; }
return option; };
return this._parent && getGlobalOption( this._parent, this._noGlobals, ); }
/** * Remove option by name. * @param name Name of the option. Must be in param-case. */ public removeOption(name: string): Option | undefined { const index = this.options.findIndex((option) => option.name === name);
if (index === -1) { return; }
return this.options.splice(index, 1)[0]; }
/** * Checks whether the command has sub-commands or not. * @param hidden Include hidden commands. */ public hasCommands(hidden?: boolean): boolean { return this.getCommands(hidden).length > 0; }
/** * Get commands. * @param hidden Include hidden commands. */ public getCommands(hidden?: boolean): Array<Command<any>> { return this.getGlobalCommands(hidden).concat(this.getBaseCommands(hidden)); }
/** * Get base commands. * @param hidden Include hidden commands. */ public getBaseCommands(hidden?: boolean): Array<Command<any>> { const commands = Array.from(this.commands.values()); return hidden ? commands : commands.filter((cmd) => !cmd.isHidden); }
/** * Get global commands. * @param hidden Include hidden commands. */ public getGlobalCommands(hidden?: boolean): Array<Command<any>> { const getCommands = ( command: Command<any>, noGlobals: boolean, commands: Array<Command<any>> = [], names: string[] = [], ): Array<Command<any>> => { if (command.commands.size) { for (const [_, cmd] of command.commands) { if ( cmd.isGlobal && this !== cmd && !this.commands.has(cmd._name) && names.indexOf(cmd._name) === -1 && (hidden || !cmd.isHidden) ) { if (noGlobals && cmd?.getName() !== "help") { continue; }
names.push(cmd._name); commands.push(cmd); } } }
return command._parent ? getCommands( command._parent, noGlobals || command._noGlobals, commands, names, ) : commands; };
return this._parent ? getCommands(this._parent, this._noGlobals) : []; }
/** * Checks whether a child command exists by given name or alias. * @param name Name or alias of the command. * @param hidden Include hidden commands. */ public hasCommand(name: string, hidden?: boolean): boolean { return !!this.getCommand(name, hidden); }
/** * Get command by name or alias. * @param name Name or alias of the command. * @param hidden Include hidden commands. */ public getCommand<TCommand extends Command<any>>( name: string, hidden?: boolean, ): TCommand | undefined { return this.getBaseCommand(name, hidden) ?? this.getGlobalCommand(name, hidden); }
/** * Get base command by name or alias. * @param name Name or alias of the command. * @param hidden Include hidden commands. */ public getBaseCommand<TCommand extends Command<any>>( name: string, hidden?: boolean, ): TCommand | undefined { for (const cmd of this.commands.values()) { if (cmd._name === name || cmd.aliases.includes(name)) { return (cmd && (hidden || !cmd.isHidden) ? cmd : undefined) as | TCommand | undefined; } } }
/** * Get global command by name or alias. * @param name Name or alias of the command. * @param hidden Include hidden commands. */ public getGlobalCommand<TCommand extends Command<any>>( name: string, hidden?: boolean, ): TCommand | undefined { const getGlobalCommand = ( parent: Command, noGlobals: boolean, ): Command | undefined => { const cmd: Command | undefined = parent.getBaseCommand(name, hidden);
if (!cmd?.isGlobal) { return parent._parent && getGlobalCommand(parent._parent, noGlobals || parent._noGlobals); } if (noGlobals && cmd.getName() !== "help") { return; }
return cmd; };
return this._parent && getGlobalCommand(this._parent, this._noGlobals) as TCommand; }
/** * Remove sub-command by name or alias. * @param name Name or alias of the command. */ public removeCommand(name: string): Command<any> | undefined { const command = this.getBaseCommand(name, true);
if (command) { this.commands.delete(command._name); }
return command; }
/** Get types. */ public getTypes(): Array<TypeDef> { return this.getGlobalTypes().concat(this.getBaseTypes()); }
/** Get base types. */ public getBaseTypes(): Array<TypeDef> { return Array.from(this.types.values()); }
/** Get global types. */ public getGlobalTypes(): Array<TypeDef> { const getTypes = ( cmd: Command<any> | undefined, types: Array<TypeDef> = [], names: Array<string> = [], ): Array<TypeDef> => { if (cmd) { if (cmd.types.size) { cmd.types.forEach((type: TypeDef) => { if ( type.global && !this.types.has(type.name) && names.indexOf(type.name) === -1 ) { names.push(type.name); types.push(type); } }); }
return getTypes(cmd._parent, types, names); }
return types; };
return getTypes(this._parent); }
/** * Get type by name. * @param name Name of the type. */ public getType(name: string): TypeDef | undefined { return this.getBaseType(name) ?? this.getGlobalType(name); }
/** * Get base type by name. * @param name Name of the type. */ public getBaseType(name: string): TypeDef | undefined { return this.types.get(name); }
/** * Get global type by name. * @param name Name of the type. */ public getGlobalType(name: string): TypeDef | undefined { if (!this._parent) { return; }
const cmd: TypeDef | undefined = this._parent.getBaseType(name);
if (!cmd?.global) { return this._parent.getGlobalType(name); }
return cmd; }
/** Get completions. */ public getCompletions() { return this.getGlobalCompletions().concat(this.getBaseCompletions()); }
/** Get base completions. */ public getBaseCompletions(): Completion[] { return Array.from(this.completions.values()); }
/** Get global completions. */ public getGlobalCompletions(): Completion[] { const getCompletions = ( cmd: Command<any> | undefined, completions: Completion[] = [], names: string[] = [], ): Completion[] => { if (cmd) { if (cmd.completions.size) { cmd.completions.forEach((completion: Completion) => { if ( completion.global && !this.completions.has(completion.name) && names.indexOf(completion.name) === -1 ) { names.push(completion.name); completions.push(completion); } }); }
return getCompletions(cmd._parent, completions, names); }
return completions; };
return getCompletions(this._parent); }
/** * Get completion by name. * @param name Name of the completion. */ public getCompletion(name: string): Completion | undefined { return this.getBaseCompletion(name) ?? this.getGlobalCompletion(name); }
/** * Get base completion by name. * @param name Name of the completion. */ public getBaseCompletion(name: string): Completion | undefined { return this.completions.get(name); }
/** * Get global completions by name. * @param name Name of the completion. */ public getGlobalCompletion(name: string): Completion | undefined { if (!this._parent) { return; }
const completion: Completion | undefined = this._parent.getBaseCompletion( name, );
if (!completion?.global) { return this._parent.getGlobalCompletion(name); }
return completion; }
/** * Checks whether the command has environment variables or not. * @param hidden Include hidden environment variable. */ public hasEnvVars(hidden?: boolean): boolean { return this.getEnvVars(hidden).length > 0; }
/** * Get environment variables. * @param hidden Include hidden environment variable. */ public getEnvVars(hidden?: boolean): EnvVar[] { return this.getGlobalEnvVars(hidden).concat(this.getBaseEnvVars(hidden)); }
/** * Get base environment variables. * @param hidden Include hidden environment variable. */ public getBaseEnvVars(hidden?: boolean): EnvVar[] { if (!this.envVars.length) { return []; }
return hidden ? this.envVars.slice(0) : this.envVars.filter((env) => !env.hidden); }
/** * Get global environment variables. * @param hidden Include hidden environment variable. */ public getGlobalEnvVars(hidden?: boolean): EnvVar[] { if (this._noGlobals) { return []; }
const getEnvVars = ( cmd: Command<any> | undefined, envVars: EnvVar[] = [], names: string[] = [], ): EnvVar[] => { if (cmd) { if (cmd.envVars.length) { cmd.envVars.forEach((envVar: EnvVar) => { if ( envVar.global && !this.envVars.find((env) => env.names[0] === envVar.names[0]) && names.indexOf(envVar.names[0]) === -1 && (hidden || !envVar.hidden) ) { names.push(envVar.names[0]); envVars.push(envVar); } }); }
return getEnvVars(cmd._parent, envVars, names); }
return envVars; };
return getEnvVars(this._parent); }
/** * Checks whether the command has an environment variable with given name or not. * @param name Name of the environment variable. * @param hidden Include hidden environment variable. */ public hasEnvVar(name: string, hidden?: boolean): boolean { return !!this.getEnvVar(name, hidden); }
/** * Get environment variable by name. * @param name Name of the environment variable. * @param hidden Include hidden environment variable. */ public getEnvVar(name: string, hidden?: boolean): EnvVar | undefined { return this.getBaseEnvVar(name, hidden) ?? this.getGlobalEnvVar(name, hidden); }
/** * Get base environment variable by name. * @param name Name of the environment variable. * @param hidden Include hidden environment variable. */ public getBaseEnvVar(name: string, hidden?: boolean): EnvVar | undefined { const envVar: EnvVar | undefined = this.envVars.find((env) => env.names.indexOf(name) !== -1 );
return envVar && (hidden || !envVar.hidden) ? envVar : undefined; }
/** * Get global environment variable by name. * @param name Name of the environment variable. * @param hidden Include hidden environment variable. */ public getGlobalEnvVar(name: string, hidden?: boolean): EnvVar | undefined { if (!this._parent || this._noGlobals) { return; }
const envVar: EnvVar | undefined = this._parent.getBaseEnvVar( name, hidden, );
if (!envVar?.global) { return this._parent.getGlobalEnvVar(name, hidden); }
return envVar; }
/** Checks whether the command has examples or not. */ public hasExamples(): boolean { return this.examples.length > 0; }
/** Get all examples. */ public getExamples(): Example[] { return this.examples; }
/** Checks whether the command has an example with given name or not. */ public hasExample(name: string): boolean { return !!this.getExample(name); }
/** Get example with given name. */ public getExample(name: string): Example | undefined { return this.examples.find((example) => example.name === name); }
private getHelpOption(): Option | undefined { return this._helpOption ?? this._parent?.getHelpOption(); }}
function isUpgradeCommand(command: unknown): command is UpgradeCommandImpl { return command instanceof Command && "getLatestVersion" in command;}
interface UpgradeCommandImpl { getLatestVersion(): Promise<string>;}
interface DefaultOption { flags: string; desc?: string; opts?: OptionOptions;}
type ActionOption = Option & { action: ActionHandler };
interface ParseContext extends ParseFlagsContext<Record<string, unknown>> { action?: ActionOption; env: Record<string, unknown>;}
interface ParseOptionsOptions { stopEarly?: boolean; stopOnUnknown?: boolean; dotted?: boolean;}
type TrimLeft<TValue extends string, TTrimValue extends string | undefined> = TValue extends `${TTrimValue}${infer TRest}` ? TRest : TValue;
type TrimRight<TValue extends string, TTrimValue extends string> = TValue extends `${infer TRest}${TTrimValue}` ? TRest : TValue;
type Lower<TValue extends string> = TValue extends Uppercase<TValue> ? Lowercase<TValue> : Uncapitalize<TValue>;
type CamelCase<TValue extends string> = TValue extends `${infer TPart}_${infer TRest}` ? `${Lower<TPart>}${Capitalize<CamelCase<TRest>>}` : TValue extends `${infer TPart}-${infer TRest}` ? `${Lower<TPart>}${Capitalize<CamelCase<TRest>>}` : Lower<TValue>;
type OneOf<TValue, TDefault> = TValue extends void ? TDefault : TValue;
type Merge<TLeft, TRight> = TLeft extends void ? TRight : TRight extends void ? TLeft : TLeft & TRight;
// type Merge<L, R> = L extends void ? R// : R extends void ? L// : Omit<L, keyof R> & R;
type MergeRecursive<TLeft, TRight> = TLeft extends void ? TRight : TRight extends void ? TLeft : TLeft & TRight;
type OptionalOrRequiredValue<TType extends string> = | `[${TType}]` | `<${TType}>`;type RestValue = `...${string}` | `${string}...`;
/** * Rest args with list type and completions. * * - `[...name:type[]:completion]` * - `<...name:type[]:completion>` * - `[name...:type[]:completion]` * - `<name...:type[]:completion>` */type RestArgsListTypeCompletion<TType extends string> = OptionalOrRequiredValue< `${RestValue}:${TType}[]:${string}`>;
/** * Rest args with list type. * * - `[...name:type[]]` * - `<...name:type[]>` * - `[name...:type[]]` * - `<name...:type[]>` */type RestArgsListType<TType extends string> = OptionalOrRequiredValue< `${RestValue}:${TType}[]`>;
/** * Rest args with type and completions. * * - `[...name:type:completion]` * - `<...name:type:completion>` * - `[name...:type:completion]` * - `<name...:type:completion>` */type RestArgsTypeCompletion<TType extends string> = OptionalOrRequiredValue< `${RestValue}:${TType}:${string}`>;
/** * Rest args with type. * * - `[...name:type]` * - `<...name:type>` * - `[name...:type]` * - `<name...:type>` */type RestArgsType<TType extends string> = OptionalOrRequiredValue< `${RestValue}:${TType}`>;
/** * Rest args. * - `[...name]` * - `<...name>` * - `[name...]` * - `<name...>` */type RestArgs = OptionalOrRequiredValue< `${RestValue}`>;
/** * Single arg with list type and completions. * * - `[name:type[]:completion]` * - `<name:type[]:completion>` */type SingleArgListTypeCompletion<TType extends string> = OptionalOrRequiredValue< `${string}:${TType}[]:${string}` >;
/** * Single arg with list type. * * - `[name:type[]]` * - `<name:type[]>` */type SingleArgListType<TType extends string> = OptionalOrRequiredValue< `${string}:${TType}[]`>;
/** * Single arg with type and completion. * * - `[name:type:completion]` * - `<name:type:completion>` */type SingleArgTypeCompletion<TType extends string> = OptionalOrRequiredValue< `${string}:${TType}:${string}`>;
/** * Single arg with type. * * - `[name:type]` * - `<name:type>` */type SingleArgType<TType extends string> = OptionalOrRequiredValue< `${string}:${TType}`>;
/** * Single arg. * * - `[name]` * - `<name>` */type SingleArg = OptionalOrRequiredValue< `${string}`>;
type DefaultTypes = { number: NumberType; integer: IntegerType; string: StringType; boolean: BooleanType; file: FileType;};
type ArgumentType< TArg extends string, TCustomTypes, TTypes = Merge<DefaultTypes, TCustomTypes>,> = TArg extends RestArgsListTypeCompletion<infer Type> ? TTypes extends Record<Type, infer R> ? Array<Array<R>> : unknown : TArg extends RestArgsListType<infer Type> ? TTypes extends Record<Type, infer R> ? Array<Array<R>> : unknown : TArg extends RestArgsTypeCompletion<infer Type> ? TTypes extends Record<Type, infer R> ? Array<R> : unknown : TArg extends RestArgsType<infer Type> ? TTypes extends Record<Type, infer R> ? Array<R> : unknown : TArg extends RestArgs ? Array<string> : TArg extends SingleArgListTypeCompletion<infer Type> ? TTypes extends Record<Type, infer R> ? Array<R> : unknown : TArg extends SingleArgListType<infer Type> ? TTypes extends Record<Type, infer R> ? Array<R> : unknown : TArg extends SingleArgTypeCompletion<infer Type> ? TTypes extends Record<Type, infer R> ? R : unknown : TArg extends SingleArgType<infer Type> ? TTypes extends Record<Type, infer R> ? R : unknown : TArg extends SingleArg ? string : unknown;
type ArgumentTypes<TFlags extends string, T> = TFlags extends `${string} ${string}` ? TypedArguments<TFlags, T> : ArgumentType<TFlags, T>;
type GetArguments<TFlags extends string> = TFlags extends `-${string}=${infer RestFlags}` ? GetArguments<RestFlags> : TFlags extends `-${string} ${infer RestFlags}` ? GetArguments<RestFlags> : TFlags;
type OptionName<Name extends string> = Name extends "*" ? string : CamelCase<TrimRight<Name, ",">>;
type IsRequired<TRequired extends boolean | undefined, TDefault> = TRequired extends true ? true : TDefault extends undefined ? false : true;
type NegatableOption< TName extends string, TOptions, TDefault,> = TDefault extends undefined ? OptionName<TName> extends keyof TOptions ? { [Key in OptionName<TName>]?: false } : { [Key in OptionName<TName>]: boolean } : { [Key in OptionName<TName>]: NonNullable<TDefault> | false };
type BooleanOption< TName extends string, TOptions, TRequired extends boolean | undefined = undefined, TDefault = undefined,> = TName extends `no-${infer Name}` ? NegatableOption<Name, TOptions, TDefault> : TName extends `${infer Name}.${infer Rest}` ? (TRequired extends true ? { [Key in OptionName<Name>]: BooleanOption< Rest, TOptions, TRequired, TDefault >; } : { [Key in OptionName<Name>]?: BooleanOption< Rest, TOptions, TRequired, TDefault >; }) : (TRequired extends true ? { [Key in OptionName<TName>]: true | TDefault } : { [Key in OptionName<TName>]?: true | TDefault });
type ValueOption< TName extends string, TRestFlags extends string, TTypes, TRequired extends boolean | undefined = undefined, TDefault = undefined,> = TName extends `${infer Name}.${infer RestName}` ? (TRequired extends true ? { [Key in OptionName<Name>]: ValueOption< RestName, TRestFlags, TTypes, TRequired, TDefault >; } : { [Key in OptionName<Name>]?: ValueOption< RestName, TRestFlags, TTypes, TRequired, TDefault >; }) : (TRequired extends true ? { [Key in OptionName<TName>]: GetArguments<TRestFlags> extends `[${string}]` ? | NonNullable<TDefault> | true | ArgumentType<GetArguments<TRestFlags>, TTypes> : | NonNullable<TDefault> | ArgumentType<GetArguments<TRestFlags>, TTypes>; } : { [Key in OptionName<TName>]?: GetArguments<TRestFlags> extends `[${string}]` ? | NonNullable<TDefault> | true | ArgumentType<GetArguments<TRestFlags>, TTypes> : | NonNullable<TDefault> | ArgumentType<GetArguments<TRestFlags>, TTypes>; });
type ValuesOption< TName extends string, TRestFlags extends string, TTypes, TRequired extends boolean | undefined = undefined, TDefault = undefined,> = TName extends `${infer Name}.${infer RestName}` ? (TRequired extends true ? { [Key in OptionName<Name>]: ValuesOption< RestName, TRestFlags, TTypes, TRequired, TDefault >; } : { [Key in OptionName<Name>]?: ValuesOption< RestName, TRestFlags, TTypes, TRequired, TDefault >; }) : (TRequired extends true ? { [Key in OptionName<TName>]: GetArguments<TRestFlags> extends `[${string}]` ? | NonNullable<TDefault> | true | ArgumentTypes<GetArguments<TRestFlags>, TTypes> : | NonNullable<TDefault> | ArgumentTypes<GetArguments<TRestFlags>, TTypes>; } : { [Key in OptionName<TName>]?: GetArguments<TRestFlags> extends `[${string}]` ? | NonNullable<TDefault> | true | ArgumentTypes<GetArguments<TRestFlags>, TTypes> : | NonNullable<TDefault> | ArgumentTypes<GetArguments<TRestFlags>, TTypes>; });
type MapValue<TOptions, TMappedOptions, TCollect = undefined> = TMappedOptions extends undefined ? TCollect extends true ? { [Key in keyof TOptions]: TOptions[Key] extends (Record<string, unknown> | undefined) ? MapValue<TOptions[Key], TMappedOptions> : Array<NonNullable<TOptions[Key]>>; } : TOptions : { [Key in keyof TOptions]: TOptions[Key] extends (Record<string, unknown> | undefined) ? MapValue<TOptions[Key], TMappedOptions> : TMappedOptions; };
type GetOptionName<TFlags> = TFlags extends `${string}--${infer Name}=${string}` ? TrimRight<Name, ","> : TFlags extends `${string}--${infer Name} ${string}` ? TrimRight<Name, ","> : TFlags extends `${string}--${infer Name}` ? Name : TFlags extends `-${infer Name}=${string}` ? TrimRight<Name, ","> : TFlags extends `-${infer Name} ${string}` ? TrimRight<Name, ","> : TFlags extends `-${infer Name}` ? Name : unknown;
type MergeOptions< TFlags, TOptions, TMappedOptions, TName = GetOptionName<TFlags>,> = TName extends `no-${string}` ? Spread<TOptions, TMappedOptions> : TName extends `${string}.${string}` ? MergeRecursive<TOptions, TMappedOptions> : Merge<TOptions, TMappedOptions>;
// type MergeOptions<T, CO, O, N = GetOptionName<T>> = N extends `no-${string}`// ? Spread<CO, O>// : N extends `${infer Name}.${infer Child}`// ? (OptionName<Name> extends keyof Merge<CO, O>// ? OptionName<Child> extends// keyof NonNullable<Merge<CO, O>[OptionName<Name>]> ? SpreadTwo<CO, O>// : MergeRecursive<CO, O>// : MergeRecursive<CO, O>)// : Merge<CO, O>;
type TypedOption< TFlags extends string, TOptions, TTypes, TRequired extends boolean | undefined = undefined, TDefault = undefined,> = number extends TTypes ? any : TFlags extends `${string}--${infer Name}=${infer TRestFlags}` ? ValuesOption< Name, TRestFlags, TTypes, IsRequired<TRequired, TDefault>, TDefault > : TFlags extends `${string}--${infer Name} ${infer TRestFlags}` ? ValuesOption< Name, TRestFlags, TTypes, IsRequired<TRequired, TDefault>, TDefault > : TFlags extends `${string}--${infer Name}` ? BooleanOption<Name, TOptions, IsRequired<TRequired, TDefault>, TDefault> : TFlags extends `-${infer Name}=${infer TRestFlags}` ? ValuesOption< Name, TRestFlags, TTypes, IsRequired<TRequired, TDefault>, TDefault > : TFlags extends `-${infer Name} ${infer TRestFlags}` ? ValuesOption< Name, TRestFlags, TTypes, IsRequired<TRequired, TDefault>, TDefault > : TFlags extends `-${infer Name}` ? BooleanOption<Name, TOptions, IsRequired<TRequired, TDefault>, TDefault> : Record<string, unknown>;
type TypedArguments<TArgs extends string, TTypes> = number extends TTypes ? any : TArgs extends `${infer TArg} ${infer TRestArgs}` ? TArg extends `[${string}]` ? [ArgumentType<TArg, TTypes>?, ...TypedArguments<TRestArgs, TTypes>] : [ArgumentType<TArg, TTypes>, ...TypedArguments<TRestArgs, TTypes>] : TArgs extends `${string}...${string}` ? [ ...ArgumentType<TArgs, TTypes> extends Array<infer TValue> ? TArgs extends `[${string}]` ? Array<TValue> : [TValue, ...Array<TValue>] : never, ] : TArgs extends `[${string}]` ? [ArgumentType<TArgs, TTypes>?] : [ArgumentType<TArgs, TTypes>];
type TypedCommandArguments<TNameAndArguments extends string, TTypes> = number extends TTypes ? any : TNameAndArguments extends `${string} ${infer TFlags}` ? TypedArguments<TFlags, TTypes> : [];
type TypedEnv< TNameAndValue extends string, TPrefix extends string | undefined, TOptions, TTypes, TRequired extends boolean | undefined = undefined, TDefault = undefined,> = number extends TTypes ? any : TNameAndValue extends `${infer Name}=${infer Rest}` ? ValueOption<TrimLeft<Name, TPrefix>, Rest, TTypes, TRequired, TDefault> : TNameAndValue extends `${infer Name} ${infer Rest}` ? ValueOption<TrimLeft<Name, TPrefix>, Rest, TTypes, TRequired, TDefault> : TNameAndValue extends `${infer Name}` ? BooleanOption<TrimLeft<Name, TPrefix>, TOptions, TRequired, TDefault> : Record<string, unknown>;
type TypedType< TName extends string, THandler extends TypeOrTypeHandler<unknown>,> = { [Name in TName]: THandler };
type RequiredKeys<TRecord> = { // deno-lint-ignore ban-types [Key in keyof TRecord]-?: {} extends Pick<TRecord, Key> ? never : Key;}[keyof TRecord];
type OptionalKeys<TRecord> = { // deno-lint-ignore ban-types [Key in keyof TRecord]-?: {} extends Pick<TRecord, Key> ? Key : never;}[keyof TRecord];
type SpreadRequiredProperties< TTarget, TSource, TKeys extends keyof TTarget & keyof TSource,> = { [Key in TKeys]: | Exclude<TTarget[Key], undefined> | Exclude<TSource[Key], undefined>;};
type SpreadOptionalProperties< TTarget, TSource, TKeys extends keyof TTarget & keyof TSource,> = { [Key in TKeys]?: TTarget[Key] | TSource[Key];};
/** Merge types of two objects. */type Spread<TTarget, TSource> = TTarget extends void ? TSource : TSource extends void ? TTarget // Properties in L that don't exist in R. : & Omit<TTarget, keyof TSource> // Properties in R that don't exist in L. & Omit<TSource, keyof TTarget> // Required properties in R that exist in L. & SpreadRequiredProperties< TTarget, TSource, RequiredKeys<TSource> & keyof TTarget > // Required properties in L that exist in R. & SpreadRequiredProperties< TTarget, TSource, RequiredKeys<TTarget> & keyof TSource > // Optional properties in L and R. & SpreadOptionalProperties< TTarget, TSource, OptionalKeys<TTarget> & OptionalKeys<TSource> >;
type ValueOf<TValue> = TValue extends Record<string, infer V> ? ValueOf<V> : TValue;