From dad68bb20050da9f868e69abead1fc06f456ea92 Mon Sep 17 00:00:00 2001 From: Edmund Hung Date: Tue, 22 Oct 2024 16:15:20 +0100 Subject: [PATCH] feat(create-cloudflare): prompt instead of terminating if the argument is invalid --- packages/cli/interactive.ts | 41 +++++++++++++++---- .../create-cloudflare/src/helpers/args.ts | 1 + 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/cli/interactive.ts b/packages/cli/interactive.ts index 8af97023e16b..e80e7480469e 100644 --- a/packages/cli/interactive.ts +++ b/packages/cli/interactive.ts @@ -59,6 +59,8 @@ export type BasePromptConfig = { renderers?: Partial>; // Whether to throw an error if the prompt is crashed or cancelled throwOnError?: boolean; + // Prompt instead of terminating if the default value is invalid (i.e. `acceptDefault` is true with invalid `defaultValue`) + promptOnDefaultInvalid?: boolean; }; export type TextPromptConfig = BasePromptConfig & { @@ -140,8 +142,24 @@ export const inputPrompt = async ( | SelectRefreshablePrompt; // Looks up the needed renderer by the current state ('initial', 'submitted', etc.) - const dispatchRender = (props: RenderProps, p: Prompt): string | void => { - const renderedLines = renderers[props.state](props, p); + const dispatchRender = ( + props: RenderProps, + p: Prompt, + initialValue: Arg + ): string | void => { + let state = props.state; + + if (state === "initial" && promptConfig.acceptDefault) { + const error = promptConfig.validate?.(initialValue); + + if (error) { + state = "error"; + props.error = error; + props.value = initialValue; + } + } + + const renderedLines = renderers[state](props, p); return renderedLines.join("\n"); }; @@ -157,7 +175,7 @@ export const inputPrompt = async ( options: promptConfig.options.filter((o) => !o.hidden), initialValue, render() { - return dispatchRender(this, prompt); + return dispatchRender(this, prompt, initialValue); }, }); } else if (promptConfig.type === "confirm") { @@ -173,7 +191,7 @@ export const inputPrompt = async ( active: promptConfig.activeText || "", inactive: promptConfig.inactiveText || "", render() { - return dispatchRender(this, prompt); + return dispatchRender(this, prompt, initialValue); }, }); } else if (promptConfig.type == "multiselect") { @@ -193,7 +211,7 @@ export const inputPrompt = async ( options: promptConfig.options, initialValues: initialValues, render() { - return dispatchRender(this, prompt); + return dispatchRender(this, prompt, initialValues); }, }); } else if (promptConfig.type === "list") { @@ -209,7 +227,7 @@ export const inputPrompt = async ( promptConfig.onRefresh ?? (() => Promise.resolve(promptConfig.options)), initialValue, render() { - return dispatchRender(this, prompt); + return dispatchRender(this, prompt, initialValue); }, }); } else { @@ -217,15 +235,20 @@ export const inputPrompt = async ( promptConfig.initialValue ?? String(promptConfig.defaultValue ?? ""); if (promptConfig.acceptDefault) { - return acceptDefault(promptConfig, renderers, initialValue as T); + if ( + !promptConfig.promptOnDefaultInvalid || + !promptConfig.validate?.(initialValue as Arg) + ) { + return acceptDefault(promptConfig, renderers, initialValue as T); + } } prompt = new TextPrompt({ ...promptConfig, - initialValue: promptConfig.initialValue, + initialValue: promptConfig.initialValue ?? initialValue, defaultValue: String(promptConfig.defaultValue ?? ""), render() { - return dispatchRender(this, prompt); + return dispatchRender(this, prompt, initialValue); }, }); } diff --git a/packages/create-cloudflare/src/helpers/args.ts b/packages/create-cloudflare/src/helpers/args.ts index a8d6639c377a..009da868b3ae 100644 --- a/packages/create-cloudflare/src/helpers/args.ts +++ b/packages/create-cloudflare/src/helpers/args.ts @@ -421,6 +421,7 @@ export const processArgument = async ( acceptDefault: promptConfig.acceptDefault ?? value !== undefined, defaultValue: value ?? promptConfig.defaultValue, throwOnError: true, + promptOnDefaultInvalid: true, }); // Update value in args before returning the result