diff --git a/esm.mjs b/esm.mjs index 77a8f1b..cfb03d5 100644 --- a/esm.mjs +++ b/esm.mjs @@ -12,5 +12,5 @@ export const { Command, Argument, Option, - Help + Help, } = extraTypingsCommander; diff --git a/index.d.ts b/index.d.ts index 7ace8e7..52d11da 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,3 @@ - type TrimRight = S extends `${infer R} ` ? TrimRight : S; type TrimLeft = S extends ` ${infer R}` ? TrimLeft : S; type Trim = TrimLeft>; @@ -20,152 +19,299 @@ type Resolve = T extends infer O ? { [K in keyof O]: O[K] } : never; // - https://github.com/microsoft/TypeScript/issues/29729 // - https://github.com/sindresorhus/type-fest/blob/main/source/literal-union.d.ts // - https://github.com/sindresorhus/type-fest/blob/main/source/primitive.d.ts -type LiteralUnion = LiteralType | (BaseType & Record); +type LiteralUnion = + | LiteralType + | (BaseType & Record); // Side note: not trying to represent arrays as non-empty, keep it simple. // https://stackoverflow.com/a/56006703/1082434 -type InferVariadic = - S extends `${string}...` - ? ArgT[] - : ArgT; - -type InferArgumentType = - [CoerceT] extends [undefined] - ? InferVariadic | DefaultT - : ([ChoicesT] extends [undefined] - ? CoerceT | DefaultT - : CoerceT | DefaultT | ChoicesT - ) +type InferVariadic = S extends `${string}...` + ? ArgT[] + : ArgT; +type InferArgumentType = [ + CoerceT, +] extends [undefined] + ? + | InferVariadic + | DefaultT + : [ChoicesT] extends [undefined] + ? CoerceT | DefaultT + : CoerceT | DefaultT | ChoicesT; // Special handling for optional variadic argument, won't be undefined as implementation returns []. -type InferArgumentOptionalType = - Value extends `${string}...` - ? InferArgumentType - : InferArgumentType +type InferArgumentOptionalType< + Value extends string, + DefaultT, + CoerceT, + ChoicesT, +> = Value extends `${string}...` + ? InferArgumentType< + Value, + [DefaultT] extends [undefined] ? never : DefaultT, + CoerceT, + ChoicesT + > + : InferArgumentType; // ArgRequired comes from .argRequired()/.argOptional(), and ArgRequiredFromUsage is implied by usage /[optional] -type ResolveRequired = - ArgRequired extends undefined - ? ArgRequiredFromUsage - : ArgRequired; +type ResolveRequired< + ArgRequired extends boolean | undefined, + ArgRequiredFromUsage extends boolean, +> = ArgRequired extends undefined ? ArgRequiredFromUsage : ArgRequired; -type InferArgumentTypeResolvedRequired = - ArgRequired extends true - ? InferArgumentType - : InferArgumentOptionalType; +type InferArgumentTypeResolvedRequired< + Value extends string, + DefaultT, + CoerceT, + ArgRequired extends boolean, + ChoicesT, +> = ArgRequired extends true + ? InferArgumentType + : InferArgumentOptionalType; // Resolve whether argument required, and strip []/<> from around value. -type InferArgument = - S extends `<${infer Value}>` - ? InferArgumentTypeResolvedRequired, ChoicesT> - : S extends `[${infer Value}]` - ? InferArgumentTypeResolvedRequired, ChoicesT> - : InferArgumentTypeResolvedRequired, ChoicesT>; // the implementation fallback is treat as - -type InferArguments = - S extends `${infer First} ${infer Rest}` - ? [InferArgument, ...InferArguments>] - : [InferArgument]; +type InferArgument< + S extends string, + DefaultT = undefined, + CoerceT = undefined, + ArgRequired extends boolean | undefined = undefined, + ChoicesT = undefined, +> = S extends `<${infer Value}>` + ? InferArgumentTypeResolvedRequired< + Value, + DefaultT, + CoerceT, + ResolveRequired, + ChoicesT + > + : S extends `[${infer Value}]` + ? InferArgumentTypeResolvedRequired< + Value, + DefaultT, + CoerceT, + ResolveRequired, + ChoicesT + > + : InferArgumentTypeResolvedRequired< + S, + DefaultT, + CoerceT, + ResolveRequired, + ChoicesT + >; // the implementation fallback is treat as + +type InferArguments = S extends `${infer First} ${infer Rest}` + ? [InferArgument, ...InferArguments>] + : [InferArgument]; type InferCommmandArguments = - S extends `${string} ${infer Args}` - ? InferArguments> - : []; + S extends `${string} ${infer Args}` ? InferArguments> : []; type FlagsToFlag = - Flags extends `${string},${infer LongFlag}` - ? TrimLeft - : Flags extends `${string}|${infer LongFlag}` + Flags extends `${string},${infer LongFlag}` + ? TrimLeft + : Flags extends `${string}|${infer LongFlag}` + ? TrimLeft + : Flags extends `${string} ${infer LongFlag}` ? TrimLeft - : Flags extends `${string} ${infer LongFlag}` - ? TrimLeft - : Flags; - -type ConvertFlagToName = - Flag extends `--no-${infer Name}` - ? CamelCase - : Flag extends `--${infer Name}` - ? CamelCase - : Flag extends `-${infer Name}` - ? CamelCase - : never; - -type CombineOptions = - keyof O extends keyof Options - ? { [K in keyof Options]: K extends keyof O ? Options[K] | O[keyof O] : Options[K] } - : Options & O; - -type IsAlwaysDefined = - Mandatory extends true - ? true - : [undefined] extends [DefaulT] - ? false - : true; + : Flags; + +type ConvertFlagToName = Flag extends `--no-${infer Name}` + ? CamelCase + : Flag extends `--${infer Name}` + ? CamelCase + : Flag extends `-${infer Name}` + ? CamelCase + : never; + +type CombineOptions = keyof O extends keyof Options + ? { + [K in keyof Options]: K extends keyof O + ? Options[K] | O[keyof O] + : Options[K]; + } + : Options & O; + +type IsAlwaysDefined< + DefaulT, + Mandatory extends boolean, +> = Mandatory extends true + ? true + : [undefined] extends [DefaulT] + ? false + : true; // Modify PresetT to take into account negated. -type NegatePresetType = - Flag extends `--no-${string}` - ? undefined extends PresetT ? false : PresetT - : undefined extends PresetT ? true : PresetT; - +type NegatePresetType< + Flag extends string, + PresetT, +> = Flag extends `--no-${string}` + ? undefined extends PresetT + ? false + : PresetT + : undefined extends PresetT + ? true + : PresetT; + // Modify DefaultT to take into account negated. -type NegateDefaultType = - Flag extends `--no-${string}` - ? [undefined] extends [DefaultT] ? true : DefaultT - : [undefined] extends [DefaultT] ? never : DefaultT; // don't add undefined, will make property optional later - +type NegateDefaultType< + Flag extends string, + DefaultT, +> = Flag extends `--no-${string}` + ? [undefined] extends [DefaultT] + ? true + : DefaultT + : [undefined] extends [DefaultT] + ? never + : DefaultT; // don't add undefined, will make property optional later + // Modify ValueT to take into account coerce function. -type CoerceValueType = - [ValueT] extends [never] - ? never - : [CoerceT] extends [undefined] - ? ValueT - : CoerceT; - +type CoerceValueType = [ValueT] extends [never] + ? never + : [CoerceT] extends [undefined] + ? ValueT + : CoerceT; + // Modify PresetT to take into account coerce function. -type CoercePresetType = - [PresetT] extends [never] - ? never - : [CoerceT] extends [undefined] - ? PresetT - : undefined extends PresetT ? undefined : CoerceT; +type CoercePresetType = [PresetT] extends [never] + ? never + : [CoerceT] extends [undefined] + ? PresetT + : undefined extends PresetT + ? undefined + : CoerceT; -type BuildOptionProperty = - AlwaysDefined extends true - ? { [K in Name]: FullValueT } - : { [K in Name]?: FullValueT }; +type BuildOptionProperty< + Name extends string, + FullValueT, + AlwaysDefined extends boolean, +> = AlwaysDefined extends true + ? { [K in Name]: FullValueT } + : { [K in Name]?: FullValueT }; -type InferOptionsCombine = - Resolve>>; +type InferOptionsCombine< + Options, + Name extends string, + FullValueT, + AlwaysDefined extends boolean, +> = Resolve< + CombineOptions> +>; // Combine the possible types -type InferOptionsNegateCombo = - Flag extends `--no-${string}` - ? Name extends keyof Options - ? InferOptionsCombine // combo does not set default, leave that to positive option - : InferOptionsCombine // lone negated option sets default - : InferOptionsCombine; +type InferOptionsNegateCombo< + Options, + Flag extends string, + Name extends string, + ValueT, + PresetT, + DefaultT, + AlwaysDefined extends boolean, +> = Flag extends `--no-${string}` + ? Name extends keyof Options + ? InferOptionsCombine // combo does not set default, leave that to positive option + : InferOptionsCombine // lone negated option sets default + : InferOptionsCombine< + Options, + Name, + ValueT | PresetT | DefaultT, + AlwaysDefined + >; // Recalc values taking into account negated option. // Fill in appropriate PresetT value if undefined. -type InferOptionTypes = - InferOptionsNegateCombo, - CoerceValueType : InferVariadic>, - NegatePresetType>, - NegateDefaultType, - IsAlwaysDefined>; +type InferOptionTypes< + Options, + Flag extends string, + Value extends string, + ValueT, + PresetT, + DefaultT, + CoerceT, + Mandatory extends boolean, + ChoicesT, +> = InferOptionsNegateCombo< + Options, + Flag, + ConvertFlagToName, + CoerceValueType< + CoerceT, + [ChoicesT] extends [undefined] + ? InferVariadic + : InferVariadic + >, + NegatePresetType>, + NegateDefaultType, + IsAlwaysDefined +>; -type InferOptionsFlag = - InferOptionTypes>, Trim, ValueT, PresetT, DefaultT, CoerceT, Mandatory, ChoicesT>; +type InferOptionsFlag< + Options, + Flags extends string, + Value extends string, + ValueT, + PresetT, + DefaultT, + CoerceT, + Mandatory extends boolean, + ChoicesT, +> = InferOptionTypes< + Options, + FlagsToFlag>, + Trim, + ValueT, + PresetT, + DefaultT, + CoerceT, + Mandatory, + ChoicesT +>; // Split up Usage into Flags and Value -type InferOptions = - Usage extends `${infer Flags} <${infer Value}>` - ? InferOptionsFlag - : Usage extends `${infer Flags} [${infer Value}]` - ? InferOptionsFlag - : InferOptionsFlag; +type InferOptions< + Options, + Usage extends string, + DefaultT, + CoerceT, + Mandatory extends boolean, + PresetT = undefined, + ChoicesT = undefined, +> = Usage extends `${infer Flags} <${infer Value}>` + ? InferOptionsFlag< + Options, + Flags, + Value, + string, + never, + DefaultT, + CoerceT, + Mandatory, + ChoicesT + > + : Usage extends `${infer Flags} [${infer Value}]` + ? InferOptionsFlag< + Options, + Flags, + Value, + string, + PresetT, + DefaultT, + CoerceT, + Mandatory, + ChoicesT + > + : InferOptionsFlag< + Options, + Usage, + '', + never, + PresetT, + DefaultT, + CoerceT, + Mandatory, + ChoicesT + >; export type CommandUnknownOpts = Command; @@ -176,910 +322,1052 @@ export type CommandUnknownOpts = Command; /* eslint-disable @typescript-eslint/no-explicit-any */ export class CommanderError extends Error { - code: string; - exitCode: number; - message: string; - nestedError?: string; - - /** - * Constructs the CommanderError class - * @param exitCode - suggested exit code which could be used with process.exit - * @param code - an id string representing the error - * @param message - human-readable description of the error - * @constructor - */ - constructor(exitCode: number, code: string, message: string); - } - - export class InvalidArgumentError extends CommanderError { - /** - * Constructs the InvalidArgumentError class - * @param message - explanation of why argument is invalid - * @constructor - */ - constructor(message: string); - } - export { InvalidArgumentError as InvalidOptionArgumentError }; // deprecated old name - - export interface ErrorOptions { // optional parameter for error() - /** an id string representing the error */ - code?: string; - /** suggested exit code which could be used with process.exit */ - exitCode?: number; - } - - export class Argument { - description: string; - required: boolean; - variadic: boolean; - defaultValue?: any; - defaultValueDescription?: string; - argChoices?: string[]; - - /** - * Initialize a new command argument with the given name and description. - * The default is that the argument is required, and you can explicitly - * indicate this with <> around the name. Put [] around the name for an optional argument. - */ - constructor(arg: Usage, description?: string); - - /** - * Return argument name. - */ - name(): string; - - /** - * Set the default value, and optionally supply the description to be displayed in the help. - */ - default(value: T, description?: string): Argument; - - /** - * Set the custom handler for processing CLI command arguments into argument values. - */ - argParser(fn: (value: string, previous: T) => T): Argument; // setting ChoicesT to undefined because argParser overwrites choices - - /** - * Only allow argument value to be one of choices. - */ - choices(values: T): Argument; // setting CoerceT to undefined because choices overrides argParser - - /** - * Make argument required. - */ - argRequired(): Argument; - - /** - * Make argument optional. - */ - argOptional(): Argument; - } - - export class Option { - flags: string; - description: string; - - required: boolean; // A value must be supplied when the option is specified. - optional: boolean; // A value is optional when the option is specified. - variadic: boolean; - mandatory: boolean; // The option must have a value after parsing, which usually means it must be specified on command line. - short?: string; - long?: string; - negate: boolean; - defaultValue?: any; - defaultValueDescription?: string; - presetArg?: unknown; - envVar?: string; - parseArg?: (value: string, previous: T) => T; - hidden: boolean; - argChoices?: string[]; - - constructor(flags: Usage, description?: string); - - /** - * Set the default value, and optionally supply the description to be displayed in the help. - */ - default(value: T, description?: string): Option; - - /** - * Preset to use when option used without option-argument, especially optional but also boolean and negated. - * The custom processing (parseArg) is called. - * - * @example - * ```ts - * new Option('--color').default('GREYSCALE').preset('RGB'); - * new Option('--donate [amount]').preset('20').argParser(parseFloat); - * ``` - */ - preset(arg: T): Option; - - /** - * Add option name(s) that conflict with this option. - * An error will be displayed if conflicting options are found during parsing. - * - * @example - * ```ts - * new Option('--rgb').conflicts('cmyk'); - * new Option('--js').conflicts(['ts', 'jsx']); - * ``` - */ - conflicts(names: string | string[]): this; - - /** - * Specify implied option values for when this option is set and the implied options are not. - * - * The custom processing (parseArg) is not called on the implied values. - * - * @example - * program - * .addOption(new Option('--log', 'write logging information to file')) - * .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' })); - */ - implies(optionValues: OptionValues): this; - - /** - * Set environment variable to check for option value. - * - * An environment variables is only used if when processed the current option value is - * undefined, or the source of the current value is 'default' or 'config' or 'env'. - */ - env(name: string): this; - - /** - * Calculate the full description, including defaultValue etc. - */ - fullDescription(): string; - - /** - * Set the custom handler for processing CLI option arguments into option values. - */ - argParser(fn: (value: string, previous: T) => T): Option; // setting ChoicesT to undefined because argParser overrides choices - - /** - * Whether the option is mandatory and must have a value after parsing. - */ - makeOptionMandatory(mandatory?: M): Option; - - /** - * Hide option in help. - */ - hideHelp(hide?: boolean): this; - - /** - * Only allow option value to be one of choices. - */ - choices(values: T): Option; // setting CoerceT to undefined becuase choices overrides argParser - - /** - * Return option name. - */ - name(): string; - - /** - * Return option name, in a camelcase format that can be used - * as a object attribute key. - */ - attributeName(): string; - - /** - * Return whether a boolean option. - * - * Options are one of boolean, negated, required argument, or optional argument. - */ - isBoolean(): boolean; - } - - export class Help { - /** output helpWidth, long lines are wrapped to fit */ - helpWidth?: number; - sortSubcommands: boolean; - sortOptions: boolean; - showGlobalOptions: boolean; - - constructor(); - - /** Get the command term to show in the list of subcommands. */ - subcommandTerm(cmd: CommandUnknownOpts): string; - /** Get the command summary to show in the list of subcommands. */ - subcommandDescription(cmd: CommandUnknownOpts): string; - /** Get the option term to show in the list of options. */ - optionTerm(option: Option): string; - /** Get the option description to show in the list of options. */ - optionDescription(option: Option): string; - /** Get the argument term to show in the list of arguments. */ - argumentTerm(argument: Argument): string; - /** Get the argument description to show in the list of arguments. */ - argumentDescription(argument: Argument): string; - - /** Get the command usage to be displayed at the top of the built-in help. */ - commandUsage(cmd: CommandUnknownOpts): string; - /** Get the description for the command. */ - commandDescription(cmd: CommandUnknownOpts): string; - - /** Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one. */ - visibleCommands(cmd: CommandUnknownOpts): CommandUnknownOpts[]; - /** Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one. */ - visibleOptions(cmd: CommandUnknownOpts): Option[]; - /** Get an array of the visible global options. (Not including help.) */ - visibleGlobalOptions(cmd: CommandUnknownOpts): Option[]; - /** Get an array of the arguments which have descriptions. */ - visibleArguments(cmd: CommandUnknownOpts): Argument[]; - - /** Get the longest command term length. */ - longestSubcommandTermLength(cmd: CommandUnknownOpts, helper: Help): number; - /** Get the longest option term length. */ - longestOptionTermLength(cmd: CommandUnknownOpts, helper: Help): number; - /** Get the longest global option term length. */ - longestGlobalOptionTermLength(cmd: CommandUnknownOpts, helper: Help): number; - /** Get the longest argument term length. */ - longestArgumentTermLength(cmd: CommandUnknownOpts, helper: Help): number; - /** Calculate the pad width from the maximum term length. */ - padWidth(cmd: CommandUnknownOpts, helper: Help): number; - - /** - * Wrap the given string to width characters per line, with lines after the first indented. - * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted. - */ - wrap(str: string, width: number, indent: number, minColumnWidth?: number): string; - - /** Generate the built-in help text. */ - formatHelp(cmd: CommandUnknownOpts, helper: Help): string; - } - export type HelpConfiguration = Partial; - - export interface ParseOptions { - from: 'node' | 'electron' | 'user'; - } - export interface HelpContext { // optional parameter for .help() and .outputHelp() - error: boolean; - } - export interface AddHelpTextContext { // passed to text function used with .addHelpText() - error: boolean; - command: Command; - } - export interface OutputConfiguration { - writeOut?(str: string): void; - writeErr?(str: string): void; - getOutHelpWidth?(): number; - getErrHelpWidth?(): number; - outputError?(str: string, write: (str: string) => void): void; - - } - - export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll'; - export type HookEvent = 'preSubcommand' | 'preAction' | 'postAction'; - // The source is a string so author can define their own too. - export type OptionValueSource = LiteralUnion<'default' | 'config' | 'env' | 'cli' | 'implied', string> | undefined; - - export type OptionValues = Record; - - // eslint unimpressed with `OptionValues = {}`, but not sure what to use instead. - // eslint-disable-next-line @typescript-eslint/ban-types - export class Command { - args: string[]; - processedArgs: Args; - readonly commands: readonly CommandUnknownOpts[]; - readonly options: readonly Option[]; - readonly registeredArguments: readonly Argument[]; - parent: CommandUnknownOpts | null; - - constructor(name?: string); - - /** - * Set the program version to `str`. - * - * This method auto-registers the "-V, --version" flag - * which will print the version number when passed. - * - * You can optionally supply the flags and description to override the defaults. - */ - version(str: string, flags?: string, description?: string): this; - /** - * Get the program version. - */ - version(): string | undefined; - /** - * Define a command, implemented using an action handler. - * - * @remarks - * The command description is supplied using `.description`, not as a parameter to `.command`. - * - * @example - * ```ts - * program - * .command('clone [destination]') - * .description('clone a repository into a newly created directory') - * .action((source, destination) => { - * console.log('clone command called'); - * }); - * ``` - * - * @param nameAndArgs - command name and arguments, args are `` or `[optional]` and last may also be `variadic...` - * @param opts - configuration options - * @returns new command - */ - command(nameAndArgs: Usage, opts?: CommandOptions): Command<[...InferCommmandArguments]>; - /** - * Define a command, implemented in a separate executable file. - * - * @remarks - * The command description is supplied as the second parameter to `.command`. - * - * @example - * ```ts - * program - * .command('start ', 'start named service') - * .command('stop [service]', 'stop named service, or all if no name supplied'); - * ``` - * - * @param nameAndArgs - command name and arguments, args are `` or `[optional]` and last may also be `variadic...` - * @param description - description of executable command - * @param opts - configuration options - * @returns `this` command for chaining - */ - command(nameAndArgs: string, description: string, opts?: ExecutableCommandOptions): this; - - /** - * Factory routine to create a new unattached command. - * - * See .command() for creating an attached subcommand, which uses this routine to - * create the command. You can override createCommand to customise subcommands. - */ - createCommand(name?: string): Command; - - /** - * Add a prepared subcommand. - * - * See .command() for creating an attached subcommand which inherits settings from its parent. - * - * @returns `this` command for chaining - */ - addCommand(cmd: CommandUnknownOpts, opts?: CommandOptions): this; - - /** - * Factory routine to create a new unattached argument. - * - * See .argument() for creating an attached argument, which uses this routine to - * create the argument. You can override createArgument to return a custom argument. - */ - createArgument(name: Usage, description?: string): Argument; - - /** - * Define argument syntax for command. - * - * The default is that the argument is required, and you can explicitly - * indicate this with <> around the name. Put [] around the name for an optional argument. - * - * @example - * ``` - * program.argument(''); - * program.argument('[output-file]'); - * ``` - * - * @returns `this` command for chaining - */ - argument( - flags: S, description: string, fn: (value: string, previous: T) => T): Command<[...Args, InferArgument], Opts>; - argument( - flags: S, description: string, fn: (value: string, previous: T) => T, defaultValue: T): Command<[...Args, InferArgument], Opts>; - argument( - usage: S, description?: string): Command<[...Args, InferArgument], Opts>; - argument( - usage: S, description: string, defaultValue: DefaultT): Command<[...Args, InferArgument], Opts>; - - /** - * Define argument syntax for command, adding a prepared argument. - * - * @returns `this` command for chaining - */ - addArgument( - arg: Argument): Command<[...Args, InferArgument], Opts>; - - - /** - * Define argument syntax for command, adding multiple at once (without descriptions). - * - * See also .argument(). - * - * @example - * ``` - * program.arguments(' [env]'); - * ``` - * - * @returns `this` command for chaining - */ - arguments(args: Names): Command<[...Args, ...InferArguments], Opts>; - - /** - * Customise or override default help command. By default a help command is automatically added if your command has subcommands. - * - * @example - * ```ts - * program.helpCommand('help [cmd]'); - * program.helpCommand('help [cmd]', 'show help'); - * program.helpCommand(false); // suppress default help command - * program.helpCommand(true); // add help command even if no subcommands - * ``` - */ - helpCommand(nameAndArgs: string, description?: string): this; - helpCommand(enable: boolean): this; - - /** - * Add prepared custom help command. - */ - addHelpCommand(cmd: Command): this; - /** @deprecated since v12, instead use helpCommand */ - addHelpCommand(nameAndArgs: string, description?: string): this; - /** @deprecated since v12, instead use helpCommand */ - addHelpCommand(enable?: boolean): this; - - /** - * Add hook for life cycle event. - */ - hook(event: HookEvent, listener: (thisCommand: this, actionCommand: CommandUnknownOpts) => void | Promise): this; - - /** - * Register callback to use as replacement for calling process.exit. - */ - exitOverride(callback?: (err: CommanderError) => never | void): this; - - /** - * Display error message and exit (or call exitOverride). - */ - error(message: string, errorOptions?: ErrorOptions): never; - - /** - * You can customise the help with a subclass of Help by overriding createHelp, - * or by overriding Help properties using configureHelp(). - */ - createHelp(): Help; - - /** - * You can customise the help by overriding Help properties using configureHelp(), - * or with a subclass of Help by overriding createHelp(). - */ - configureHelp(configuration: HelpConfiguration): this; - /** Get configuration */ - configureHelp(): HelpConfiguration; - - /** - * The default output goes to stdout and stderr. You can customise this for special - * applications. You can also customise the display of errors by overriding outputError. - * - * The configuration properties are all functions: - * ``` - * // functions to change where being written, stdout and stderr - * writeOut(str) - * writeErr(str) - * // matching functions to specify width for wrapping help - * getOutHelpWidth() - * getErrHelpWidth() - * // functions based on what is being written out - * outputError(str, write) // used for displaying errors, and not used for displaying help - * ``` - */ - configureOutput(configuration: OutputConfiguration): this; - /** Get configuration */ - configureOutput(): OutputConfiguration; - - /** - * Copy settings that are useful to have in common across root command and subcommands. - * - * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.) - */ - copyInheritedSettings(sourceCommand: CommandUnknownOpts): this; - - /** - * Display the help or a custom message after an error occurs. - */ - showHelpAfterError(displayHelp?: boolean | string): this; - - /** - * Display suggestion of similar commands for unknown commands, or options for unknown options. - */ - showSuggestionAfterError(displaySuggestion?: boolean): this; - - /** - * Register callback `fn` for the command. - * - * @example - * ``` - * program - * .command('serve') - * .description('start service') - * .action(function() { - * // do work here - * }); - * ``` - * - * @returns `this` command for chaining - */ - action(fn: (this: this, ...args: [...Args, Opts, this]) => void | Promise): this; - - /** - * Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both. - * - * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required - * option-argument is indicated by `<>` and an optional option-argument by `[]`. - * - * See the README for more details, and see also addOption() and requiredOption(). - * - * @example - * - * ```js - * program - * .option('-p, --pepper', 'add pepper') - * .option('-p, --pizza-type ', 'type of pizza') // required option-argument - * .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default - * .option('-t, --tip ', 'add tip to purchase cost', parseFloat) // custom parse function - * ``` - * - * @returns `this` command for chaining - */ - option( - usage: S, description?: string): Command>; - option( - usage: S, description?: string, defaultValue?: DefaultT): Command>; - option( - usage: S, description: string, parseArg: (value: string, previous: T) => T): Command>; - option( - usage: S, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): Command>; - - /** - * Define a required option, which must have a value after parsing. This usually means - * the option must be specified on the command line. (Otherwise the same as .option().) - * - * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. - */ - requiredOption( - usage: S, description?: string): Command>; - requiredOption( - usage: S, description?: string, defaultValue?: DefaultT): Command>; - requiredOption( - usage: S, description: string, parseArg: (value: string, previous: T) => T): Command>; - requiredOption( - usage: S, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: D): Command>; - - /** - * Factory routine to create a new unattached option. - * - * See .option() for creating an attached option, which uses this routine to - * create the option. You can override createOption to return a custom option. - */ - - createOption(flags: Usage, description?: string): Option; - - /** - * Add a prepared Option. - * - * See .option() and .requiredOption() for creating and attaching an option in a single call. - */ - addOption( - option: Option): Command>; - - /** - * Whether to store option values as properties on command object, - * or store separately (specify false). In both cases the option values can be accessed using .opts(). - * - * @returns `this` command for chaining - */ - storeOptionsAsProperties(): this & T; - storeOptionsAsProperties(storeAsProperties: true): this & T; - storeOptionsAsProperties(storeAsProperties?: boolean): this; - - /** - * Retrieve option value. - */ - getOptionValue(key: K): Opts[K]; - getOptionValue(key: string): unknown; - - /** - * Store option value. - */ - setOptionValue(key: K, value: unknown): this; - setOptionValue(key: string, value: unknown): this; - - /** - * Store option value and where the value came from. - */ - setOptionValueWithSource(key: K, value: unknown, source: OptionValueSource): this; - setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this; - - /** - * Get source of option value. - */ - getOptionValueSource(key: K): OptionValueSource | undefined; - getOptionValueSource(key: string): OptionValueSource | undefined; - - /** - * Get source of option value. See also .optsWithGlobals(). - */ - getOptionValueSourceWithGlobals(key: K): OptionValueSource | undefined; - getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined; - - /** - * Alter parsing of short flags with optional values. - * - * @example - * ``` - * // for `.option('-f,--flag [value]'): - * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour - * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b` - * ``` - * - * @returns `this` command for chaining - */ - combineFlagAndOptionalValue(combine?: boolean): this; - - /** - * Allow unknown options on the command line. - * - * @returns `this` command for chaining - */ - allowUnknownOption(allowUnknown?: boolean): this; - - /** - * Allow excess command-arguments on the command line. Pass false to make excess arguments an error. - * - * @returns `this` command for chaining - */ - allowExcessArguments(allowExcess?: boolean): this; - - /** - * Enable positional options. Positional means global options are specified before subcommands which lets - * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions. - * - * The default behaviour is non-positional and global options may appear anywhere on the command line. - * - * @returns `this` command for chaining - */ - enablePositionalOptions(positional?: boolean): this; - - /** - * Pass through options that come after command-arguments rather than treat them as command-options, - * so actual command-options come before command-arguments. Turning this on for a subcommand requires - * positional options to have been enabled on the program (parent commands). - * - * The default behaviour is non-positional and options may appear before or after command-arguments. - * - * @returns `this` command for chaining - */ - passThroughOptions(passThrough?: boolean): this; - - /** - * Parse `argv`, setting options and invoking commands when defined. - * - * The default expectation is that the arguments are from node and have the application as argv[0] - * and the script being run in argv[1], with user parameters after that. - * - * @example - * ``` - * program.parse(process.argv); - * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions - * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0] - * ``` - * - * @returns `this` command for chaining - */ - parse(argv?: readonly string[], options?: ParseOptions): this; - - /** - * Parse `argv`, setting options and invoking commands when defined. - * - * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise. - * - * The default expectation is that the arguments are from node and have the application as argv[0] - * and the script being run in argv[1], with user parameters after that. - * - * @example - * ``` - * program.parseAsync(process.argv); - * program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions - * program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0] - * ``` - * - * @returns Promise - */ - parseAsync(argv?: readonly string[], options?: ParseOptions): Promise; - - /** - * Parse options from `argv` removing known options, - * and return argv split into operands and unknown arguments. - * - * argv => operands, unknown - * --known kkk op => [op], [] - * op --known kkk => [op], [] - * sub --unknown uuu op => [sub], [--unknown uuu op] - * sub -- --unknown uuu op => [sub --unknown uuu op], [] - */ - parseOptions(argv: string[]): ParseOptionsResult; - - /** - * Return an object containing local option values as key-value pairs - */ - opts(): Opts; - - /** - * Return an object containing merged local and global option values as key-value pairs. - */ - optsWithGlobals(): T; - - /** - * Set the description. - * - * @returns `this` command for chaining - */ - - description(str: string): this; - /** @deprecated since v8, instead use .argument to add command argument with description */ - description(str: string, argsDescription: Record): this; - /** - * Get the description. - */ - description(): string; - - /** - * Set the summary. Used when listed as subcommand of parent. - * - * @returns `this` command for chaining - */ - - summary(str: string): this; - /** - * Get the summary. - */ - summary(): string; - - /** - * Set an alias for the command. - * - * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. - * - * @returns `this` command for chaining - */ - alias(alias: string): this; - /** - * Get alias for the command. - */ - alias(): string; - - /** - * Set aliases for the command. - * - * Only the first alias is shown in the auto-generated help. - * - * @returns `this` command for chaining - */ - aliases(aliases: readonly string[]): this; - /** - * Get aliases for the command. - */ - aliases(): string[]; - - /** - * Set the command usage. - * - * @returns `this` command for chaining - */ - usage(str: string): this; - /** - * Get the command usage. - */ - usage(): string; - - /** - * Set the name of the command. - * - * @returns `this` command for chaining - */ - name(str: string): this; - /** - * Get the name of the command. - */ - name(): string; - - /** - * Set the name of the command from script filename, such as process.argv[1], - * or require.main.filename, or __filename. - * - * (Used internally and public although not documented in README.) - * - * @example - * ```ts - * program.nameFromFilename(require.main.filename); - * ``` - * - * @returns `this` command for chaining - */ - nameFromFilename(filename: string): this; - - /** - * Set the directory for searching for executable subcommands of this command. - * - * @example - * ```ts - * program.executableDir(__dirname); - * // or - * program.executableDir('subcommands'); - * ``` - * - * @returns `this` command for chaining - */ - executableDir(path: string): this; - /** - * Get the executable search directory. - */ - executableDir(): string | null; - - /** - * Output help information for this command. - * - * Outputs built-in help, and custom text added using `.addHelpText()`. - * - */ - outputHelp(context?: HelpContext): void; - /** @deprecated since v7 */ - outputHelp(cb?: (str: string) => string): void; - - /** - * Return command help documentation. - */ - helpInformation(context?: HelpContext): string; - - /** - * You can pass in flags and a description to override the help - * flags and help description for your command. Pass in false - * to disable the built-in help option. - */ - helpOption(flags?: string | boolean, description?: string): this; - - /** - * Supply your own option to use for the built-in help option. - * This is an alternative to using helpOption() to customise the flags and description etc. - */ - addHelpOption(option: Option): this; - - /** - * Output help information and exit. - * - * Outputs built-in help, and custom text added using `.addHelpText()`. - */ - help(context?: HelpContext): never; - /** @deprecated since v7 */ - help(cb?: (str: string) => string): never; - - /** - * Add additional text to be displayed with the built-in help. - * - * Position is 'before' or 'after' to affect just this command, - * and 'beforeAll' or 'afterAll' to affect this command and all its subcommands. - */ - addHelpText(position: AddHelpTextPosition, text: string): this; - addHelpText(position: AddHelpTextPosition, text: (context: AddHelpTextContext) => string): this; - - /** - * Add a listener (callback) for when events occur. (Implemented using EventEmitter.) - */ - on(event: string | symbol, listener: (...args: any[]) => void): this; - } - - export interface CommandOptions { - hidden?: boolean; - isDefault?: boolean; - /** @deprecated since v7, replaced by hidden */ - noHelp?: boolean; - } - export interface ExecutableCommandOptions extends CommandOptions { - executableFile?: string; - } - - export interface ParseOptionsResult { - operands: string[]; - unknown: string[]; - } - - export function createCommand(name?: string): Command; - export function createOption(flags: Usage, description?: string): Option; - export function createArgument(name: Usage, description?: string): Argument; - - export const program: Command; - + code: string; + exitCode: number; + message: string; + nestedError?: string; + + /** + * Constructs the CommanderError class + * @param exitCode - suggested exit code which could be used with process.exit + * @param code - an id string representing the error + * @param message - human-readable description of the error + * @constructor + */ + constructor(exitCode: number, code: string, message: string); +} + +export class InvalidArgumentError extends CommanderError { + /** + * Constructs the InvalidArgumentError class + * @param message - explanation of why argument is invalid + * @constructor + */ + constructor(message: string); +} +export { InvalidArgumentError as InvalidOptionArgumentError }; // deprecated old name + +export interface ErrorOptions { + // optional parameter for error() + /** an id string representing the error */ + code?: string; + /** suggested exit code which could be used with process.exit */ + exitCode?: number; +} + +export class Argument< + Usage extends string = '', + DefaultT = undefined, + CoerceT = undefined, + ArgRequired extends boolean | undefined = undefined, + ChoicesT = undefined, +> { + description: string; + required: boolean; + variadic: boolean; + defaultValue?: any; + defaultValueDescription?: string; + argChoices?: string[]; + + /** + * Initialize a new command argument with the given name and description. + * The default is that the argument is required, and you can explicitly + * indicate this with <> around the name. Put [] around the name for an optional argument. + */ + constructor(arg: Usage, description?: string); + + /** + * Return argument name. + */ + name(): string; + + /** + * Set the default value, and optionally supply the description to be displayed in the help. + */ + default( + value: T, + description?: string, + ): Argument; + + /** + * Set the custom handler for processing CLI command arguments into argument values. + */ + argParser( + fn: (value: string, previous: T) => T, + ): Argument; // setting ChoicesT to undefined because argParser overwrites choices + + /** + * Only allow argument value to be one of choices. + */ + choices( + values: T, + ): Argument; // setting CoerceT to undefined because choices overrides argParser + + /** + * Make argument required. + */ + argRequired(): Argument; + + /** + * Make argument optional. + */ + argOptional(): Argument; +} + +export class Option< + Usage extends string = '', + PresetT = undefined, + DefaultT = undefined, + CoerceT = undefined, + Mandatory extends boolean = false, + ChoicesT = undefined, +> { + flags: string; + description: string; + + required: boolean; // A value must be supplied when the option is specified. + optional: boolean; // A value is optional when the option is specified. + variadic: boolean; + mandatory: boolean; // The option must have a value after parsing, which usually means it must be specified on command line. + short?: string; + long?: string; + negate: boolean; + defaultValue?: any; + defaultValueDescription?: string; + presetArg?: unknown; + envVar?: string; + parseArg?: (value: string, previous: T) => T; + hidden: boolean; + argChoices?: string[]; + + constructor(flags: Usage, description?: string); + + /** + * Set the default value, and optionally supply the description to be displayed in the help. + */ + default( + value: T, + description?: string, + ): Option; + + /** + * Preset to use when option used without option-argument, especially optional but also boolean and negated. + * The custom processing (parseArg) is called. + * + * @example + * ```ts + * new Option('--color').default('GREYSCALE').preset('RGB'); + * new Option('--donate [amount]').preset('20').argParser(parseFloat); + * ``` + */ + preset(arg: T): Option; + + /** + * Add option name(s) that conflict with this option. + * An error will be displayed if conflicting options are found during parsing. + * + * @example + * ```ts + * new Option('--rgb').conflicts('cmyk'); + * new Option('--js').conflicts(['ts', 'jsx']); + * ``` + */ + conflicts(names: string | string[]): this; + + /** + * Specify implied option values for when this option is set and the implied options are not. + * + * The custom processing (parseArg) is not called on the implied values. + * + * @example + * program + * .addOption(new Option('--log', 'write logging information to file')) + * .addOption(new Option('--trace', 'log extra details').implies({ log: 'trace.txt' })); + */ + implies(optionValues: OptionValues): this; + + /** + * Set environment variable to check for option value. + * + * An environment variables is only used if when processed the current option value is + * undefined, or the source of the current value is 'default' or 'config' or 'env'. + */ + env(name: string): this; + + /** + * Calculate the full description, including defaultValue etc. + */ + fullDescription(): string; + + /** + * Set the custom handler for processing CLI option arguments into option values. + */ + argParser( + fn: (value: string, previous: T) => T, + ): Option; // setting ChoicesT to undefined because argParser overrides choices + + /** + * Whether the option is mandatory and must have a value after parsing. + */ + makeOptionMandatory( + mandatory?: M, + ): Option; + + /** + * Hide option in help. + */ + hideHelp(hide?: boolean): this; + + /** + * Only allow option value to be one of choices. + */ + choices( + values: T, + ): Option; // setting CoerceT to undefined becuase choices overrides argParser + + /** + * Return option name. + */ + name(): string; + + /** + * Return option name, in a camelcase format that can be used + * as a object attribute key. + */ + attributeName(): string; + + /** + * Return whether a boolean option. + * + * Options are one of boolean, negated, required argument, or optional argument. + */ + isBoolean(): boolean; +} + +export class Help { + /** output helpWidth, long lines are wrapped to fit */ + helpWidth?: number; + sortSubcommands: boolean; + sortOptions: boolean; + showGlobalOptions: boolean; + + constructor(); + + /** Get the command term to show in the list of subcommands. */ + subcommandTerm(cmd: CommandUnknownOpts): string; + /** Get the command summary to show in the list of subcommands. */ + subcommandDescription(cmd: CommandUnknownOpts): string; + /** Get the option term to show in the list of options. */ + optionTerm(option: Option): string; + /** Get the option description to show in the list of options. */ + optionDescription(option: Option): string; + /** Get the argument term to show in the list of arguments. */ + argumentTerm(argument: Argument): string; + /** Get the argument description to show in the list of arguments. */ + argumentDescription(argument: Argument): string; + + /** Get the command usage to be displayed at the top of the built-in help. */ + commandUsage(cmd: CommandUnknownOpts): string; + /** Get the description for the command. */ + commandDescription(cmd: CommandUnknownOpts): string; + + /** Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one. */ + visibleCommands(cmd: CommandUnknownOpts): CommandUnknownOpts[]; + /** Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one. */ + visibleOptions(cmd: CommandUnknownOpts): Option[]; + /** Get an array of the visible global options. (Not including help.) */ + visibleGlobalOptions(cmd: CommandUnknownOpts): Option[]; + /** Get an array of the arguments which have descriptions. */ + visibleArguments(cmd: CommandUnknownOpts): Argument[]; + + /** Get the longest command term length. */ + longestSubcommandTermLength(cmd: CommandUnknownOpts, helper: Help): number; + /** Get the longest option term length. */ + longestOptionTermLength(cmd: CommandUnknownOpts, helper: Help): number; + /** Get the longest global option term length. */ + longestGlobalOptionTermLength(cmd: CommandUnknownOpts, helper: Help): number; + /** Get the longest argument term length. */ + longestArgumentTermLength(cmd: CommandUnknownOpts, helper: Help): number; + /** Calculate the pad width from the maximum term length. */ + padWidth(cmd: CommandUnknownOpts, helper: Help): number; + + /** + * Wrap the given string to width characters per line, with lines after the first indented. + * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted. + */ + wrap( + str: string, + width: number, + indent: number, + minColumnWidth?: number, + ): string; + + /** Generate the built-in help text. */ + formatHelp(cmd: CommandUnknownOpts, helper: Help): string; +} +export type HelpConfiguration = Partial; + +export interface ParseOptions { + from: 'node' | 'electron' | 'user'; +} +export interface HelpContext { + // optional parameter for .help() and .outputHelp() + error: boolean; +} +export interface AddHelpTextContext { + // passed to text function used with .addHelpText() + error: boolean; + command: Command; +} +export interface OutputConfiguration { + writeOut?(str: string): void; + writeErr?(str: string): void; + getOutHelpWidth?(): number; + getErrHelpWidth?(): number; + outputError?(str: string, write: (str: string) => void): void; +} + +export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll'; +export type HookEvent = 'preSubcommand' | 'preAction' | 'postAction'; +// The source is a string so author can define their own too. +export type OptionValueSource = + | LiteralUnion<'default' | 'config' | 'env' | 'cli' | 'implied', string> + | undefined; + +export type OptionValues = Record; + +// eslint unimpressed with `OptionValues = {}`, but not sure what to use instead. +// eslint-disable-next-line @typescript-eslint/ban-types +export class Command { + args: string[]; + processedArgs: Args; + readonly commands: readonly CommandUnknownOpts[]; + readonly options: readonly Option[]; + readonly registeredArguments: readonly Argument[]; + parent: CommandUnknownOpts | null; + + constructor(name?: string); + + /** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * You can optionally supply the flags and description to override the defaults. + */ + version(str: string, flags?: string, description?: string): this; + /** + * Get the program version. + */ + version(): string | undefined; + /** + * Define a command, implemented using an action handler. + * + * @remarks + * The command description is supplied using `.description`, not as a parameter to `.command`. + * + * @example + * ```ts + * program + * .command('clone [destination]') + * .description('clone a repository into a newly created directory') + * .action((source, destination) => { + * console.log('clone command called'); + * }); + * ``` + * + * @param nameAndArgs - command name and arguments, args are `` or `[optional]` and last may also be `variadic...` + * @param opts - configuration options + * @returns new command + */ + command( + nameAndArgs: Usage, + opts?: CommandOptions, + ): Command<[...InferCommmandArguments]>; + /** + * Define a command, implemented in a separate executable file. + * + * @remarks + * The command description is supplied as the second parameter to `.command`. + * + * @example + * ```ts + * program + * .command('start ', 'start named service') + * .command('stop [service]', 'stop named service, or all if no name supplied'); + * ``` + * + * @param nameAndArgs - command name and arguments, args are `` or `[optional]` and last may also be `variadic...` + * @param description - description of executable command + * @param opts - configuration options + * @returns `this` command for chaining + */ + command( + nameAndArgs: string, + description: string, + opts?: ExecutableCommandOptions, + ): this; + + /** + * Factory routine to create a new unattached command. + * + * See .command() for creating an attached subcommand, which uses this routine to + * create the command. You can override createCommand to customise subcommands. + */ + createCommand(name?: string): Command; + + /** + * Add a prepared subcommand. + * + * See .command() for creating an attached subcommand which inherits settings from its parent. + * + * @returns `this` command for chaining + */ + addCommand(cmd: CommandUnknownOpts, opts?: CommandOptions): this; + + /** + * Factory routine to create a new unattached argument. + * + * See .argument() for creating an attached argument, which uses this routine to + * create the argument. You can override createArgument to return a custom argument. + */ + createArgument( + name: Usage, + description?: string, + ): Argument; + + /** + * Define argument syntax for command. + * + * The default is that the argument is required, and you can explicitly + * indicate this with <> around the name. Put [] around the name for an optional argument. + * + * @example + * ``` + * program.argument(''); + * program.argument('[output-file]'); + * ``` + * + * @returns `this` command for chaining + */ + argument( + flags: S, + description: string, + fn: (value: string, previous: T) => T, + ): Command<[...Args, InferArgument], Opts>; + argument( + flags: S, + description: string, + fn: (value: string, previous: T) => T, + defaultValue: T, + ): Command<[...Args, InferArgument], Opts>; + argument( + usage: S, + description?: string, + ): Command<[...Args, InferArgument], Opts>; + argument( + usage: S, + description: string, + defaultValue: DefaultT, + ): Command<[...Args, InferArgument], Opts>; + + /** + * Define argument syntax for command, adding a prepared argument. + * + * @returns `this` command for chaining + */ + addArgument< + Usage extends string, + DefaultT, + CoerceT, + ArgRequired extends boolean | undefined, + ChoicesT, + >( + arg: Argument, + ): Command< + [...Args, InferArgument], + Opts + >; + + /** + * Define argument syntax for command, adding multiple at once (without descriptions). + * + * See also .argument(). + * + * @example + * ``` + * program.arguments(' [env]'); + * ``` + * + * @returns `this` command for chaining + */ + arguments( + args: Names, + ): Command<[...Args, ...InferArguments], Opts>; + + /** + * Customise or override default help command. By default a help command is automatically added if your command has subcommands. + * + * @example + * ```ts + * program.helpCommand('help [cmd]'); + * program.helpCommand('help [cmd]', 'show help'); + * program.helpCommand(false); // suppress default help command + * program.helpCommand(true); // add help command even if no subcommands + * ``` + */ + helpCommand(nameAndArgs: string, description?: string): this; + helpCommand(enable: boolean): this; + + /** + * Add prepared custom help command. + */ + addHelpCommand(cmd: Command): this; + /** @deprecated since v12, instead use helpCommand */ + addHelpCommand(nameAndArgs: string, description?: string): this; + /** @deprecated since v12, instead use helpCommand */ + addHelpCommand(enable?: boolean): this; + + /** + * Add hook for life cycle event. + */ + hook( + event: HookEvent, + listener: ( + thisCommand: this, + actionCommand: CommandUnknownOpts, + ) => void | Promise, + ): this; + + /** + * Register callback to use as replacement for calling process.exit. + */ + exitOverride(callback?: (err: CommanderError) => never | void): this; + + /** + * Display error message and exit (or call exitOverride). + */ + error(message: string, errorOptions?: ErrorOptions): never; + + /** + * You can customise the help with a subclass of Help by overriding createHelp, + * or by overriding Help properties using configureHelp(). + */ + createHelp(): Help; + + /** + * You can customise the help by overriding Help properties using configureHelp(), + * or with a subclass of Help by overriding createHelp(). + */ + configureHelp(configuration: HelpConfiguration): this; + /** Get configuration */ + configureHelp(): HelpConfiguration; + + /** + * The default output goes to stdout and stderr. You can customise this for special + * applications. You can also customise the display of errors by overriding outputError. + * + * The configuration properties are all functions: + * ``` + * // functions to change where being written, stdout and stderr + * writeOut(str) + * writeErr(str) + * // matching functions to specify width for wrapping help + * getOutHelpWidth() + * getErrHelpWidth() + * // functions based on what is being written out + * outputError(str, write) // used for displaying errors, and not used for displaying help + * ``` + */ + configureOutput(configuration: OutputConfiguration): this; + /** Get configuration */ + configureOutput(): OutputConfiguration; + + /** + * Copy settings that are useful to have in common across root command and subcommands. + * + * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.) + */ + copyInheritedSettings(sourceCommand: CommandUnknownOpts): this; + + /** + * Display the help or a custom message after an error occurs. + */ + showHelpAfterError(displayHelp?: boolean | string): this; + + /** + * Display suggestion of similar commands for unknown commands, or options for unknown options. + */ + showSuggestionAfterError(displaySuggestion?: boolean): this; + + /** + * Register callback `fn` for the command. + * + * @example + * ``` + * program + * .command('serve') + * .description('start service') + * .action(function() { + * // do work here + * }); + * ``` + * + * @returns `this` command for chaining + */ + action( + fn: (this: this, ...args: [...Args, Opts, this]) => void | Promise, + ): this; + + /** + * Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both. + * + * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required + * option-argument is indicated by `<>` and an optional option-argument by `[]`. + * + * See the README for more details, and see also addOption() and requiredOption(). + * + * @example + * + * ```js + * program + * .option('-p, --pepper', 'add pepper') + * .option('-p, --pizza-type ', 'type of pizza') // required option-argument + * .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default + * .option('-t, --tip ', 'add tip to purchase cost', parseFloat) // custom parse function + * ``` + * + * @returns `this` command for chaining + */ + option( + usage: S, + description?: string, + ): Command>; + option( + usage: S, + description?: string, + defaultValue?: DefaultT, + ): Command>; + option( + usage: S, + description: string, + parseArg: (value: string, previous: T) => T, + ): Command>; + option( + usage: S, + description: string, + parseArg: (value: string, previous: T) => T, + defaultValue?: T, + ): Command>; + + /** + * Define a required option, which must have a value after parsing. This usually means + * the option must be specified on the command line. (Otherwise the same as .option().) + * + * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. + */ + requiredOption( + usage: S, + description?: string, + ): Command>; + requiredOption< + S extends string, + DefaultT extends string | boolean | string[], + >( + usage: S, + description?: string, + defaultValue?: DefaultT, + ): Command>; + requiredOption( + usage: S, + description: string, + parseArg: (value: string, previous: T) => T, + ): Command>; + requiredOption( + usage: S, + description: string, + parseArg: (value: string, previous: T) => T, + defaultValue?: D, + ): Command>; + + /** + * Factory routine to create a new unattached option. + * + * See .option() for creating an attached option, which uses this routine to + * create the option. You can override createOption to return a custom option. + */ + + createOption( + flags: Usage, + description?: string, + ): Option; + + /** + * Add a prepared Option. + * + * See .option() and .requiredOption() for creating and attaching an option in a single call. + */ + addOption< + Usage extends string, + PresetT, + DefaultT, + CoerceT, + Mandatory extends boolean, + ChoicesT, + >( + option: Option, + ): Command< + Args, + InferOptions + >; + + /** + * Whether to store option values as properties on command object, + * or store separately (specify false). In both cases the option values can be accessed using .opts(). + * + * @returns `this` command for chaining + */ + storeOptionsAsProperties(): this & T; + storeOptionsAsProperties( + storeAsProperties: true, + ): this & T; + storeOptionsAsProperties(storeAsProperties?: boolean): this; + + /** + * Retrieve option value. + */ + getOptionValue(key: K): Opts[K]; + getOptionValue(key: string): unknown; + + /** + * Store option value. + */ + setOptionValue(key: K, value: unknown): this; + setOptionValue(key: string, value: unknown): this; + + /** + * Store option value and where the value came from. + */ + setOptionValueWithSource( + key: K, + value: unknown, + source: OptionValueSource, + ): this; + setOptionValueWithSource( + key: string, + value: unknown, + source: OptionValueSource, + ): this; + + /** + * Get source of option value. + */ + getOptionValueSource( + key: K, + ): OptionValueSource | undefined; + getOptionValueSource(key: string): OptionValueSource | undefined; + + /** + * Get source of option value. See also .optsWithGlobals(). + */ + getOptionValueSourceWithGlobals( + key: K, + ): OptionValueSource | undefined; + getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined; + + /** + * Alter parsing of short flags with optional values. + * + * @example + * ``` + * // for `.option('-f,--flag [value]'): + * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour + * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b` + * ``` + * + * @returns `this` command for chaining + */ + combineFlagAndOptionalValue(combine?: boolean): this; + + /** + * Allow unknown options on the command line. + * + * @returns `this` command for chaining + */ + allowUnknownOption(allowUnknown?: boolean): this; + + /** + * Allow excess command-arguments on the command line. Pass false to make excess arguments an error. + * + * @returns `this` command for chaining + */ + allowExcessArguments(allowExcess?: boolean): this; + + /** + * Enable positional options. Positional means global options are specified before subcommands which lets + * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions. + * + * The default behaviour is non-positional and global options may appear anywhere on the command line. + * + * @returns `this` command for chaining + */ + enablePositionalOptions(positional?: boolean): this; + + /** + * Pass through options that come after command-arguments rather than treat them as command-options, + * so actual command-options come before command-arguments. Turning this on for a subcommand requires + * positional options to have been enabled on the program (parent commands). + * + * The default behaviour is non-positional and options may appear before or after command-arguments. + * + * @returns `this` command for chaining + */ + passThroughOptions(passThrough?: boolean): this; + + /** + * Parse `argv`, setting options and invoking commands when defined. + * + * The default expectation is that the arguments are from node and have the application as argv[0] + * and the script being run in argv[1], with user parameters after that. + * + * @example + * ``` + * program.parse(process.argv); + * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions + * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0] + * ``` + * + * @returns `this` command for chaining + */ + parse(argv?: readonly string[], options?: ParseOptions): this; + + /** + * Parse `argv`, setting options and invoking commands when defined. + * + * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise. + * + * The default expectation is that the arguments are from node and have the application as argv[0] + * and the script being run in argv[1], with user parameters after that. + * + * @example + * ``` + * program.parseAsync(process.argv); + * program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions + * program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0] + * ``` + * + * @returns Promise + */ + parseAsync(argv?: readonly string[], options?: ParseOptions): Promise; + + /** + * Parse options from `argv` removing known options, + * and return argv split into operands and unknown arguments. + * + * argv => operands, unknown + * --known kkk op => [op], [] + * op --known kkk => [op], [] + * sub --unknown uuu op => [sub], [--unknown uuu op] + * sub -- --unknown uuu op => [sub --unknown uuu op], [] + */ + parseOptions(argv: string[]): ParseOptionsResult; + + /** + * Return an object containing local option values as key-value pairs + */ + opts(): Opts; + + /** + * Return an object containing merged local and global option values as key-value pairs. + */ + optsWithGlobals(): T; + + /** + * Set the description. + * + * @returns `this` command for chaining + */ + + description(str: string): this; + /** @deprecated since v8, instead use .argument to add command argument with description */ + description(str: string, argsDescription: Record): this; + /** + * Get the description. + */ + description(): string; + + /** + * Set the summary. Used when listed as subcommand of parent. + * + * @returns `this` command for chaining + */ + + summary(str: string): this; + /** + * Get the summary. + */ + summary(): string; + + /** + * Set an alias for the command. + * + * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. + * + * @returns `this` command for chaining + */ + alias(alias: string): this; + /** + * Get alias for the command. + */ + alias(): string; + + /** + * Set aliases for the command. + * + * Only the first alias is shown in the auto-generated help. + * + * @returns `this` command for chaining + */ + aliases(aliases: readonly string[]): this; + /** + * Get aliases for the command. + */ + aliases(): string[]; + + /** + * Set the command usage. + * + * @returns `this` command for chaining + */ + usage(str: string): this; + /** + * Get the command usage. + */ + usage(): string; + + /** + * Set the name of the command. + * + * @returns `this` command for chaining + */ + name(str: string): this; + /** + * Get the name of the command. + */ + name(): string; + + /** + * Set the name of the command from script filename, such as process.argv[1], + * or require.main.filename, or __filename. + * + * (Used internally and public although not documented in README.) + * + * @example + * ```ts + * program.nameFromFilename(require.main.filename); + * ``` + * + * @returns `this` command for chaining + */ + nameFromFilename(filename: string): this; + + /** + * Set the directory for searching for executable subcommands of this command. + * + * @example + * ```ts + * program.executableDir(__dirname); + * // or + * program.executableDir('subcommands'); + * ``` + * + * @returns `this` command for chaining + */ + executableDir(path: string): this; + /** + * Get the executable search directory. + */ + executableDir(): string | null; + + /** + * Output help information for this command. + * + * Outputs built-in help, and custom text added using `.addHelpText()`. + * + */ + outputHelp(context?: HelpContext): void; + /** @deprecated since v7 */ + outputHelp(cb?: (str: string) => string): void; + + /** + * Return command help documentation. + */ + helpInformation(context?: HelpContext): string; + + /** + * You can pass in flags and a description to override the help + * flags and help description for your command. Pass in false + * to disable the built-in help option. + */ + helpOption(flags?: string | boolean, description?: string): this; + + /** + * Supply your own option to use for the built-in help option. + * This is an alternative to using helpOption() to customise the flags and description etc. + */ + addHelpOption(option: Option): this; + + /** + * Output help information and exit. + * + * Outputs built-in help, and custom text added using `.addHelpText()`. + */ + help(context?: HelpContext): never; + /** @deprecated since v7 */ + help(cb?: (str: string) => string): never; + + /** + * Add additional text to be displayed with the built-in help. + * + * Position is 'before' or 'after' to affect just this command, + * and 'beforeAll' or 'afterAll' to affect this command and all its subcommands. + */ + addHelpText(position: AddHelpTextPosition, text: string): this; + addHelpText( + position: AddHelpTextPosition, + text: (context: AddHelpTextContext) => string, + ): this; + + /** + * Add a listener (callback) for when events occur. (Implemented using EventEmitter.) + */ + on(event: string | symbol, listener: (...args: any[]) => void): this; +} + +export interface CommandOptions { + hidden?: boolean; + isDefault?: boolean; + /** @deprecated since v7, replaced by hidden */ + noHelp?: boolean; +} +export interface ExecutableCommandOptions extends CommandOptions { + executableFile?: string; +} + +export interface ParseOptionsResult { + operands: string[]; + unknown: string[]; +} + +export function createCommand(name?: string): Command; +export function createOption( + flags: Usage, + description?: string, +): Option; +export function createArgument( + name: Usage, + description?: string, +): Argument; + +export const program: Command; diff --git a/index.js b/index.js index c1e8a58..2dbceee 100644 --- a/index.js +++ b/index.js @@ -22,5 +22,7 @@ exports.Option = commander.Option; // methods on the global program due to the (deprecated) legacy default export. // Here we roll our own, the way Commander might in future. exports.createCommand = (name) => new commander.Command(name); -exports.createOption = (flags, description) => new commander.Option(flags, description); -exports.createArgument = (name, description) => new commander.Argument(name, description); +exports.createOption = (flags, description) => + new commander.Option(flags, description); +exports.createArgument = (name, description) => + new commander.Argument(name, description); diff --git a/tests/arguments.test-d.ts b/tests/arguments.test-d.ts index c9bdf12..28bc6a4 100644 --- a/tests/arguments.test-d.ts +++ b/tests/arguments.test-d.ts @@ -8,28 +8,23 @@ const program = new Command(); * Check when no command-arguments. */ -program - .action((options) => { - expectAssignable(options); - }); +program.action((options) => { + expectAssignable(options); +}); /** * Check command-arguments from .argument */ -program - .argument('') - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.argument('').action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .argument('[bar]') - .action((bar, options) => { - expectType(bar); - expectAssignable(options); - }); +program.argument('[bar]').action((bar, options) => { + expectType(bar); + expectAssignable(options); +}); program .argument('') @@ -47,27 +42,21 @@ program expectAssignable(options); }); -program - .argument('') - .action((m, options) => { - expectType(m); - expectAssignable(options); - }); +program.argument('').action((m, options) => { + expectType(m); + expectAssignable(options); +}); -program - .argument('[mult...]') - .action((m, options) => { - expectType(m); - expectAssignable(options); - }); +program.argument('[mult...]').action((m, options) => { + expectType(m); + expectAssignable(options); +}); -program - .argument('[mult...]', 'description', []) - .action((m, options) => { - // The wild looking never[] is how TypeScript represents the type of the untyped empty array passed as default. - expectType(m); - expectAssignable(options); - }); +program.argument('[mult...]', 'description', []).action((m, options) => { + // The wild looking never[] is how TypeScript represents the type of the untyped empty array passed as default. + expectType(m); + expectAssignable(options); +}); function myParseInt(arg: string, previous: number): number { return parseInt(arg); @@ -76,19 +65,15 @@ function myParseInts(arg: string, previous: number[]): number[] { return previous.concat(parseInt(arg)); } -program - .argument('', 'description', myParseInt) - .action((h, options) => { - expectType(h); - expectAssignable(options); - }); +program.argument('', 'description', myParseInt).action((h, options) => { + expectType(h); + expectAssignable(options); +}); -program - .argument('[height]', 'description', myParseInt) - .action((h, options) => { - expectType(h); - expectAssignable(options); - }); +program.argument('[height]', 'description', myParseInt).action((h, options) => { + expectType(h); + expectAssignable(options); +}); program .argument('[height...]', 'description', myParseInts, []) @@ -101,28 +86,21 @@ program * Check command-arguments from .arguments() */ -program - .arguments('') - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); - -program - .arguments('[bar]') - .action((bar, options) => { - expectType(bar); - expectAssignable(options); - }); +program.arguments('').action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .arguments(' [bar]') - .action((foo, bar, options) => { - expectType(foo); - expectType(bar); - expectAssignable(options); - }); +program.arguments('[bar]').action((bar, options) => { + expectType(bar); + expectAssignable(options); +}); +program.arguments(' [bar]').action((foo, bar, options) => { + expectType(foo); + expectType(bar); + expectAssignable(options); +}); program .arguments(' [files...]') @@ -137,19 +115,15 @@ program * Check command-arguments from .addArgument() */ -program - .addArgument(new Argument('')) - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.addArgument(new Argument('')).action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .addArgument(new Argument('[bar]')) - .action((bar, options, cmd) => { - expectType(bar); - expectAssignable(options); - }); +program.addArgument(new Argument('[bar]')).action((bar, options, cmd) => { + expectType(bar); + expectAssignable(options); +}); program .addArgument(new Argument('')) @@ -160,19 +134,15 @@ program expectAssignable(options); }); -program - .addArgument(new Argument('')) - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.addArgument(new Argument('')).action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .addArgument(new Argument('[foo...]')) - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.addArgument(new Argument('[foo...]')).action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); program .addArgument(new Argument('[foo]').default('x')) @@ -182,19 +152,15 @@ program }); // mixed types possible, but unusual -program - .addArgument(new Argument('[foo]').default(3)) - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.addArgument(new Argument('[foo]').default(3)).action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .addArgument(new Argument('foo')) - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.addArgument(new Argument('foo')).action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); program .addArgument(new Argument('foo').argRequired()) @@ -261,56 +227,50 @@ program expectAssignable(options); }); -/** +/** * Check command-arguments from .command('name ') */ -program - .command('sub1') - .action((options) => { - expectAssignable(options); - }); +program.command('sub1').action((options) => { + expectAssignable(options); +}); -program - .command('sub2 ') - .action((foo, options) => { - expectType(foo); - expectAssignable(options); - }); +program.command('sub2 ').action((foo, options) => { + expectType(foo); + expectAssignable(options); +}); -program - .command('sub3 [bar]') - .action((bar, options) => { - expectType(bar); - expectAssignable(options); - }); +program.command('sub3 [bar]').action((bar, options) => { + expectType(bar); + expectAssignable(options); +}); // choices program - .addArgument(new Argument("").choices(["A", "B"] as const)) + .addArgument(new Argument('').choices(['A', 'B'] as const)) .action((foo, options) => { - expectType<"A" | "B">(foo); + expectType<'A' | 'B'>(foo); expectAssignable(options); }); program - .addArgument(new Argument("[foo]").choices(["A", "B"] as const)) + .addArgument(new Argument('[foo]').choices(['A', 'B'] as const)) .action((foo, options) => { - expectType<"A" | "B" | undefined>(foo); + expectType<'A' | 'B' | undefined>(foo); expectAssignable(options); }); program - .addArgument(new Argument("").choices(["A", "B"] as const)) + .addArgument(new Argument('').choices(['A', 'B'] as const)) .action((foo, options) => { - expectType<("A" | "B")[]>(foo); + expectType<('A' | 'B')[]>(foo); expectAssignable(options); }); program - .addArgument(new Argument("[foo...]").choices(["A", "B"] as const)) + .addArgument(new Argument('[foo...]').choices(['A', 'B'] as const)) .action((foo, options) => { - expectType<("A" | "B")[]>(foo); + expectType<('A' | 'B')[]>(foo); expectAssignable(options); }); @@ -321,102 +281,99 @@ program .argument('') .argument('') .action(function () { - expectType>(this) - }) + expectType>(this); + }); // default type ignored when arg is required -expectType<('C')>( +expectType<'C'>( program - .addArgument(new Argument('').default('D' as const).choices(['C'] as const)) - .parse() - .processedArgs[0] -) + .addArgument( + new Argument('').default('D' as const).choices(['C'] as const), + ) + .parse().processedArgs[0], +); // default before choices results in union when arg optional -expectType<('C' | 'D')>( +expectType<'C' | 'D'>( program - .addArgument(new Argument('[foo]').default('D' as const).choices(['C'] as const)) - .parse() - .processedArgs[0] -) + .addArgument( + new Argument('[foo]').default('D' as const).choices(['C'] as const), + ) + .parse().processedArgs[0], +); // default after choices is still union type -expectType<('C' | 'D')>( +expectType<'C' | 'D'>( program - .addArgument(new Argument('[foo]').choices(['C'] as const).default('D' as const)) - .parse() - .processedArgs[0] -) + .addArgument( + new Argument('[foo]').choices(['C'] as const).default('D' as const), + ) + .parse().processedArgs[0], +); // argRequired after choices still narrows type -expectType<('C')>( +expectType<'C'>( program .addArgument(new Argument('foo').choices(['C'] as const).argRequired()) - .parse() - .processedArgs[0] -) + .parse().processedArgs[0], +); // argRequired before choices still narrows type -expectType<('C')>( +expectType<'C'>( program .addArgument(new Argument('foo').argRequired().choices(['C'] as const)) - .parse() - .processedArgs[0] -) + .parse().processedArgs[0], +); // argOptional after choices narrows type and includes undefined -expectType<('C' | undefined)>( +expectType<'C' | undefined>( program .addArgument(new Argument('foo').choices(['C'] as const).argOptional()) - .parse() - .processedArgs[0] -) + .parse().processedArgs[0], +); // argOptional before choices narrows type and includes undefined -expectType<('C' | undefined)>( +expectType<'C' | undefined>( program .addArgument(new Argument('foo').argOptional().choices(['C'] as const)) - .parse() - .processedArgs[0] -) + .parse().processedArgs[0], +); // argParser after choices overrides choice type -expectType<(number)>( +expectType( program - .addArgument(new Argument('').choices(['C'] as const).argParser((val: string, prev: number) => prev + Number.parseInt(val))) - .parse() - .processedArgs[0] -) + .addArgument( + new Argument('') + .choices(['C'] as const) + .argParser((val: string, prev: number) => prev + Number.parseInt(val)), + ) + .parse().processedArgs[0], +); // choices after argParser overrides argParser type -expectType<('C')>( +expectType<'C'>( program - .addArgument(new Argument('').argParser((val: string, prev: number) => prev + Number.parseInt(val)).choices(['C'] as const)) - .parse() - .processedArgs[0] -) + .addArgument( + new Argument('') + .argParser((val: string, prev: number) => prev + Number.parseInt(val)) + .choices(['C'] as const), + ) + .parse().processedArgs[0], +); // adding argument preserves options -expectType<({ example?: true })>( +expectType<{ example?: true }>( program .option('--example') .argument('', 'arg description') .parse() - .opts() -) + .opts(), +); -expectType<({ example?: true })>( - program - .option('--example') - .arguments(' [arg2]') - .parse() - .opts() -) +expectType<{ example?: true }>( + program.option('--example').arguments(' [arg2]').parse().opts(), +); -expectType<({ example?: true })>( - program - .option('--example') - .addArgument(new Argument('')) - .parse() - .opts() -) +expectType<{ example?: true }>( + program.option('--example').addArgument(new Argument('')).parse().opts(), +); diff --git a/tests/assignment.test-d.ts b/tests/assignment.test-d.ts index dce6d33..0b6ff2e 100644 --- a/tests/assignment.test-d.ts +++ b/tests/assignment.test-d.ts @@ -1,4 +1,3 @@ - import { expectAssignable, expectNotAssignable } from 'tsd'; import { Command, CommandUnknownOpts, Option } from '..'; diff --git a/tests/commander.test-d.ts b/tests/commander.test-d.ts index 068b501..a8c238a 100644 --- a/tests/commander.test-d.ts +++ b/tests/commander.test-d.ts @@ -10,8 +10,7 @@ import { expectType, expectAssignable } from 'tsd'; // can change as needed, and weaker test that extends CommandUnknownOpts doesn't fail due to // generic typing of options and arguments. -function expectChainedCommand(cmd: T) { -} +function expectChainedCommand(cmd: T) {} // We are are not just checking return types here, we are also implicitly checking that the expected syntax is allowed. @@ -29,9 +28,15 @@ expectType(commander.program); expectType(new commander.Command()); expectType(new commander.Command('name')); expectType(new commander.Option('-f')); -expectType(new commander.CommanderError(1, 'code', 'message')); -expectType(new commander.InvalidArgumentError('message')); -expectType(new commander.InvalidOptionArgumentError('message')); +expectType( + new commander.CommanderError(1, 'code', 'message'), +); +expectType( + new commander.InvalidArgumentError('message'), +); +expectType( + new commander.InvalidOptionArgumentError('message'), +); expectType(commander.createCommand()); expectType(commander.createOption('--demo')); expectType(commander.createArgument('')); @@ -48,14 +53,25 @@ expectType(program.parent); // version expectChainedCommand(program.version('1.2.3')); expectChainedCommand(program.version('1.2.3', '-r,--revision')); -expectChainedCommand(program.version('1.2.3', '-r,--revision', 'show revision information')); +expectChainedCommand( + program.version('1.2.3', '-r,--revision', 'show revision information'), +); expectType(program.version()); // command (and CommandOptions) expectChainedCommand(program.command('action')); -expectChainedCommand(program.command('action', { isDefault: true, hidden: true, noHelp: true })); +expectChainedCommand( + program.command('action', { isDefault: true, hidden: true, noHelp: true }), +); expectType(program.command('exec', 'exec description')); -expectType(program.command('exec', 'exec description', { isDefault: true, hidden: true, noHelp: true, executableFile: 'foo' })); +expectType( + program.command('exec', 'exec description', { + isDefault: true, + hidden: true, + noHelp: true, + executableFile: 'foo', + }), +); // addCommand expectChainedCommand(program.addCommand(new commander.Command('abc'))); @@ -66,38 +82,50 @@ expectChainedCommand(program.argument('')); expectChainedCommand(program.argument('', 'description')); expectChainedCommand(program.argument('[value]', 'description', 'default')); expectChainedCommand(program.argument('[value]', 'description', parseFloat)); -expectChainedCommand(program.argument('[value]', 'description', parseFloat, 1.23)); +expectChainedCommand( + program.argument('[value]', 'description', parseFloat, 1.23), +); // arguments expectChainedCommand(program.arguments(' [env]')); // addHelpCommand -expectType(program.addHelpCommand(new commander.Command('assist'))); +expectType( + program.addHelpCommand(new commander.Command('assist')), +); // Deprecated uses expectType(program.addHelpCommand()); expectType(program.addHelpCommand(false)); expectType(program.addHelpCommand(true)); expectType(program.addHelpCommand('assist [cmd]')); -expectType(program.addHelpCommand('assist [file]', 'display help')); +expectType( + program.addHelpCommand('assist [file]', 'display help'), +); // helpCommand expectType(program.helpCommand(false)); expectType(program.helpCommand(true)); expectType(program.helpCommand('assist [cmd]')); -expectType(program.helpCommand('assist [file]', 'display help')); +expectType( + program.helpCommand('assist [file]', 'display help'), +); // exitOverride expectChainedCommand(program.exitOverride()); -expectChainedCommand(program.exitOverride((err): never => { - return process.exit(err.exitCode); -})); -expectChainedCommand(program.exitOverride((err): void => { - if (err.code !== 'commander.executeSubCommandAsync') { - throw err; - } else { - // Async callback from spawn events, not useful to throw. - } -})); +expectChainedCommand( + program.exitOverride((err): never => { + return process.exit(err.exitCode); + }), +); +expectChainedCommand( + program.exitOverride((err): void => { + if (err.code !== 'commander.executeSubCommandAsync') { + throw err; + } else { + // Async callback from spawn events, not useful to throw. + } + }), +); // error expectType(program.error('Goodbye')); @@ -108,27 +136,33 @@ expectType(program.error('Goodbye', { code: 'my.error', exitCode: 2 })); // hook expectChainedCommand(program.hook('preAction', () => {})); expectChainedCommand(program.hook('postAction', () => {})); -expectChainedCommand(program.hook('preAction', async() => {})); -expectChainedCommand(program.hook('preAction', (thisCommand, actionCommand) => { - // implicit parameter types - expectType(thisCommand); - expectType(actionCommand); -})); +expectChainedCommand(program.hook('preAction', async () => {})); +expectChainedCommand( + program.hook('preAction', (thisCommand, actionCommand) => { + // implicit parameter types + expectType(thisCommand); + expectType(actionCommand); + }), +); expectChainedCommand(program.hook('preSubcommand', () => {})); -expectChainedCommand(program.hook('preSubcommand', (thisCommand, subcommand) => { - // implicit parameter types - expectType(thisCommand); - expectType(subcommand); -})); +expectChainedCommand( + program.hook('preSubcommand', (thisCommand, subcommand) => { + // implicit parameter types + expectType(thisCommand); + expectType(subcommand); + }), +); // action expectChainedCommand(program.action(() => {})); -expectChainedCommand(program.action(async() => {})); +expectChainedCommand(program.action(async () => {})); // option expectChainedCommand(program.option('-a,--alpha')); expectChainedCommand(program.option('-p, --peppers', 'Add peppers')); -expectChainedCommand(program.option('-s, --string [value]', 'default string', 'value')); +expectChainedCommand( + program.option('-s, --string [value]', 'default string', 'value'), +); expectChainedCommand(program.option('-b, --boolean', 'default boolean', false)); // Breaking change: drop support for regular expression // expectChainedCommand(program.option('--drink ', 'float argument', parseFloat)); -expectChainedCommand(program.option('-f, --float ', 'float argument', parseFloat, 3.2)); -expectChainedCommand(program.option('-i, --integer ', 'integer argument', myParseInt)); -expectChainedCommand(program.option('-i, --integer ', 'integer argument', myParseInt, 5)); -expectChainedCommand(program.option('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0)); -expectChainedCommand(program.option('-c, --collect ', 'repeatable value', collect, [])); -expectChainedCommand(program.option('-l, --list ', 'comma separated list', commaSeparatedList)); +expectChainedCommand( + program.option('-f, --float ', 'float argument', parseFloat), +); +expectChainedCommand( + program.option('-f, --float ', 'float argument', parseFloat, 3.2), +); +expectChainedCommand( + program.option('-i, --integer ', 'integer argument', myParseInt), +); +expectChainedCommand( + program.option('-i, --integer ', 'integer argument', myParseInt, 5), +); +expectChainedCommand( + program.option( + '-v, --verbose', + 'verbosity that can be increased', + increaseVerbosity, + 0, + ), +); +expectChainedCommand( + program.option('-c, --collect ', 'repeatable value', collect, []), +); +expectChainedCommand( + program.option( + '-l, --list ', + 'comma separated list', + commaSeparatedList, + ), +); // requiredOption, same tests as option expectChainedCommand(program.requiredOption('-a,--alpha')); expectChainedCommand(program.requiredOption('-p, --peppers', 'Add peppers')); -expectChainedCommand(program.requiredOption('-s, --string [value]', 'default string', 'value')); -expectChainedCommand(program.requiredOption('-b, --boolean', 'default boolean', false)); +expectChainedCommand( + program.requiredOption('-s, --string [value]', 'default string', 'value'), +); +expectChainedCommand( + program.requiredOption('-b, --boolean', 'default boolean', false), +); // Breaking change: drop support for regular expression // expectChainedCommand(program.requiredOption('--drink ', 'float argument', parseFloat)); -expectChainedCommand(program.requiredOption('-f, --float ', 'float argument', parseFloat, 3.2)); -expectChainedCommand(program.requiredOption('-i, --integer ', 'integer argument', myParseInt)); -expectChainedCommand(program.requiredOption('-i, --integer ', 'integer argument', myParseInt, 5)); -expectChainedCommand(program.requiredOption('-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0)); -expectChainedCommand(program.requiredOption('-c, --collect ', 'repeatable value', collect, [])); -expectChainedCommand(program.requiredOption('-l, --list ', 'comma separated list', commaSeparatedList)); +expectChainedCommand( + program.requiredOption('-f, --float ', 'float argument', parseFloat), +); +expectChainedCommand( + program.requiredOption( + '-f, --float ', + 'float argument', + parseFloat, + 3.2, + ), +); +expectChainedCommand( + program.requiredOption( + '-i, --integer ', + 'integer argument', + myParseInt, + ), +); +expectChainedCommand( + program.requiredOption( + '-i, --integer ', + 'integer argument', + myParseInt, + 5, + ), +); +expectChainedCommand( + program.requiredOption( + '-v, --verbose', + 'verbosity that can be increased', + increaseVerbosity, + 0, + ), +); +expectChainedCommand( + program.requiredOption( + '-c, --collect ', + 'repeatable value', + collect, + [], + ), +); +expectChainedCommand( + program.requiredOption( + '-l, --list ', + 'comma separated list', + commaSeparatedList, + ), +); // createOption expectType(program.createOption('a, --alpha')); @@ -183,8 +286,12 @@ expectType(program.createOption('a, --alpha', 'description')); expectChainedCommand(program.addOption(new commander.Option('-s,--simple'))); // storeOptionsAsProperties -expectType(program.storeOptionsAsProperties()); -expectType(program.storeOptionsAsProperties(true)); +expectType( + program.storeOptionsAsProperties(), +); +expectType( + program.storeOptionsAsProperties(true), +); expectChainedCommand(program.storeOptionsAsProperties(false)); // getOptionValue @@ -198,10 +305,14 @@ expectChainedCommand(program.setOptionValue('example', true)); expectChainedCommand(program.setOptionValueWithSource('example', [], 'cli')); // getOptionValueSource -expectType(program.getOptionValueSource('example')); +expectType( + program.getOptionValueSource('example'), +); // getOptionValueSourceWithGlobals -expectType(program.getOptionValueSourceWithGlobals('example')); +expectType( + program.getOptionValueSourceWithGlobals('example'), +); // combineFlagAndOptionalValue expectChainedCommand(program.combineFlagAndOptionalValue()); @@ -227,20 +338,32 @@ expectChainedCommand(program.passThroughOptions(false)); expectChainedCommand(program.parse()); expectChainedCommand(program.parse(process.argv)); expectChainedCommand(program.parse(['node', 'script.js'], { from: 'node' })); -expectChainedCommand(program.parse(['node', 'script.js'], { from: 'electron' })); +expectChainedCommand( + program.parse(['node', 'script.js'], { from: 'electron' }), +); expectChainedCommand(program.parse(['--option'], { from: 'user' })); expectChainedCommand(program.parse(['node', 'script.js'] as const)); // parseAsync, same tests as parse expectType>(program.parseAsync()); expectType>(program.parseAsync(process.argv)); -expectType>(program.parseAsync(['node', 'script.js'], { from: 'node' })); -expectType>(program.parseAsync(['node', 'script.js'], { from: 'electron' })); -expectType>(program.parseAsync(['--option'], { from: 'user' })); -expectType>(program.parseAsync(['node', 'script.js'] as const)); +expectType>( + program.parseAsync(['node', 'script.js'], { from: 'node' }), +); +expectType>( + program.parseAsync(['node', 'script.js'], { from: 'electron' }), +); +expectType>( + program.parseAsync(['--option'], { from: 'user' }), +); +expectType>( + program.parseAsync(['node', 'script.js'] as const), +); // parseOptions (and ParseOptionsResult) -expectType<{operands: string[]; unknown: string[]}>(program.parseOptions(['node', 'script.js', 'hello'])); +expectType<{ operands: string[]; unknown: string[] }>( + program.parseOptions(['node', 'script.js', 'hello']), +); // opts const opts = program.opts(); @@ -275,7 +398,11 @@ expectType(optsWithGlobals.bar); // description expectChainedCommand(program.description('my description')); expectType(program.description()); -expectChainedCommand(program.description('my description of command with arg foo', { foo: 'foo description' })); // deprecated +expectChainedCommand( + program.description('my description of command with arg foo', { + foo: 'foo description', + }), +); // deprecated // summary expectChainedCommand(program.summary('my summary')); @@ -309,13 +436,21 @@ expectType(program.executableDir()); // eslint-disable-next-line @typescript-eslint/no-invalid-void-type expectType(program.outputHelp()); // eslint-disable-next-line @typescript-eslint/no-invalid-void-type -expectType(program.outputHelp((str: string) => { return str; })); +expectType( + program.outputHelp((str: string) => { + return str; + }), +); // eslint-disable-next-line @typescript-eslint/no-invalid-void-type expectType(program.outputHelp({ error: true })); // help expectType(program.help()); -expectType(program.help((str: string) => { return str; })); +expectType( + program.help((str: string) => { + return str; + }), +); expectType(program.help({ error: true })); // helpInformation @@ -329,17 +464,21 @@ expectChainedCommand(program.helpOption(undefined, 'custom description')); expectChainedCommand(program.helpOption(false)); // addHelpOption -expectType(program.addHelpOption(new commander.Option('-h,--help'))); +expectType( + program.addHelpOption(new commander.Option('-h,--help')), +); // addHelpText expectChainedCommand(program.addHelpText('after', 'text')); expectChainedCommand(program.addHelpText('afterAll', 'text')); expectChainedCommand(program.addHelpText('before', () => 'before')); -expectChainedCommand(program.addHelpText('beforeAll', (context) => { - expectType(context.error); - expectType(context.command); - return ''; -})); +expectChainedCommand( + program.addHelpText('beforeAll', (context) => { + expectType(context.error); + expectType(context.command); + return ''; + }), +); // on expectChainedCommand(program.on('command:foo', () => {})); @@ -363,10 +502,12 @@ const myProgram = new MyCommand(); // configureHelp expectType(program.createHelp()); -expectChainedCommand(program.configureHelp({ - sortSubcommands: true, // override property - visibleCommands: () => [] // override method -})); +expectChainedCommand( + program.configureHelp({ + sortSubcommands: true, // override property + visibleCommands: () => [], // override method + }), +); expectType(program.configureHelp()); // copyInheritedSettings @@ -382,16 +523,20 @@ expectChainedCommand(program.showSuggestionAfterError()); expectChainedCommand(program.showSuggestionAfterError(false)); // configureOutput -expectChainedCommand(program.configureOutput({ })); +expectChainedCommand(program.configureOutput({})); expectType(program.configureOutput()); -expectChainedCommand(program.configureOutput({ - writeOut: (str: string) => console.log(str), - writeErr: (str: string) => console.error(str), - getOutHelpWidth: () => 80, - getErrHelpWidth: () => 80, - outputError: (str: string, write: (str: string) => void) => { write(str); } -})); +expectChainedCommand( + program.configureOutput({ + writeOut: (str: string) => console.log(str), + writeErr: (str: string) => console.error(str), + getOutHelpWidth: () => 80, + getErrHelpWidth: () => 80, + outputError: (str: string, write: (str: string) => void) => { + write(str); + }, + }), +); // Help const helper = new commander.Help(); @@ -413,7 +558,9 @@ expectType(helper.optionDescription(helperOption)); expectType(helper.argumentTerm(helperArgument)); expectType(helper.argumentDescription(helperArgument)); -expectType(helper.visibleCommands(helperCommand)); +expectType( + helper.visibleCommands(helperCommand), +); expectType(helper.visibleOptions(helperCommand)); expectType(helper.visibleGlobalOptions(helperCommand)); expectType(helper.visibleArguments(helperCommand)); @@ -465,8 +612,14 @@ expectType(baseOption.env('PORT')); expectType(baseOption.fullDescription()); // argParser -expectType(baseOption.argParser((value: string) => parseInt(value))); -expectType(baseOption.argParser((value: string, previous: string[]) => { return previous.concat(value); })); +expectType( + baseOption.argParser((value: string) => parseInt(value)), +); +expectType( + baseOption.argParser((value: string, previous: string[]) => { + return previous.concat(value); + }), +); // makeOptionMandatory expectType(baseOption.makeOptionMandatory()); @@ -486,7 +639,9 @@ expectType(baseOption.conflicts('a')); expectType(baseOption.conflicts(['a', 'b'])); // implies -expectType(baseOption.implies({ option: 'VALUE', colour: false })); +expectType( + baseOption.implies({ option: 'VALUE', colour: false }), +); // name expectType(baseOption.name()); @@ -517,8 +672,14 @@ expectType(baseArgument.default(3)); expectType(baseArgument.default(60, 'one minute')); // argParser -expectType(baseArgument.argParser((value: string) => parseInt(value))); -expectType(baseArgument.argParser((value: string, previous: string[]) => { return previous.concat(value); })); +expectType( + baseArgument.argParser((value: string) => parseInt(value)), +); +expectType( + baseArgument.argParser((value: string, previous: string[]) => { + return previous.concat(value); + }), +); // choices expectType(baseArgument.choices(['a', 'b'])); diff --git a/tests/create-argument.test-d.ts b/tests/create-argument.test-d.ts index 953d300..593d110 100644 --- a/tests/create-argument.test-d.ts +++ b/tests/create-argument.test-d.ts @@ -5,71 +5,62 @@ import { Command, createArgument } from '..'; if ('when cmd.createArgument with required then type is string') { const program = new Command(); - program - .addArgument(program.createArgument('')) - .action(arg => { - expectType(arg) - }); + program.addArgument(program.createArgument('')).action((arg) => { + expectType(arg); + }); } if ('when cmd.createArgument with optional then type is string|undefined') { const program = new Command(); - program - .addArgument(program.createArgument('[value]')) - .action(arg => { - expectType(arg) - }); + program.addArgument(program.createArgument('[value]')).action((arg) => { + expectType(arg); + }); } if ('when cmd.createArgument with variadic then type is string[]') { const program = new Command(); - program - .addArgument(program.createArgument('')) - .action(arg => { - expectType(arg) - }); + program.addArgument(program.createArgument('')).action((arg) => { + expectType(arg); + }); } if ('when global createArgument with required then type is string') { const program = new Command(); - program - .addArgument(createArgument('')) - .action(arg => { - expectType(arg) - }); + program.addArgument(createArgument('')).action((arg) => { + expectType(arg); + }); } if ('when global createArgument with optional then type is string|undefined') { const program = new Command(); - program - .addArgument(createArgument('[value]')) - .action(arg => { - expectType(arg) - }); + program.addArgument(createArgument('[value]')).action((arg) => { + expectType(arg); + }); } if ('when global createArgument with variadic then type is string[]') { const program = new Command(); - program - .addArgument(createArgument('')) - .action(arg => { - expectType(arg) - }); + program.addArgument(createArgument('')).action((arg) => { + expectType(arg); + }); } if ('when global createArgument with const choices then type is string union') { const program = new Command(); program .addArgument(createArgument('').choices(['A', 'B', 'C'] as const)) - .action(arg => { - expectType<'A' | 'B' | 'C'>(arg) - }) + .action((arg) => { + expectType<'A' | 'B' | 'C'>(arg); + }); } -if ('when global createArgument with variadic and const choices then type is array of string union') { +if ( + 'when global createArgument with variadic and const choices then type is array of string union' +) { const program = new Command(); - program.addArgument(createArgument('').choices(['A', 'B', 'C'] as const)) - .action(arg => { - expectType<('A' | 'B' | 'C')[]>(arg) - }) + program + .addArgument(createArgument('').choices(['A', 'B', 'C'] as const)) + .action((arg) => { + expectType<('A' | 'B' | 'C')[]>(arg); + }); } diff --git a/tests/create-option.test-d.ts b/tests/create-option.test-d.ts index 2152e02..7377ade 100644 --- a/tests/create-option.test-d.ts +++ b/tests/create-option.test-d.ts @@ -7,8 +7,7 @@ if ('when cmd.createOption with boolean then type is boolean') { const program = new Command(); const foo = program .addOption(program.createOption('-f, --foo', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } @@ -16,17 +15,17 @@ if ('when cmd.createOption with required option-argument then type is string') { const program = new Command(); const foo = program .addOption(program.createOption('-f, --foo ', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } -if ('when cmd.createOption with optional option-argument then type is string|true') { +if ( + 'when cmd.createOption with optional option-argument then type is string|true' +) { const program = new Command(); const foo = program .addOption(program.createOption('-f, --foo [value]', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } @@ -34,43 +33,54 @@ if ('when global createOption with boolean then type is boolean') { const program = new Command(); const foo = program .addOption(createOption('-f, --foo', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } -if ('when global createOption with required option-argument then type is string') { +if ( + 'when global createOption with required option-argument then type is string' +) { const program = new Command(); const foo = program .addOption(createOption('-f, --foo ', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } -if ('when global createOption with optional option-argument then type is string|true') { +if ( + 'when global createOption with optional option-argument then type is string|true' +) { const program = new Command(); const foo = program .addOption(createOption('-f, --foo [value]', 'decription')) - .opts() - .foo; + .opts().foo; expectType(foo); } if ('when global createOption with const choices then type is string union') { const program = new Command(); const foo = program - .addOption(createOption('-f, --foo ', 'description').choices(['A', 'B', 'C'] as const)) - .opts() - .foo; + .addOption( + createOption('-f, --foo ', 'description').choices([ + 'A', + 'B', + 'C', + ] as const), + ) + .opts().foo; expectType<'A' | 'B' | 'C' | undefined>(foo); } -if ('when global createOption with variadic and const choices then type is string union array') { +if ( + 'when global createOption with variadic and const choices then type is string union array' +) { const program = new Command(); const foo = program - .addOption(createOption('-f, --foo ', 'description').choices(['A', 'B', 'C'] as const).makeOptionMandatory()) - .opts() - .foo; + .addOption( + createOption('-f, --foo ', 'description') + .choices(['A', 'B', 'C'] as const) + .makeOptionMandatory(), + ) + .opts().foo; expectType<('A' | 'B' | 'C')[]>(foo); } diff --git a/tests/global-createArgument.test.ts b/tests/global-createArgument.test.ts index bd2bf17..3283219 100644 --- a/tests/global-createArgument.test.ts +++ b/tests/global-createArgument.test.ts @@ -1,4 +1,3 @@ - import { createArgument } from '..'; test('when createArgument without description then argument has name', () => { diff --git a/tests/global-createCommand.test.ts b/tests/global-createCommand.test.ts index cdc7ac8..625a442 100644 --- a/tests/global-createCommand.test.ts +++ b/tests/global-createCommand.test.ts @@ -1,12 +1,11 @@ - import { createCommand } from '..'; test('when createCommand without name then command has empty name', () => { const cmd = createCommand(); - expect(cmd.name()).toEqual('') + expect(cmd.name()).toEqual(''); }); test('when createCommand with name then command has name', () => { const cmd = createCommand('name'); - expect(cmd.name()).toEqual('name') + expect(cmd.name()).toEqual('name'); }); diff --git a/tests/global-createOption.test.ts b/tests/global-createOption.test.ts index 51cd7c4..d1fdbf9 100644 --- a/tests/global-createOption.test.ts +++ b/tests/global-createOption.test.ts @@ -1,4 +1,3 @@ - import { createOption } from '..'; test('when createOption without description then option has flags', () => { @@ -12,6 +11,6 @@ test('when createOption with description then option has flags and description', const flags = '-e, --example'; const description = 'example option'; const option = createOption(flags, description); - expect(option.flags).toEqual(flags) - expect(option.description).toEqual(description) + expect(option.flags).toEqual(flags); + expect(option.description).toEqual(description); }); diff --git a/tests/help.test-d.ts b/tests/help.test-d.ts index c8e4f65..a87c657 100644 --- a/tests/help.test-d.ts +++ b/tests/help.test-d.ts @@ -6,7 +6,9 @@ import { Help, Command, CommandUnknownOpts, Option } from '..'; // Reminder: we pass the command into the Help methods using JavaScript so // no type checking there, but subclass is TypeScript. -if ('when subclass Help method with cmd arg as CommandUnknownOpts then no error') { +if ( + 'when subclass Help method with cmd arg as CommandUnknownOpts then no error' +) { class MyHelp extends Help { subcommandTerm(cmd: CommandUnknownOpts) { return cmd.name(); @@ -23,9 +25,7 @@ if ('when subclass Help method with cmd arg as Command then no error') { } if ('when pass type Command to visibleOptions then no error') { - const program = new Command() - .option('--foo') - .argument(''); + const program = new Command().option('--foo').argument(''); program.createHelp().visibleOptions(program); } @@ -37,9 +37,7 @@ if ('when pass type CommandUnknownOpts to visibleOptions then no error') { } if ('when call visibleCommands then returns CommandUnknownOpts[]') { - const program = new Command() - .option('--foo') - .argument(''); + const program = new Command().option('--foo').argument(''); const vo = program.createHelp().visibleCommands(program); expectType(vo); } diff --git a/tests/hook.test-d.ts b/tests/hook.test-d.ts index 42e7ebb..acd2584 100644 --- a/tests/hook.test-d.ts +++ b/tests/hook.test-d.ts @@ -54,4 +54,3 @@ if ('when add preSubcommand hook then activeCommand strongly typed') { expectType(o); }); } - diff --git a/tests/option-value.test-d.ts b/tests/option-value.test-d.ts index ee73368..3a1d8d7 100644 --- a/tests/option-value.test-d.ts +++ b/tests/option-value.test-d.ts @@ -2,16 +2,14 @@ import { expectType } from 'tsd'; import { Command } from '..'; if ('when getOptionValue is unknown then key is unknown') { - const program = new Command() - .option('-f, --foo'); - + const program = new Command().option('-f, --foo'); + const v = program.getOptionValue('bar'); expectType(v); } if ('when getOptionValue result is typed then key is known') { - const program = new Command() - .option('-f, --foo'); + const program = new Command().option('-f, --foo'); const v = program.getOptionValue('foo'); expectType(v); } diff --git a/tests/options.test-d.ts b/tests/options.test-d.ts index 39e8fc6..e171d4e 100644 --- a/tests/options.test-d.ts +++ b/tests/options.test-d.ts @@ -11,64 +11,42 @@ function myParseInts(arg: string, previous: number[]): number[] { // Reusing same program variable through tests for convenience. const program = new Command(); -const o1 = program - .option('-d, --debug') - .opts(); +const o1 = program.option('-d, --debug').opts(); expectType<{ debug?: true }>(o1); -const o2 = program - .option('--debug ') - .opts(); +const o2 = program.option('--debug ').opts(); expectType<{ debug?: string }>(o2); -const o3 = program - .option('-o [optional]') - .opts(); +const o3 = program.option('-o [optional]').opts(); expectType<{ o?: string | true }>(o3); -const o4 = program - .option('--debug ') - .opts(); +const o4 = program.option('--debug ').opts(); expectType<{ debug?: string[] }>(o4); -const o5 = program - .option('--debug [value...]') - .opts(); +const o5 = program.option('--debug [value...]').opts(); expectType<{ debug?: string[] | true }>(o5); if (o5.debug !== true && o5.debug !== undefined) o5.debug[0] = 'a'; // check expecting non-empty // with default -const o6 = program - .option('--debug', 'description', false) - .opts(); +const o6 = program.option('--debug', 'description', false).opts(); expectType<{ debug: boolean }>(o6); -const o7 = program - .option('--debug ', 'description', 'default') - .opts(); +const o7 = program.option('--debug ', 'description', 'default').opts(); expectType<{ debug: string }>(o7); -const o8 = program - .option('--debug [value]', 'description', 'default') - .opts(); +const o8 = program.option('--debug [value]', 'description', 'default').opts(); expectType<{ debug: string | true }>(o8); -const o9 = program - .option('--debug ', 'description', []) - .opts(); +const o9 = program.option('--debug ', 'description', []).opts(); expectType<{ debug: [] | string[] }>(o9); -const o10 = program - .option('--debug [value...]', 'description', []) - .opts(); +const o10 = program.option('--debug [value...]', 'description', []).opts(); expectType<{ debug: string[] | true | [] }>(o10); // Coerce/custom, w/wo defaults -const o11 = program - .option('--debug ', 'description', myParseInt) - .opts(); +const o11 = program.option('--debug ', 'description', myParseInt).opts(); expectType<{ debug?: number }>(o11); const o12 = program @@ -88,21 +66,15 @@ expectType<{ debug: true | number[] }>(o14); // requiredOption -const o15 = program - .requiredOption('--debug ', 'description') - .opts(); +const o15 = program.requiredOption('--debug ', 'description').opts(); expectType<{ debug: string }>(o15); -const o16 = program - .requiredOption('--debug [value]', 'description') - .opts(); +const o16 = program.requiredOption('--debug [value]', 'description').opts(); expectType<{ debug: string | true }>(o16); // negated -const o17 = program - .option('--C, --no-colour') - .opts(); +const o17 = program.option('--C, --no-colour').opts(); expectType<{ colour: boolean }>(o17); const o18 = program @@ -130,23 +102,17 @@ const m1 = program .option('--two ') .option('--three [opt]'); const m2 = m1.opts(); -expectType<{ one?: true; two?: string; three?: string | true}>(m2); +expectType<{ one?: true; two?: string; three?: string | true }>(m2); // addOption -const ao1 = program - .addOption(new Option('-de, --debug')) - .opts(); +const ao1 = program.addOption(new Option('-de, --debug')).opts(); expectType<{ debug?: true }>(ao1); -const ao2 = program - .addOption(new Option('-de, --debug ')) - .opts(); +const ao2 = program.addOption(new Option('-de, --debug ')).opts(); expectType<{ debug?: string }>(ao2); -const ao9 = program - .addOption(new Option('-de, --debug [value]')) - .opts(); +const ao9 = program.addOption(new Option('-de, --debug [value]')).opts(); expectType<{ debug?: string | true }>(ao9); const ao3 = program @@ -181,67 +147,47 @@ program .option('-d, --debug') .option('--required ') .option('--optional [o]') - .action(options => { - expectType<{ debug?: true, required?: string, optional?: true | string }>(options); + .action((options) => { + expectType<{ debug?: true; required?: string; optional?: true | string }>( + options, + ); }); // option names -const on1 = program - .addOption(new Option('-d, --debug')) - .opts(); +const on1 = program.addOption(new Option('-d, --debug')).opts(); expectType<{ debug?: true }>(on1); -const on2 = program - .addOption(new Option('-C, --no-colour')) - .opts(); +const on2 = program.addOption(new Option('-C, --no-colour')).opts(); expectType<{ colour: boolean }>(on2); -const on3 = program - .addOption(new Option('--camel-case')) - .opts(); +const on3 = program.addOption(new Option('--camel-case')).opts(); expectType<{ camelCase?: true }>(on3); -const on4 = program - .addOption(new Option('--no-camel-case')) - .opts(); +const on4 = program.addOption(new Option('--no-camel-case')).opts(); expectType<{ camelCase: boolean }>(on4); // usage styles -const us1 = program - .addOption(new Option('-d, --debug')) - .opts(); +const us1 = program.addOption(new Option('-d, --debug')).opts(); expectType<{ debug?: true }>(us1); -const us2 = program - .addOption(new Option('-d')) - .opts(); +const us2 = program.addOption(new Option('-d')).opts(); expectType<{ d?: true }>(us2); -const us3 = program - .addOption(new Option('--debug')) - .opts(); +const us3 = program.addOption(new Option('--debug')).opts(); expectType<{ debug?: true }>(us3); -const us4 = program - .addOption(new Option('-d,--debug')) - .opts(); +const us4 = program.addOption(new Option('-d,--debug')).opts(); expectType<{ debug?: true }>(us4); -const us5 = program - .addOption(new Option('-d|--debug')) - .opts(); +const us5 = program.addOption(new Option('-d|--debug')).opts(); expectType<{ debug?: true }>(us5); -const us6 = program - .addOption(new Option('-d | --debug')) - .opts(); +const us6 = program.addOption(new Option('-d | --debug')).opts(); expectType<{ debug?: true }>(us6); -const us7 = program - .addOption(new Option('-d --debug')) - .opts(); +const us7 = program.addOption(new Option('-d --debug')).opts(); expectType<{ debug?: true }>(us7); // choices @@ -250,58 +196,76 @@ expectType<{ debug?: true }>(us7); const co1 = program .addOption(new Option('-d, --debug ').choices(['A', 'B'] as const)) .opts(); -expectType<{debug?: 'A' | 'B'}>(co1); +expectType<{ debug?: 'A' | 'B' }>(co1); // narrows optional value to union of given choices and true const co2 = program .addOption(new Option('-d, --debug [val]').choices(['A', 'B'] as const)) .opts(); -expectType<{debug?: 'A' | 'B' | true}>(co2); +expectType<{ debug?: 'A' | 'B' | true }>(co2); // narrows required option to given choices const co3 = program - .addOption(new Option('-d, --debug ').choices(['A', 'B'] as const).makeOptionMandatory()) + .addOption( + new Option('-d, --debug ') + .choices(['A', 'B'] as const) + .makeOptionMandatory(), + ) .opts(); -expectType<{debug: 'A' | 'B'}>(co3) +expectType<{ debug: 'A' | 'B' }>(co3); // narrows variadic value to choices array const co4 = program .addOption(new Option('-d, --debug ').choices(['A', 'B'] as const)) .opts(); -expectType<{debug?: ('A' | 'B')[]}>(co4) +expectType<{ debug?: ('A' | 'B')[] }>(co4); // narrows optional variadic value to choices | true array const co5 = program .addOption(new Option('-d, --debug [val...]').choices(['A', 'B'] as const)) .opts(); -expectType<{debug?: ('A' | 'B')[] | true}>(co5) +expectType<{ debug?: ('A' | 'B')[] | true }>(co5); // narrows required option with optional variadic value const co6 = program - .addOption(new Option('-d, --debug [val...]').choices(['A', 'B'] as const).makeOptionMandatory()) + .addOption( + new Option('-d, --debug [val...]') + .choices(['A', 'B'] as const) + .makeOptionMandatory(), + ) .opts(); -expectType<{debug: ('A' | 'B')[] | true}>(co6) +expectType<{ debug: ('A' | 'B')[] | true }>(co6); // narrows required option with required variadic value const co7 = program - .addOption(new Option('-d, --debug ').choices(['A', 'B'] as const).makeOptionMandatory()) + .addOption( + new Option('-d, --debug ') + .choices(['A', 'B'] as const) + .makeOptionMandatory(), + ) .opts(); -expectType<{debug: ('A' | 'B')[]}>(co7) +expectType<{ debug: ('A' | 'B')[] }>(co7); // default before choices creates union type const co8 = program - .addOption(new Option('--foo ').default('D' as const).choices(['C'] as const)) + .addOption( + new Option('--foo ').default('D' as const).choices(['C'] as const), + ) .opts(); -expectType<{foo: 'C' | 'D'}>(co8); - +expectType<{ foo: 'C' | 'D' }>(co8); + // default after choices creates union type const co9 = program - .addOption(new Option('--foo ').choices(['C'] as const).default('D' as const)) + .addOption( + new Option('--foo ').choices(['C'] as const).default('D' as const), + ) .opts(); -expectType<{foo: 'C' | 'D'}>(co9); - +expectType<{ foo: 'C' | 'D' }>(co9); + // make mandatory before choices makes option mandatory const c10 = program - .addOption(new Option('--foo ').makeOptionMandatory().choices(['C'] as const)) + .addOption( + new Option('--foo ').makeOptionMandatory().choices(['C'] as const), + ) .opts(); -expectType<{foo: 'C'}>(c10); +expectType<{ foo: 'C' }>(c10); diff --git a/tests/processed-args.test-d.ts b/tests/processed-args.test-d.ts index 357f461..16721d4 100644 --- a/tests/processed-args.test-d.ts +++ b/tests/processed-args.test-d.ts @@ -5,36 +5,25 @@ import { Command } from '..'; if ('when no arguments then empty array') { const program = new Command(); - const args = program - .parse() - .processedArgs; + const args = program.parse().processedArgs; expectType<[]>(args); } if ('when required argument then string element') { const program = new Command(); - const args = program - .argument('') - .parse() - .processedArgs; + const args = program.argument('').parse().processedArgs; expectType<[string]>(args); } if ('when optional argument then string|undefined element') { const program = new Command(); - const args = program - .argument('[value]') - .parse() - .processedArgs; - expectType<[string| undefined]>(args); + const args = program.argument('[value]').parse().processedArgs; + expectType<[string | undefined]>(args); } if ('when variadic argument then string[] element') { const program = new Command(); - const args = program - .argument('') - .parse() - .processedArgs; + const args = program.argument('').parse().processedArgs; expectType<[string[]]>(args); } @@ -43,8 +32,7 @@ if ('when multiple arguments then multiple elements') { const args = program .argument('') .argument('[value]') - .parse() - .processedArgs; + .parse().processedArgs; expectType<[string, string | undefined]>(args); } @@ -52,7 +40,6 @@ if ('when custom argument processing then custom type') { const program = new Command(); const args = program .argument('', 'description', parseFloat) - .parse() - .processedArgs; + .parse().processedArgs; expectType<[number]>(args); } diff --git a/tests/subclass.test-d.ts b/tests/subclass.test-d.ts index a279618..e6f9d88 100644 --- a/tests/subclass.test-d.ts +++ b/tests/subclass.test-d.ts @@ -3,8 +3,8 @@ import { Command, Option } from '..'; // Breaking: need to add type parameter for use in super constructor class MyOption extends Option { - myFunction(): void { - // do nothing + myFunction(): void { + // do nothing } } @@ -21,7 +21,9 @@ class MyCommand extends Command { } } -if ('when add subcommand to MyCommand then return type is not MyCommand (limitation)') { +if ( + 'when add subcommand to MyCommand then return type is not MyCommand (limitation)' +) { const myProgram = new MyCommand(); const mySub = myProgram.command('sub'); // Breaking: lost automatic custom typing of subcommands @@ -29,7 +31,9 @@ if ('when add subcommand to MyCommand then return type is not MyCommand (limitat mySub.myFunction(); } -if ('when call chaining method using inference on MyCommand then return type not MyCommand (limitation)') { +if ( + 'when call chaining method using inference on MyCommand then return type not MyCommand (limitation)' +) { new MyCommand().myFunction(); // Breaking: lost subclass when chain // @ts-expect-error because lost subclass and so myFunction unknown @@ -37,20 +41,22 @@ if ('when call chaining method using inference on MyCommand then return type not } if ('when add option to MyCommand then option type inferred') { - const program = new MyCommand() - .option('-f, --foo', 'foo description'); + const program = new MyCommand().option('-f, --foo', 'foo description'); const foo = program.opts().foo; expectType(foo); } if ('when add MyOption to Command then option type inferred') { - const program = new Command() - .addOption(new MyOption('-f, --foo ', 'foo description').makeOptionMandatory()); + const program = new Command().addOption( + new MyOption('-f, --foo ', 'foo description').makeOptionMandatory(), + ); const foo = program.opts().foo; expectType(foo); } -if ('when call chaining method using inference on MyOption then return type not MyOption (limitation)') { +if ( + 'when call chaining method using inference on MyOption then return type not MyOption (limitation)' +) { new MyOption('-f, --foo').myFunction(); // Breaking: lost subclass when chain // @ts-expect-error because lost subclass and so myFunction unknown diff --git a/tsconfig.json b/tsconfig.json index 83d0218..3bda6f3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,14 @@ { - "compilerOptions": { - "module": "commonjs", - "lib": [ - "es6" - ], - "types": ["node", "jest"], - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - // "exactOptionalPropertyTypes": true, - "noEmit": true, - "forceConsistentCasingInFileNames": true - }, - "include": ["**/*.ts"], + "compilerOptions": { + "module": "commonjs", + "lib": ["es6"], + "types": ["node", "jest"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + // "exactOptionalPropertyTypes": true, + "noEmit": true, + "forceConsistentCasingInFileNames": true, + }, + "include": ["**/*.ts"], }