Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions packages/cli/src/create/bin.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
41 changes: 17 additions & 24 deletions packages/cli/src/migration/bin.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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: [
Expand Down Expand Up @@ -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: [
Expand Down Expand Up @@ -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[] = [];
Expand All @@ -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}`);
}
Expand Down
8 changes: 3 additions & 5 deletions packages/cli/src/migration/migrator.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -5715,8 +5715,7 @@ export async function confirmEslintMigration(interactive: boolean): Promise<bool
const confirmed = await prompts.confirm({
message:
'Migrate ESLint rules to Oxlint using @oxlint/migrate?\n ' +
styleText(
'gray',
colors.gray(
"Oxlint is Vite+'s built-in linter — significantly faster than ESLint with compatible rule support. @oxlint/migrate converts your existing rules automatically.",
),
initialValue: true,
Expand Down Expand Up @@ -5779,8 +5778,7 @@ export async function confirmPrettierMigration(interactive: boolean): Promise<bo
const confirmed = await prompts.confirm({
message:
'Migrate Prettier to Oxfmt?\n ' +
styleText(
'gray',
colors.gray(
"Oxfmt is Vite+'s built-in formatter that replaces Prettier with faster performance. Your configuration will be converted automatically.",
),
initialValue: true,
Expand Down
5 changes: 2 additions & 3 deletions packages/cli/src/utils/agent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import path from 'node:path';
import { styleText } from 'node:util';

import * as prompts from '@voidzero-dev/vite-plus-prompts';
import colors from 'picocolors';

import { pkgRoot } from './path.ts';

Expand Down Expand Up @@ -490,8 +490,7 @@ export async function writeAgentInstructions({
const action = await prompts.select({
message:
`Agent instructions already exist at ${targetPathToWrite}.\n ` +
styleText(
'gray',
colors.gray(
'The Vite+ template includes guidance on `vp` commands, the build pipeline, and project conventions.',
),
options: [
Expand Down
11 changes: 4 additions & 7 deletions packages/cli/src/utils/editor.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import path from 'node:path';
import { styleText } from 'node:util';

import * as prompts from '@voidzero-dev/vite-plus-prompts';
import colors from 'picocolors';

import { readJsonFile, writeJsonFile } from './json.ts';

Expand Down Expand Up @@ -193,8 +193,7 @@ export async function selectEditor({
const selectedEditor = await prompts.select({
message:
'Which editor are you using?\n ' +
styleText(
'gray',
colors.gray(
'Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations.',
),
options: [...editorOptions, otherOption],
Expand Down Expand Up @@ -236,8 +235,7 @@ export async function selectEditors({
const selectedEditors = await prompts.multiselect({
message:
'Which editors are you using?\n ' +
styleText(
'gray',
colors.gray(
'Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations.',
),
options: EDITORS.map((option) => ({
Expand Down Expand Up @@ -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: [
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/utils/help.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { stripVTControlCharacters, styleText } from 'node:util';
import { stripVTControlCharacters } from 'node:util';

import colors from 'picocolors';

export type CliDoc = {
usage?: string;
Expand Down Expand Up @@ -71,17 +73,15 @@ 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 {
const color = options.color ?? true;
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}`);
}

Expand Down
18 changes: 9 additions & 9 deletions packages/cli/src/utils/terminal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { styleText } from 'node:util';
import colors from 'picocolors';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Preserve non-TTY color suppression

With the default picocolors export, color support is computed from environment variables and treats CI=true as color-capable even when stdout is not a TTY; styleText() previously returned plain text in that same non-TTY CI case unless color was explicitly forced. For invocations such as CI=true vp create ... > log without NO_COLOR, these shared helpers now inject ANSI escapes into captured output, which changes the documented no-behavior-change refactor and can break log/snapshot consumers. Consider creating a local colors wrapper with the previous stream/TTY gating instead of using the default export directly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does bring a behavior change, but I think it should be treated as an improvement.

In Rust side, we use owo_colors which emits ANSI chars, the same behavior as Vite, who has already used picocolors for a long time. So use default picocolors exports can help create more united mental model.


import { shouldPrintVitePlusHeader, vitePlusHeader } from '../../binding/index.js';

Expand All @@ -21,40 +21,40 @@ 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.
// info/note go to stdout (normal output), warn/error go to stderr (diagnostics).

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);
}
9 changes: 3 additions & 6 deletions packages/cli/src/utils/tsconfig.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -60,12 +60,9 @@ export async function confirmBaseUrlFix(interactive: boolean): Promise<boolean>
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)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"magic-string": "^0.30.21",
"oxc-parser": "catalog:",
"oxfmt": "catalog:",
"picocolors": "^1.1.1",
"picocolors": "catalog:",
Comment thread
liangmiQwQ marked this conversation as resolved.
"picomatch": "^4.0.3",
"pkg-types": "^2.3.0",
"rolldown": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/prompts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"dependencies": {
"@clack/core": "^1.0.0",
"picocolors": "^1.0.0",
"picocolors": "catalog:",
"sisteransi": "^1.0.5"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading