diff --git a/examples/basic/package.json b/examples/basic/package.json index e0f6b14..410a7fa 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -14,7 +14,8 @@ "progress": "jiti ./progress.ts", "spinner": "jiti ./spinner.ts", "spinner-ci": "npx cross-env CI=\"true\" jiti ./spinner-ci.ts", - "spinner-timer": "jiti ./spinner-timer.ts" + "spinner-timer": "jiti ./spinner-timer.ts", + "task-log": "jiti ./task-log.ts" }, "devDependencies": { "cross-env": "^7.0.3" diff --git a/examples/basic/task-log.ts b/examples/basic/task-log.ts new file mode 100644 index 0000000..f345bd5 --- /dev/null +++ b/examples/basic/task-log.ts @@ -0,0 +1,28 @@ +import { setTimeout } from 'node:timers/promises'; +import * as p from '@clack/prompts'; + +async function main() { + p.intro('task log start...'); + + const log = p.taskLog({ + title: 'Running npm install', + limit: 5, + }); + + for await (const line of fakeCommand()) { + log.message(line); + } + + log.success('Done!'); + + p.outro('task log stop...'); +} + +async function* fakeCommand() { + for (let i = 0; i < 100; i++) { + yield `line \x1b[32m${i}\x1b[39m...`; + await setTimeout(80); + } +} + +main(); diff --git a/packages/prompts/README.md b/packages/prompts/README.md index 662c975..50c16f1 100644 --- a/packages/prompts/README.md +++ b/packages/prompts/README.md @@ -253,7 +253,7 @@ When executing a sub-process or a similar sub-task, `taskLog` can be used to ren import { taskLog } from '@clack/prompts'; const log = taskLog({ - message: 'Running npm install' + title: 'Running npm install' }); for await (const line of npmInstall()) { diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index 2233fe5..4992f33 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -4,6 +4,7 @@ import isUnicodeSupported from 'is-unicode-supported'; import color from 'picocolors'; export const unicode = isUnicodeSupported(); +export const isCI = (): boolean => process.env.CI === 'true'; export const unicodeOr = (c: string, fallback: string) => (unicode ? c : fallback); export const S_STEP_ACTIVE = unicodeOr('◆', '*'); export const S_STEP_CANCEL = unicodeOr('■', 'x'); diff --git a/packages/prompts/src/spinner.ts b/packages/prompts/src/spinner.ts index 3430c9e..4bad168 100644 --- a/packages/prompts/src/spinner.ts +++ b/packages/prompts/src/spinner.ts @@ -7,6 +7,7 @@ import { S_STEP_CANCEL, S_STEP_ERROR, S_STEP_SUBMIT, + isCI as isCIFn, unicode, } from './common.js'; @@ -33,7 +34,7 @@ export const spinner = ({ }: SpinnerOptions = {}): SpinnerResult => { const frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0']; const delay = unicode ? 80 : 120; - const isCI = process.env.CI === 'true'; + const isCI = isCIFn(); let unblock: () => void; let loop: NodeJS.Timeout; diff --git a/packages/prompts/src/task-log.ts b/packages/prompts/src/task-log.ts index 5f90614..fa8768d 100644 --- a/packages/prompts/src/task-log.ts +++ b/packages/prompts/src/task-log.ts @@ -2,7 +2,7 @@ import type { Writable } from 'node:stream'; import { getColumns } from '@clack/core'; import * as color from 'picocolors'; import { erase } from 'sisteransi'; -import { type CommonOptions, S_BAR, S_STEP_SUBMIT } from './common.js'; +import { type CommonOptions, S_BAR, S_STEP_SUBMIT, isCI as isCIFn } from './common.js'; import { log } from './log.js'; export interface TaskLogOptions extends CommonOptions { @@ -26,11 +26,11 @@ export interface TaskLogCompletionOptions { export const taskLog = (opts: TaskLogOptions) => { const output: Writable = opts.output ?? process.stdout; const columns = getColumns(output); - const secondarySymbol = color.dim(S_BAR); + const secondarySymbol = color.gray(S_BAR); const spacing = opts.spacing ?? 1; const barSize = 3; const retainLog = opts.retainLog === true; - const isCI = process.env.CI === 'true'; + const isCI = isCIFn(); output.write(`${secondarySymbol}\n`); output.write(`${color.green(S_STEP_SUBMIT)} ${opts.title}\n`); diff --git a/packages/prompts/test/__snapshots__/task-log.test.ts.snap b/packages/prompts/test/__snapshots__/task-log.test.ts.snap index 72cd5db..f44d30f 100644 --- a/packages/prompts/test/__snapshots__/task-log.test.ts.snap +++ b/packages/prompts/test/__snapshots__/task-log.test.ts.snap @@ -2,20 +2,20 @@ exports[`taskLog (isCI = false) > error > clears output if showLog = false 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ + "│ ■ some error! ", ] @@ -23,536 +23,536 @@ exports[`taskLog (isCI = false) > error > clears output if showLog = false 1`] = exports[`taskLog (isCI = false) > error > renders output with message 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ + "│ ■ some error! ", - "│ -│ line 0 -│ line 1 + "│ +│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > message > can write line by line 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > message > can write multiple lines 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > message > enforces limit if set 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", ] `; exports[`taskLog (isCI = false) > message > prints empty lines 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│  + "│  ", - "│ line 1 + "│ line 1 ", "", - "│ line 1 -│  + "│ line 1 +│  ", "", - "│ line 1 -│  -│ line 3 + "│ line 1 +│  +│ line 3 ", ] `; exports[`taskLog (isCI = false) > message > raw = true appends message text until newline 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0still line 0 + "│ line 0still line 0 ", "", - "│ line 0still line 0 -│ line 1 + "│ line 0still line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > message > raw = true works when mixed with non-raw messages 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0still line 0 + "│ line 0still line 0 ", "", - "│ line 0still line 0 -│ line 1 + "│ line 0still line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > message > raw = true works when started with non-raw messages 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 0 -│ line 1still line 1 + "│ line 0 +│ line 1still line 1 ", ] `; exports[`taskLog (isCI = false) > retainLog > error > outputs limited log with limit by default 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ■ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > error > retainLog = false outputs full log without limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 0 -│ line 1 -│ line 2 + "│ line 0 +│ line 1 +│ line 2 ", "", - "│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ line 0 +│ line 1 +│ line 2 +│ line 3 ", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > error > retainLog = false outputs limited log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ■ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > error > retainLog = true outputs full log 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 0 -│ line 1 -│ line 2 + "│ line 0 +│ line 1 +│ line 2 ", "", - "│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ line 0 +│ line 1 +│ line 2 +│ line 3 ", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > error > retainLog = true outputs full log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > success > outputs limited log with limit by default 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ◆ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > success > retainLog = false outputs full log without limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 0 -│ line 1 -│ line 2 + "│ line 0 +│ line 1 +│ line 2 ", "", - "│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ line 0 +│ line 1 +│ line 2 +│ line 3 ", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > success > retainLog = false outputs limited log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ◆ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > success > retainLog = true outputs full log 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 0 -│ line 1 -│ line 2 + "│ line 0 +│ line 1 +│ line 2 ", "", - "│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ line 0 +│ line 1 +│ line 2 +│ line 3 ", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > retainLog > success > retainLog = true outputs full log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ line 1 -│ line 2 + "│ line 1 +│ line 2 ", "", - "│ line 2 -│ line 3 + "│ line 2 +│ line 3 ", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = false) > success > clears output and renders message 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ + "│ ◆ success! ", ] @@ -560,51 +560,51 @@ exports[`taskLog (isCI = false) > success > clears output and renders message 1` exports[`taskLog (isCI = false) > success > renders output if showLog = true 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", - "│ line 0 + "│ line 0 ", "", - "│ line 0 -│ line 1 + "│ line 0 +│ line 1 ", "", - "│ + "│ ◆ success! ", - "│ -│ line 0 -│ line 1 + "│ +│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = false) > writes message header 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", ] `; exports[`taskLog (isCI = true) > error > clears output if showLog = false 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", - "│ + "│ ■ some error! ", ] @@ -612,31 +612,31 @@ exports[`taskLog (isCI = true) > error > clears output if showLog = false 1`] = exports[`taskLog (isCI = true) > error > renders output with message 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", - "│ + "│ ■ some error! ", - "│ -│ line 0 -│ line 1 + "│ +│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = true) > message > can write line by line 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", ] @@ -644,22 +644,22 @@ exports[`taskLog (isCI = true) > message > can write line by line 1`] = ` exports[`taskLog (isCI = true) > message > can write multiple lines 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", ] `; exports[`taskLog (isCI = true) > message > enforces limit if set 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", @@ -668,11 +668,11 @@ exports[`taskLog (isCI = true) > message > enforces limit if set 1`] = ` exports[`taskLog (isCI = true) > message > prints empty lines 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", @@ -681,11 +681,11 @@ exports[`taskLog (isCI = true) > message > prints empty lines 1`] = ` exports[`taskLog (isCI = true) > message > raw = true appends message text until newline 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", @@ -694,11 +694,11 @@ exports[`taskLog (isCI = true) > message > raw = true appends message text until exports[`taskLog (isCI = true) > message > raw = true works when mixed with non-raw messages 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", @@ -707,11 +707,11 @@ exports[`taskLog (isCI = true) > message > raw = true works when mixed with non- exports[`taskLog (isCI = true) > message > raw = true works when started with non-raw messages 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", @@ -720,247 +720,247 @@ exports[`taskLog (isCI = true) > message > raw = true works when started with no exports[`taskLog (isCI = true) > retainLog > error > outputs limited log with limit by default 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ■ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > error > retainLog = false outputs full log without limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > error > retainLog = false outputs limited log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ■ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > error > retainLog = true outputs full log 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > error > retainLog = true outputs full log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ■ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > success > outputs limited log with limit by default 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ◆ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > success > retainLog = false outputs full log without limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > success > retainLog = false outputs limited log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ◆ woo! ", - "│ -│ line 2 -│ line 3 + "│ +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > success > retainLog = true outputs full log 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > retainLog > success > retainLog = true outputs full log with limit 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", "", "", - "│ + "│ ◆ woo! ", - "│ -│ line 0 -│ line 1 -│ line 2 -│ line 3 + "│ +│ line 0 +│ line 1 +│ line 2 +│ line 3 ", ] `; exports[`taskLog (isCI = true) > success > clears output and renders message 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", - "│ + "│ ◆ success! ", ] @@ -968,31 +968,31 @@ exports[`taskLog (isCI = true) > success > clears output and renders message 1`] exports[`taskLog (isCI = true) > success > renders output if showLog = true 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", "", "", - "│ + "│ ◆ success! ", - "│ -│ line 0 -│ line 1 + "│ +│ line 0 +│ line 1 ", ] `; exports[`taskLog (isCI = true) > writes message header 1`] = ` [ - "│ + "│ ", "◇ foo ", - "│ + "│ ", ] `;