Addition: opts.allowMultipleFlagValues & more accurate return flag types
opts.allowMultipleFlagValues -> returns flag values as array and add duplicate flags to the array instead of picking the last one (eg ["--foo", "hello", "--foo=world!"] -> { foo: ["hello", "world!"] }).
Flag return types are now typed as Partials if a default is not present and opts.allowMultipleFlagValues is not enabled.
This commit is contained in:
parent
c16dbc7d56
commit
86b8cfcfaa
21
index.js
21
index.js
@ -210,6 +210,7 @@ function validateAndFillDefaults(opts) {
|
|||||||
validateOrDefaultIfUnset(opts, 'allowSingularDashLongFlags', 'boolean', false);
|
validateOrDefaultIfUnset(opts, 'allowSingularDashLongFlags', 'boolean', false);
|
||||||
validateOrDefaultIfUnset(opts, 'lowerCaseFlagValues', 'boolean', false);
|
validateOrDefaultIfUnset(opts, 'lowerCaseFlagValues', 'boolean', false);
|
||||||
validateOrDefaultIfUnset(opts, 'lowerCaseInputValues', 'boolean', false);
|
validateOrDefaultIfUnset(opts, 'lowerCaseInputValues', 'boolean', false);
|
||||||
|
validateOrDefaultIfUnset(opts, 'allowMultipleFlagValues', 'boolean', false);
|
||||||
|
|
||||||
if(opts.shouldStopParsingFunc !== null)
|
if(opts.shouldStopParsingFunc !== null)
|
||||||
validateOrDefaultIfUnset(opts, 'shouldStopParsingFunc', 'function', null);
|
validateOrDefaultIfUnset(opts, 'shouldStopParsingFunc', 'function', null);
|
||||||
@ -825,9 +826,10 @@ function parseShorthandFlags(shorthandFlags, shorthandString) {
|
|||||||
// TODO: Fix typescript types of returned flags to include opts.resolveFlagValueTypes into it as well as checking if it has a default and if not make it an optional?, and type never if it's not in the flags object
|
// TODO: Fix typescript types of returned flags to include opts.resolveFlagValueTypes into it as well as checking if it has a default and if not make it an optional?, and type never if it's not in the flags object
|
||||||
/**
|
/**
|
||||||
* @template {FlagsI} Flags
|
* @template {FlagsI} Flags
|
||||||
* @template {{ [K in keyof Flags]: Flags[K]['type'] extends 'boolean' ? boolean : Flags[K]['type'] extends 'number' ? number : Flags[K]['type'] extends 'string' ? string : string | boolean | number }} FlagsReturn
|
* @template {ParserOpts<Flags>} Opts
|
||||||
|
* @template {import('./types.d.ts').FlagsReturn<Opts>} FlagsReturn
|
||||||
* @param {string[]} argv
|
* @param {string[]} argv
|
||||||
* @param {ParserOpts<Flags>} opts
|
* @param {Opts} opts
|
||||||
* @returns {{ input: string[], flags: FlagsReturn, unparsed: string[] } }}
|
* @returns {{ input: string[], flags: FlagsReturn, unparsed: string[] } }}
|
||||||
*/
|
*/
|
||||||
function parser(argv, opts) {
|
function parser(argv, opts) {
|
||||||
@ -1060,6 +1062,13 @@ function parser(argv, opts) {
|
|||||||
|
|
||||||
const flagName = flag.config?.name || flag.key;
|
const flagName = flag.config?.name || flag.key;
|
||||||
|
|
||||||
|
if(opts.allowMultipleFlagValues) {
|
||||||
|
if(!Array.isArray(flagReturnObj[flagName]))
|
||||||
|
flagReturnObj[flagName] = [flag.value];
|
||||||
|
else
|
||||||
|
flagReturnObj[flagName].push(flag.value);
|
||||||
|
return
|
||||||
|
}
|
||||||
flagReturnObj[flagName] = flag.value;
|
flagReturnObj[flagName] = flag.value;
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1067,10 +1076,16 @@ function parser(argv, opts) {
|
|||||||
Object.entries(opts.flags).forEach(([flagName, flagConfig]) => {
|
Object.entries(opts.flags).forEach(([flagName, flagConfig]) => {
|
||||||
if(typeof flagReturnObj[flagName] !== 'undefined')
|
if(typeof flagReturnObj[flagName] !== 'undefined')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(opts.allowMultipleFlagValues) {
|
||||||
|
if(typeof flagConfig.default === 'undefined')
|
||||||
|
return flagReturnObj[flagName] = [];
|
||||||
|
return flagReturnObj[flagName] = [flagConfig.default];
|
||||||
|
}
|
||||||
/** Not a needed check as you can tell, but present to explicitly highlight that behavior in the code */
|
/** Not a needed check as you can tell, but present to explicitly highlight that behavior in the code */
|
||||||
if(typeof flagConfig.default === 'undefined')
|
if(typeof flagConfig.default === 'undefined')
|
||||||
return flagReturnObj[flagName] = undefined;
|
return flagReturnObj[flagName] = undefined;
|
||||||
flagReturnObj[flagName] = flagConfig.default;
|
return flagReturnObj[flagName] = flagConfig.default;
|
||||||
});
|
});
|
||||||
|
|
||||||
/// @ts-ignore - falsly wrong flags type
|
/// @ts-ignore - falsly wrong flags type
|
||||||
|
|||||||
46
types.d.ts
vendored
46
types.d.ts
vendored
@ -11,6 +11,8 @@ interface FlagT<T, Z> {
|
|||||||
acceptsNaturalValue?: boolean
|
acceptsNaturalValue?: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FlagTypes = "string" | "boolean" | "bigint" | "number" | "any";
|
||||||
|
|
||||||
export type FlagAny = FlagT<string, "string"> | FlagT<boolean, "boolean"> | FlagT<number, "number"> | FlagT<bigint, "bigint"> | FlagT<string | number | bigint | boolean, "any">
|
export type FlagAny = FlagT<string, "string"> | FlagT<boolean, "boolean"> | FlagT<number, "number"> | FlagT<bigint, "bigint"> | FlagT<string | number | bigint | boolean, "any">
|
||||||
|
|
||||||
export interface FlagsI { [key: readonly string]: FlagAny }
|
export interface FlagsI { [key: readonly string]: FlagAny }
|
||||||
@ -49,7 +51,7 @@ export interface ParserOpts<Flags extends FlagsI> {
|
|||||||
/** Override to change how warnings are emitted @default console.warn */
|
/** Override to change how warnings are emitted @default console.warn */
|
||||||
warningLogger?(log: string): void;
|
warningLogger?(log: string): void;
|
||||||
/** Default type for either unknown flags or flags without an explicit type being set @default "any" */
|
/** Default type for either unknown flags or flags without an explicit type being set @default "any" */
|
||||||
defaultFlagType?: "string" | "boolean" | "bigint" | "number" | "any";
|
defaultFlagType?: FlagTypes;
|
||||||
/**
|
/**
|
||||||
* Behavior when input does not follow the provided flags constraints (eg: flag assigned value (--flag=value) not being the correct type).
|
* Behavior when input does not follow the provided flags constraints (eg: flag assigned value (--flag=value) not being the correct type).
|
||||||
*
|
*
|
||||||
@ -125,6 +127,24 @@ export interface ParserOpts<Flags extends FlagsI> {
|
|||||||
* @default null
|
* @default null
|
||||||
*/
|
*/
|
||||||
parseFilterFunc?(arg: string, index: number, argv: string[], argMeta: argMeta, input: string[]): boolean
|
parseFilterFunc?(arg: string, index: number, argv: string[], argMeta: argMeta, input: string[]): boolean
|
||||||
|
/**
|
||||||
|
* Allows multiple flag values & changes the returned flags to always be arrays, with the default being the first value if the flag was not set, or an
|
||||||
|
* empty array if the flag does not have a default but was not present.
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
* const { flags } = parser(["--flag=hello", "--flag=world"], {
|
||||||
|
* flags: {
|
||||||
|
* defaultedFlag: { default: "missing", type: "string" },
|
||||||
|
* missingFlag: { type: "string" },
|
||||||
|
* },
|
||||||
|
* allowMultipleFlagValues: true,
|
||||||
|
* allowUnknownFlags: true,
|
||||||
|
* });
|
||||||
|
* flags === { flag: ["hello", "world"], defaultedFlag: ["missing"], missingFlag: [] }
|
||||||
|
* ```
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
allowMultipleFlagValues?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type argMeta = {
|
export type argMeta = {
|
||||||
@ -146,4 +166,26 @@ export type argMeta = {
|
|||||||
|
|
||||||
function __type__getType(e: any) { return typeof e };
|
function __type__getType(e: any) { return typeof e };
|
||||||
|
|
||||||
export type JSTypes = ReturnType<typeof __type__getType>
|
export type JSTypes = ReturnType<typeof __type__getType>;
|
||||||
|
|
||||||
|
export type FlagReturnType<FlagType extends FlagTypes> =
|
||||||
|
FlagType extends 'boolean' ? boolean
|
||||||
|
: FlagType extends 'number' ? number
|
||||||
|
: FlagType extends 'bigint' ? bigint
|
||||||
|
: FlagType extends 'string' ? string
|
||||||
|
: string | boolean | number | bigint;
|
||||||
|
|
||||||
|
type ValueOf<T> = T[keyof T];
|
||||||
|
type OptionalKeys<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;
|
||||||
|
|
||||||
|
type _OptionalIfFlagDefaultNotPresent<T> = OptionalKeys<T, ValueOf<{ [V in keyof T]: 'default' extends keyof T[V] ? never : V }>>;
|
||||||
|
type _ResolveFlagTypes<Flags extends FlagsI, allowMultipleFlagValues extends boolean> = {
|
||||||
|
[K in keyof Flags]: allowMultipleFlagValues extends true
|
||||||
|
? FlagReturnType<Flags[K]['type']>[]
|
||||||
|
: FlagReturnType<Flags[K]['type']>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FlagsReturn<Opts extends ParserOpts> = _ResolveFlagTypes<
|
||||||
|
Opts['allowMultipleFlagValues'] extends true ? Opts['flags'] : _OptionalIfFlagDefaultNotPresent<Opts['flags']>,
|
||||||
|
Opts['allowMultipleFlagValues'] extends true ? true : false
|
||||||
|
>;
|
||||||
Loading…
x
Reference in New Issue
Block a user