Skip to content

Commit 02b7dfc

Browse files
JoshuaKGoldbergnatemoo-re
authored andcommitted
feat: added p.spinnerGroup API
1 parent 0ada41e commit 02b7dfc

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

examples/basic/spinnerGroup.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as p from '@clack/prompts';
2+
3+
p.intro('spinner groups start...');
4+
5+
const s = p.spinner();
6+
s.start('example start');
7+
await new Promise((resolve) => setTimeout(resolve, 500));
8+
s.stop('example stopped');
9+
10+
await p.spinnerGroup('Outer group', [
11+
[
12+
'First sub-task',
13+
async () => {
14+
await new Promise((resolve) => setTimeout(resolve, 1000));
15+
},
16+
],
17+
[
18+
'Second sub-task',
19+
async () => {
20+
if (process.env.THROW_ERROR) {
21+
throw new Error(process.env.THROW_ERROR);
22+
}
23+
await new Promise((resolve) => setTimeout(resolve, 1000));
24+
},
25+
],
26+
[
27+
'Third sub-task',
28+
async () => {
29+
await new Promise((resolve) => setTimeout(resolve, 1000));
30+
},
31+
],
32+
]);
33+
34+
p.outro('spinner group stop...');

packages/prompts/src/index.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from '@clack/core';
1313
import isUnicodeSupported from 'is-unicode-supported';
1414
import color from 'picocolors';
15+
import readline from 'readline';
1516
import { cursor, erase } from 'sisteransi';
1617

1718
export { isCancel } from '@clack/core';
@@ -740,6 +741,14 @@ export const spinner = () => {
740741
};
741742
};
742743

744+
function stepForCode(code: number) {
745+
return code === 0
746+
? color.green(S_STEP_SUBMIT)
747+
: code === 1
748+
? color.red(S_STEP_CANCEL)
749+
: color.red(S_STEP_ERROR);
750+
}
751+
743752
// Adapted from https://github.com/chalk/ansi-regex
744753
// @see LICENSE
745754
function ansiRegex() {
@@ -751,6 +760,53 @@ function ansiRegex() {
751760
return new RegExp(pattern, 'g');
752761
}
753762

763+
export type SpinnerGroup = [message: string, run: () => Promise<void>];
764+
765+
export const spinnerGroup = async (outerMessage: string, groups: SpinnerGroup[]) => {
766+
process.stdout.write(`${color.gray(S_BAR)}\n ${S_BAR_START} ${outerMessage}\n`);
767+
768+
const s = spinner();
769+
let caught: [group: SpinnerGroup, error: unknown] | undefined;
770+
771+
for (const [message, run] of groups) {
772+
const line = `${color.gray(S_BAR)} ${message}`;
773+
readline.clearLine(process.stdout, -1);
774+
readline.moveCursor(process.stdout, -999, -1);
775+
776+
s.start(line);
777+
await run().catch((error) => {
778+
caught = [[message, run], error];
779+
});
780+
s.stop(line);
781+
782+
if (caught) {
783+
break;
784+
}
785+
}
786+
787+
if (caught) {
788+
readline.moveCursor(process.stdout, -999, -1);
789+
process.stdout.write(
790+
[
791+
color.gray(S_BAR),
792+
'',
793+
stepForCode(1),
794+
caught[0][0],
795+
color.gray('>'),
796+
(caught[1] as any).message || caught[1],
797+
].join(' ')
798+
);
799+
} else {
800+
readline.moveCursor(process.stdout, -999, -groups.length - 1);
801+
readline.clearScreenDown(process.stdout);
802+
process.stdout.write(`${stepForCode(0)} ${outerMessage}`);
803+
}
804+
805+
process.stdout.write('\n');
806+
807+
return caught;
808+
};
809+
754810
export type PromptGroupAwaitedReturn<T> = {
755811
[P in keyof T]: Exclude<Awaited<T[P]>, symbol>;
756812
};

pnpm-lock.yaml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)