Skip to content

Commit 1fd6e3a

Browse files
committed
feat(prompts): Merge stream and log utilities
1 parent 7a08f3d commit 1fd6e3a

File tree

8 files changed

+107
-90
lines changed

8 files changed

+107
-90
lines changed

examples/basic/log.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { setTimeout } from 'node:timers/promises';
2+
import * as p from '@clack/prompts';
3+
import color from 'picocolors';
4+
5+
async function main() {
6+
console.clear();
7+
8+
await setTimeout(1000);
9+
10+
p.intro(`${color.bgCyan(color.black(' create-app '))}`);
11+
12+
await p.log.message('Entering directory "src"');
13+
await p.log.info('No files to update');
14+
await p.log.warn('Directory is empty, skipping');
15+
await p.log.warning('Directory is empty, skipping');
16+
await p.log.error('Permission denied on file src/secret.js');
17+
await p.log.success('Installation complete');
18+
await p.log.step('Check files');
19+
await p.log.step('Line 1\nLine 2');
20+
await p.log.step(['Line 1 (array)', 'Line 2 (array)']);
21+
22+
p.outro(`Problems? ${color.underline(color.cyan('https://example.com/issues'))}`);
23+
}
24+
25+
main().catch(console.error);

examples/basic/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"jiti": "^1.17.0"
1010
},
1111
"scripts": {
12+
"log": "jiti ./log.ts",
1213
"start": "jiti ./index.ts",
1314
"stream": "jiti ./stream.ts",
1415
"progress": "jiti ./progress.ts",

examples/basic/stream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async function main() {
99

1010
p.intro(`${color.bgCyan(color.black(' create-app '))}`);
1111

12-
await p.stream.step(
12+
await p.log.step(
1313
(async function* () {
1414
for (const line of lorem) {
1515
for (const word of line.split(' ')) {

packages/prompts/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
},
5252
"dependencies": {
5353
"@clack/core": "workspace:*",
54+
"@macfja/ansi": "^1.0.0",
5455
"picocolors": "^1.0.0",
5556
"sisteransi": "^1.0.5"
5657
},

packages/prompts/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export * from './progress-bar.js';
1414
export * from './select-key.js';
1515
export * from './select.js';
1616
export * from './spinner.js';
17-
export * from './stream.js';
1817
export * from './task.js';
1918
export * from './task-log.js';
2019
export * from './text.js';

packages/prompts/src/log.ts

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { getColumns } from '@clack/core';
2+
import { wrap } from '@macfja/ansi';
13
import color from 'picocolors';
4+
import { cursor, erase } from 'sisteransi';
25
import {
36
type CommonOptions,
47
S_BAR,
@@ -13,57 +16,70 @@ export interface LogMessageOptions extends CommonOptions {
1316
symbol?: string;
1417
spacing?: number;
1518
secondarySymbol?: string;
19+
removeLeadingSpace?: boolean;
1620
}
1721

1822
export const log = {
19-
message: (
20-
message: string | string[] = [],
23+
message: async (
24+
message: string | Iterable<string> | AsyncIterable<string> = [],
2125
{
2226
symbol = color.gray(S_BAR),
27+
spacing = 0,
2328
secondarySymbol = color.gray(S_BAR),
2429
output = process.stdout,
25-
spacing = 1,
30+
removeLeadingSpace = true,
2631
}: LogMessageOptions = {}
2732
) => {
28-
const parts: string[] = [];
29-
for (let i = 0; i < spacing; i++) {
30-
parts.push(`${secondarySymbol}`);
31-
}
32-
const messageParts = Array.isArray(message) ? message : message.split('\n');
33-
if (messageParts.length > 0) {
34-
const [firstLine, ...lines] = messageParts;
35-
if (firstLine.length > 0) {
36-
parts.push(`${symbol} ${firstLine}`);
37-
} else {
38-
parts.push(symbol);
33+
output.write(`${color.gray(S_BAR)}\n`);
34+
let first = true;
35+
let lastLine = '';
36+
const iterable = typeof message === 'string' ? [message] : message;
37+
const isAsync = Symbol.asyncIterator in iterable;
38+
for await (let chunk of iterable) {
39+
const width = getColumns(output);
40+
if (first) {
41+
lastLine = `${symbol} `;
42+
chunk = '\n'.repeat(spacing) + chunk;
43+
first = false;
3944
}
40-
for (const ln of lines) {
41-
if (ln.length > 0) {
42-
parts.push(`${secondarySymbol} ${ln}`);
43-
} else {
44-
parts.push(secondarySymbol);
45-
}
45+
const newLineRE = removeLeadingSpace ? /\n */g : /\n/g;
46+
const lines =
47+
lastLine.substring(0, 3) +
48+
wrap(`${lastLine.substring(3)}${chunk}`, width).replace(
49+
newLineRE,
50+
`\n${secondarySymbol} `
51+
);
52+
output?.write(cursor.move(-999, 0) + erase.lines(1));
53+
output?.write(lines);
54+
lastLine = lines.substring(Math.max(0, lines.lastIndexOf('\n') + 1));
55+
if (!isAsync) {
56+
lastLine = `${secondarySymbol} `;
57+
output?.write('\n');
4658
}
4759
}
48-
output.write(`${parts.join('\n')}\n`);
60+
if (isAsync) {
61+
output.write('\n');
62+
}
4963
},
50-
info: (message: string, opts?: LogMessageOptions) => {
51-
log.message(message, { ...opts, symbol: color.blue(S_INFO) });
64+
info: async (message: string, opts?: LogMessageOptions) => {
65+
await log.message(message, { ...opts, symbol: color.blue(S_INFO) });
5266
},
53-
success: (message: string, opts?: LogMessageOptions) => {
54-
log.message(message, { ...opts, symbol: color.green(S_SUCCESS) });
67+
success: async (message: string, opts?: LogMessageOptions) => {
68+
await log.message(message, { ...opts, symbol: color.green(S_SUCCESS) });
5569
},
56-
step: (message: string, opts?: LogMessageOptions) => {
57-
log.message(message, { ...opts, symbol: color.green(S_STEP_SUBMIT) });
70+
step: async (message: string, opts?: LogMessageOptions) => {
71+
await log.message(message, { ...opts, symbol: color.green(S_STEP_SUBMIT) });
5872
},
59-
warn: (message: string, opts?: LogMessageOptions) => {
60-
log.message(message, { ...opts, symbol: color.yellow(S_WARN) });
73+
warn: async (message: string, opts?: LogMessageOptions) => {
74+
await log.message(message, { ...opts, symbol: color.yellow(S_WARN) });
6175
},
6276
/** alias for `log.warn()`. */
63-
warning: (message: string, opts?: LogMessageOptions) => {
64-
log.warn(message, opts);
77+
warning: async (message: string, opts?: LogMessageOptions) => {
78+
await log.warn(message, opts);
6579
},
66-
error: (message: string, opts?: LogMessageOptions) => {
67-
log.message(message, { ...opts, symbol: color.red(S_ERROR) });
80+
error: async (message: string, opts?: LogMessageOptions) => {
81+
await log.message(message, { ...opts, symbol: color.red(S_ERROR) });
6882
},
6983
};
84+
85+
export const stream = log;

packages/prompts/src/stream.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)