diff --git a/.changeset/tall-keys-allow.md b/.changeset/tall-keys-allow.md new file mode 100644 index 00000000..5d5566ce --- /dev/null +++ b/.changeset/tall-keys-allow.md @@ -0,0 +1,6 @@ +--- +"@clack/prompts": patch +"@clack/core": patch +--- + +Add a new `withGuide` option to all prompts to disable the default clack border diff --git a/packages/core/src/utils/settings.ts b/packages/core/src/utils/settings.ts index 9ebb7fdb..7a2d880a 100644 --- a/packages/core/src/utils/settings.ts +++ b/packages/core/src/utils/settings.ts @@ -9,6 +9,7 @@ interface InternalClackSettings { cancel: string; error: string; }; + withGuide: boolean; } export const settings: InternalClackSettings = { @@ -27,6 +28,7 @@ export const settings: InternalClackSettings = { cancel: 'Canceled', error: 'Something went wrong', }, + withGuide: true, }; export interface ClackSettings { @@ -54,6 +56,8 @@ export interface ClackSettings { */ error?: string; }; + + withGuide?: boolean; } export function updateSettings(updates: ClackSettings) { @@ -81,6 +85,10 @@ export function updateSettings(updates: ClackSettings) { settings.messages.error = messages.error; } } + + if (updates.withGuide !== undefined) { + settings.withGuide = updates.withGuide !== false; + } } /** diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 596e2cf7..ef502e89 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src", "test"] } diff --git a/packages/prompts/src/box.ts b/packages/prompts/src/box.ts index 060ada1e..af299172 100644 --- a/packages/prompts/src/box.ts +++ b/packages/prompts/src/box.ts @@ -1,5 +1,5 @@ import type { Writable } from 'node:stream'; -import { getColumns } from '@clack/core'; +import { getColumns, settings } from '@clack/core'; import stringWidth from 'fast-string-width'; import { wrapAnsi } from 'fast-wrap-ansi'; import { @@ -35,7 +35,6 @@ export interface BoxOptions extends CommonOptions { titlePadding?: number; contentPadding?: number; rounded?: boolean; - includePrefix?: boolean; formatBorder?: (text: string) => string; } @@ -68,7 +67,8 @@ export const box = (message = '', title = '', opts?: BoxOptions) => { const titlePadding = opts?.titlePadding ?? 1; const contentPadding = opts?.contentPadding ?? 2; const width = opts?.width === undefined || opts.width === 'auto' ? 1 : Math.min(1, opts.width); - const linePrefix = opts?.includePrefix ? `${S_BAR} ` : ''; + const hasGuide = (opts?.withGuide ?? settings.withGuide) !== false; + const linePrefix = !hasGuide ? '' : `${S_BAR} `; const formatBorder = opts?.formatBorder ?? defaultFormatBorder; const symbols = (opts?.rounded ? roundedSymbols : squareSymbols).map(formatBorder); const hSymbol = formatBorder(S_BAR_H); diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index 2489a815..64359053 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -71,4 +71,5 @@ export interface CommonOptions { input?: Readable; output?: Writable; signal?: AbortSignal; + withGuide?: boolean; } diff --git a/packages/prompts/src/log.ts b/packages/prompts/src/log.ts index ff4e00cd..6a896d58 100644 --- a/packages/prompts/src/log.ts +++ b/packages/prompts/src/log.ts @@ -1,3 +1,4 @@ +import { settings } from '@clack/core'; import color from 'picocolors'; import { type CommonOptions, @@ -23,25 +24,32 @@ export const log = { secondarySymbol = color.gray(S_BAR), output = process.stdout, spacing = 1, + withGuide, }: LogMessageOptions = {} ) => { const parts: string[] = []; + const hasGuide = (withGuide ?? settings.withGuide) !== false; + const spacingString = !hasGuide ? '' : secondarySymbol; + const prefix = !hasGuide ? '' : `${symbol} `; + const secondaryPrefix = !hasGuide ? '' : `${secondarySymbol} `; + for (let i = 0; i < spacing; i++) { - parts.push(`${secondarySymbol}`); + parts.push(spacingString); } + const messageParts = Array.isArray(message) ? message : message.split('\n'); if (messageParts.length > 0) { const [firstLine, ...lines] = messageParts; if (firstLine.length > 0) { - parts.push(`${symbol} ${firstLine}`); + parts.push(`${prefix}${firstLine}`); } else { - parts.push(symbol); + parts.push(hasGuide ? '' : symbol); } for (const ln of lines) { if (ln.length > 0) { - parts.push(`${secondarySymbol} ${ln}`); + parts.push(`${secondaryPrefix}${ln}`); } else { - parts.push(secondarySymbol); + parts.push(hasGuide ? '' : secondarySymbol); } } } diff --git a/packages/prompts/src/text.ts b/packages/prompts/src/text.ts index 244f7c8d..2c3dbc8c 100644 --- a/packages/prompts/src/text.ts +++ b/packages/prompts/src/text.ts @@ -1,4 +1,4 @@ -import { TextPrompt } from '@clack/core'; +import { settings, TextPrompt } from '@clack/core'; import color from 'picocolors'; import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js'; @@ -20,7 +20,9 @@ export const text = (opts: TextOptions) => { signal: opts.signal, input: opts.input, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const hasGuide = (opts?.withGuide ?? settings.withGuide) !== false; + const titlePrefix = `${hasGuide ? `${color.gray(S_BAR)}\n` : ''}${symbol(this.state)} `; + const title = `${titlePrefix}${opts.message}\n`; const placeholder = opts.placeholder ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) : color.inverse(color.hidden('_')); @@ -30,20 +32,25 @@ export const text = (opts: TextOptions) => { switch (this.state) { case 'error': { const errorText = this.error ? ` ${color.yellow(this.error)}` : ''; - return `${title.trim()}\n${color.yellow(S_BAR)} ${userInput}\n${color.yellow( - S_BAR_END - )}${errorText}\n`; + const errorPrefix = hasGuide ? `${color.yellow(S_BAR)} ` : ''; + const errorPrefixEnd = hasGuide ? color.yellow(S_BAR_END) : ''; + return `${title.trim()}\n${errorPrefix}${userInput}\n${errorPrefixEnd}${errorText}\n`; } case 'submit': { const valueText = value ? ` ${color.dim(value)}` : ''; - return `${title}${color.gray(S_BAR)}${valueText}`; + const submitPrefix = hasGuide ? color.gray(S_BAR) : ''; + return `${title}${submitPrefix}${valueText}`; } case 'cancel': { const valueText = value ? ` ${color.strikethrough(color.dim(value))}` : ''; - return `${title}${color.gray(S_BAR)}${valueText}${value.trim() ? `\n${color.gray(S_BAR)}` : ''}`; + const cancelPrefix = hasGuide ? color.gray(S_BAR) : ''; + return `${title}${cancelPrefix}${valueText}${value.trim() ? `\n${cancelPrefix}` : ''}`; + } + default: { + const defaultPrefix = hasGuide ? `${color.cyan(S_BAR)} ` : ''; + const defaultPrefixEnd = hasGuide ? color.cyan(S_BAR_END) : ''; + return `${title}${defaultPrefix}${userInput}\n${defaultPrefixEnd}\n`; } - default: - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; } }, }).prompt() as Promise; diff --git a/packages/prompts/test/__snapshots__/box.test.ts.snap b/packages/prompts/test/__snapshots__/box.test.ts.snap index 63f3b8c7..b8926767 100644 --- a/packages/prompts/test/__snapshots__/box.test.ts.snap +++ b/packages/prompts/test/__snapshots__/box.test.ts.snap @@ -2,528 +2,550 @@ exports[`box (isCI = false) > cannot have width larger than 100% 1`] = ` [ - "┌─title────────────────────────────────────────────────────────────────────────┐ + "│ ┌─title──────────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders as specified width 1`] = ` [ - "┌─title────────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ short │ + "│ │ short │ ", - "│ somewhat questionably long line │ + "│ │ somewhat questionably long line │ ", - "└──────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders as wide as longest line with width: auto 1`] = ` [ - "┌─title──────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ short │ + "│ │ short │ ", - "│ somewhat questionably long line │ + "│ │ somewhat questionably long line │ ", - "└────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders auto width with content longer than title 1`] = ` [ - "┌─title──────────────────────────┐ + "│ ┌─title──────────────────────────┐ ", - "│ messagemessagemessagemessage │ + "│ │ messagemessagemessagemessage │ ", - "└────────────────────────────────┘ + "│ └────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders auto width with title longer than content 1`] = ` [ - "┌─titletitletitletitle─┐ + "│ ┌─titletitletitletitle─┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────┘ + "│ └──────────────────────┘ ", ] `; exports[`box (isCI = false) > renders center aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders center aligned title 1`] = ` [ - "┌───title────┐ + "│ ┌───title────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders left aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders left aligned title 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders message 1`] = ` [ - "┌──────────────────────────────────────────────────────────────────────────────┐ + "│ ┌────────────────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders message with title 1`] = ` [ - "┌─some title───────────────────────────────────────────────────────────────────┐ + "│ ┌─some title─────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders right aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders right aligned title 1`] = ` [ - "┌──────title─┐ + "│ ┌──────title─┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders rounded corners when rounded is true 1`] = ` [ - "╭─title──────╮ + "│ ╭─title──────╮ ", - "│ message │ + "│ │ message │ ", - "╰────────────╯ + "│ ╰────────────╯ ", ] `; exports[`box (isCI = false) > renders specified contentPadding 1`] = ` [ - "┌─title──────────────┐ + "│ ┌─title──────────────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────────────┘ + "│ └────────────────────┘ ", ] `; exports[`box (isCI = false) > renders specified titlePadding 1`] = ` [ - "┌──────title───────┐ + "│ ┌──────title───────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────┘ + "│ └──────────────────┘ ", ] `; exports[`box (isCI = false) > renders truncated long titles 1`] = ` [ - "┌─foofoofoo...─┐ + "│ ┌─foofoof...─┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders wide characters with auto width 1`] = ` [ - "┌─这是标题─────────────────┐ + "│ ┌─这是标题─────────────────┐ ", - "│ 이게 첫 번째 줄이에요 │ + "│ │ 이게 첫 번째 줄이에요 │ ", - "│ これは次の行です │ + "│ │ これは次の行です │ ", - "└──────────────────────────┘ + "│ └──────────────────────────┘ ", ] `; exports[`box (isCI = false) > renders wide characters with specified width 1`] = ` [ - "┌─这是标题─────┐ + "│ ┌─这是标题───┐ ", - "│ 이게 첫 │ + "│ │ 이게 첫 │ ", - "│ 번째 │ + "│ │ 번째 │ ", - "│ 줄이에요 │ + "│ │ 줄이에요 │ ", - "│ これは次の │ + "│ │ これは次 │ ", - "│ 行です │ + "│ │ の行です │ ", - "└──────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = false) > renders with formatBorder formatting 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; -exports[`box (isCI = false) > renders with prefix when includePrefix is true 1`] = ` +exports[`box (isCI = false) > renders without guide when global withGuide is false 1`] = ` [ - "│ ┌─title──────┐ + "┌─title──────┐ ", - "│ │ message │ + "│ message │ ", - "│ └────────────┘ + "└────────────┘ +", +] +`; + +exports[`box (isCI = false) > renders without guide when withGuide is false 1`] = ` +[ + "┌─title──────┐ +", + "│ message │ +", + "└────────────┘ ", ] `; exports[`box (isCI = false) > wraps content to fit within specified width 1`] = ` [ - "┌─title────────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ foo barfoo barfoo barfoo barfoo │ + "│ │ foo barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo bar │ + "│ │ barfoo barfoo barfoo bar │ ", - "└──────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > cannot have width larger than 100% 1`] = ` [ - "┌─title────────────────────────────────────────────────────────────────────────┐ + "│ ┌─title──────────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders as specified width 1`] = ` [ - "┌─title────────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ short │ + "│ │ short │ ", - "│ somewhat questionably long line │ + "│ │ somewhat questionably long line │ ", - "└──────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders as wide as longest line with width: auto 1`] = ` [ - "┌─title──────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ short │ + "│ │ short │ ", - "│ somewhat questionably long line │ + "│ │ somewhat questionably long line │ ", - "└────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders auto width with content longer than title 1`] = ` [ - "┌─title──────────────────────────┐ + "│ ┌─title──────────────────────────┐ ", - "│ messagemessagemessagemessage │ + "│ │ messagemessagemessagemessage │ ", - "└────────────────────────────────┘ + "│ └────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders auto width with title longer than content 1`] = ` [ - "┌─titletitletitletitle─┐ + "│ ┌─titletitletitletitle─┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────┘ + "│ └──────────────────────┘ ", ] `; exports[`box (isCI = true) > renders center aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders center aligned title 1`] = ` [ - "┌───title────┐ + "│ ┌───title────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders left aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders left aligned title 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders message 1`] = ` [ - "┌──────────────────────────────────────────────────────────────────────────────┐ + "│ ┌────────────────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders message with title 1`] = ` [ - "┌─some title───────────────────────────────────────────────────────────────────┐ + "│ ┌─some title─────────────────────────────────────────────────────────────────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────────────────────────────────────────────────────────────────┘ + "│ └────────────────────────────────────────────────────────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders right aligned content 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders right aligned title 1`] = ` [ - "┌──────title─┐ + "│ ┌──────title─┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders rounded corners when rounded is true 1`] = ` [ - "╭─title──────╮ + "│ ╭─title──────╮ ", - "│ message │ + "│ │ message │ ", - "╰────────────╯ + "│ ╰────────────╯ ", ] `; exports[`box (isCI = true) > renders specified contentPadding 1`] = ` [ - "┌─title──────────────┐ + "│ ┌─title──────────────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────────────┘ + "│ └────────────────────┘ ", ] `; exports[`box (isCI = true) > renders specified titlePadding 1`] = ` [ - "┌──────title───────┐ + "│ ┌──────title───────┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────────┘ + "│ └──────────────────┘ ", ] `; exports[`box (isCI = true) > renders truncated long titles 1`] = ` [ - "┌─foofoofoo...─┐ + "│ ┌─foofoof...─┐ ", - "│ message │ + "│ │ message │ ", - "└──────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders wide characters with auto width 1`] = ` [ - "┌─这是标题─────────────────┐ + "│ ┌─这是标题─────────────────┐ ", - "│ 이게 첫 번째 줄이에요 │ + "│ │ 이게 첫 번째 줄이에요 │ ", - "│ これは次の行です │ + "│ │ これは次の行です │ ", - "└──────────────────────────┘ + "│ └──────────────────────────┘ ", ] `; exports[`box (isCI = true) > renders wide characters with specified width 1`] = ` [ - "┌─这是标题─────┐ + "│ ┌─这是标题───┐ ", - "│ 이게 첫 │ + "│ │ 이게 첫 │ ", - "│ 번째 │ + "│ │ 번째 │ ", - "│ 줄이에요 │ + "│ │ 줄이에요 │ ", - "│ これは次の │ + "│ │ これは次 │ ", - "│ 行です │ + "│ │ の行です │ ", - "└──────────────┘ + "│ └────────────┘ ", ] `; exports[`box (isCI = true) > renders with formatBorder formatting 1`] = ` [ - "┌─title──────┐ + "│ ┌─title──────┐ ", - "│ message │ + "│ │ message │ ", - "└────────────┘ + "│ └────────────┘ ", ] `; -exports[`box (isCI = true) > renders with prefix when includePrefix is true 1`] = ` +exports[`box (isCI = true) > renders without guide when global withGuide is false 1`] = ` [ - "│ ┌─title──────┐ + "┌─title──────┐ ", - "│ │ message │ + "│ message │ ", - "│ └────────────┘ + "└────────────┘ +", +] +`; + +exports[`box (isCI = true) > renders without guide when withGuide is false 1`] = ` +[ + "┌─title──────┐ +", + "│ message │ +", + "└────────────┘ ", ] `; exports[`box (isCI = true) > wraps content to fit within specified width 1`] = ` [ - "┌─title────────────────────────────────┐ + "│ ┌─title──────────────────────────────┐ ", - "│ foo barfoo barfoo barfoo barfoo │ + "│ │ foo barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo barfoo barfoo barfoo barfoo │ + "│ │ barfoo barfoo barfoo barfoo │ ", - "│ barfoo bar │ + "│ │ barfoo barfoo barfoo bar │ ", - "└──────────────────────────────────────┘ + "│ └────────────────────────────────────┘ ", ] `; diff --git a/packages/prompts/test/__snapshots__/log.test.ts.snap b/packages/prompts/test/__snapshots__/log.test.ts.snap new file mode 100644 index 00000000..2bc884ed --- /dev/null +++ b/packages/prompts/test/__snapshots__/log.test.ts.snap @@ -0,0 +1,211 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`log (isCI = false) > error > renders error message 1`] = ` +[ + "│ +■ error message +", +] +`; + +exports[`log (isCI = false) > info > renders info message 1`] = ` +[ + "│ +● info message +", +] +`; + +exports[`log (isCI = false) > message > renders message 1`] = ` +[ + "│ +│ message +", +] +`; + +exports[`log (isCI = false) > message > renders message from array 1`] = ` +[ + "│ +│ line 1 +│ line 2 +│ line 3 +", +] +`; + +exports[`log (isCI = false) > message > renders message with custom spacing 1`] = ` +[ + "│ +│ +│ +│ spaced message +", +] +`; + +exports[`log (isCI = false) > message > renders message with custom symbols and spacing 1`] = ` +[ + "-- +>> custom +-- symbols +", +] +`; + +exports[`log (isCI = false) > message > renders message with guide disabled 1`] = ` +[ + " +standalone message +", +] +`; + +exports[`log (isCI = false) > message > renders multiline message 1`] = ` +[ + "│ +│ line 1 +│ line 2 +│ line 3 +", +] +`; + +exports[`log (isCI = false) > message > renders multiline message with guide disabled 1`] = ` +[ + " +line 1 +line 2 +line 3 +", +] +`; + +exports[`log (isCI = false) > step > renders step message 1`] = ` +[ + "│ +◇ step message +", +] +`; + +exports[`log (isCI = false) > success > renders success message 1`] = ` +[ + "│ +◆ success message +", +] +`; + +exports[`log (isCI = false) > warn > renders warn message 1`] = ` +[ + "│ +▲ warn message +", +] +`; + +exports[`log (isCI = true) > error > renders error message 1`] = ` +[ + "│ +■ error message +", +] +`; + +exports[`log (isCI = true) > info > renders info message 1`] = ` +[ + "│ +● info message +", +] +`; + +exports[`log (isCI = true) > message > renders message 1`] = ` +[ + "│ +│ message +", +] +`; + +exports[`log (isCI = true) > message > renders message from array 1`] = ` +[ + "│ +│ line 1 +│ line 2 +│ line 3 +", +] +`; + +exports[`log (isCI = true) > message > renders message with custom spacing 1`] = ` +[ + "│ +│ +│ +│ spaced message +", +] +`; + +exports[`log (isCI = true) > message > renders message with custom symbols and spacing 1`] = ` +[ + "-- +>> custom +-- symbols +", +] +`; + +exports[`log (isCI = true) > message > renders message with guide disabled 1`] = ` +[ + " +standalone message +", +] +`; + +exports[`log (isCI = true) > message > renders multiline message 1`] = ` +[ + "│ +│ line 1 +│ line 2 +│ line 3 +", +] +`; + +exports[`log (isCI = true) > message > renders multiline message with guide disabled 1`] = ` +[ + " +line 1 +line 2 +line 3 +", +] +`; + +exports[`log (isCI = true) > step > renders step message 1`] = ` +[ + "│ +◇ step message +", +] +`; + +exports[`log (isCI = true) > success > renders success message 1`] = ` +[ + "│ +◆ success message +", +] +`; + +exports[`log (isCI = true) > warn > renders warn message 1`] = ` +[ + "│ +▲ warn message +", +] +`; diff --git a/packages/prompts/test/__snapshots__/text.test.ts.snap b/packages/prompts/test/__snapshots__/text.test.ts.snap index 29abf5b4..43c38c78 100644 --- a/packages/prompts/test/__snapshots__/text.test.ts.snap +++ b/packages/prompts/test/__snapshots__/text.test.ts.snap @@ -71,6 +71,23 @@ exports[`text (isCI = false) > empty string when no value and no default 1`] = ` ] `; +exports[`text (isCI = false) > global withGuide: false removes guide 1`] = ` +[ + "", + "◆ foo +_ + +", + "", + "", + "◇ foo +", + " +", + "", +] +`; + exports[`text (isCI = false) > placeholder is not used as value when pressing enter 1`] = ` [ "", @@ -263,6 +280,23 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = ` ] `; +exports[`text (isCI = false) > withGuide: false removes guide 1`] = ` +[ + "", + "◆ foo +_ + +", + "", + "", + "◇ foo +", + " +", + "", +] +`; + exports[`text (isCI = true) > can be aborted by a signal 1`] = ` [ "", @@ -334,6 +368,23 @@ exports[`text (isCI = true) > empty string when no value and no default 1`] = ` ] `; +exports[`text (isCI = true) > global withGuide: false removes guide 1`] = ` +[ + "", + "◆ foo +_ + +", + "", + "", + "◇ foo +", + " +", + "", +] +`; + exports[`text (isCI = true) > placeholder is not used as value when pressing enter 1`] = ` [ "", @@ -525,3 +576,20 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = ` "", ] `; + +exports[`text (isCI = true) > withGuide: false removes guide 1`] = ` +[ + "", + "◆ foo +_ + +", + "", + "", + "◇ foo +", + " +", + "", +] +`; diff --git a/packages/prompts/test/box.test.ts b/packages/prompts/test/box.test.ts index 31105984..faa34a4d 100644 --- a/packages/prompts/test/box.test.ts +++ b/packages/prompts/test/box.test.ts @@ -1,3 +1,4 @@ +import { updateSettings } from '@clack/core'; import colors from 'picocolors'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; import * as prompts from '../src/index.js'; @@ -24,6 +25,7 @@ describe.each(['true', 'false'])('box (isCI = %s)', (isCI) => { afterEach(() => { vi.restoreAllMocks(); + updateSettings({ withGuide: true }); }); test('renders message', () => { @@ -96,11 +98,23 @@ describe.each(['true', 'false'])('box (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); - test('renders with prefix when includePrefix is true', () => { + test('renders without guide when withGuide is false', () => { + prompts.box('message', 'title', { + input, + output, + withGuide: false, + width: 'auto', + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders without guide when global withGuide is false', () => { + updateSettings({ withGuide: false }); + prompts.box('message', 'title', { input, output, - includePrefix: true, width: 'auto', }); diff --git a/packages/prompts/test/log.test.ts b/packages/prompts/test/log.test.ts new file mode 100644 index 00000000..57fa0d42 --- /dev/null +++ b/packages/prompts/test/log.test.ts @@ -0,0 +1,139 @@ +import colors from 'picocolors'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import * as prompts from '../src/index.js'; +import { MockWritable } from './test-utils.js'; + +describe.each(['true', 'false'])('log (isCI = %s)', (isCI) => { + let originalCI: string | undefined; + let output: MockWritable; + + beforeAll(() => { + originalCI = process.env.CI; + process.env.CI = isCI; + }); + + afterAll(() => { + process.env.CI = originalCI; + }); + + beforeEach(() => { + output = new MockWritable(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('message', () => { + test('renders message', () => { + prompts.log.message('message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders multiline message', () => { + prompts.log.message('line 1\nline 2\nline 3', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders message from array', () => { + prompts.log.message(['line 1', 'line 2', 'line 3'], { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders message with custom symbols and spacing', () => { + prompts.log.message('custom\nsymbols', { + symbol: colors.red('>>'), + secondarySymbol: colors.yellow('--'), + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders message with guide disabled', () => { + prompts.log.message('standalone message', { + withGuide: false, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders multiline message with guide disabled', () => { + prompts.log.message('line 1\nline 2\nline 3', { + withGuide: false, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders message with custom spacing', () => { + prompts.log.message('spaced message', { + spacing: 3, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('info', () => { + test('renders info message', () => { + prompts.log.info('info message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('success', () => { + test('renders success message', () => { + prompts.log.success('success message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('step', () => { + test('renders step message', () => { + prompts.log.step('step message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('warn', () => { + test('renders warn message', () => { + prompts.log.warn('warn message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('error', () => { + test('renders error message', () => { + prompts.log.error('error message', { + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/prompts/test/text.test.ts b/packages/prompts/test/text.test.ts index 13a00bab..62de9067 100644 --- a/packages/prompts/test/text.test.ts +++ b/packages/prompts/test/text.test.ts @@ -1,3 +1,4 @@ +import { updateSettings } from '@clack/core'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; import * as prompts from '../src/index.js'; import { MockReadable, MockWritable } from './test-utils.js'; @@ -23,6 +24,7 @@ describe.each(['true', 'false'])('text (isCI = %s)', (isCI) => { afterEach(() => { vi.restoreAllMocks(); + updateSettings({ withGuide: true }); }); test('renders message', async () => { @@ -205,4 +207,35 @@ describe.each(['true', 'false'])('text (isCI = %s)', (isCI) => { expect(prompts.isCancel(value)).toBe(true); expect(output.buffer).toMatchSnapshot(); }); + + test('withGuide: false removes guide', async () => { + const result = prompts.text({ + message: 'foo', + withGuide: false, + input, + output, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + + expect(output.buffer).toMatchSnapshot(); + }); + + test('global withGuide: false removes guide', async () => { + updateSettings({ withGuide: false }); + + const result = prompts.text({ + message: 'foo', + input, + output, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + + expect(output.buffer).toMatchSnapshot(); + }); }); diff --git a/packages/prompts/tsconfig.json b/packages/prompts/tsconfig.json index 596e2cf7..ef502e89 100644 --- a/packages/prompts/tsconfig.json +++ b/packages/prompts/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src", "test"] } diff --git a/tsconfig.json b/tsconfig.json index 5350b4a8..1b74280c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,8 @@ "skipLibCheck": true, "isolatedModules": true, "verbatimModuleSyntax": true, + "noUnusedParameters": true, + "noUnusedLocals": true, "lib": ["ES2022"], "paths": { "@clack/core": ["./packages/core/src/index.ts"],