diff --git a/.changeset/evil-rats-turn.md b/.changeset/evil-rats-turn.md new file mode 100644 index 00000000..ae5cdef9 --- /dev/null +++ b/.changeset/evil-rats-turn.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": patch +--- + +Strip destructive ANSI codes from task log messages. diff --git a/packages/prompts/src/task-log.ts b/packages/prompts/src/task-log.ts index a484499f..f92069c8 100644 --- a/packages/prompts/src/task-log.ts +++ b/packages/prompts/src/task-log.ts @@ -36,6 +36,10 @@ interface BufferEntry { }; } +const stripDestructiveANSI = (input: string): string => { + return input.replace(/\x1b\[(?:\d+;)*\d*[ABCDEFGHfJKSTsu]|\x1b\[(s|u)/g, ''); +}; + /** * Renders a log which clears on success and remains on failure */ @@ -131,7 +135,7 @@ export const taskLog = (opts: TaskLogOptions) => { if ((mopts?.raw !== true || !lastMessageWasRaw) && buffer.value !== '') { buffer.value += '\n'; } - buffer.value += msg; + buffer.value += stripDestructiveANSI(msg); lastMessageWasRaw = mopts?.raw === true; if (opts.limit !== undefined) { const lines = buffer.value.split('\n'); diff --git a/packages/prompts/test/__snapshots__/task-log.test.ts.snap b/packages/prompts/test/__snapshots__/task-log.test.ts.snap index b9d1fa58..39242084 100644 --- a/packages/prompts/test/__snapshots__/task-log.test.ts.snap +++ b/packages/prompts/test/__snapshots__/task-log.test.ts.snap @@ -627,6 +627,28 @@ exports[`taskLog (isCI = false) > message > can write multiple lines 1`] = ` ] `; +exports[`taskLog (isCI = false) > message > destructive ansi codes are stripped 1`] = ` +[ + "│ +", + "◇ foo +", + "│ +", + "│ line 1 +", + "", + "│ line 1 +│ line 2 bad ansi! +", + "", + "│ line 1 +│ line 2 bad ansi! +│ line 3 +", +] +`; + exports[`taskLog (isCI = false) > message > enforces limit if set 1`] = ` [ "│ @@ -1358,6 +1380,19 @@ exports[`taskLog (isCI = true) > message > can write multiple lines 1`] = ` ] `; +exports[`taskLog (isCI = true) > message > destructive ansi codes are stripped 1`] = ` +[ + "│ +", + "◇ foo +", + "│ +", + "", + "", +] +`; + exports[`taskLog (isCI = true) > message > enforces limit if set 1`] = ` [ "│ diff --git a/packages/prompts/test/task-log.test.ts b/packages/prompts/test/task-log.test.ts index 971076f1..dbbc31a6 100644 --- a/packages/prompts/test/task-log.test.ts +++ b/packages/prompts/test/task-log.test.ts @@ -132,6 +132,20 @@ describe.each(['true', 'false'])('taskLog (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); + + test('destructive ansi codes are stripped', async () => { + const log = prompts.taskLog({ + input, + output, + title: 'foo', + }); + + log.message('line 1'); + log.message('line 2\x1b[2K bad ansi!'); + log.message('line 3'); + + expect(output.buffer).toMatchSnapshot(); + }); }); describe('error', () => {