From f663324dd508fd0a866e15c91f31f5d5b64a80e2 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Wed, 7 Aug 2024 18:00:22 -0300 Subject: [PATCH] feat(prompts): add new methods to workflow --- examples/basic/workflow.ts | 51 ++++++++------- packages/prompts/src/index.ts | 114 +++++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 47 deletions(-) diff --git a/examples/basic/workflow.ts b/examples/basic/workflow.ts index b60805aa..917d635a 100644 --- a/examples/basic/workflow.ts +++ b/examples/basic/workflow.ts @@ -25,33 +25,32 @@ import * as p from '@clack/prompts'; initialValue: false, }) ) - .step('fork', ({ results }) => { - if (results.install === true) { - return p - .workflow() - .step('package', () => - p.select({ - message: 'Pick a package manager:', - initialValue: 'pnpm', - options: [ - { - label: 'npm', - value: 'npm', - }, - { - label: 'yarn', - value: 'yarn', - }, - { - label: 'pnpm', - value: 'pnpm', - }, - ], - }) - ) - .run(); + .forkStep( + 'fork', + ({ results }) => results.install, + ({ results }) => { + return p.workflow().step('package', () => + p.select({ + message: 'Pick a package manager:', + initialValue: 'pnpm', + options: [ + { + label: 'npm', + value: 'npm', + }, + { + label: 'yarn', + value: 'yarn', + }, + { + label: 'pnpm', + value: 'pnpm', + }, + ], + }) + ); } - }) + ) .run(); await p diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index 311cabe0..dffc0d46 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -734,13 +734,19 @@ type Prettify = { [P in keyof T]: T[P]; } & {}; +export type PromptAwaitedReturn = Exclude, symbol>; + export type PromptGroupAwaitedReturn = Prettify<{ - [P in keyof T]: Exclude, symbol>; + [P in keyof T]: PromptAwaitedReturn; }>; -export type PromptWithOptions = (opts: { - results: PromptGroupAwaitedReturn; -}) => TReturn; +export type PromptWithOptions = {}> = ( + opts: Prettify< + { + results: PromptGroupAwaitedReturn; + } & TOptions + > +) => TResult; export type PromptGroup = { [P in keyof T]: PromptWithOptions>, void | Promise>; @@ -821,24 +827,85 @@ type NextWorkflowBuilder< TKey extends string, TResult, > = WorkflowBuilder< - { - [Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key]; - } & { - [Key in TKey]: TResult; - } + Prettify< + { + [Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key]; + } & { + [Key in TKey as undefined extends TResult ? never : TKey]: TResult; + } & { + [Key in TKey as undefined extends TResult ? TKey : never]?: TResult; + } + > >; +type WorkflowStep = { + name: TName; + prompt: PromptWithOptions; + setResult: boolean; + condition?: PromptWithOptions; +}; + class WorkflowBuilder = {}> { private results: TResults = {} as TResults; - private prompts: Record> = {}; + private steps: WorkflowStep[] = []; private cancelCallback: PromptWithOptions, void> | undefined; - public step( - key: TKey extends keyof TResults ? never : TKey, + public step( + name: TName extends keyof TResults ? never : TName, prompt: PromptWithOptions - ): NextWorkflowBuilder { - this.prompts[key] = prompt; - return this as NextWorkflowBuilder; + ): NextWorkflowBuilder> { + this.steps.push({ name, prompt, setResult: true }); + return this as any; + } + + public conditionalStep( + name: TName, + condition: PromptWithOptions, + prompt: PromptWithOptions + ): NextWorkflowBuilder< + TResults, + TName, + | (TName extends keyof TResults ? TResults[TName] : never) + | PromptAwaitedReturn + | undefined + > { + this.steps.push({ name, prompt, condition, setResult: true }); + return this as any; + } + + public forkStep>( + name: TName, + condition: PromptWithOptions, + subWorkflow: PromptWithOptions> + ): NextWorkflowBuilder< + TResults, + TName, + (TName extends keyof TResults ? TResults[TName] : never) | TResult | undefined + > { + this.steps.push({ + name, + prompt: ({ results }) => { + return subWorkflow({ results }).run(); + }, + condition, + setResult: true, + }); + return this as any; + } + + public logStep( + name: string, + prompt: PromptWithOptions + ): WorkflowBuilder { + this.steps.push({ name, prompt, setResult: false }); + return this; + } + + public customStep( + step: WorkflowStep + ): NextWorkflowBuilder> { + this.steps.push(step); + return this as any; } public onCancel(cb: PromptWithOptions, void>): WorkflowBuilder { @@ -846,17 +913,22 @@ class WorkflowBuilder = {}> { return this; } - public async run(): Promise> { - for (const [key, prompt] of Object.entries(this.prompts)) { - const result = await prompt({ results: this.results as any }); + public async run(): Promise { + for (const step of this.steps) { + if (step.condition && !step.condition({ results: this.results as any })) { + continue; + } + const result = await step.prompt({ results: this.results as any }); if (isCancel(result)) { this.cancelCallback?.({ results: this.results as any }); continue; } - //@ts-ignore - this.results[key] = result; + if (step.setResult) { + //@ts-ignore + this.results[step.name] = result; + } } - return this.results as PromptGroupAwaitedReturn; + return this.results; } }