Skip to content

Commit

Permalink
feat: support enum arg type (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
Barbapapazes authored and pi0 committed Mar 18, 2024
1 parent 34c52a2 commit 02f4fdb
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 6 deletions.
9 changes: 8 additions & 1 deletion playground/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ const command = defineCommand({
type: "boolean",
description: "Use friendly greeting",
},
adjective: {
type: "enum",
description: "Adjective to use in greeting",
options: ["awesome", "cool", "nice"],
default: "awesome",
require: false,
}
},
run({ args }) {
consola.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
consola.log(`${args.friendly ? "Hi" : "Greetings"} ${args.adjective} ${args.name}!`);
},
});

Expand Down
18 changes: 18 additions & 0 deletions src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
const parseOptions = {
boolean: [] as string[],
string: [] as string[],
enum: [] as (number | string)[],
mixed: [] as string[],
alias: {} as Record<string, string | string[]>,
default: {} as Record<string, boolean | string>,
Expand All @@ -21,11 +22,15 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
if (arg.type === "positional") {
continue;
}
// 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 === "enum") {
parseOptions.enum.push(...(arg.options || []));
}

if (arg.default !== undefined) {
parseOptions.default[arg.name] = arg.default;
}
Expand Down Expand Up @@ -56,6 +61,19 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
} else {
parsedArgsProxy[arg.name] = arg.default;
}
} else if (arg.type === "enum") {
const argument = parsedArgsProxy[arg.name];
const options = arg.options || [];
if (
argument !== undefined &&
options.length > 0 &&
!options.includes(argument)
) {
throw new CLIError(
`Invalid value for argument: \`--${arg.name}\` (\`${argument}\`). Expected one of: ${options.map((o) => `\`${o}\``).join(", ")}.`,
"EARG",
);
}
} else if (arg.required && parsedArgsProxy[arg.name] === undefined) {
throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
}
Expand Down
27 changes: 22 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ----- Args -----

export type ArgType = "boolean" | "string" | "positional" | undefined;
export type ArgType = "boolean" | "string" | "positional" | "enum" | undefined;

export type _ArgDef<T extends ArgType, VT extends boolean | string> = {
type?: T;
Expand All @@ -9,12 +9,21 @@ export type _ArgDef<T extends ArgType, VT extends boolean | string> = {
alias?: string | string[];
default?: VT;
required?: boolean;
options?: (string | number)[];
};

export type BooleanArgDef = _ArgDef<"boolean", boolean>;
export type StringArgDef = _ArgDef<"string", string>;
export type PositionalArgDef = Omit<_ArgDef<"positional", string>, "alias">;
export type ArgDef = BooleanArgDef | StringArgDef | PositionalArgDef;
export type BooleanArgDef = Omit<_ArgDef<"boolean", boolean>, "options">;
export type StringArgDef = Omit<_ArgDef<"string", string>, "options">;
export type PositionalArgDef = Omit<
_ArgDef<"positional", string>,
"alias" | "options"
>;
export type EnumArgDef = _ArgDef<"enum", string>;
export type ArgDef =
| BooleanArgDef
| StringArgDef
| PositionalArgDef
| EnumArgDef;
export type ArgsDef = Record<string, ArgDef>;
export type Arg = ArgDef & { name: string; alias: string[] };

Expand All @@ -34,6 +43,14 @@ export type ParsedArgs<T extends ArgsDef = ArgsDef> = { _: string[] } & Record<
}[keyof T],
boolean
> &
Record<
{
[K in keyof T]: T[K] extends { type: "enum" } ? K : never;
}[keyof T],
{
[K in keyof T]: T[K] extends { options: infer U } ? U : never;
}[keyof T]
> &
Record<string, string | boolean | string[]>;

// ----- Command -----
Expand Down
3 changes: 3 additions & 0 deletions src/usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export async function renderUsage<T extends ArgsDef = ArgsDef>(
? `=${
arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`
}`
: "") +
(arg.type === "enum" && arg.options
? `=<${arg.options.join("|")}>`
: "");
argLines.push([
"`" + argStr + (isRequired ? " (required)" : "") + "`",
Expand Down

0 comments on commit 02f4fdb

Please sign in to comment.