export type Flag = (args: string[]) => ParseResult; export type ParseResult = { [key in N]: { value?: T; option: FlagOption; position?: number[]; }; }; export type NumberFlag = Flag; export type StringFlag = Flag; export type BooleanFlag = Flag; export type FlagOption = { alias?: string; default?: T; usage?: string; }; function findIndex(args: string[], ...names: string[]): number { return args.findIndex(a => { const result = names.findIndex(n => { return a === `--${n}` || a === `-${n}`; }); return result > -1; }); } export function makeNumberFlag( name: N, option: FlagOption = {} ): NumberFlag { return (args: string[]) => { const idx = findIndex(args, name, option.alias || ""); if (idx < 0 || idx + 1 >= args.length) { return { [name]: { value: option.default, option } }; } const v = args[idx + 1]; return { [name]: { value: parseInt(v, 10), option, position: [idx, idx + 1] } }; }; } export function makeStringFlag( name: N, option: FlagOption = {} ): StringFlag { return (args: string[]) => { const idx = findIndex(args, name, option.alias); if (idx < 0 || idx + 1 >= args.length) { return { [name]: { value: option.default, option } }; } const v = args[idx + 1]; return { [name]: { value: v, option, position: v ? [idx, idx + 1] : undefined } }; }; } export function makeBooleanFlag( name: N, option: FlagOption = {} ): BooleanFlag { return (args: string[]) => { const idx = findIndex(args, name, option.alias); if (idx < 0) { return { [name]: { value: option.default, option } }; } return { [name]: { value: true, option, position: [idx] } }; }; } export type TupleTypes = { [P in keyof T]: T[P] } extends { [key: number]: (args: any) => infer V; } ? V : never; export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; export function mergeFlag< T extends Array<(args: string[]) => { [key: string]: any }> >(...src: T): (args: string[]) => UnionToIntersection> { return src.splice(1).reduce((acc, cur) => { return _compose(acc, cur); }, src[0]); } // TODO: remove export function reduceFlag< T extends Array<(args: string[]) => { [key: string]: any }> >(...src: T): (args: string[]) => UnionToIntersection> { return src.splice(1).reduce((acc, cur) => { return _compose(acc, cur); }, src[0]); } function _compose( t1: Flag, t2: Flag ): Flag<{ [key in T1Name]: T1 } & { [key in T2Name]: T2 }, T1Name | T2Name> { return (args: string[]) => { const t1v = t1(args); const t2v = t2(args); return { ...t1v, ...t2v }; }; }