[READ-ONLY] Mirror of https://github.com/bombshell-dev/clack. Effortlessly build beautiful command-line apps bomb.sh/docs/clack/basics/getting-started/
cli command-line command-line-app node prompt prompts
5

Configure Feed

Select the types of activity you want to include in your feed.

refactor: replaces picocolors with node:util's styleText

Paul Valladares (Jun 12, 2025, 2:50 PM -0500) ad4b715c 99831e90

+350 -309
+12 -12
examples/basic/autocomplete-multiselect.ts
··· 1 1 import * as p from '@clack/prompts'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 4 4 /** 5 5 * Example demonstrating the integrated autocomplete multiselect component ··· 9 9 async function main() { 10 10 console.clear(); 11 11 12 - p.intro(`${color.bgCyan(color.black(' Integrated Autocomplete Multiselect Example '))}`); 12 + p.intro(`${styleText('bgCyan', styleText('black', ' Integrated Autocomplete Multiselect Example '))}`); 13 13 14 14 p.note( 15 15 ` 16 - ${color.cyan('Filter and select multiple items in a single interface:')} 17 - - ${color.yellow('Type')} to filter the list in real-time 18 - - Use ${color.yellow('up/down arrows')} to navigate with improved stability 19 - - Press ${color.yellow('Space')} to select/deselect the highlighted item ${color.green('(multiple selections allowed)')} 20 - - Use ${color.yellow('Backspace')} to modify your filter text when searching for different options 21 - - Press ${color.yellow('Enter')} when done selecting all items 22 - - Press ${color.yellow('Ctrl+C')} to cancel 16 + ${styleText('cyan', 'Filter and select multiple items in a single interface:')} 17 + - ${styleText('yellow', 'Type')} to filter the list in real-time 18 + - Use ${styleText('yellow', 'up/down arrows')} to navigate with improved stability 19 + - Press ${styleText('yellow', 'Space')} to select/deselect the highlighted item ${styleText('green', '(multiple selections allowed)')} 20 + - Use ${styleText('yellow', 'Backspace')} to modify your filter text when searching for different options 21 + - Press ${styleText('yellow', 'Enter')} when done selecting all items 22 + - Press ${styleText('yellow', 'Ctrl+C')} to cancel 23 23 `, 24 24 'Instructions' 25 25 ); ··· 79 79 80 80 // Display selected frameworks with detailed information 81 81 p.note( 82 - `You selected ${color.green(selectedFrameworks.length)} frameworks:`, 82 + `You selected ${styleText('green', `${selectedFrameworks.length}`)} frameworks:`, 83 83 'Selection Complete' 84 84 ); 85 85 ··· 88 88 .map((value) => { 89 89 const framework = frameworks.find((f) => f.value === value); 90 90 return framework 91 - ? `${color.cyan(framework.label)} ${color.dim(`- ${framework.hint}`)}` 91 + ? `${styleText('cyan', framework.label)} ${styleText('dim', `- ${framework.hint}`)}` 92 92 : value; 93 93 }) 94 94 .join('\n'); 95 95 96 96 p.log.message(selectedDetails); 97 - p.outro(`Successfully selected ${color.green(selectedFrameworks.length)} frameworks.`); 97 + p.outro(`Successfully selected ${styleText('green', `${selectedFrameworks.length}`)} frameworks.`); 98 98 } 99 99 100 100 main().catch(console.error);
+14 -8
examples/basic/autocomplete.ts
··· 1 1 import * as p from '@clack/prompts'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 4 4 async function main() { 5 5 console.clear(); 6 6 7 - p.intro(`${color.bgCyan(color.black(' Autocomplete Example '))}`); 7 + p.intro(`${styleText('bgCyan', styleText('black', ' Autocomplete Example '))}`); 8 8 9 9 p.note( 10 10 ` 11 - ${color.cyan('This example demonstrates the type-ahead autocomplete feature:')} 12 - - ${color.yellow('Type')} to filter the list in real-time 13 - - Use ${color.yellow('up/down arrows')} to navigate the filtered results 14 - - Press ${color.yellow('Enter')} to select the highlighted option 15 - - Press ${color.yellow('Ctrl+C')} to cancel 11 + ${styleText('cyan', 'This example demonstrates the type-ahead autocomplete feature:')} 12 + - ${styleText('yellow', 'Type')} to filter the list in real-time 13 + - Use ${styleText('yellow', 'up/down arrows')} to navigate the filtered results 14 + - Press ${styleText('yellow', 'Enter')} to select the highlighted option 15 + - Press ${styleText('yellow', 'Ctrl+C')} to cancel 16 16 `, 17 17 'Instructions' 18 18 ); ··· 53 53 } 54 54 55 55 const selected = countries.find((c) => c.value === result); 56 - p.outro(`You selected: ${color.cyan(selected?.label)} (${color.yellow(selected?.hint)})`); 56 + 57 + if (!selected) { 58 + p.outro('No country selected.'); 59 + process.exit(1); 60 + } 61 + 62 + p.outro(`You selected: ${styleText('cyan', selected?.label)} (${styleText('yellow', selected?.hint)})`); 57 63 } 58 64 59 65 main().catch(console.error);
+2 -2
examples/basic/default-value.ts
··· 1 1 import * as p from '@clack/prompts'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 4 4 async function main() { 5 5 const defaultPath = 'my-project'; ··· 24 24 process.exit(0); 25 25 } 26 26 27 - p.outro(`Let's bootstrap the project in ${color.cyan(result)}`); 27 + p.outro(`Let's bootstrap the project in ${styleText('cyan', result)}`); 28 28 } 29 29 30 30 main().catch(console.error);
+3 -3
examples/basic/index.ts
··· 1 1 import { setTimeout } from 'node:timers/promises'; 2 2 import * as p from '@clack/prompts'; 3 - import color from 'picocolors'; 3 + import { styleText } from 'node:util'; 4 4 5 5 async function main() { 6 6 console.clear(); ··· 16 16 }, 17 17 }); 18 18 19 - p.intro(`${color.bgCyan(color.black(' create-app '))}`); 19 + p.intro(`${styleText('bgCyan', styleText('black', ' create-app '))}`); 20 20 21 21 const project = await p.group( 22 22 { ··· 87 87 88 88 p.note(nextSteps, 'Next steps.'); 89 89 90 - p.outro(`Problems? ${color.underline(color.cyan('https://example.com/issues'))}`); 90 + p.outro(`Problems? ${styleText('underline', styleText('cyan', 'https://example.com/issues'))}`); 91 91 } 92 92 93 93 main().catch(console.error);
+7 -2
examples/basic/package.json
··· 5 5 "type": "module", 6 6 "dependencies": { 7 7 "@clack/prompts": "workspace:*", 8 - "picocolors": "^1.0.0", 9 8 "jiti": "^1.17.0" 10 9 }, 11 10 "scripts": { 11 + "autocomplete": "jiti ./autocomplete.ts", 12 + "autocomplete-multiselect": "jiti ./autocomplete-multiselect.ts", 13 + "default-value": "jiti ./default-value.ts", 14 + "spinner-cancel": "jiti ./spinner-cancel.ts", 15 + "spinner-cancel-advanced": "jiti ./spinner-cancel-advanced.ts", 12 16 "start": "jiti ./index.ts", 13 17 "stream": "jiti ./stream.ts", 14 18 "progress": "jiti ./progress.ts", ··· 16 20 "path": "jiti ./path.ts", 17 21 "spinner-ci": "npx cross-env CI=\"true\" jiti ./spinner-ci.ts", 18 22 "spinner-timer": "jiti ./spinner-timer.ts", 19 - "task-log": "jiti ./task-log.ts" 23 + "task-log": "jiti ./task-log.ts", 24 + "text-validation": "jiti ./text-validation.ts" 20 25 }, 21 26 "devDependencies": { 22 27 "cross-env": "^7.0.3"
+1 -1
examples/basic/spinner-ci.ts
··· 14 14 const s = p.spinner(); 15 15 let progress = 0; 16 16 let counter = 0; 17 - let loop: NodeJS.Timer; 17 + let loop: NodeJS.Timeout; 18 18 19 19 p.intro('Running spinner in CI environment'); 20 20 s.start('spinner.start');
+3 -3
examples/basic/stream.ts
··· 1 1 import { setTimeout } from 'node:timers/promises'; 2 2 import * as p from '@clack/prompts'; 3 - import color from 'picocolors'; 3 + import { styleText } from 'node:util'; 4 4 5 5 async function main() { 6 6 console.clear(); 7 7 8 8 await setTimeout(1000); 9 9 10 - p.intro(`${color.bgCyan(color.black(' create-app '))}`); 10 + p.intro(`${styleText('bgCyan', styleText('black', ' create-app '))}`); 11 11 12 12 await p.stream.step( 13 13 (async function* () { ··· 25 25 })() 26 26 ); 27 27 28 - p.outro(`Problems? ${color.underline(color.cyan('https://example.com/issues'))}`); 28 + p.outro(`Problems? ${styleText('underline', styleText('cyan', 'https://example.com/issues'))}`); 29 29 } 30 30 31 31 const lorem = [
+2
examples/basic/text-validation.ts
··· 9 9 message: 'Enter your name (letters and spaces only)', 10 10 initialValue: 'John123', // Invalid initial value with numbers 11 11 validate: (value) => { 12 + if (!value) return 'Name is required'; 12 13 if (!/^[a-zA-Z\s]+$/.test(value)) return 'Name can only contain letters and spaces'; 13 14 return undefined; 14 15 }, ··· 25 26 message: 'Enter another name (letters and spaces only)', 26 27 initialValue: 'John Doe', // Valid initial value 27 28 validate: (value) => { 29 + if (!value) return 'Name is required'; 28 30 if (!/^[a-zA-Z\s]+$/.test(value)) return 'Name can only contain letters and spaces'; 29 31 return undefined; 30 32 },
+7 -7
examples/changesets/index.ts
··· 1 1 import { setTimeout } from 'node:timers/promises'; 2 2 import * as p from '@clack/prompts'; 3 - import color from 'picocolors'; 3 + import { styleText } from 'node:util'; 4 4 5 5 function onCancel() { 6 6 p.cancel('Operation cancelled.'); ··· 12 12 13 13 await setTimeout(1000); 14 14 15 - p.intro(`${color.bgCyan(color.black(' changesets '))}`); 15 + p.intro(`${styleText('bgCyan', styleText('black', ' changesets '))}`); 16 16 17 17 const changeset = await p.group( 18 18 { ··· 35 35 major: ({ results }) => { 36 36 const packages = results.packages ?? []; 37 37 return p.multiselect({ 38 - message: `Which packages should have a ${color.red('major')} bump?`, 38 + message: `Which packages should have a ${styleText('red', 'major')} bump?`, 39 39 options: packages.map((value) => ({ value })), 40 40 required: false, 41 41 }); ··· 46 46 const possiblePackages = packages.filter((pkg) => !major.includes(pkg)); 47 47 if (possiblePackages.length === 0) return; 48 48 return p.multiselect({ 49 - message: `Which packages should have a ${color.yellow('minor')} bump?`, 49 + message: `Which packages should have a ${styleText('yellow', 'minor')} bump?`, 50 50 options: possiblePackages.map((value) => ({ value })), 51 51 required: false, 52 52 }); ··· 59 59 (pkg) => !major.includes(pkg) && !minor.includes(pkg) 60 60 ); 61 61 if (possiblePackages.length === 0) return; 62 - const note = possiblePackages.join(color.dim(', ')); 62 + const note = possiblePackages.join(styleText('dim', ', ')); 63 63 64 - p.log.step(`These packages will have a ${color.green('patch')} bump.\n${color.dim(note)}`); 64 + p.log.step(`These packages will have a ${styleText('green', 'patch')} bump.\n${styleText('dim', note)}`); 65 65 return possiblePackages; 66 66 }, 67 67 }, ··· 79 79 return onCancel(); 80 80 } 81 81 82 - p.outro(`Changeset added! ${color.underline(color.cyan('.changeset/orange-crabs-sing.md'))}`); 82 + p.outro(`Changeset added! ${styleText('underline', styleText('cyan', '.changeset/orange-crabs-sing.md'))}`); 83 83 } 84 84 85 85 main().catch(console.error);
+1 -2
examples/changesets/package.json
··· 5 5 "type": "module", 6 6 "dependencies": { 7 7 "jiti": "^1.17.0", 8 - "@clack/prompts": "workspace:*", 9 - "picocolors": "^1.0.0" 8 + "@clack/prompts": "workspace:*" 10 9 }, 11 10 "scripts": { 12 11 "start": "jiti ./index.ts"
+4 -4
package.json
··· 17 17 "devDependencies": { 18 18 "@biomejs/biome": "1.9.4", 19 19 "@changesets/cli": "^2.26.2", 20 - "@types/node": "^18.16.0", 20 + "@types/node": "^20.12.0", 21 + "jsr": "^0.13.4", 21 22 "knip": "^5.50.4", 22 23 "typescript": "^5.8.3", 23 - "unbuild": "^2.0.0", 24 - "jsr": "^0.13.4" 24 + "unbuild": "^2.0.0" 25 25 }, 26 - "packageManager": "pnpm@9.14.2", 26 + "packageManager": "pnpm@10.12.1", 27 27 "volta": { 28 28 "node": "20.18.1" 29 29 }
-1
packages/core/package.json
··· 50 50 "test": "vitest run" 51 51 }, 52 52 "dependencies": { 53 - "picocolors": "^1.0.0", 54 53 "sisteransi": "^1.0.5" 55 54 }, 56 55 "devDependencies": {
+3 -3
packages/core/src/prompts/autocomplete.ts
··· 1 1 import type { Key } from 'node:readline'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 import Prompt, { type PromptOptions } from './prompt.js'; 4 4 5 5 interface OptionLike { ··· 71 71 72 72 get userInputWithCursor() { 73 73 if (!this.userInput) { 74 - return color.inverse(color.hidden('_')); 74 + return styleText(['inverse', 'hidden'], '_'); 75 75 } 76 76 if (this._cursor >= this.userInput.length) { 77 77 return `${this.userInput}█`; 78 78 } 79 79 const s1 = this.userInput.slice(0, this._cursor); 80 80 const [s2, ...s3] = this.userInput.slice(this._cursor); 81 - return `${s1}${color.inverse(s2)}${s3.join('')}`; 81 + return `${s1}${styleText(['inverse'], s2)}${s3.join('')}`; 82 82 } 83 83 84 84 get options(): T[] {
+3 -3
packages/core/src/prompts/password.ts
··· 1 - import color from 'picocolors'; 1 + import { styleText } from 'node:util'; 2 2 import Prompt, { type PromptOptions } from './prompt.js'; 3 3 4 4 interface PasswordOptions extends PromptOptions<string, PasswordPrompt> { ··· 18 18 } 19 19 const userInput = this.userInput; 20 20 if (this.cursor >= userInput.length) { 21 - return `${this.masked}${color.inverse(color.hidden('_'))}`; 21 + return `${this.masked}${styleText(['inverse', 'hidden'], '_')}`; 22 22 } 23 23 const masked = this.masked; 24 24 const s1 = masked.slice(0, this.cursor); 25 25 const s2 = masked.slice(this.cursor); 26 - return `${s1}${color.inverse(s2[0])}${s2.slice(1)}`; 26 + return `${s1}${styleText(['inverse'], s2[0])}${s2.slice(1)}`; 27 27 } 28 28 constructor({ mask, ...opts }: PasswordOptions) { 29 29 super(opts);
+2 -2
packages/core/src/prompts/text.ts
··· 1 - import color from 'picocolors'; 1 + import { styleText } from 'node:util'; 2 2 import Prompt, { type PromptOptions } from './prompt.js'; 3 3 4 4 interface TextOptions extends PromptOptions<string, TextPrompt> { ··· 17 17 } 18 18 const s1 = userInput.slice(0, this.cursor); 19 19 const [s2, ...s3] = userInput.slice(this.cursor); 20 - return `${s1}${color.inverse(s2)}${s3.join('')}`; 20 + return `${s1}${styleText(['inverse'], s2)}${s3.join('')}`; 21 21 } 22 22 get cursor() { 23 23 return this._cursor;
-1
packages/core/test/prompts/confirm.test.ts
··· 1 - import color from 'picocolors'; 2 1 import { cursor } from 'sisteransi'; 3 2 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; 4 3 import { default as ConfirmPrompt } from '../../src/prompts/confirm.js';
+4 -4
packages/core/test/prompts/password.test.ts
··· 1 - import color from 'picocolors'; 1 + import { styleText } from 'node:util'; 2 2 import { cursor } from 'sisteransi'; 3 3 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; 4 4 import { default as PasswordPrompt } from '../../src/prompts/password.js'; ··· 65 65 }); 66 66 instance.prompt(); 67 67 input.emit('keypress', 'x', { name: 'x' }); 68 - expect(instance.userInputWithCursor).to.equal(`•${color.inverse(color.hidden('_'))}`); 68 + expect(instance.userInputWithCursor).to.equal(`•${styleText('inverse', styleText('hidden', '_'))}`); 69 69 }); 70 70 71 71 test('renders cursor inside value', () => { ··· 80 80 input.emit('keypress', 'z', { name: 'z' }); 81 81 input.emit('keypress', 'left', { name: 'left' }); 82 82 input.emit('keypress', 'left', { name: 'left' }); 83 - expect(instance.userInputWithCursor).to.equal(`•${color.inverse('•')}•`); 83 + expect(instance.userInputWithCursor).to.equal(`•${styleText('inverse', '•')}•`); 84 84 }); 85 85 86 86 test('renders custom mask', () => { ··· 92 92 }); 93 93 instance.prompt(); 94 94 input.emit('keypress', 'x', { name: 'x' }); 95 - expect(instance.userInputWithCursor).to.equal(`X${color.inverse(color.hidden('_'))}`); 95 + expect(instance.userInputWithCursor).to.equal(`X${styleText('inverse', styleText('hidden', '_'))}`); 96 96 }); 97 97 }); 98 98 });
-1
packages/core/test/prompts/select.test.ts
··· 1 - import color from 'picocolors'; 2 1 import { cursor } from 'sisteransi'; 3 2 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; 4 3 import { default as SelectPrompt } from '../../src/prompts/select.js';
+2 -2
packages/core/test/prompts/text.test.ts
··· 1 - import color from 'picocolors'; 1 + import { styleText } from "node:util" 2 2 import { cursor } from 'sisteransi'; 3 3 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; 4 4 import { default as TextPrompt } from '../../src/prompts/text.js'; ··· 93 93 input.emit('keypress', keys[i], { name: keys[i] }); 94 94 } 95 95 input.emit('keypress', 'left', { name: 'left' }); 96 - expect(instance.userInputWithCursor).to.equal(`fo${color.inverse('o')}`); 96 + expect(instance.userInputWithCursor).to.equal(`fo${styleText('inverse', 'o')}`); 97 97 }); 98 98 99 99 test('shows cursor at end if beyond value', () => {
-1
packages/prompts/package.json
··· 51 51 }, 52 52 "dependencies": { 53 53 "@clack/core": "workspace:*", 54 - "picocolors": "^1.0.0", 55 54 "sisteransi": "^1.0.5" 56 55 }, 57 56 "devDependencies": {
+60 -39
packages/prompts/src/autocomplete.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { AutocompletePrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { 4 4 type CommonOptions, 5 5 S_BAR, ··· 89 89 validate: opts.validate, 90 90 render() { 91 91 // Title and message display 92 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 92 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n)}`; 93 93 const userInput = this.userInput; 94 94 const valueAsString = String(this.value ?? ''); 95 95 const options = this.options; ··· 102 102 // Show selected value 103 103 const selected = getSelectedOptions(this.selectedValues, options); 104 104 const label = selected.length > 0 ? selected.map(getLabel).join(', ') : ''; 105 - return `${title}${color.gray(S_BAR)} ${color.dim(label)}`; 105 + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', label)}`; 106 106 } 107 107 108 108 case 'cancel': { 109 - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(userInput))}`; 109 + return `${title}${styleText('gray', S_BAR)} ${styleText( 110 + 'strikethrough', 111 + styleText('dim', userInput) 112 + )}`; 110 113 } 111 114 112 115 default: { 113 116 // Display cursor position - show plain text in navigation mode 114 117 const searchText = 115 118 this.isNavigating || showPlaceholder 116 - ? color.dim(showPlaceholder ? placeholder : userInput) 119 + ? styleText('dim', showPlaceholder ? placeholder : userInput) 117 120 : this.userInputWithCursor; 118 121 119 122 // Show match count if filtered 120 123 const matches = 121 124 this.filteredOptions.length !== options.length 122 - ? color.dim( 123 - ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})` 125 + ? styleText( 126 + 'dim', 127 + ` (${this.filteredOptions.length} match${ 128 + this.filteredOptions.length === 1 ? '' : 'es' 129 + })` 124 130 ) 125 131 : ''; 126 132 ··· 135 141 const label = getLabel(option); 136 142 const hint = 137 143 option.hint && option.value === this.focusedValue 138 - ? color.dim(` (${option.hint})`) 144 + ? styleText('dim', ` (${option.hint})`) 139 145 : ''; 140 146 141 147 return active 142 - ? `${color.green(S_RADIO_ACTIVE)} ${label}${hint}` 143 - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}${hint}`; 148 + ? `${styleText('green', S_RADIO_ACTIVE)} ${label}${hint}` 149 + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}${hint}`; 144 150 }, 145 151 maxItems: opts.maxItems, 146 152 output: opts.output, ··· 148 154 149 155 // Show instructions 150 156 const instructions = [ 151 - `${color.dim('↑/↓')} to select`, 152 - `${color.dim('Enter:')} confirm`, 153 - `${color.dim('Type:')} to search`, 157 + `${styleText('dim', '↑/↓')} to select`, 158 + `${styleText('dim', 'Enter:')} confirm`, 159 + `${styleText('dim', 'Type:')} to search`, 154 160 ]; 155 161 156 162 // No matches message 157 163 const noResults = 158 164 this.filteredOptions.length === 0 && userInput 159 - ? [`${color.cyan(S_BAR)} ${color.yellow('No matches found')}`] 165 + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', 'No matches found')}`] 160 166 : []; 161 167 162 168 const validationError = 163 - this.state === 'error' ? [`${color.yellow(S_BAR)} ${color.yellow(this.error)}`] : []; 169 + this.state === 'error' 170 + ? [`${styleText('yellow', S_BAR)} ${styleText('yellow', this.error)}`] 171 + : []; 164 172 165 173 // Return the formatted prompt 166 174 return [ 167 175 title, 168 - `${color.cyan(S_BAR)} ${color.dim('Search:')} ${searchText}${matches}`, 176 + `${styleText('cyan', S_BAR)} ${styleText('dim', 'Search:')} ${searchText}${matches}`, 169 177 ...noResults, 170 178 ...validationError, 171 - ...displayOptions.map((option) => `${color.cyan(S_BAR)} ${option}`), 172 - `${color.cyan(S_BAR)} ${color.dim(instructions.join(' • '))}`, 173 - `${color.cyan(S_BAR_END)}`, 179 + ...displayOptions.map((option) => `${styleText('cyan', S_BAR)} ${option}`), 180 + `${styleText('cyan', S_BAR)} ${styleText('dim', instructions.join(' • '))}`, 181 + `${styleText('cyan', S_BAR_END)}`, 174 182 ].join('\n'); 175 183 } 176 184 } ··· 207 215 const label = option.label ?? String(option.value ?? ''); 208 216 const hint = 209 217 option.hint && focusedValue !== undefined && option.value === focusedValue 210 - ? color.dim(` (${option.hint})`) 218 + ? styleText('dim', ` (${option.hint})`) 211 219 : ''; 212 - const checkbox = isSelected ? color.green(S_CHECKBOX_SELECTED) : color.dim(S_CHECKBOX_INACTIVE); 220 + const checkbox = isSelected 221 + ? styleText('green', S_CHECKBOX_SELECTED) 222 + : styleText('dim', S_CHECKBOX_INACTIVE); 213 223 214 224 if (active) { 215 225 return `${checkbox} ${label}${hint}`; 216 226 } 217 - return `${checkbox} ${color.dim(label)}`; 227 + return `${checkbox} ${styleText('dim', label)}${hint}`; 218 228 }; 219 229 220 230 // Create text prompt which we'll use as foundation ··· 236 246 output: opts.output, 237 247 render() { 238 248 // Title and symbol 239 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 249 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 240 250 241 251 // Selection counter 242 252 const userInput = this.userInput; ··· 246 256 // Search input display 247 257 const searchText = 248 258 this.isNavigating || showPlaceholder 249 - ? color.dim(showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode 259 + ? styleText('dim', showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode 250 260 : this.userInputWithCursor; 251 261 252 262 const options = this.options; 253 263 254 264 const matches = 255 265 this.filteredOptions.length !== options.length 256 - ? color.dim( 257 - ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})` 258 - ) 266 + ? styleText( 267 + 'dim', 268 + ` (${this.filteredOptions.length} match${ 269 + this.filteredOptions.length === 1 ? '' : 'es' 270 + })` 271 + ) 259 272 : ''; 260 273 261 274 // Render prompt state 262 275 switch (this.state) { 263 276 case 'submit': { 264 - return `${title}${color.gray(S_BAR)} ${color.dim(`${this.selectedValues.length} items selected`)}`; 277 + return `${title}${styleText('gray', S_BAR)} ${styleText( 278 + 'dim', 279 + `${this.selectedValues.length} items selected` 280 + )}`; 265 281 } 266 282 case 'cancel': { 267 - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(userInput))}`; 283 + return `${title}${styleText('gray', S_BAR)} ${styleText( 284 + 'strikethrough', 285 + styleText('dim', userInput) 286 + )}`; 268 287 } 269 288 default: { 270 289 // Instructions 271 290 const instructions = [ 272 - `${color.dim('↑/↓')} to navigate`, 273 - `${color.dim('Space:')} select`, 274 - `${color.dim('Enter:')} confirm`, 275 - `${color.dim('Type:')} to search`, 291 + `${styleText('dim', '↑/↓')} to navigate`, 292 + `${styleText('dim', 'Space:')} select`, 293 + `${styleText('dim', 'Enter:')} confirm`, 294 + `${styleText('dim', 'Type:')} to search`, 276 295 ]; 277 296 278 297 // No results message 279 298 const noResults = 280 299 this.filteredOptions.length === 0 && userInput 281 - ? [`${color.cyan(S_BAR)} ${color.yellow('No matches found')}`] 300 + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', 'No matches found')}`] 282 301 : []; 283 302 284 303 const errorMessage = 285 - this.state === 'error' ? [`${color.cyan(S_BAR)} ${color.yellow(this.error)}`] : []; 304 + this.state === 'error' 305 + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', this.error)}`] 306 + : []; 286 307 287 308 // Get limited options for display 288 309 const displayOptions = limitOptions({ ··· 297 318 // Build the prompt display 298 319 return [ 299 320 title, 300 - `${color.cyan(S_BAR)} ${color.dim('Search:')} ${searchText}${matches}`, 321 + `${styleText('cyan', S_BAR)} ${styleText('dim', 'Search:')} ${searchText}${matches}`, 301 322 ...noResults, 302 323 ...errorMessage, 303 - ...displayOptions.map((option) => `${color.cyan(S_BAR)} ${option}`), 304 - `${color.cyan(S_BAR)} ${color.dim(instructions.join(' • '))}`, 305 - `${color.cyan(S_BAR_END)}`, 324 + ...displayOptions.map((option) => `${styleText('cyan', S_BAR)} ${option}`), 325 + `${styleText('cyan', S_BAR)} ${styleText('dim', instructions.join(' • '))}`, 326 + `${styleText('cyan', S_BAR_END)}`, 306 327 ].join('\n'); 307 328 } 308 329 }
+5 -5
packages/prompts/src/common.ts
··· 1 1 import type { Readable, Writable } from 'node:stream'; 2 + import { styleText } from 'node:util'; 2 3 import type { State } from '@clack/core'; 3 4 import isUnicodeSupported from 'is-unicode-supported'; 4 - import color from 'picocolors'; 5 5 6 6 export const unicode = isUnicodeSupported(); 7 7 export const isCI = (): boolean => process.env.CI === 'true'; ··· 36 36 switch (state) { 37 37 case 'initial': 38 38 case 'active': 39 - return color.cyan(S_STEP_ACTIVE); 39 + return styleText('cyan', S_STEP_ACTIVE); 40 40 case 'cancel': 41 - return color.red(S_STEP_CANCEL); 41 + return styleText('red', S_STEP_CANCEL); 42 42 case 'error': 43 - return color.yellow(S_STEP_ERROR); 43 + return styleText('yellow', S_STEP_ERROR); 44 44 case 'submit': 45 - return color.green(S_STEP_SUBMIT); 45 + return styleText('green', S_STEP_SUBMIT); 46 46 } 47 47 }; 48 48
+11 -13
packages/prompts/src/confirm.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { ConfirmPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { 4 4 type CommonOptions, 5 5 S_BAR, ··· 26 26 output: opts.output, 27 27 initialValue: opts.initialValue ?? true, 28 28 render() { 29 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 29 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 30 30 const value = this.value ? active : inactive; 31 31 32 32 switch (this.state) { 33 33 case 'submit': 34 - return `${title}${color.gray(S_BAR)} ${color.dim(value)}`; 34 + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', value)}`; 35 35 case 'cancel': 36 - return `${title}${color.gray(S_BAR)} ${color.strikethrough( 37 - color.dim(value) 38 - )}\n${color.gray(S_BAR)}`; 36 + return `${title}${styleText('gray', S_BAR)} ${styleText('strikethrough', styleText('dim', value))}\n${styleText('gray', S_BAR)}`; 39 37 default: { 40 - return `${title}${color.cyan(S_BAR)} ${ 38 + return `${title}${styleText('cyan', S_BAR)} ${ 41 39 this.value 42 - ? `${color.green(S_RADIO_ACTIVE)} ${active}` 43 - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(active)}` 44 - } ${color.dim('/')} ${ 40 + ? `${styleText('green', S_RADIO_ACTIVE)} ${active}` 41 + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', active)}` 42 + } ${styleText('dim', '/')} ${ 45 43 !this.value 46 - ? `${color.green(S_RADIO_ACTIVE)} ${inactive}` 47 - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(inactive)}` 48 - }\n${color.cyan(S_BAR_END)}\n`; 44 + ? `${styleText('green', S_RADIO_ACTIVE)} ${inactive}` 45 + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', inactive)}` 46 + }\n${styleText('cyan', S_BAR_END)}\n`; 49 47 } 50 48 } 51 49 },
+36 -30
packages/prompts/src/group-multi-select.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { GroupMultiSelectPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { 4 4 type CommonOptions, 5 5 S_BAR, ··· 41 41 const isLast = isItem && (next as any).group === true; 42 42 const prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : ''; 43 43 const spacingPrefix = 44 - groupSpacing > 0 && !isItem ? `\n${color.cyan(S_BAR)} `.repeat(groupSpacing) : ''; 44 + // groupSpacing > 0 && !isItem ? `\n${color.cyan(S_BAR)} `.repeat(groupSpacing) : ''; 45 + groupSpacing > 0 && !isItem ? `\n${styleText('cyan', S_BAR)} `.repeat(groupSpacing) : ''; 45 46 46 47 if (state === 'active') { 47 - return `${spacingPrefix}${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ 48 - option.hint ? color.dim(`(${option.hint})`) : '' 48 + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label} ${ 49 + option.hint ? styleText('dim', `(${option.hint})`) : '' 49 50 }`; 50 51 } 51 52 if (state === 'group-active') { 52 - return `${spacingPrefix}${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`; 53 + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${styleText('dim', label)}`; 53 54 } 54 55 if (state === 'group-active-selected') { 55 - return `${spacingPrefix}${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; 56 + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)}`; 56 57 } 57 58 if (state === 'selected') { 58 - const selectedCheckbox = isItem || selectableGroups ? color.green(S_CHECKBOX_SELECTED) : ''; 59 - return `${spacingPrefix}${color.dim(prefix)}${selectedCheckbox} ${color.dim(label)} ${ 60 - option.hint ? color.dim(`(${option.hint})`) : '' 59 + const selectedCheckbox = 60 + isItem || selectableGroups ? styleText('green', S_CHECKBOX_SELECTED) : ''; 61 + return `${spacingPrefix}${styleText('dim', prefix)}${selectedCheckbox} ${styleText('dim', label)} ${ 62 + option.hint ? styleText('dim', `(${option.hint})`) : '' 61 63 }`; 62 64 } 63 65 if (state === 'cancelled') { 64 - return `${color.strikethrough(color.dim(label))}`; 66 + return `${styleText('strikethrough', styleText('dim', label))}`; 65 67 } 66 68 if (state === 'active-selected') { 67 - return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${ 68 - option.hint ? color.dim(`(${option.hint})`) : '' 69 + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${label} ${ 70 + option.hint ? styleText('dim', `(${option.hint})`) : '' 69 71 }`; 70 72 } 71 73 if (state === 'submitted') { 72 - return `${color.dim(label)}`; 74 + return `${styleText('dim', label)}`; 73 75 } 74 - const unselectedCheckbox = isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : ''; 75 - return `${spacingPrefix}${color.dim(prefix)}${unselectedCheckbox} ${color.dim(label)}`; 76 + const unselectedCheckbox = 77 + isItem || selectableGroups ? styleText('dim', S_CHECKBOX_INACTIVE) : ''; 78 + return `${spacingPrefix}${styleText('dim', prefix)}${unselectedCheckbox} ${styleText('dim', label)}`; 76 79 }; 77 80 const required = opts.required ?? true; 78 81 ··· 87 90 selectableGroups, 88 91 validate(selected: Value[] | undefined) { 89 92 if (required && (selected === undefined || selected.length === 0)) 90 - return `Please select at least one option.\n${color.reset( 91 - color.dim( 92 - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( 93 - color.bgWhite(color.inverse(' enter ')) 93 + return `Please select at least one option.\n${styleText( 94 + 'reset', 95 + styleText( 96 + 'dim', 97 + `Press ${styleText('gray', styleText('bgWhite', styleText('inverse', ' space ')))} to select, ${styleText( 98 + 'gray', 99 + styleText('bgWhite', styleText('inverse', ' enter ')) 94 100 )} to submit` 95 101 ) 96 102 )}`; 97 103 }, 98 104 render() { 99 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 105 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 100 106 const value = this.value ?? []; 101 107 102 108 switch (this.state) { 103 109 case 'submit': { 104 - return `${title}${color.gray(S_BAR)} ${this.options 110 + return `${title}${styleText('gray', S_BAR)} ${this.options 105 111 .filter(({ value: optionValue }) => value.includes(optionValue)) 106 112 .map((option) => opt(option, 'submitted')) 107 - .join(color.dim(', '))}`; 113 + .join(styleText('dim', ', '))}`; 108 114 } 109 115 case 'cancel': { 110 116 const label = this.options 111 117 .filter(({ value: optionValue }) => value.includes(optionValue)) 112 118 .map((option) => opt(option, 'cancelled')) 113 - .join(color.dim(', ')); 114 - return `${title}${color.gray(S_BAR)} ${ 115 - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' 119 + .join(styleText('dim', ', ')); 120 + return `${title}${styleText('gray', S_BAR)} ${ 121 + label.trim() ? `${label}\n${styleText('gray', S_BAR)}` : '' 116 122 }`; 117 123 } 118 124 case 'error': { 119 125 const footer = this.error 120 126 .split('\n') 121 127 .map((ln, i) => 122 - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` 128 + i === 0 ? `${styleText('yellow', S_BAR_END)} ${styleText('yellow', ln)}` : ` ${ln}` 123 129 ) 124 130 .join('\n'); 125 - return `${title}${color.yellow(S_BAR)} ${this.options 131 + return `${title}${styleText('yellow', S_BAR)} ${this.options 126 132 .map((option, i, options) => { 127 133 const selected = 128 134 value.includes(option.value) || ··· 143 149 } 144 150 return opt(option, active ? 'active' : 'inactive', options); 145 151 }) 146 - .join(`\n${color.yellow(S_BAR)} `)}\n${footer}\n`; 152 + .join(`\n${styleText('yellow', S_BAR)} `)}\n${footer}\n`; 147 153 } 148 154 default: { 149 - return `${title}${color.cyan(S_BAR)} ${this.options 155 + return `${title}${styleText('cyan', S_BAR)} ${this.options 150 156 .map((option, i, options) => { 151 157 const selected = 152 158 value.includes(option.value) || ··· 167 173 } 168 174 return opt(option, active ? 'active' : 'inactive', options); 169 175 }) 170 - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; 176 + .join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; 171 177 } 172 178 } 173 179 },
+2 -2
packages/prompts/src/limit-options.ts
··· 1 1 import type { Writable } from 'node:stream'; 2 2 import { WriteStream } from 'node:tty'; 3 - import color from 'picocolors'; 3 + import { styleText } from 'node:util'; 4 4 import type { CommonOptions } from './common.js'; 5 5 6 6 export interface LimitOptionsParams<TOption> extends CommonOptions { ··· 14 14 const { cursor, options, style } = params; 15 15 const output: Writable = params.output ?? process.stdout; 16 16 const rows = output instanceof WriteStream && output.rows !== undefined ? output.rows : 10; 17 - const overflowFormat = color.dim('...'); 17 + const overflowFormat = styleText('dim', '...'); 18 18 19 19 const paramMaxItems = params.maxItems ?? Number.POSITIVE_INFINITY; 20 20 const outputMaxItems = Math.max(rows - 4, 0);
+8 -8
packages/prompts/src/log.ts
··· 1 - import color from 'picocolors'; 1 + import { styleText } from 'node:util'; 2 2 import { 3 3 type CommonOptions, 4 4 S_BAR, ··· 19 19 message: ( 20 20 message: string | string[] = [], 21 21 { 22 - symbol = color.gray(S_BAR), 23 - secondarySymbol = color.gray(S_BAR), 22 + symbol = styleText('gray', S_BAR), 23 + secondarySymbol = styleText('gray', S_BAR), 24 24 output = process.stdout, 25 25 spacing = 1, 26 26 }: LogMessageOptions = {} ··· 48 48 output.write(`${parts.join('\n')}\n`); 49 49 }, 50 50 info: (message: string, opts?: LogMessageOptions) => { 51 - log.message(message, { ...opts, symbol: color.blue(S_INFO) }); 51 + log.message(message, { ...opts, symbol: styleText('blue', S_INFO) }); 52 52 }, 53 53 success: (message: string, opts?: LogMessageOptions) => { 54 - log.message(message, { ...opts, symbol: color.green(S_SUCCESS) }); 54 + log.message(message, { ...opts, symbol: styleText('green', S_SUCCESS) }); 55 55 }, 56 56 step: (message: string, opts?: LogMessageOptions) => { 57 - log.message(message, { ...opts, symbol: color.green(S_STEP_SUBMIT) }); 57 + log.message(message, { ...opts, symbol: styleText('green', S_STEP_SUBMIT) }); 58 58 }, 59 59 warn: (message: string, opts?: LogMessageOptions) => { 60 - log.message(message, { ...opts, symbol: color.yellow(S_WARN) }); 60 + log.message(message, { ...opts, symbol: styleText('yellow', S_WARN) }); 61 61 }, 62 62 /** alias for `log.warn()`. */ 63 63 warning: (message: string, opts?: LogMessageOptions) => { 64 64 log.warn(message, opts); 65 65 }, 66 66 error: (message: string, opts?: LogMessageOptions) => { 67 - log.message(message, { ...opts, symbol: color.red(S_ERROR) }); 67 + log.message(message, { ...opts, symbol: styleText('red', S_ERROR) }); 68 68 }, 69 69 };
+4 -4
packages/prompts/src/messages.ts
··· 1 1 import type { Writable } from 'node:stream'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 import { type CommonOptions, S_BAR, S_BAR_END, S_BAR_START } from './common.js'; 4 4 5 5 export const cancel = (message = '', opts?: CommonOptions) => { 6 6 const output: Writable = opts?.output ?? process.stdout; 7 - output.write(`${color.gray(S_BAR_END)} ${color.red(message)}\n\n`); 7 + output.write(`${styleText('gray', S_BAR_END)} ${styleText('red', message)}\n\n`); 8 8 }; 9 9 10 10 export const intro = (title = '', opts?: CommonOptions) => { 11 11 const output: Writable = opts?.output ?? process.stdout; 12 - output.write(`${color.gray(S_BAR_START)} ${title}\n`); 12 + output.write(`${styleText('gray', S_BAR_START)} ${title}\n`); 13 13 }; 14 14 15 15 export const outro = (message = '', opts?: CommonOptions) => { 16 16 const output: Writable = opts?.output ?? process.stdout; 17 - output.write(`${color.gray(S_BAR)}\n${color.gray(S_BAR_END)} ${message}\n\n`); 17 + output.write(`${styleText('gray', S_BAR)}\n${styleText('gray', S_BAR_END)} ${message}\n\n`); 18 18 };
+28 -25
packages/prompts/src/multi-select.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { MultiSelectPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { 4 4 type CommonOptions, 5 5 S_BAR, ··· 27 27 ) => { 28 28 const label = option.label ?? String(option.value); 29 29 if (state === 'active') { 30 - return `${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ 31 - option.hint ? color.dim(`(${option.hint})`) : '' 30 + return `${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label} ${ 31 + option.hint ? styleText('dim', `(${option.hint})`) : '' 32 32 }`; 33 33 } 34 34 if (state === 'selected') { 35 - return `${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)} ${ 36 - option.hint ? color.dim(`(${option.hint})`) : '' 35 + return `${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)} ${ 36 + option.hint ? styleText('dim', `(${option.hint})`) : '' 37 37 }`; 38 38 } 39 39 if (state === 'cancelled') { 40 - return `${color.strikethrough(color.dim(label))}`; 40 + return `${styleText('strikethrough', styleText('dim', label))}`; 41 41 } 42 42 if (state === 'active-selected') { 43 - return `${color.green(S_CHECKBOX_SELECTED)} ${label} ${ 44 - option.hint ? color.dim(`(${option.hint})`) : '' 43 + return `${styleText('green', S_CHECKBOX_SELECTED)} ${label} ${ 44 + option.hint ? styleText('dim', `(${option.hint})`) : '' 45 45 }`; 46 46 } 47 47 if (state === 'submitted') { 48 - return `${color.dim(label)}`; 48 + return `${styleText('dim', label)}`; 49 49 } 50 - return `${color.dim(S_CHECKBOX_INACTIVE)} ${color.dim(label)}`; 50 + return `${styleText('dim', S_CHECKBOX_INACTIVE)} ${styleText('dim', label)}`; 51 51 }; 52 52 const required = opts.required ?? true; 53 53 ··· 61 61 cursorAt: opts.cursorAt, 62 62 validate(selected: Value[] | undefined) { 63 63 if (required && (selected === undefined || selected.length === 0)) 64 - return `Please select at least one option.\n${color.reset( 65 - color.dim( 66 - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( 67 - color.bgWhite(color.inverse(' enter ')) 64 + return `Please select at least one option.\n${styleText( 65 + 'reset', 66 + styleText( 67 + 'dim', 68 + `Press ${styleText('gray', styleText('bgWhite', styleText('inverse', ' space ')))} to select, ${styleText( 69 + 'gray', 70 + styleText('bgWhite', styleText('inverse', ' enter ')) 68 71 )} to submit` 69 72 ) 70 73 )}`; 71 74 }, 72 75 render() { 73 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 76 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 74 77 const value = this.value ?? []; 75 78 76 79 const styleOption = (option: Option<Value>, active: boolean) => { ··· 86 89 87 90 switch (this.state) { 88 91 case 'submit': { 89 - return `${title}${color.gray(S_BAR)} ${ 92 + return `${title}${styleText('gray', S_BAR)} ${ 90 93 this.options 91 94 .filter(({ value: optionValue }) => value.includes(optionValue)) 92 95 .map((option) => opt(option, 'submitted')) 93 - .join(color.dim(', ')) || color.dim('none') 96 + .join(styleText('dim', ', ')) || styleText('dim', 'none') 94 97 }`; 95 98 } 96 99 case 'cancel': { 97 100 const label = this.options 98 101 .filter(({ value: optionValue }) => value.includes(optionValue)) 99 102 .map((option) => opt(option, 'cancelled')) 100 - .join(color.dim(', ')); 101 - return `${title}${color.gray(S_BAR)} ${ 102 - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' 103 + .join(styleText('dim', ', ')); 104 + return `${title}${styleText('gray', S_BAR)} ${ 105 + label.trim() ? `${label}\n${styleText('gray', S_BAR)}` : '' 103 106 }`; 104 107 } 105 108 case 'error': { 106 109 const footer = this.error 107 110 .split('\n') 108 111 .map((ln, i) => 109 - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` 112 + i === 0 ? `${styleText('yellow', S_BAR_END)} ${styleText('yellow', ln)}` : ` ${ln}` 110 113 ) 111 114 .join('\n'); 112 - return `${title + color.yellow(S_BAR)} ${limitOptions({ 115 + return `${title + styleText('yellow', S_BAR)} ${limitOptions({ 113 116 output: opts.output, 114 117 options: this.options, 115 118 cursor: this.cursor, 116 119 maxItems: opts.maxItems, 117 120 style: styleOption, 118 - }).join(`\n${color.yellow(S_BAR)} `)}\n${footer}\n`; 121 + }).join(`\n${styleText('yellow', S_BAR)} `)}\n${footer}\n`; 119 122 } 120 123 default: { 121 - return `${title}${color.cyan(S_BAR)} ${limitOptions({ 124 + return `${title}${styleText('cyan', S_BAR)} ${limitOptions({ 122 125 output: opts.output, 123 126 options: this.options, 124 127 cursor: this.cursor, 125 128 maxItems: opts.maxItems, 126 129 style: styleOption, 127 - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; 130 + }).join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; 128 131 } 129 132 } 130 133 },
+8 -5
packages/prompts/src/note.ts
··· 1 1 import type { Writable } from 'node:stream'; 2 2 import { stripVTControlCharacters as strip } from 'node:util'; 3 - import color from 'picocolors'; 3 + import { styleText } from 'node:util'; 4 4 import { 5 5 type CommonOptions, 6 6 S_BAR, ··· 15 15 format?: (line: string) => string; 16 16 } 17 17 18 - const defaultNoteFormatter = (line: string): string => color.dim(line); 18 + // const defaultNoteFormatter = (line: string): string => color.dim(line); 19 + const defaultNoteFormatter = (line: string): string => styleText('dim', line); 19 20 20 21 export const note = (message = '', title = '', opts?: NoteOptions) => { 21 22 const format = opts?.format ?? defaultNoteFormatter; ··· 32 33 ) + 2; 33 34 const msg = lines 34 35 .map( 35 - (ln) => `${color.gray(S_BAR)} ${ln}${' '.repeat(len - strip(ln).length)}${color.gray(S_BAR)}` 36 + (ln) => 37 + `${styleText('gray', S_BAR)} ${ln}${' '.repeat(len - strip(ln).length)}${styleText('gray', S_BAR)}` 36 38 ) 37 39 .join('\n'); 38 40 output.write( 39 - `${color.gray(S_BAR)}\n${color.green(S_STEP_SUBMIT)} ${color.reset(title)} ${color.gray( 41 + `${styleText('gray', S_BAR)}\n${styleText('green', S_STEP_SUBMIT)} ${styleText('reset', title)} ${styleText( 42 + 'gray', 40 43 S_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT 41 - )}\n${msg}\n${color.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n` 44 + )})\n${msg}\n${styleText('gray', S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n` 42 45 ); 43 46 };
+10 -8
packages/prompts/src/password.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { PasswordPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js'; 4 4 5 5 export interface PasswordOptions extends CommonOptions { ··· 15 15 input: opts.input, 16 16 output: opts.output, 17 17 render() { 18 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 18 + // const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 19 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 19 20 const userInput = this.userInputWithCursor; 20 21 const masked = this.masked; 21 22 22 23 switch (this.state) { 23 24 case 'error': 24 - return `${title.trim()}\n${color.yellow(S_BAR)} ${masked}\n${color.yellow( 25 + return `${title.trim()}\n${styleText('yellow', S_BAR)} ${masked}\n${styleText( 26 + 'yellow', 25 27 S_BAR_END 26 - )} ${color.yellow(this.error)}\n`; 28 + )} ${styleText('yellow', this.error)}\n`; 27 29 case 'submit': 28 - return `${title}${color.gray(S_BAR)} ${color.dim(masked)}`; 30 + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', masked)}`; 29 31 case 'cancel': 30 - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(masked))}${ 31 - masked ? `\n${color.gray(S_BAR)}` : '' 32 + return `${title}${styleText('gray', S_BAR)} ${styleText('strikethrough', styleText('dim', masked))}${ 33 + masked ? `\n${styleText('gray', S_BAR)}` : '' 32 34 }`; 33 35 default: 34 - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; 36 + return `${title}${styleText('cyan', S_BAR)} ${userInput}\n${styleText('cyan', S_BAR_END)}\n`; 35 37 } 36 38 }, 37 39 }).prompt() as Promise<string | symbol>;
+6 -6
packages/prompts/src/progress-bar.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import type { State } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { unicodeOr } from './common.js'; 4 4 import { type SpinnerOptions, type SpinnerResult, spinner } from './spinner.js'; 5 5 ··· 36 36 switch (state) { 37 37 case 'initial': 38 38 case 'active': 39 - return color.magenta; 39 + return (text: string) => styleText('magenta', text); 40 40 case 'error': 41 41 case 'cancel': 42 - return color.red; 42 + return (text: string) => styleText('red', text); 43 43 case 'submit': 44 - return color.green; 44 + return (text: string) => styleText('green', text); 45 45 default: 46 - return color.magenta; 46 + return (text: string) => styleText('magenta', text); 47 47 } 48 48 }; 49 49 const drawProgress = (state: State, msg: string) => { 50 50 const active = Math.floor((value / max) * size); 51 - return `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${color.dim(S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`; 51 + return `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${styleText('dim', S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`; 52 52 }; 53 53 54 54 const start = (msg = '') => {
+13 -12
packages/prompts/src/select-key.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { SelectKeyPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { S_BAR, S_BAR_END, symbol } from './common.js'; 4 4 import type { Option, SelectOptions } from './select.js'; 5 5 ··· 10 10 ) => { 11 11 const label = option.label ?? String(option.value); 12 12 if (state === 'selected') { 13 - return `${color.dim(label)}`; 13 + return `${styleText('dim', label)}`; 14 14 } 15 15 if (state === 'cancelled') { 16 - return `${color.strikethrough(color.dim(label))}`; 16 + return `${styleText('strikethrough', styleText('dim', label))}`; 17 17 } 18 18 if (state === 'active') { 19 - return `${color.bgCyan(color.gray(` ${option.value} `))} ${label} ${ 20 - option.hint ? color.dim(`(${option.hint})`) : '' 19 + return `${styleText('bgCyan', styleText('gray', ` ${option.value} `))} ${label} ${ 20 + option.hint ? styleText('dim', `(${option.hint})`) : '' 21 21 }`; 22 22 } 23 - return `${color.gray(color.bgWhite(color.inverse(` ${option.value} `)))} ${label} ${ 24 - option.hint ? color.dim(`(${option.hint})`) : '' 23 + return `${styleText('gray', styleText('bgWhite', styleText('inverse', ` ${option.value} `)))} ${label} ${ 24 + option.hint ? styleText('dim', `(${option.hint})`) : '' 25 25 }`; 26 26 }; 27 27 ··· 32 32 output: opts.output, 33 33 initialValue: opts.initialValue, 34 34 render() { 35 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 35 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 36 36 37 37 switch (this.state) { 38 38 case 'submit': 39 - return `${title}${color.gray(S_BAR)} ${opt( 39 + return `${title}${styleText('gray', S_BAR)} ${opt( 40 40 this.options.find((opt) => opt.value === this.value) ?? opts.options[0], 41 41 'selected' 42 42 )}`; 43 43 case 'cancel': 44 - return `${title}${color.gray(S_BAR)} ${opt(this.options[0], 'cancelled')}\n${color.gray( 44 + return `${title}${styleText('gray', S_BAR)} ${opt(this.options[0], 'cancelled')}\n${styleText( 45 + 'gray', 45 46 S_BAR 46 47 )}`; 47 48 default: { 48 - return `${title}${color.cyan(S_BAR)} ${this.options 49 + return `${title}${styleText('cyan', S_BAR)} ${this.options 49 50 .map((option, i) => opt(option, i === this.cursor ? 'active' : 'inactive')) 50 - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; 51 + .join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; 51 52 } 52 53 } 53 54 },
+15 -12
packages/prompts/src/select.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { SelectPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { 4 4 type CommonOptions, 5 5 S_BAR, ··· 62 62 const label = option.label ?? String(option.value); 63 63 switch (state) { 64 64 case 'selected': 65 - return `${color.dim(label)}`; 65 + return `${styleText('dim', label)}`; 66 66 case 'active': 67 - return `${color.green(S_RADIO_ACTIVE)} ${label} ${ 68 - option.hint ? color.dim(`(${option.hint})`) : '' 67 + return `${styleText('green', S_RADIO_ACTIVE)} ${label} ${ 68 + option.hint ? styleText('dim', `(${option.hint})`) : '' 69 69 }`; 70 70 case 'cancelled': 71 - return `${color.strikethrough(color.dim(label))}`; 71 + return `${styleText('strikethrough', styleText('dim', label))}`; 72 72 default: 73 - return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}`; 73 + return `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}`; 74 74 } 75 75 }; 76 76 ··· 81 81 output: opts.output, 82 82 initialValue: opts.initialValue, 83 83 render() { 84 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 84 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 85 85 86 86 switch (this.state) { 87 87 case 'submit': 88 - return `${title}${color.gray(S_BAR)} ${opt(this.options[this.cursor], 'selected')}`; 88 + return `${title}${styleText('gray', S_BAR)} ${opt( 89 + this.options[this.cursor], 90 + 'selected' 91 + )}`; 89 92 case 'cancel': 90 - return `${title}${color.gray(S_BAR)} ${opt( 93 + return `${title}${styleText('gray', S_BAR)} ${opt( 91 94 this.options[this.cursor], 92 95 'cancelled' 93 - )}\n${color.gray(S_BAR)}`; 96 + )}\n${styleText('gray', S_BAR)}`; 94 97 default: { 95 - return `${title}${color.cyan(S_BAR)} ${limitOptions({ 98 + return `${title}${styleText('cyan', S_BAR)} ${limitOptions({ 96 99 output: opts.output, 97 100 cursor: this.cursor, 98 101 options: this.options, 99 102 maxItems: opts.maxItems, 100 103 style: (item, active) => opt(item, active ? 'active' : 'inactive'), 101 - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; 104 + }).join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; 102 105 } 103 106 } 104 107 },
+7 -6
packages/prompts/src/spinner.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { block, settings } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { cursor, erase } from 'sisteransi'; 4 4 import { 5 5 type CommonOptions, ··· 115 115 unblock = block({ output }); 116 116 _message = removeTrailingDots(msg); 117 117 _origin = performance.now(); 118 - output.write(`${color.gray(S_BAR)}\n`); 118 + // output.write(`${color.gray(S_BAR)}\n`); 119 + output.write(`${styleText('gray', S_BAR)}\n`); 119 120 let frameIndex = 0; 120 121 let indicatorTimer = 0; 121 122 registerHooks(); ··· 125 126 } 126 127 clearPrevMessage(); 127 128 _prevMessage = _message; 128 - const frame = color.magenta(frames[frameIndex]); 129 + const frame = styleText('magenta', frames[frameIndex]); 129 130 130 131 if (isCI) { 131 132 output.write(`${frame} ${_message}...`); ··· 148 149 clearPrevMessage(); 149 150 const step = 150 151 code === 0 151 - ? color.green(S_STEP_SUBMIT) 152 + ? styleText('green', S_STEP_SUBMIT) 152 153 : code === 1 153 - ? color.red(S_STEP_CANCEL) 154 - : color.red(S_STEP_ERROR); 154 + ? styleText('red', S_STEP_CANCEL) 155 + : styleText('red', S_STEP_ERROR); 155 156 _message = msg ?? _message; 156 157 if (indicator === 'timer') { 157 158 output.write(`${step} ${_message} ${formatTimer(_origin)}\n`);
+9 -9
packages/prompts/src/stream.ts
··· 1 1 import { stripVTControlCharacters as strip } from 'node:util'; 2 - import color from 'picocolors'; 2 + import { styleText } from 'node:util'; 3 3 import { S_BAR, S_ERROR, S_INFO, S_STEP_SUBMIT, S_SUCCESS, S_WARN } from './common.js'; 4 4 import type { LogMessageOptions } from './log.js'; 5 5 6 - const prefix = `${color.gray(S_BAR)} `; 6 + const prefix = `${styleText('gray', S_BAR)} `; 7 7 8 8 // TODO (43081j): this currently doesn't support custom `output` writables 9 9 // because we rely on `columns` existing (i.e. `process.stdout.columns). ··· 13 13 export const stream = { 14 14 message: async ( 15 15 iterable: Iterable<string> | AsyncIterable<string>, 16 - { symbol = color.gray(S_BAR) }: LogMessageOptions = {} 16 + { symbol = styleText('gray', S_BAR) }: LogMessageOptions = {} 17 17 ) => { 18 - process.stdout.write(`${color.gray(S_BAR)}\n${symbol} `); 18 + process.stdout.write(`${styleText('gray', S_BAR)}\n${symbol} `); 19 19 let lineWidth = 3; 20 20 for await (let chunk of iterable) { 21 21 chunk = chunk.replace(/\n/g, `\n${prefix}`); ··· 34 34 process.stdout.write('\n'); 35 35 }, 36 36 info: (iterable: Iterable<string> | AsyncIterable<string>) => { 37 - return stream.message(iterable, { symbol: color.blue(S_INFO) }); 37 + return stream.message(iterable, { symbol: styleText('blue', S_INFO) }); 38 38 }, 39 39 success: (iterable: Iterable<string> | AsyncIterable<string>) => { 40 - return stream.message(iterable, { symbol: color.green(S_SUCCESS) }); 40 + return stream.message(iterable, { symbol: styleText('green', S_SUCCESS) }); 41 41 }, 42 42 step: (iterable: Iterable<string> | AsyncIterable<string>) => { 43 - return stream.message(iterable, { symbol: color.green(S_STEP_SUBMIT) }); 43 + return stream.message(iterable, { symbol: styleText('green', S_STEP_SUBMIT) }); 44 44 }, 45 45 warn: (iterable: Iterable<string> | AsyncIterable<string>) => { 46 - return stream.message(iterable, { symbol: color.yellow(S_WARN) }); 46 + return stream.message(iterable, { symbol: styleText('yellow', S_WARN) }); 47 47 }, 48 48 /** alias for `log.warn()`. */ 49 49 warning: (iterable: Iterable<string> | AsyncIterable<string>) => { 50 50 return stream.warn(iterable); 51 51 }, 52 52 error: (iterable: Iterable<string> | AsyncIterable<string>) => { 53 - return stream.message(iterable, { symbol: color.red(S_ERROR) }); 53 + return stream.message(iterable, { symbol: styleText('red', S_ERROR) }); 54 54 }, 55 55 };
+12 -9
packages/prompts/src/task-log.ts
··· 1 1 import type { Writable } from 'node:stream'; 2 + import { styleText } from 'node:util'; 2 3 import { getColumns } from '@clack/core'; 3 - import color from 'picocolors'; 4 4 import { erase } from 'sisteransi'; 5 5 import { type CommonOptions, S_BAR, S_STEP_SUBMIT, isCI as isCIFn } from './common.js'; 6 6 import { log } from './log.js'; ··· 26 26 export const taskLog = (opts: TaskLogOptions) => { 27 27 const output: Writable = opts.output ?? process.stdout; 28 28 const columns = getColumns(output); 29 - const secondarySymbol = color.gray(S_BAR); 29 + const secondarySymbol = styleText('gray', S_BAR); 30 30 const spacing = opts.spacing ?? 1; 31 31 const barSize = 3; 32 32 const retainLog = opts.retainLog === true; 33 33 const isCI = isCIFn(); 34 34 35 35 output.write(`${secondarySymbol}\n`); 36 - output.write(`${color.green(S_STEP_SUBMIT)} ${opts.title}\n`); 36 + output.write(`${styleText('green', S_STEP_SUBMIT)} ${opts.title}\n`); 37 37 for (let i = 0; i < spacing; i++) { 38 38 output.write(`${secondarySymbol}\n`); 39 39 } ··· 56 56 output.write(erase.lines(lines)); 57 57 }; 58 58 const printBuffer = (buf: string, messageSpacing?: number): void => { 59 - log.message(buf.split('\n').map(color.dim), { 60 - output, 61 - secondarySymbol, 62 - symbol: secondarySymbol, 63 - spacing: messageSpacing ?? spacing, 64 - }); 59 + log.message( 60 + buf.split('\n').map((line) => styleText('dim', line)), 61 + { 62 + output, 63 + secondarySymbol, 64 + symbol: secondarySymbol, 65 + spacing: messageSpacing ?? spacing, 66 + } 67 + ); 65 68 }; 66 69 const renderBuffer = (): void => { 67 70 if (retainLog === true && fullBuffer.length > 0) {
+13 -11
packages/prompts/src/text.ts
··· 1 + import { styleText } from 'node:util'; 1 2 import { TextPrompt } from '@clack/core'; 2 - import color from 'picocolors'; 3 3 import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js'; 4 4 5 5 export interface TextOptions extends CommonOptions { ··· 20 20 signal: opts.signal, 21 21 input: opts.input, 22 22 render() { 23 - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 23 + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; 24 24 const placeholder = opts.placeholder 25 - ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) 26 - : color.inverse(color.hidden('_')); 25 + ? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1)) 26 + : styleText('inverse', styleText('hidden', '_')); 27 27 const userInput = !this.userInput ? placeholder : this.userInputWithCursor; 28 28 const value = this.value ?? ''; 29 29 30 30 switch (this.state) { 31 31 case 'error': 32 - return `${title.trim()}\n${color.yellow(S_BAR)} ${userInput}\n${color.yellow( 32 + return `${title.trim()}\n${styleText('yellow', S_BAR)} ${userInput}\n${styleText( 33 + 'yellow', 33 34 S_BAR_END 34 - )} ${color.yellow(this.error)}\n`; 35 + )} ${styleText('yellow', this.error)}\n`; 35 36 case 'submit': { 36 - return `${title}${color.gray(S_BAR)} ${color.dim(value)}`; 37 + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', value)}`; 37 38 } 38 39 case 'cancel': 39 - return `${title}${color.gray(S_BAR)} ${color.strikethrough( 40 - color.dim(value) 41 - )}${value.trim() ? `\n${color.gray(S_BAR)}` : ''}`; 40 + return `${title}${styleText('gray', S_BAR)} ${styleText( 41 + 'strikethrough', 42 + styleText('dim', value) 43 + )}${value.trim() ? `\n${styleText('gray', S_BAR)}` : ''}`; 42 44 default: 43 - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; 45 + return `${title}${styleText('cyan', S_BAR)} ${userInput}\n${styleText('cyan', S_BAR_END)}\n`; 44 46 } 45 47 }, 46 48 }).prompt() as Promise<string | symbol>;
+2 -2
packages/prompts/test/note.test.ts
··· 1 - import colors from 'picocolors'; 2 1 import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; 3 2 import * as prompts from '../src/index.js'; 4 3 import { MockReadable, MockWritable } from './test-utils.js'; 4 + import { styleText } from 'node:util'; 5 5 6 6 describe.each(['true', 'false'])('note (isCI = %s)', (isCI) => { 7 7 let originalCI: string | undefined; ··· 56 56 57 57 test('formatter which adds colors works', () => { 58 58 prompts.note('line 0\nline 1\nline 2', 'title', { 59 - format: (line) => colors.red(line), 59 + format: (line) => styleText('red', line), 60 60 input, 61 61 output, 62 62 });
+31 -41
pnpm-lock.yaml
··· 15 15 specifier: ^2.26.2 16 16 version: 2.26.2 17 17 '@types/node': 18 - specifier: ^18.16.0 19 - version: 18.16.0 18 + specifier: ^20.12.0 19 + version: 20.19.0 20 20 jsr: 21 21 specifier: ^0.13.4 22 22 version: 0.13.4 23 23 knip: 24 24 specifier: ^5.50.4 25 - version: 5.50.4(@types/node@18.16.0)(typescript@5.8.3) 25 + version: 5.50.4(@types/node@20.19.0)(typescript@5.8.3) 26 26 typescript: 27 27 specifier: ^5.8.3 28 28 version: 5.8.3 ··· 38 38 jiti: 39 39 specifier: ^1.17.0 40 40 version: 1.17.0 41 - picocolors: 42 - specifier: ^1.0.0 43 - version: 1.0.0 44 41 devDependencies: 45 42 cross-env: 46 43 specifier: ^7.0.3 ··· 54 51 jiti: 55 52 specifier: ^1.17.0 56 53 version: 1.17.0 57 - picocolors: 58 - specifier: ^1.0.0 59 - version: 1.0.0 60 54 61 55 packages/core: 62 56 dependencies: 63 - picocolors: 64 - specifier: ^1.0.0 65 - version: 1.0.0 66 57 sisteransi: 67 58 specifier: ^1.0.5 68 59 version: 1.0.5 69 60 devDependencies: 70 61 vitest: 71 62 specifier: ^3.1.1 72 - version: 3.1.1(@types/node@18.16.0) 63 + version: 3.1.1(@types/node@20.19.0) 73 64 wrap-ansi: 74 65 specifier: ^8.1.0 75 66 version: 8.1.0 ··· 79 70 '@clack/core': 80 71 specifier: workspace:* 81 72 version: link:../core 82 - picocolors: 83 - specifier: ^1.0.0 84 - version: 1.0.0 85 73 sisteransi: 86 74 specifier: ^1.0.5 87 75 version: 1.0.5 ··· 94 82 version: 4.17.1 95 83 vitest: 96 84 specifier: ^3.1.1 97 - version: 3.1.1(@types/node@18.16.0) 85 + version: 3.1.1(@types/node@20.19.0) 98 86 vitest-ansi-serializer: 99 87 specifier: ^0.1.2 100 - version: 0.1.2(vitest@3.1.1(@types/node@18.16.0)) 88 + version: 0.1.2(vitest@3.1.1(@types/node@20.19.0)) 101 89 102 90 packages: 103 91 ··· 915 903 '@types/node@12.20.55': 916 904 resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} 917 905 918 - '@types/node@18.16.0': 919 - resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==} 906 + '@types/node@20.19.0': 907 + resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==} 920 908 921 909 '@types/normalize-package-data@2.4.4': 922 910 resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} ··· 2016 2004 resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 2017 2005 engines: {node: '>= 14.16'} 2018 2006 2019 - picocolors@1.0.0: 2020 - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 2021 - 2022 2007 picocolors@1.1.1: 2023 2008 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 2024 2009 ··· 2608 2593 typescript: 2609 2594 optional: true 2610 2595 2596 + undici-types@6.21.0: 2597 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 2598 + 2611 2599 universalify@0.1.2: 2612 2600 resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} 2613 2601 engines: {node: '>= 4.0.0'} ··· 3471 3459 3472 3460 '@types/node@12.20.55': {} 3473 3461 3474 - '@types/node@18.16.0': {} 3462 + '@types/node@20.19.0': 3463 + dependencies: 3464 + undici-types: 6.21.0 3475 3465 3476 3466 '@types/normalize-package-data@2.4.4': {} 3477 3467 ··· 3486 3476 chai: 5.2.0 3487 3477 tinyrainbow: 2.0.0 3488 3478 3489 - '@vitest/mocker@3.1.1(vite@5.4.11(@types/node@18.16.0))': 3479 + '@vitest/mocker@3.1.1(vite@5.4.11(@types/node@20.19.0))': 3490 3480 dependencies: 3491 3481 '@vitest/spy': 3.1.1 3492 3482 estree-walker: 3.0.3 3493 3483 magic-string: 0.30.17 3494 3484 optionalDependencies: 3495 - vite: 5.4.11(@types/node@18.16.0) 3485 + vite: 5.4.11(@types/node@20.19.0) 3496 3486 3497 3487 '@vitest/pretty-format@3.1.1': 3498 3488 dependencies: ··· 4448 4438 4449 4439 kleur@4.1.5: {} 4450 4440 4451 - knip@5.50.4(@types/node@18.16.0)(typescript@5.8.3): 4441 + knip@5.50.4(@types/node@20.19.0)(typescript@5.8.3): 4452 4442 dependencies: 4453 4443 '@nodelib/fs.walk': 1.2.8 4454 - '@types/node': 18.16.0 4444 + '@types/node': 20.19.0 4455 4445 easy-table: 1.2.0 4456 4446 enhanced-resolve: 5.18.1 4457 4447 fast-glob: 3.3.3 ··· 4678 4668 pathe@2.0.3: {} 4679 4669 4680 4670 pathval@2.0.0: {} 4681 - 4682 - picocolors@1.0.0: {} 4683 4671 4684 4672 picocolors@1.1.1: {} 4685 4673 ··· 5306 5294 - supports-color 5307 5295 - vue-tsc 5308 5296 5297 + undici-types@6.21.0: {} 5298 + 5309 5299 universalify@0.1.2: {} 5310 5300 5311 5301 untyped@1.5.1: ··· 5333 5323 spdx-correct: 3.2.0 5334 5324 spdx-expression-parse: 3.0.1 5335 5325 5336 - vite-node@3.1.1(@types/node@18.16.0): 5326 + vite-node@3.1.1(@types/node@20.19.0): 5337 5327 dependencies: 5338 5328 cac: 6.7.14 5339 5329 debug: 4.4.0 5340 5330 es-module-lexer: 1.6.0 5341 5331 pathe: 2.0.3 5342 - vite: 5.4.11(@types/node@18.16.0) 5332 + vite: 5.4.11(@types/node@20.19.0) 5343 5333 transitivePeerDependencies: 5344 5334 - '@types/node' 5345 5335 - less ··· 5351 5341 - supports-color 5352 5342 - terser 5353 5343 5354 - vite@5.4.11(@types/node@18.16.0): 5344 + vite@5.4.11(@types/node@20.19.0): 5355 5345 dependencies: 5356 5346 esbuild: 0.21.5 5357 5347 postcss: 8.4.49 5358 5348 rollup: 4.27.3 5359 5349 optionalDependencies: 5360 - '@types/node': 18.16.0 5350 + '@types/node': 20.19.0 5361 5351 fsevents: 2.3.3 5362 5352 5363 - vitest-ansi-serializer@0.1.2(vitest@3.1.1(@types/node@18.16.0)): 5353 + vitest-ansi-serializer@0.1.2(vitest@3.1.1(@types/node@20.19.0)): 5364 5354 dependencies: 5365 - vitest: 3.1.1(@types/node@18.16.0) 5355 + vitest: 3.1.1(@types/node@20.19.0) 5366 5356 5367 - vitest@3.1.1(@types/node@18.16.0): 5357 + vitest@3.1.1(@types/node@20.19.0): 5368 5358 dependencies: 5369 5359 '@vitest/expect': 3.1.1 5370 - '@vitest/mocker': 3.1.1(vite@5.4.11(@types/node@18.16.0)) 5360 + '@vitest/mocker': 3.1.1(vite@5.4.11(@types/node@20.19.0)) 5371 5361 '@vitest/pretty-format': 3.1.1 5372 5362 '@vitest/runner': 3.1.1 5373 5363 '@vitest/snapshot': 3.1.1 ··· 5383 5373 tinyexec: 0.3.2 5384 5374 tinypool: 1.0.2 5385 5375 tinyrainbow: 2.0.0 5386 - vite: 5.4.11(@types/node@18.16.0) 5387 - vite-node: 3.1.1(@types/node@18.16.0) 5376 + vite: 5.4.11(@types/node@20.19.0) 5377 + vite-node: 3.1.1(@types/node@20.19.0) 5388 5378 why-is-node-running: 2.3.0 5389 5379 optionalDependencies: 5390 - '@types/node': 18.16.0 5380 + '@types/node': 20.19.0 5391 5381 transitivePeerDependencies: 5392 5382 - less 5393 5383 - lightningcss