Skip to content
5 changes: 5 additions & 0 deletions .changeset/famous-turkeys-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clack/prompts": minor
---

Added new `taskLog` prompt for log output which is cleared on success
22 changes: 22 additions & 0 deletions packages/prompts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,26 @@ stream.error((function *() { yield 'Error!'; })());
stream.message((function *() { yield 'Hello'; yield ", World" })(), { symbol: color.cyan('~') });
```

### Task Log

When executing a sub-process or a similar sub-task, `taskLog` can be used to render the output continuously and clear it at the end if it was successful.

```js
import { taskLog } from '@clack/prompts';

const log = taskLog({
message: 'Running npm install'
});

for await (const line of npmInstall()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for await (const line of npmInstall()) {
for await (const line of npmInstall()) {

Not sure if the npmInstall makes it clear enough for the people, but I don't have any better idea

log.message(line);
}

if (success) {
log.success('Done!');
} else {
log.error('Failed!');
}
```

![clack-log-prompts](https://github.com/bombshell-dev/clack/blob/main/.github/assets/clack-logs.png)
224 changes: 224 additions & 0 deletions packages/prompts/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,118 @@ exports[`prompts (isCI = false) > spinner > stop > renders submit symbol and sto
]
`;

exports[`prompts (isCI = false) > taskLog > error > maintains output but replaces message 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"■ some error!
",
"│
│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = false) > taskLog > message > can write line by line 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = false) > taskLog > message > can write multiple lines 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = false) > taskLog > message > enforces limit if set 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"│ line 1
│ line 2
",
]
`;

exports[`prompts (isCI = false) > taskLog > success > clears output and renders message 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"◆ success!
",
]
`;

exports[`prompts (isCI = false) > taskLog > writes message header 1`] = `
[
"│
",
"◇ foo
",
"│
",
]
`;

exports[`prompts (isCI = false) > text > can cancel 1`] = `
[
"[?25l",
Expand Down Expand Up @@ -3266,6 +3378,118 @@ exports[`prompts (isCI = true) > spinner > stop > renders submit symbol and stop
]
`;

exports[`prompts (isCI = true) > taskLog > error > maintains output but replaces message 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"■ some error!
",
"│
│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = true) > taskLog > message > can write line by line 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = true) > taskLog > message > can write multiple lines 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
│ line 1
",
]
`;

exports[`prompts (isCI = true) > taskLog > message > enforces limit if set 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"│ line 1
│ line 2
",
]
`;

exports[`prompts (isCI = true) > taskLog > success > clears output and renders message 1`] = `
[
"│
",
"◇ foo
",
"│
",
"",
"│ line 0
",
"",
"│ line 0
│ line 1
",
"",
"◆ success!
",
]
`;

exports[`prompts (isCI = true) > taskLog > writes message header 1`] = `
[
"│
",
"◇ foo
",
"│
",
]
`;

exports[`prompts (isCI = true) > text > can cancel 1`] = `
[
"[?25l",
Expand Down
88 changes: 88 additions & 0 deletions packages/prompts/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1181,4 +1181,92 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
expect(output.buffer).toMatchSnapshot();
});
});

describe('taskLog', () => {
test('writes message header', () => {
prompts.taskLog({
input,
output,
message: 'foo',
});

expect(output.buffer).toMatchSnapshot();
});

describe('message', () => {
test('can write line by line', () => {
const log = prompts.taskLog({
input,
output,
message: 'foo',
});

log.message('line 0');
log.message('line 1');

expect(output.buffer).toMatchSnapshot();
});

test('can write multiple lines', () => {
const log = prompts.taskLog({
input,
output,
message: 'foo',
});

log.message('line 0\nline 1');

expect(output.buffer).toMatchSnapshot();
});

test('enforces limit if set', () => {
const log = prompts.taskLog({
input,
output,
message: 'foo',
limit: 2,
});

log.message('line 0');
log.message('line 1');
log.message('line 2');

expect(output.buffer).toMatchSnapshot();
});
});

describe('error', () => {
test('maintains output but replaces message', () => {
const log = prompts.taskLog({
input,
output,
message: 'foo',
});

log.message('line 0');
log.message('line 1');

log.error('some error!');

expect(output.buffer).toMatchSnapshot();
});
});

describe('success', () => {
test('clears output and renders message', () => {
const log = prompts.taskLog({
input,
output,
message: 'foo',
});

log.message('line 0');
log.message('line 1');

log.success('success!');

expect(output.buffer).toMatchSnapshot();
});
});
});
});
Loading