Skip to content

Commit f663324

Browse files
committed
feat(prompts): add new methods to workflow
1 parent 9f6914a commit f663324

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
@@ -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>>;
@@ -821,42 +827,108 @@ type NextWorkflowBuilder<
821827
TKey extends string,
822828
TResult,
823829
> = WorkflowBuilder<
824-
{
825-
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
826-
} & {
827-
[Key in TKey]: TResult;
828-
}
830+
Prettify<
831+
{
832+
[Key in keyof TResults]: Key extends TKey ? TResult : TResults[Key];
833+
} & {
834+
[Key in TKey as undefined extends TResult ? never : TKey]: TResult;
835+
} & {
836+
[Key in TKey as undefined extends TResult ? TKey : never]?: TResult;
837+
}
838+
>
829839
>;
830840

841+
type WorkflowStep<TName extends string, TResults, TResult = unknown> = {
842+
name: TName;
843+
prompt: PromptWithOptions<TResults, TResult>;
844+
setResult: boolean;
845+
condition?: PromptWithOptions<TResults, boolean>;
846+
};
847+
831848
class WorkflowBuilder<TResults extends Record<string, unknown> = {}> {
832849
private results: TResults = {} as TResults;
833-
private prompts: Record<string, PromptWithOptions<TResults, unknown>> = {};
850+
private steps: WorkflowStep<string, TResults>[] = [];
834851
private cancelCallback: PromptWithOptions<Partial<TResults>, void> | undefined;
835852

836-
public step<TKey extends string, TResult>(
837-
key: TKey extends keyof TResults ? never : TKey,
853+
public step<TName extends string, TResult>(
854+
name: TName extends keyof TResults ? never : TName,
838855
prompt: PromptWithOptions<TResults, TResult>
839-
): NextWorkflowBuilder<TResults, TKey, TResult> {
840-
this.prompts[key] = prompt;
841-
return this as NextWorkflowBuilder<TResults, TKey, TResult>;
856+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
857+
this.steps.push({ name, prompt, setResult: true });
858+
return this as any;
859+
}
860+
861+
public conditionalStep<TName extends string, TResult>(
862+
name: TName,
863+
condition: PromptWithOptions<TResults, boolean>,
864+
prompt: PromptWithOptions<TResults, TResult>
865+
): NextWorkflowBuilder<
866+
TResults,
867+
TName,
868+
| (TName extends keyof TResults ? TResults[TName] : never)
869+
| PromptAwaitedReturn<TResult>
870+
| undefined
871+
> {
872+
this.steps.push({ name, prompt, condition, setResult: true });
873+
return this as any;
874+
}
875+
876+
public forkStep<TName extends string, TResult extends Record<string, unknown>>(
877+
name: TName,
878+
condition: PromptWithOptions<TResults, boolean>,
879+
subWorkflow: PromptWithOptions<TResults, WorkflowBuilder<TResult>>
880+
): NextWorkflowBuilder<
881+
TResults,
882+
TName,
883+
(TName extends keyof TResults ? TResults[TName] : never) | TResult | undefined
884+
> {
885+
this.steps.push({
886+
name,
887+
prompt: ({ results }) => {
888+
return subWorkflow({ results }).run();
889+
},
890+
condition,
891+
setResult: true,
892+
});
893+
return this as any;
894+
}
895+
896+
public logStep(
897+
name: string,
898+
prompt: PromptWithOptions<TResults, void>
899+
): WorkflowBuilder<TResults> {
900+
this.steps.push({ name, prompt, setResult: false });
901+
return this;
902+
}
903+
904+
public customStep<TName extends string, TResult>(
905+
step: WorkflowStep<TName, TResults, TResult>
906+
): NextWorkflowBuilder<TResults, TName, PromptAwaitedReturn<TResult>> {
907+
this.steps.push(step);
908+
return this as any;
842909
}
843910

844911
public onCancel(cb: PromptWithOptions<Partial<TResults>, void>): WorkflowBuilder<TResults> {
845912
this.cancelCallback = cb;
846913
return this;
847914
}
848915

849-
public async run(): Promise<PromptGroupAwaitedReturn<TResults>> {
850-
for (const [key, prompt] of Object.entries(this.prompts)) {
851-
const result = await prompt({ results: this.results as any });
916+
public async run(): Promise<TResults> {
917+
for (const step of this.steps) {
918+
if (step.condition && !step.condition({ results: this.results as any })) {
919+
continue;
920+
}
921+
const result = await step.prompt({ results: this.results as any });
852922
if (isCancel(result)) {
853923
this.cancelCallback?.({ results: this.results as any });
854924
continue;
855925
}
856-
//@ts-ignore
857-
this.results[key] = result;
926+
if (step.setResult) {
927+
//@ts-ignore
928+
this.results[step.name] = result;
929+
}
858930
}
859-
return this.results as PromptGroupAwaitedReturn<TResults>;
931+
return this.results;
860932
}
861933
}
862934

0 commit comments

Comments
 (0)