diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts index feb23261d8..40e129266c 100644 --- a/packages/cli/src/create/bin.ts +++ b/packages/cli/src/create/bin.ts @@ -1,10 +1,10 @@ import fs from 'node:fs'; import path from 'node:path'; -import { styleText } from 'node:util'; import * as prompts from '@voidzero-dev/vite-plus-prompts'; import spawn from 'cross-spawn'; import mri from 'mri'; +import colors from 'picocolors'; import { vitePlusHeader } from '../../binding/index.js'; import { @@ -433,21 +433,19 @@ function showCreateSummary(options: { } = options; log( - `${styleText('magenta', '◇')} Scaffolded ${accent(projectDir)}${ + `${colors.magenta('◇')} Scaffolded ${accent(projectDir)}${ description ? ` with ${description}` : '' }`, ); log( - `${styleText('gray', '•')} Node ${process.versions.node} ${packageManager} ${packageManagerVersion}`, + `${colors.gray('•')} Node ${process.versions.node} ${packageManager} ${packageManagerVersion}`, ); if (installSummary?.status === 'installed') { log( - `${styleText('green', '✓')} Dependencies installed in ${formatDuration( - installSummary.durationMs, - )}`, + `${colors.green('✓')} Dependencies installed in ${formatDuration(installSummary.durationMs)}`, ); } - log(`${styleText('blue', '→')} Next: ${accent(nextCommand)}`); + log(`${colors.blue('→')} Next: ${accent(nextCommand)}`); } async function main() { diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index 58ea7e75da..51a410f592 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -1,8 +1,8 @@ import path from 'node:path'; -import { styleText } from 'node:util'; import * as prompts from '@voidzero-dev/vite-plus-prompts'; import mri from 'mri'; +import colors from 'picocolors'; import semver from 'semver'; import { @@ -110,8 +110,7 @@ async function confirmFrameworkShim(framework: Framework, interactive: boolean): const confirmed = await prompts.confirm({ message: `Add TypeScript shim for ${name} component files (*.${framework})?\n ` + - styleText( - 'gray', + colors.gray( `Lets TypeScript recognize .${framework} files until vp check fully supports them.`, ), initialValue: true, @@ -487,8 +486,7 @@ async function collectAgentInstructionPlan( const action = await prompts.select({ message: `Agent instructions already exist at ${conflict.targetPath}.\n ` + - styleText( - 'gray', + colors.gray( 'The Vite+ template includes guidance on `vp` commands, the build pipeline, and project conventions.', ), options: [ @@ -532,8 +530,7 @@ async function collectEditorConfigPlan( const action = await prompts.select({ message: `${conflict.displayPath} already exists.\n ` + - styleText( - 'gray', + colors.gray( 'Vite+ adds editor settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones.', ), options: [ @@ -756,21 +753,19 @@ function showMigrationSummary(options: { report.wrappedPluginConfigCount; log( - `${styleText('magenta', '◇')} ${updatedExistingVitePlus ? 'Updated' : 'Migrated'} ${accent(projectLabel)}${ + `${colors.magenta('◇')} ${updatedExistingVitePlus ? 'Updated' : 'Migrated'} ${accent(projectLabel)}${ updatedExistingVitePlus ? '' : ' to Vite+' }`, ); log( - `${styleText('gray', '•')} Node ${process.versions.node} ${packageManager} ${packageManagerVersion}`, + `${colors.gray('•')} Node ${process.versions.node} ${packageManager} ${packageManagerVersion}`, ); // Gate the green success line on the FINAL install actually succeeding. // A nonzero duration could come from a successful pre-migration install // followed by a failed post-migration reinstall — in that case node_modules // is desynced and reporting success would mislead the user. if (finalInstallOk && installDurationMs > 0) { - log( - `${styleText('green', '✓')} Dependencies installed in ${formatDuration(installDurationMs)}`, - ); + log(`${colors.green('✓')} Dependencies installed in ${formatDuration(installDurationMs)}`); } if (configUpdates > 0 || report.rewrittenImportFileCount > 0) { const parts: string[] = []; @@ -786,39 +781,37 @@ function showMigrationSummary(options: { } imports rewritten`, ); } - log(`${styleText('gray', '•')} ${parts.join(', ')}`); + log(`${colors.gray('•')} ${parts.join(', ')}`); } if (report.eslintMigrated) { - log(`${styleText('gray', '•')} ESLint rules migrated to Oxlint`); + log(`${colors.gray('•')} ESLint rules migrated to Oxlint`); } if (report.prettierMigrated) { - log(`${styleText('gray', '•')} Prettier migrated to Oxfmt`); + log(`${colors.gray('•')} Prettier migrated to Oxfmt`); } if (report.nodeVersionFileMigrated) { - log(`${styleText('gray', '•')} Node version manager file migrated to .node-version`); + log(`${colors.gray('•')} Node version manager file migrated to .node-version`); } if (report.wrappedPluginConfigCount > 0) { - log( - `${styleText('gray', '•')} Inline Vite plugins wrapped with lazyPlugins for check/lint/fmt`, - ); + log(`${colors.gray('•')} Inline Vite plugins wrapped with lazyPlugins for check/lint/fmt`); } if (report.gitHooksConfigured) { - log(`${styleText('gray', '•')} Git hooks configured`); + log(`${colors.gray('•')} Git hooks configured`); } if (report.frameworkShimAdded) { - log(`${styleText('gray', '•')} TypeScript shim added for framework component files`); + log(`${colors.gray('•')} TypeScript shim added for framework component files`); } if (report.packageManagerBootstrapConfigured) { - log(`${styleText('gray', '•')} Package manager settings configured`); + log(`${colors.gray('•')} Package manager settings configured`); } if (report.warnings.length > 0) { - log(`${styleText('yellow', '!')} Warnings:`); + log(`${colors.yellow('!')} Warnings:`); for (const warning of report.warnings) { log(` - ${warning}`); } } if (report.manualSteps.length > 0) { - log(`${styleText('blue', '→')} Manual follow-up:`); + log(`${colors.blue('→')} Manual follow-up:`); for (const step of report.manualSteps) { log(` - ${step}`); } diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 8d41d2344e..c34b4dd771 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1,11 +1,11 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { styleText } from 'node:util'; import * as prompts from '@voidzero-dev/vite-plus-prompts'; import spawn from 'cross-spawn'; import type { OxlintConfig } from 'oxlint'; +import colors from 'picocolors'; import semver from 'semver'; import { Scalar, YAMLMap, YAMLSeq } from 'yaml'; @@ -5715,8 +5715,7 @@ export async function confirmEslintMigration(interactive: boolean): Promise ({ @@ -396,8 +394,7 @@ async function writeEditorConfig({ const action = await prompts.select({ message: `${displayPath} already exists.\n ` + - styleText( - 'gray', + colors.gray( `Vite+ adds ${editorConfig.label} settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones.`, ), options: [ diff --git a/packages/cli/src/utils/help.ts b/packages/cli/src/utils/help.ts index 4a9b9bfe1a..c1e02cd8d4 100644 --- a/packages/cli/src/utils/help.ts +++ b/packages/cli/src/utils/help.ts @@ -1,4 +1,6 @@ -import { stripVTControlCharacters, styleText } from 'node:util'; +import { stripVTControlCharacters } from 'node:util'; + +import colors from 'picocolors'; export type CliDoc = { usage?: string; @@ -71,9 +73,7 @@ function heading(label: string, color: boolean): string { return `${label}:`; } - return label === 'Usage' - ? styleText('bold', `${label}:`) - : styleText(['blue', 'bold'], `${label}:`); + return label === 'Usage' ? colors.bold(`${label}:`) : colors.bold(colors.blue(`${label}:`)); } export function renderCliDoc(doc: CliDoc, options: RenderCliDocOptions = {}): string { @@ -81,7 +81,7 @@ export function renderCliDoc(doc: CliDoc, options: RenderCliDocOptions = {}): st const output: string[] = []; if (doc.usage) { - const usage = color ? styleText('bold', doc.usage) : doc.usage; + const usage = color ? colors.bold(doc.usage) : doc.usage; output.push(`${heading('Usage', color)} ${usage}`); } diff --git a/packages/cli/src/utils/terminal.ts b/packages/cli/src/utils/terminal.ts index ea90fcf970..a73bb528a9 100644 --- a/packages/cli/src/utils/terminal.ts +++ b/packages/cli/src/utils/terminal.ts @@ -1,4 +1,4 @@ -import { styleText } from 'node:util'; +import colors from 'picocolors'; import { shouldPrintVitePlusHeader, vitePlusHeader } from '../../binding/index.js'; @@ -21,19 +21,19 @@ export function printHeader() { } export function accent(text: string) { - return styleText('blue', text); + return colors.blue(text); } export function muted(text: string) { - return styleText('gray', text); + return colors.gray(text); } export function success(text: string) { - return styleText('green', text); + return colors.green(text); } export function error(text: string) { - return styleText('red', text); + return colors.red(text); } // Standard message prefix functions matching the Rust CLI convention. @@ -41,20 +41,20 @@ export function error(text: string) { export function infoMsg(msg: string) { /* oxlint-disable-next-line no-console */ - console.log(styleText(['blue', 'bold'], 'info:'), msg); + console.log(colors.bold(colors.blue('info:')), msg); } export function warnMsg(msg: string) { /* oxlint-disable-next-line no-console */ - console.error(styleText(['yellow', 'bold'], 'warn:'), msg); + console.error(colors.bold(colors.yellow('warn:')), msg); } export function errorMsg(msg: string) { /* oxlint-disable-next-line no-console */ - console.error(styleText(['red', 'bold'], 'error:'), msg); + console.error(colors.bold(colors.red('error:')), msg); } export function noteMsg(msg: string) { /* oxlint-disable-next-line no-console */ - console.log(styleText(['gray', 'bold'], 'note:'), msg); + console.log(colors.bold(colors.gray('note:')), msg); } diff --git a/packages/cli/src/utils/tsconfig.ts b/packages/cli/src/utils/tsconfig.ts index f421dae252..eb2a760e85 100644 --- a/packages/cli/src/utils/tsconfig.ts +++ b/packages/cli/src/utils/tsconfig.ts @@ -1,9 +1,9 @@ import fs from 'node:fs'; import path from 'node:path'; -import { styleText } from 'node:util'; import * as prompts from '@voidzero-dev/vite-plus-prompts'; import { applyEdits, modify, parse as parseJsonc } from 'jsonc-parser'; +import colors from 'picocolors'; import { runCommandSilently } from './command.ts'; import { BASEURL_TSCONFIG_FIX_PACKAGE, createBaseUrlTsconfigFixArgs } from './constants.ts'; @@ -60,12 +60,9 @@ export async function confirmBaseUrlFix(interactive: boolean): Promise const confirmed = await prompts.confirm({ message: 'Your tsconfig contains `baseUrl`, which prevents enabling type-aware linting.\n ' + - styleText( - 'gray', - '`baseUrl` is deprecated in TypeScript 6.0 and removed in TypeScript 7.0.', - ) + + colors.gray('`baseUrl` is deprecated in TypeScript 6.0 and removed in TypeScript 7.0.') + `\n Download and run the external \`${BASEURL_TSCONFIG_FIX_PACKAGE}\` fixer now?\n ` + - styleText('gray', `Equivalent command: \`vp dlx ${command}\``), + colors.gray(`Equivalent command: \`vp dlx ${command}\``), initialValue: true, }); if (prompts.isCancel(confirmed)) { diff --git a/packages/core/package.json b/packages/core/package.json index 763a6bb76b..73e33ce2df 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -120,7 +120,7 @@ "magic-string": "^0.30.21", "oxc-parser": "catalog:", "oxfmt": "catalog:", - "picocolors": "^1.1.1", + "picocolors": "catalog:", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "rolldown": "workspace:*", diff --git a/packages/prompts/package.json b/packages/prompts/package.json index fa33ba4489..e40962cb71 100644 --- a/packages/prompts/package.json +++ b/packages/prompts/package.json @@ -28,7 +28,7 @@ }, "dependencies": { "@clack/core": "^1.0.0", - "picocolors": "^1.0.0", + "picocolors": "catalog:", "sisteransi": "^1.0.5" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd1e78173b..14cf08cbaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -611,7 +611,7 @@ importers: specifier: 'catalog:' version: 0.56.0(vite-plus@packages+cli) picocolors: - specifier: ^1.1.1 + specifier: 'catalog:' version: 1.1.1 picomatch: specifier: ^4.0.3 @@ -657,7 +657,7 @@ importers: specifier: ^1.0.0 version: 1.4.2 picocolors: - specifier: ^1.0.0 + specifier: 'catalog:' version: 1.1.1 sisteransi: specifier: ^1.0.5 diff --git a/rfcs/cli-output-polish.md b/rfcs/cli-output-polish.md index 97427add66..af7418af1c 100644 --- a/rfcs/cli-output-polish.md +++ b/rfcs/cli-output-polish.md @@ -52,11 +52,11 @@ Neither identifies the experience as "Vite+". Users who installed `vite-plus` se **4. Color libraries differ (but this is acceptable)** -| Layer | Library | -| ------------------ | ----------------------- | -| Rust (global CLI) | `owo_colors` | -| JS (vite-plus CLI) | `node:util styleText()` | -| vite | `picocolors` | +| Layer | Library | +| ------------------ | ------------ | +| Rust (global CLI) | `owo_colors` | +| JS (vite-plus CLI) | `picocolors` | +| vite | `picocolors` | **5. The `[vite]` logger prefix in vite** @@ -345,19 +345,19 @@ The JS code in `packages/cli/src/utils/terminal.ts` already has `accent()`, `hea ```typescript export function info(msg: string) { - console.error(styleText(['blue', 'bold'], 'info:'), msg); + console.error(colors.bold(colors.blue('info:')), msg); } export function warn(msg: string) { - console.error(styleText(['yellow', 'bold'], 'warn:'), msg); + console.error(colors.bold(colors.yellow('warn:')), msg); } export function errorMsg(msg: string) { - console.error(styleText(['red', 'bold'], 'error:'), msg); + console.error(colors.bold(colors.red('error:')), msg); } export function note(msg: string) { - console.error(styleText(['gray', 'bold'], 'note:'), msg); + console.error(colors.bold(colors.gray('note:')), msg); } ``` @@ -397,7 +397,7 @@ Migrate JS-side code (`migration/bin.ts`, `create/bin.ts`) to use these shared f ### D6: Keep each layer's color library -**Decision:** Rust keeps `owo_colors`, JS keeps `node:util styleText()`, vite keeps `picocolors`. +**Decision:** Rust keeps `owo_colors`; JS and Vite keep `picocolors`. **Rationale:** Changing color libraries is high-risk, low-reward. The shared formatting module abstracts the library choice so the output convention is consistent regardless of the underlying library.