Skip to content

Commit b17310c

Browse files
committed
feat(prompts): add new methods to workflow
1 parent a876a91 commit b17310c

File tree

2 files changed

+118
-47
lines changed

2 files changed

+118
-47
lines changed

examples/basic/workflow.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,32 @@ import * as p from '@clack/prompts';
2525
initialValue: false,
2626
})
2727
)
28-
.step('fork', ({ results }) => {
29-
if (results.install === true) {
30-
return p
31-
.workflow()
32-
.step('package', () =>
33-
p.select({
34-
message: 'Pick a package manager:',
35-
initialValue: 'pnpm',
36-
options: [
37-
{
38-
label: 'npm',
39-
value: 'npm',
40-
},
41-
{
42-
label: 'yarn',
43-
value: 'yarn',
44-
},
45-
{
46-
label: 'pnpm',
47-
value: 'pnpm',
48-
},
49-
],
50-
})
51-
)
52-
.run();
28+
.forkStep(
29+
'fork',
30+
({ results }) => results.install,
31+
({ results }) => {
32+
return p.workflow().step('package', () =>
33+
p.select({
34+
message: 'Pick a package manager:',
35+
initialValue: 'pnpm',
36+
options: [
37+
{
38+
label: 'npm',
39+
value: 'npm',
40+
},
41+
{
42+
label: 'yarn',
43+
value: 'yarn',
44+
},
45+
{
46+
label: 'pnpm',
47+
value: 'pnpm',
48+
},
49+
],
50+
})
51+
);
5352
}
54-
})
53+
)
5554
.run();
5655

5756
await p

packages/prompts/src/index.ts

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -734,13 +734,19 @@ type Prettify<T> = {
734734
[P in keyof T]: T[P];
735735
} & {};
736736

737+
export type PromptAwaitedReturn<T> = Exclude<Awaited<T>, symbol>;
738+
737739
export type PromptGroupAwaitedReturn<T> = Prettify<{
738-
[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;
740+
[P in keyof T]: PromptAwaitedReturn<T[P]>;
739741
}>;
740742

741-
export type PromptWithOptions<TResults, TReturn> = (opts: {
742-
results: PromptGroupAwaitedReturn<TResults>;
743-
}) => TReturn;
743+
export type PromptWithOptions<TResults, TResult, TOptions extends Record<string, unknown> = {}> = (
744+
opts: Prettify<
745+
{
746+
results: PromptGroupAwaitedReturn<TResults>;
747+
} & TOptions
748+
>
749+
) => TResult;
744750

745751
export type PromptGroup<T> = {
746752
[P in keyof T]: PromptWithOptions<Partial<Omit<T, P>>, void | Promise<T[P] | void>>;
@@ -791,42 +797,108 @@ type NextWorkflowBuilder<
791797
TKey extends string,
792798
TResult,
793799
> = WorkflowBuilder<
794-
{
795-
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
796-
} & {
797-
[Key in TKey]: TResult;
798-
}
800+
Prettify<
801+
{
802+
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
803+
} & {
804+
[Key in TKey as undefined extends TResult ? never : TKey]: TResult;
805+
} & {
806+
[Key in TKey as undefined extends TResult ? TKey : never]?: TResult;
807+
}
808+
>
799809
>;
800810

811+
type WorkflowStep<TName extends string, TResults, TResult = unknown> = {
812+
name: TName;
813+
prompt: PromptWithOptions<TResults, TResult>;
814+
setResult: boolean;
815+
condition?: PromptWithOptions<TResults, boolean>;
816+
};
817+
801818
class WorkflowBuilder<TResults extends Record<string, unknown> = {}> {
802819
private results: TResults = {} as TResults;
803-
private prompts: Record<string, PromptWithOptions<TResults, unknown>> = {};
820+
private steps: WorkflowStep<string, TResults>[] = [];
804821
private cancelCallback: PromptWithOptions<Partial<TResults>, void> | undefined;
805822

806-
public step<TKey extends string, TResult>(
807-
key: TKey extends keyof TResults ? never : TKey,
823+
public step<TName extends string, TResult>(
824+
name: TName extends keyof TResults ? never : TName,
808825
prompt: PromptWithOptions<TResults, TResult>
809-
): NextWorkflowBuilder<TResults, TKey, TResult> {
810-
this.prompts[key] = prompt;
811-
return this as NextWorkflowBuilder<TResults, TKey, TResult>;
826+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
827+
this.steps.push({ name, prompt, setResult: true });
828+
return this as any;
829+
}
830+
831+
public conditionalStep<TName extends string, TResult>(
832+
name: TName,
833+
condition: PromptWithOptions<TResults, boolean>,
834+
prompt: PromptWithOptions<TResults, TResult>
835+
): NextWorkflowBuilder<
836+
TResults,
837+
TName,
838+
| (TName extends keyof TResults ? TResults[TName] : never)
839+
| PromptAwaitedReturn<TResult>
840+
| undefined
841+
> {
842+
this.steps.push({ name, prompt, condition, setResult: true });
843+
return this as any;
844+
}
845+
846+
public forkStep<TName extends string, TResult extends Record<string, unknown>>(
847+
name: TName,
848+
condition: PromptWithOptions<TResults, boolean>,
849+
subWorkflow: PromptWithOptions<TResults, WorkflowBuilder<TResult>>
850+
): NextWorkflowBuilder<
851+
TResults,
852+
TName,
853+
(TName extends keyof TResults ? TResults[TName] : never) | TResult | undefined
854+
> {
855+
this.steps.push({
856+
name,
857+
prompt: ({ results }) => {
858+
return subWorkflow({ results }).run();
859+
},
860+
condition,
861+
setResult: true,
862+
});
863+
return this as any;
864+
}
865+
866+
public logStep(
867+
name: string,
868+
prompt: PromptWithOptions<TResults, void>
869+
): WorkflowBuilder<TResults> {
870+
this.steps.push({ name, prompt, setResult: false });
871+
return this;
872+
}
873+
874+
public customStep<TName extends string, TResult>(
875+
step: WorkflowStep<TName, TResults, TResult>
876+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
877+
this.steps.push(step);
878+
return this as any;
812879
}
813880

814881
public onCancel(cb: PromptWithOptions<Partial<TResults>, void>): WorkflowBuilder<TResults> {
815882
this.cancelCallback = cb;
816883
return this;
817884
}
818885

819-
public async run(): Promise<PromptGroupAwaitedReturn<TResults>> {
820-
for (const [key, prompt] of Object.entries(this.prompts)) {
821-
const result = await prompt({ results: this.results as any });
886+
public async run(): Promise<TResults> {
887+
for (const step of this.steps) {
888+
if (step.condition && !step.condition({ results: this.results as any })) {
889+
continue;
890+
}
891+
const result = await step.prompt({ results: this.results as any });
822892
if (isCancel(result)) {
823893
this.cancelCallback?.({ results: this.results as any });
824894
continue;
825895
}
826-
//@ts-ignore
827-
this.results[key] = result;
896+
if (step.setResult) {
897+
//@ts-ignore
898+
this.results[step.name] = result;
899+
}
828900
}
829-
return this.results as PromptGroupAwaitedReturn<TResults>;
901+
return this.results;
830902
}
831903
}
832904

0 commit comments

Comments
 (0)