/** * Weird AI-generated types below. There might be better ways to do this, but it works. */ // Reduces an object to a string union of only the keys that are required // Example: { a: string, b?: number, c: string } -> 'a' | 'c' type RequiredKeys = keyof { [K in keyof T as T[K] extends Required[K] ? K : never]: T[K]; }; // Reduces an object to a string union of only the keys that are optional // Example: { a: string, b?: number, c?: string } -> 'b' | 'c' type OptionalKeys = keyof { [K in keyof T as T[K] extends Required[K] ? never : K]: T[K]; }; // Reduces an object to a string union of the optional keys with the -- prefix // Example: { apple: string, banana?: number, cherry?: string } -> '--banana' | '--cherry' type LongFlags = OptionalKeys extends infer K ? K extends string ? `--${K}` : never : never; // Reduces a string to the first character // Example: 'banana' -> 'b' type FirstChar = S extends `${infer F}${string}` ? F : never; // Reduces an object to a string union of the first character of the optional keys with the - prefix // Example: { apple: string, banana?: number, cherry?: string } -> '-b' | '-c' type ShortFlags = OptionalKeys extends infer K ? K extends string ? `-${FirstChar}` : never : never; // A function that handles the value of a flag, usually a type constructor like Number, Boolean, etc. type Handler = ( value: string, name: string, previousValue?: T ) => T; // Any string prefixed with a dash type DashPrefixed = `-${string}`; // A type that defines the configuration of the command arguments // Also supports aliases // Example: { apple: string, banana?: number, cherry?: string } -> { '--banana': Number, '--cherry': String, '-b': '--banana', '-c': '--cherry' } type CommandArgsConfigType = { [K in LongFlags]?: Handler | [Handler]; } | { [K in Exclude>]?: LongFlags; }; /** * A type that defines the options of the command */ export interface CommandOptions { /** * The string rendered when a user runs `auditboard help ` * Supports markdown */ help: string; /** * The string rendered to describe the command when a user runs `auditboard list` */ description: string; /** * This is where you put required params, which are parsed positionally * * @example * // "./auditboard make:command users -o 11" would be supported with this required config: * [ * // Can be named anything, the resulting value will be the first positional argument because this is the first object in the array * { * name: 'parameterNameThatIChoose', // <-- this value will be "users" * message: 'parameterNameThatIChoose is required', // <-- (optional) failure message if param does not exist * } * ] */ required?: Array<{ name: RequiredKeys; message?: string }>; /** * This is where you put optional params, which are parsed as flags. The normal pattern is to use a long flag as the defining flag, and a short flag as an alias. * The value of the long flag property will be a type constructor like Number, Boolean, etc. * * @example * // "./auditboard make:command users -o 11" would be supported with this args config: * { * '--option': Number, * '-o': '--option', * } * // The above would come through with the value of 11 in the property 'option' */ args: CommandArgsConfigType; } /** * Defines the shape of the method that will execute your command. * Supports sync and async methods. * @param args The arguments of the command * @returns A string or a promise that resolves to a string */ export type CommandMethod = (args: T) => string | Promise | void | Promise; /** * Defines the expected shape of what you export from your command module * @param command The method that will execute your command * @param commandOptions The options of the command */ export interface CommandModule { command: CommandMethod; commandOptions: CommandOptions; }