From f32bc5b25aec40b4763a1222e7fd884fa93d1aa6 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Wed, 13 Sep 2023 02:43:32 +0200 Subject: [PATCH 1/8] feat: support number as args --- playground/hello.ts | 8 +++++++- src/_parser.ts | 9 +++++++++ src/args.ts | 5 ++++- src/types.ts | 15 +++++++++++---- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/playground/hello.ts b/playground/hello.ts index 430a4f0..47c21bf 100644 --- a/playground/hello.ts +++ b/playground/hello.ts @@ -17,9 +17,15 @@ const command = defineCommand({ type: "boolean", description: "Use friendly greeting", }, + age: { + type: "number", + description: "Your age", + required: true, + }, }, run({ args }) { - consola.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`); + consola.log(args); + consola.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}! You are ${args.age} years old.`); }, }); diff --git a/src/_parser.ts b/src/_parser.ts index bd2f6e6..29f9717 100644 --- a/src/_parser.ts +++ b/src/_parser.ts @@ -8,6 +8,7 @@ type Default = Dict; export interface Options { boolean?: Arrayable; string?: Arrayable; + number?: Arrayable; alias?: Dict>; default?: Dict; unknown?(flag: string): void; @@ -64,6 +65,7 @@ export function parseRawArgs( opts.alias = opts.alias || {}; opts.string = toArr(opts.string); opts.boolean = toArr(opts.boolean); + opts.number = toArr(opts.number); if (alibi) { for (k in opts.alias) { @@ -81,6 +83,13 @@ export function parseRawArgs( } } + for (i = opts.number.length; i-- > 0; ) { + arr = opts.alias[opts.number[i]] || []; + for (j = arr.length; j-- > 0; ) { + opts.number.push(arr[j]); + } + } + for (i = opts.string.length; i-- > 0; ) { arr = opts.alias[opts.string[i]] || []; for (j = arr.length; j-- > 0; ) { diff --git a/src/args.ts b/src/args.ts index 83235ae..b4263cd 100644 --- a/src/args.ts +++ b/src/args.ts @@ -10,9 +10,10 @@ export function parseArgs( const parseOptions = { boolean: [] as string[], string: [] as string[], + number: [] as string[], mixed: [] as string[], alias: {} as Record, - default: {} as Record, + default: {} as Record, }; const args = resolveArgs(argsDef); @@ -25,6 +26,8 @@ export function parseArgs( parseOptions.string.push(arg.name); } else if (arg.type === "boolean") { parseOptions.boolean.push(arg.name); + } else if (arg.type === "number") { + parseOptions.number.push(arg.name); } if (arg.default !== undefined) { parseOptions.default[arg.name] = arg.default; diff --git a/src/types.ts b/src/types.ts index cd71977..6a4f466 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,8 @@ // ----- Args ----- -export type ArgType = "boolean" | "string" | "positional" | undefined; +export type ArgType = "boolean" | "number" | "string" | "positional" | undefined; -export type _ArgDef = { +export type _ArgDef = { type?: T; description?: string; valueHint?: string; @@ -12,9 +12,10 @@ export type _ArgDef = { }; export type BooleanArgDef = _ArgDef<"boolean", boolean>; +export type NumberArgDef = _ArgDef<"number", number>; export type StringArgDef = _ArgDef<"string", string>; export type PositionalArgDef = Omit<_ArgDef<"positional", string>, "alias">; -export type ArgDef = BooleanArgDef | StringArgDef | PositionalArgDef; +export type ArgDef = BooleanArgDef | NumberArgDef | StringArgDef | PositionalArgDef; export type ArgsDef = Record; export type Arg = ArgDef & { name: string; alias: string[] }; @@ -28,13 +29,19 @@ export type ParsedArgs = { _: string[] } & Record< }[keyof T], string > & + Record< + { + [K in keyof T]: T[K] extends { type: "number" } ? K : never; + }[keyof T], + number + > & Record< { [K in keyof T]: T[K] extends { type: "boolean" } ? K : never; }[keyof T], boolean > & - Record; + Record; // ----- Command ----- From 73cd98ddd340a2f69bc656fdf69beffcba2bdf90 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 00:47:12 +0000 Subject: [PATCH 2/8] chore: apply automated lint fixes --- src/args.ts | 23 +++++++++++++++++------ src/types.ts | 13 +++++++++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/args.ts b/src/args.ts index b4263cd..38d9d8d 100644 --- a/src/args.ts +++ b/src/args.ts @@ -22,12 +22,23 @@ export function parseArgs( if (arg.type === "positional") { continue; } - if (arg.type === "string") { - parseOptions.string.push(arg.name); - } else if (arg.type === "boolean") { - parseOptions.boolean.push(arg.name); - } else if (arg.type === "number") { - parseOptions.number.push(arg.name); + switch (arg.type) { + case "string": { + parseOptions.string.push(arg.name); + + break; + } + case "boolean": { + parseOptions.boolean.push(arg.name); + + break; + } + case "number": { + parseOptions.number.push(arg.name); + + break; + } + // No default } if (arg.default !== undefined) { parseOptions.default[arg.name] = arg.default; diff --git a/src/types.ts b/src/types.ts index 6a4f466..2e29082 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,11 @@ // ----- Args ----- -export type ArgType = "boolean" | "number" | "string" | "positional" | undefined; +export type ArgType = + | "boolean" + | "number" + | "string" + | "positional" + | undefined; export type _ArgDef = { type?: T; @@ -15,7 +20,11 @@ export type BooleanArgDef = _ArgDef<"boolean", boolean>; export type NumberArgDef = _ArgDef<"number", number>; export type StringArgDef = _ArgDef<"string", string>; export type PositionalArgDef = Omit<_ArgDef<"positional", string>, "alias">; -export type ArgDef = BooleanArgDef | NumberArgDef | StringArgDef | PositionalArgDef; +export type ArgDef = + | BooleanArgDef + | NumberArgDef + | StringArgDef + | PositionalArgDef; export type ArgsDef = Record; export type Arg = ArgDef & { name: string; alias: string[] }; From c2c17f3259f30fa5b3e97ecc1c4eff27eebb3b6f Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 20 Nov 2023 13:49:58 +0100 Subject: [PATCH 3/8] use if --- src/args.ts | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/args.ts b/src/args.ts index 38d9d8d..ed5b76d 100644 --- a/src/args.ts +++ b/src/args.ts @@ -22,23 +22,13 @@ export function parseArgs( if (arg.type === "positional") { continue; } - switch (arg.type) { - case "string": { - parseOptions.string.push(arg.name); - - break; - } - case "boolean": { - parseOptions.boolean.push(arg.name); - - break; - } - case "number": { - parseOptions.number.push(arg.name); - - break; - } - // No default + // eslint-disable-next-line unicorn/prefer-switch + if (arg.type === "string") { + parseOptions.string.push(arg.name); + } else if (arg.type === "boolean") { + parseOptions.boolean.push(arg.name); + } else if (arg.type === "number") { + parseOptions.number.push(arg.name); } if (arg.default !== undefined) { parseOptions.default[arg.name] = arg.default; From 160bde10b0023f95fba08a067721a290e87f8a88 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 20 Nov 2023 14:12:02 +0100 Subject: [PATCH 4/8] refactor: simplify to use args alias --- src/_parser.ts | 27 +++++++++------------------ src/args.ts | 4 +--- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/_parser.ts b/src/_parser.ts index 96ee7fc..bd2f6e6 100644 --- a/src/_parser.ts +++ b/src/_parser.ts @@ -8,7 +8,6 @@ type Default = Dict; export interface Options { boolean?: Arrayable; string?: Arrayable; - number?: Arrayable; alias?: Dict>; default?: Dict; unknown?(flag: string): void; @@ -30,15 +29,15 @@ function toVal(out, key, val, opts) { ? "" : String(val) : typeof val === "boolean" - ? val - : ~opts.boolean.indexOf(key) - ? val === "false" - ? false - : val === "true" || - (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) - : ((x = +val), x * 0 === 0) - ? x - : val; + ? val + : ~opts.boolean.indexOf(key) + ? val === "false" + ? false + : val === "true" || + (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) + : ((x = +val), x * 0 === 0) + ? x + : val; out[key] = old == undefined ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt]; } @@ -65,7 +64,6 @@ export function parseRawArgs( opts.alias = opts.alias || {}; opts.string = toArr(opts.string); opts.boolean = toArr(opts.boolean); - opts.number = toArr(opts.number); if (alibi) { for (k in opts.alias) { @@ -83,13 +81,6 @@ export function parseRawArgs( } } - for (i = opts.number.length; i-- > 0; ) { - arr = opts.alias[opts.number[i]] || []; - for (j = arr.length; j-- > 0; ) { - opts.number.push(arr[j]); - } - } - for (i = opts.string.length; i-- > 0; ) { arr = opts.alias[opts.string[i]] || []; for (j = arr.length; j-- > 0; ) { diff --git a/src/args.ts b/src/args.ts index ed5b76d..f0640d9 100644 --- a/src/args.ts +++ b/src/args.ts @@ -23,12 +23,10 @@ export function parseArgs( continue; } // eslint-disable-next-line unicorn/prefer-switch - if (arg.type === "string") { + if (arg.type === "string" || arg.type === "number") { parseOptions.string.push(arg.name); } else if (arg.type === "boolean") { parseOptions.boolean.push(arg.name); - } else if (arg.type === "number") { - parseOptions.number.push(arg.name); } if (arg.default !== undefined) { parseOptions.default[arg.name] = arg.default; From d064adfdcdf2a6d4d9009d1a40ef4c516584382f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:12:36 +0000 Subject: [PATCH 5/8] chore: apply automated lint fixes --- src/_parser.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/_parser.ts b/src/_parser.ts index bd2f6e6..2a53a0f 100644 --- a/src/_parser.ts +++ b/src/_parser.ts @@ -29,15 +29,15 @@ function toVal(out, key, val, opts) { ? "" : String(val) : typeof val === "boolean" - ? val - : ~opts.boolean.indexOf(key) - ? val === "false" - ? false - : val === "true" || - (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) - : ((x = +val), x * 0 === 0) - ? x - : val; + ? val + : ~opts.boolean.indexOf(key) + ? val === "false" + ? false + : val === "true" || + (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) + : ((x = +val), x * 0 === 0) + ? x + : val; out[key] = old == undefined ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt]; } From e7854e13ae0ffbfb37c41ce80e0ab6e3f0bc99a8 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:14:15 +0000 Subject: [PATCH 6/8] chore: apply automated lint fixes --- src/_parser.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/_parser.ts b/src/_parser.ts index bd2f6e6..2a53a0f 100644 --- a/src/_parser.ts +++ b/src/_parser.ts @@ -29,15 +29,15 @@ function toVal(out, key, val, opts) { ? "" : String(val) : typeof val === "boolean" - ? val - : ~opts.boolean.indexOf(key) - ? val === "false" - ? false - : val === "true" || - (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) - : ((x = +val), x * 0 === 0) - ? x - : val; + ? val + : ~opts.boolean.indexOf(key) + ? val === "false" + ? false + : val === "true" || + (out._.push(((x = +val), x * 0 === 0) ? x : val), !!val) + : ((x = +val), x * 0 === 0) + ? x + : val; out[key] = old == undefined ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt]; } From 1ad92701754934209ff2b7ff309010378206a4d3 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 18 Mar 2024 15:24:26 +0100 Subject: [PATCH 7/8] leftover from merge --- src/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index b498913..56bf645 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,9 +21,7 @@ export type _ArgDef = { export type BooleanArgDef = Omit<_ArgDef<"boolean", boolean>, "options">; export type StringArgDef = Omit<_ArgDef<"string", string>, "options">; export type NumberArgDef = Omit<_ArgDef<"number", boolean>, "options">; - export type EnumArgDef = _ArgDef<"enum", string>; - export type PositionalArgDef = Omit< _ArgDef<"positional", string>, "alias" | "options" From 7628a033aad39fbca93c4cd93e0b1d5133e08dd2 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 18 Mar 2024 15:36:38 +0100 Subject: [PATCH 8/8] update --- playground/hello.ts | 20 ++++++++++++++------ src/args.ts | 12 ++++++++++++ src/types.ts | 1 + 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/playground/hello.ts b/playground/hello.ts index a8b552f..f49be7f 100644 --- a/playground/hello.ts +++ b/playground/hello.ts @@ -20,20 +20,28 @@ const command = defineCommand({ age: { type: "number", description: "Your age", - required: true, + required: false, }, - adjective: { + adj: { type: "enum", description: "Adjective to use in greeting", options: ["awesome", "cool", "nice"], default: "awesome", - require: false, + required: false, }, }, run({ args }) { - consola.log( - `${args.friendly ? "Hi" : "Greetings"} ${args.adjective} ${args.name}! You are ${args.age} years old.`, - ); + consola.log(args); + const msg = [ + args.friendly ? "Hi" : "Greetings", + args.adj || "", + args.name, + args.age ? `You are ${args.age} years old.` : "", + ] + .filter(Boolean) + .join(" "); + + consola.log(msg); }, }); diff --git a/src/args.ts b/src/args.ts index 6fb60ea..3d86757 100644 --- a/src/args.ts +++ b/src/args.ts @@ -50,6 +50,7 @@ export function parseArgs( }); for (const [, arg] of args.entries()) { + // eslint-disable-next-line unicorn/prefer-switch if (arg.type === "positional") { const nextPositionalArgument = positionalArguments.shift(); if (nextPositionalArgument !== undefined) { @@ -75,6 +76,17 @@ export function parseArgs( "EARG", ); } + } else if (arg.type === "number") { + const _originalValue = parsedArgsProxy[arg.name]; + parsedArgsProxy[arg.name] = Number.parseFloat( + parsedArgsProxy[arg.name] as string, + ); + if (Number.isNaN(parsedArgsProxy[arg.name])) { + throw new CLIError( + `Invalid value for argument: \`--${arg.name}\` (\`${_originalValue}\`). Expected a number.`, + "EARG", + ); + } } else if (arg.required && parsedArgsProxy[arg.name] === undefined) { throw new CLIError(`Missing required argument: --${arg.name}`, "EARG"); } diff --git a/src/types.ts b/src/types.ts index 56bf645..6e9387b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -30,6 +30,7 @@ export type PositionalArgDef = Omit< export type ArgDef = | BooleanArgDef | StringArgDef + | NumberArgDef | PositionalArgDef | EnumArgDef;