UNPKG

tiny-bin

Version:

A library for building tiny and beautiful command line apps.

45 lines (44 loc) 4.81 kB
type KebabToCamel<S extends string> = S extends `${infer First}-${infer Rest}` ? `${First}${Capitalize<KebabToCamel<Rest>>}` : S; type TrimLeft<S extends string> = S extends ` ${infer Rest}` ? TrimLeft<Rest> : S extends `,${infer Rest}` ? TrimLeft<Rest> : S; type TrimRight<S extends string> = S extends `${infer Rest} ` ? TrimRight<Rest> : S extends `${infer Rest},` ? TrimRight<Rest> : S; type Trim<S extends string> = TrimLeft<TrimRight<S>>; type HasArgument<S extends string> = S extends `${string}<${string}>` ? true : false; type IsVariadic<S extends string> = S extends `${string}<${string}...>` ? true : false; type RemoveArgument<S extends string> = S extends `${infer Before}<${string}>` ? Trim<Before> : S; type ExtractArgumentContent<S extends string> = S extends `${string}<${infer Content}>` ? Content : never; type IsEnumArg<S extends string> = S extends `${string}|${string}` ? true : false; type ParseEnumValues<S extends string> = S extends `${infer A}|${infer Rest}` ? A | ParseEnumValues<Rest> : S; type ExtractInlineEnum<S extends string> = HasArgument<S> extends true ? ExtractArgumentContent<S> extends infer Content ? Content extends string ? IsEnumArg<Content> extends true ? ParseEnumValues<Content> : never : never : never : never; type ExtractLong<S extends string, HasArg extends boolean> = S extends `--no-${infer Name}` ? (HasArg extends true ? KebabToCamel<`no-${Name extends `${infer N} ${string}` ? N : Name extends `${infer N},${string}` ? N : Name}`> : KebabToCamel<Name extends `${infer N} ${string}` ? N : Name extends `${infer N},${string}` ? N : Name>) : S extends `--${infer Name}` ? (KebabToCamel<Name extends `${infer N} ${string}` ? N : Name extends `${infer N},${string}` ? N : Name>) : never; type ExtractShort<S extends string> = S extends `-${infer C}${infer Rest}` ? (C extends '-' ? never : Rest extends '' ? C : Rest extends ` ${string}` ? C : Rest extends `,${string}` ? C : C) : never; type ParseSegment<S extends string, HasArg extends boolean> = S extends `--${string}` ? ExtractLong<S, HasArg> : S extends `-${string}` ? ExtractShort<S> : never; type SplitByComma<S extends string, HasArg extends boolean> = S extends `${infer A},${infer Rest}` ? SplitBySpace<Trim<A>, HasArg> | SplitByComma<Trim<Rest>, HasArg> : SplitBySpace<Trim<S>, HasArg>; type SplitBySpace<S extends string, HasArg extends boolean> = S extends `${infer A} ${infer Rest}` ? ParseSegment<Trim<A>, HasArg> | SplitBySpace<Trim<Rest>, HasArg> : ParseSegment<S, HasArg>; type ExtractNames<S extends string, HasArg extends boolean> = SplitByComma<Trim<S>, HasArg>; type ExtractOptionNames<S extends string> = ExtractNames<RemoveArgument<S>, HasArgument<S>>; type InferValueType<S extends string, TypeOpt extends string | undefined, EnumOpt extends readonly string[] | undefined> = EnumOpt extends readonly [infer First, ...infer Rest] ? First | (Rest extends readonly string[] ? Rest[number] : never) : ExtractInlineEnum<S> extends never ? TypeOpt extends 'integer' | 'number' ? number : TypeOpt extends 'string' ? string : string : ExtractInlineEnum<S>; type DetermineOptionType<S extends string, TypeOpt extends string | undefined = undefined, EnumOpt extends readonly string[] | undefined = undefined> = TypeOpt extends 'boolean' ? boolean : TypeOpt extends 'integer' | 'number' ? HasArgument<S> extends true ? IsVariadic<S> extends true ? number[] : number : number : TypeOpt extends 'string' ? HasArgument<S> extends true ? IsVariadic<S> extends true ? string[] : string : string : HasArgument<S> extends true ? IsVariadic<S> extends true ? InferValueType<S, TypeOpt, EnumOpt>[] : InferValueType<S, TypeOpt, EnumOpt> : boolean; type OptionRecord<S extends string, TypeOpt extends string | undefined = undefined, EnumOpt extends readonly string[] | undefined = undefined, IsRequired extends boolean = false, HasNonUndefinedDefault extends boolean = false> = IsRequired extends true ? { [K in ExtractOptionNames<S>]: DetermineOptionType<S, TypeOpt, EnumOpt>; } : HasNonUndefinedDefault extends true ? { [K in ExtractOptionNames<S>]: DetermineOptionType<S, TypeOpt, EnumOpt>; } : { [K in ExtractOptionNames<S>]?: DetermineOptionType<S, TypeOpt, EnumOpt>; }; type ExtractType<O> = O extends { type: infer T extends string; } ? T : undefined; type ExtractEnum<O> = O extends { enum: infer E extends readonly string[]; } ? E : undefined; type ExtractRequired<O> = O extends { required: true; } ? true : false; type ExtractHasDefault<O> = O extends { default: undefined; } ? false : O extends { default: any; } ? true : false; type OptionRecordFrom<N extends string, O = {}> = OptionRecord<N, ExtractType<O>, ExtractEnum<O>, ExtractRequired<O>, ExtractHasDefault<O>>; type OptionInfer<N extends string, O> = OptionRecordFrom<N, O>; export type { OptionInfer };