import { ConfirmPrompt, isCancel, MultiSelectPrompt, SelectPrompt, TextPrompt, } from "@clack/core"; import { createLogUpdate } from "log-update"; import { blue, bold, brandColor, dim, gray, white } from "./colors"; import { CancelError } from "./error"; import SelectRefreshablePrompt from "./select-list"; import { stdout } from "./streams"; import { cancel, crash, logRaw, newline, shapes, space, status, stripAnsi, } from "./index"; import type { OptionWithDetails } from "./select-list"; import type { Prompt } from "@clack/core"; // logUpdate writes text to a TTY (it uses escape sequences to move the cursor // and clear lines). This function should not be used when running // non-interactively. const logUpdate = createLogUpdate(stdout); export type Arg = string | boolean | string[] | undefined | number; export const grayBar = gray(shapes.bar); export const blCorner = gray(shapes.corners.bl); export const leftT = gray(shapes.leftT); export type Option = { label: string; // user-visible string sublabel?: string; // user-visible string description?: string; value: string; // underlying key hidden?: boolean; activeIcon?: string; inactiveIcon?: string; }; export type BasePromptConfig = { // Displayed to the user while prompting for this input question: string; // Further clarifies the question helpText?: string; // The value to use by default defaultValue?: Arg; // The error message to display if the initial value is invalid initialErrorMessage?: string | null; // Accept the initialValue/defaultValue as if the user pressed ENTER when prompted acceptDefault?: boolean; // The status label to be shown after submitting label: string; // Pretty-prints the value in the interactive prompt format?: (value: Arg) => string; // Returns a user displayed error if the value is invalid validate?: (value: Arg) => string | Error | undefined; // Override some/all renderers (can be used for custom renderers before hoisting back into shared code) renderers?: Partial>; // Whether to throw an error if the prompt is crashed or cancelled throwOnError?: boolean; }; export type TextPromptConfig = BasePromptConfig & { type: "text"; initialValue?: string; }; export type BaseSelectPromptConfig = BasePromptConfig & { options: Option[]; maxItemsPerPage?: number; }; export type SelectPromptConfig = BaseSelectPromptConfig & { type: "select"; }; export type MultiSelectPromptConfig = BaseSelectPromptConfig & { type: "multiselect"; }; export type ConfirmPromptConfig = BasePromptConfig & { type: "confirm"; activeText?: string; inactiveText?: string; }; export type ListPromptConfig = BasePromptConfig & { type: "list"; options: OptionWithDetails[]; onRefresh?: () => Promise; }; export type PromptConfig = | TextPromptConfig | ConfirmPromptConfig | SelectPromptConfig | MultiSelectPromptConfig | ListPromptConfig; type RenderProps = | Omit, "prompt"> | Omit, "prompt"> | Omit | Omit | Omit; function acceptDefault( promptConfig: PromptConfig, renderers: Pick, "submit">, initialValue: T ): T { const error = promptConfig.validate?.(initialValue as Arg); if (error) { const errorMessage = error instanceof Error ? error.message : error; if (promptConfig.throwOnError) { throw new Error(errorMessage); } else { crash(errorMessage); } } const lines = renderers.submit({ value: initialValue as Arg }); logRaw(lines.join("\n")); return initialValue as T; } export const inputPrompt = async ( promptConfig: PromptConfig ): Promise => { const renderers = { ...getRenderers(promptConfig), ...promptConfig.renderers, }; let prompt: | SelectPrompt