@visulima/cerebro
Version:
A delightful toolkit for building cross-runtime CLIs for Node.js, Deno, and Bun.
137 lines (136 loc) • 6.06 kB
TypeScript
import type { OptionDefinition as BaseOptionDefinition } from "@visulima/command-line-args";
import type { Content } from "./command-line-usage.d.ts";
import type { Toolbox as IToolbox } from "./toolbox.d.ts";
type TypeConstructor<T> = (value: unknown) => T extends (infer R)[] ? R | undefined : T | undefined;
/**
* Type constructor for environment variables.
* Environment variables are always strings (or undefined), so the transform function receives string | undefined.
*/
type EnvTypeConstructor<T> = (value: string | undefined) => T extends (infer R)[] ? R | undefined : T | undefined;
type MultiplePropertyOptions<T> = T extends ReadonlyArray<unknown> ? {
lazyMultiple: true;
} | {
multiple: true;
} : unknown;
export type OptionDefinition<T> = MultiplePropertyOptions<T> & Omit<BaseOptionDefinition, "type|defaultValue"> & {
__camelCaseName__?: string;
__negated__?: true;
/**
* A string or array of strings indicating the conflicting option(s).
* Note: The default value for an option does not cause a conflict.
*/
conflicts?: string[] | string;
/** An initial value for the option. */
defaultValue?: T | undefined;
/** A string describing the option. */
description?: string | undefined;
/** Option is hidden from help */
hidden?: boolean;
implies?: Record<string, unknown>;
/** Specifies whether the variable is required. */
required?: boolean;
/**
* A setter function (you receive the output from this) enabling you to be specific about the type and value received. Typical values
* are `String`, `Number` and `Boolean` but you can use a custom function.
*/
type?: TypeConstructor<T> | undefined;
/** A string to replace the default type string (e.g. <string>). It's often more useful to set a more descriptive type label, like <ms>, <files>, <command>, etc.. */
typeLabel?: string | undefined;
};
export type PossibleOptionDefinition<OD> = OD | OptionDefinition<boolean[]> | OptionDefinition<boolean> | OptionDefinition<number[]> | OptionDefinition<number> | OptionDefinition<string[]> | OptionDefinition<string>;
export type ArgumentDefinition<T = unknown> = Omit<OptionDefinition<T>, "multiple|lazyMultiple|defaultOption|alias|group|defaultValue">;
/**
* Environment variable definition for commands.
* Used to document and provide type-safe access to environment variables a command supports.
* @template T The type of the environment variable value
*/
export interface EnvDefinition<T = string> {
/** Default value if the environment variable is not set */
defaultValue?: T | undefined;
/** A description of what the environment variable does */
description?: string | undefined;
/** Environment variable is hidden from help */
hidden?: boolean;
/** The name of the environment variable */
name: string;
/**
* A transform function to convert the string environment variable value to the desired type.
* Typical values are `String`, `Number`, `Boolean` or custom functions.
* The function receives `string | undefined` and should return the transformed value.
*/
type?: EnvTypeConstructor<T> | undefined;
/** A string to replace the default type string (e.g. <string>). Useful for more descriptive type labels. */
typeLabel?: string | undefined;
}
export type PossibleEnvDefinition = EnvDefinition<boolean> | EnvDefinition<number> | EnvDefinition<string>;
/**
* Command interface with type-safe options and environment variables.
* @template O - The option definition type
* @template TContext - The toolbox context type (allows custom typing for better autocomplete)
* @example
* ```typescript
* // Define your options type for autocomplete
* type BuildOptions = {
* output?: string;
* production?: boolean;
* watch?: boolean;
* };
*
* type BuildEnv = {
* apiKey?: string;
* debug?: boolean;
* };
*
* cli.addCommand({
* name: "build",
* options: [
* { name: "output", type: String, alias: "o" },
* { name: "production", type: Boolean },
* { name: "watch", type: Boolean }
* ],
* env: [
* { name: "API_KEY", type: String },
* { name: "DEBUG", type: Boolean }
* ],
* execute: ({ options, env }: Toolbox<Console, BuildOptions, BuildEnv>) => {
* // Full autocomplete on options and env!
* console.log(options.output, options.production, env.apiKey);
* }
* });
* ```
*/
export interface Command<O extends OptionDefinition<unknown> = OptionDefinition<unknown>, TLogger extends Console = Console, TContext extends IToolbox<TLogger> = IToolbox<TLogger>> {
/**
* @internal
*/
__conflictingOptions__?: PossibleOptionDefinition<O>[];
/**
* @internal
*/
__requiredOptions__?: PossibleOptionDefinition<O>[];
/** Potential other names for this command */
alias?: string[] | string;
/** Positional argument */
argument?: ArgumentDefinition;
/** The command path, an array that describes how to get to this command */
commandPath?: string[];
/** A tweet-sized summary of your command */
description?: string;
/** Environment variables supported by this command */
env?: (EnvDefinition<boolean> | EnvDefinition<number> | EnvDefinition<string>)[];
/** The full command examples, can be multiple lines */
examples?: string[] | string[][];
/** The function for running your command, can be async */
execute: ((toolbox: TContext) => Promise<void>) | ((toolbox: TContext) => void);
/** The path to the file name for this command. */
file?: string;
/** Group commands together under a heading */
group?: string;
/** Should your command be shown in the listings */
hidden?: boolean;
/** The name of your command */
name: string;
options?: (O | OptionDefinition<boolean[]> | OptionDefinition<boolean> | OptionDefinition<number[]> | OptionDefinition<number> | OptionDefinition<string[]> | OptionDefinition<string>)[];
usage?: Content[];
}
export {};