Skip to content

Commit e408416

Browse files
committed
feat(prompts): add new methods to workflow
1 parent 67e471c commit e408416

File tree

2 files changed

+118
-47
lines changed

2 files changed

+118
-47
lines changed

examples/basic/workflow.ts

+25-26
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

+93-21
Original file line numberDiff line numberDiff line change
@@ -778,13 +778,19 @@ type Prettify<T> = {
778778
[P in keyof T]: T[P];
779779
} & {};
780780

781+
export type PromptAwaitedReturn<T> = Exclude<Awaited<T>, symbol>;
782+
781783
export type PromptGroupAwaitedReturn<T> = Prettify<{
782-
[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;
784+
[P in keyof T]: PromptAwaitedReturn<T[P]>;
783785
}>;
784786

785-
export type PromptWithOptions<TResults, TReturn> = (opts: {
786-
results: PromptGroupAwaitedReturn<TResults>;
787-
}) => TReturn;
787+
export type PromptWithOptions<TResults, TResult, TOptions extends Record<string, unknown> = {}> = (
788+
opts: Prettify<
789+
{
790+
results: PromptGroupAwaitedReturn<TResults>;
791+
} & TOptions
792+
>
793+
) => TResult;
788794

789795
export type PromptGroup<T> = {
790796
[P in keyof T]: PromptWithOptions<Partial<Omit<T, P>>, void | Promise<T[P] | void>>;
@@ -865,42 +871,108 @@ type NextWorkflowBuilder<
865871
TKey extends string,
866872
TResult,
867873
> = WorkflowBuilder<
868-
{
869-
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
870-
} & {
871-
[Key in TKey]: TResult;
872-
}
874+
Prettify<
875+
{
876+
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
877+
} & {
878+
[Key in TKey as undefined extends TResult ? never : TKey]: TResult;
879+
} & {
880+
[Key in TKey as undefined extends TResult ? TKey : never]?: TResult;
881+
}
882+
>
873883
>;
874884

885+
type WorkflowStep<TName extends string, TResults, TResult = unknown> = {
886+
name: TName;
887+
prompt: PromptWithOptions<TResults, TResult>;
888+
setResult: boolean;
889+
condition?: PromptWithOptions<TResults, boolean>;
890+
};
891+
875892
class WorkflowBuilder<TResults extends Record<string, unknown> = {}> {
876893
private results: TResults = {} as TResults;
877-
private prompts: Record<string, PromptWithOptions<TResults, unknown>> = {};
894+
private steps: WorkflowStep<string, TResults>[] = [];
878895
private cancelCallback: PromptWithOptions<Partial<TResults>, void> | undefined;
879896

880-
public step<TKey extends string, TResult>(
881-
key: TKey extends keyof TResults ? never : TKey,
897+
public step<TName extends string, TResult>(
898+
name: TName extends keyof TResults ? never : TName,
882899
prompt: PromptWithOptions<TResults, TResult>
883-
): NextWorkflowBuilder<TResults, TKey, TResult> {
884-
this.prompts[key] = prompt;
885-
return this as NextWorkflowBuilder<TResults, TKey, TResult>;
900+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
901+
this.steps.push({ name, prompt, setResult: true });
902+
return this as any;
903+
}
904+
905+
public conditionalStep<TName extends string, TResult>(
906+
name: TName,
907+
condition: PromptWithOptions<TResults, boolean>,
908+
prompt: PromptWithOptions<TResults, TResult>
909+
): NextWorkflowBuilder<
910+
TResults,
911+
TName,
912+
| (TName extends keyof TResults ? TResults[TName] : never)
913+
| PromptAwaitedReturn<TResult>
914+
| undefined
915+
> {
916+
this.steps.push({ name, prompt, condition, setResult: true });
917+
return this as any;
918+
}
919+
920+
public forkStep<TName extends string, TResult extends Record<string, unknown>>(
921+
name: TName,
922+
condition: PromptWithOptions<TResults, boolean>,
923+
subWorkflow: PromptWithOptions<TResults, WorkflowBuilder<TResult>>
924+
): NextWorkflowBuilder<
925+
TResults,
926+
TName,
927+
(TName extends keyof TResults ? TResults[TName] : never) | TResult | undefined
928+
> {
929+
this.steps.push({
930+
name,
931+
prompt: ({ results }) => {
932+
return subWorkflow({ results }).run();
933+
},
934+
condition,
935+
setResult: true,
936+
});
937+
return this as any;
938+
}
939+
940+
public logStep(
941+
name: string,
942+
prompt: PromptWithOptions<TResults, void>
943+
): WorkflowBuilder<TResults> {
944+
this.steps.push({ name, prompt, setResult: false });
945+
return this;
946+
}
947+
948+
public customStep<TName extends string, TResult>(
949+
step: WorkflowStep<TName, TResults, TResult>
950+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
951+
this.steps.push(step);
952+
return this as any;
886953
}
887954

888955
public onCancel(cb: PromptWithOptions<Partial<TResults>, void>): WorkflowBuilder<TResults> {
889956
this.cancelCallback = cb;
890957
return this;
891958
}
892959

893-
public async run(): Promise<PromptGroupAwaitedReturn<TResults>> {
894-
for (const [key, prompt] of Object.entries(this.prompts)) {
895-
const result = await prompt({ results: this.results as any });
960+
public async run(): Promise<TResults> {
961+
for (const step of this.steps) {
962+
if (step.condition && !step.condition({ results: this.results as any })) {
963+
continue;
964+
}
965+
const result = await step.prompt({ results: this.results as any });
896966
if (isCancel(result)) {
897967
this.cancelCallback?.({ results: this.results as any });
898968
continue;
899969
}
900-
//@ts-ignore
901-
this.results[key] = result;
970+
if (step.setResult) {
971+
//@ts-ignore
972+
this.results[step.name] = result;
973+
}
902974
}
903-
return this.results as PromptGroupAwaitedReturn<TResults>;
975+
return this.results;
904976
}
905977
}
906978

0 commit comments

Comments
 (0)