diff --git a/Cargo.lock b/Cargo.lock index 4001d290b5..6cb3e00cd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7193,6 +7193,7 @@ dependencies = [ "anyhow", "async-trait", "clap", + "cow-utils", "fspy", "napi", "napi-build", diff --git a/crates/vite_migration/src/vite_config.rs b/crates/vite_migration/src/vite_config.rs index 1c55ec1ef1..5e2edc3892 100644 --- a/crates/vite_migration/src/vite_config.rs +++ b/crates/vite_migration/src/vite_config.rs @@ -1012,6 +1012,27 @@ export default defineConfig({ ); } + #[test] + fn test_merge_json_config_content_empty_object() { + let vite_config = r#"import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { options: { typeAware: true, typeCheck: true } }, +});"#; + + let result = merge_json_config_content(vite_config, "{}", "fmt").unwrap(); + assert!(result.updated); + assert_eq!( + result.content, + r#"import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + fmt: {}, + lint: { options: { typeAware: true, typeCheck: true } }, +});"# + ); + } + #[test] fn test_merge_tsdown_config_content_simple() { let vite_config = r#"import { defineConfig } from 'vite-plus'; diff --git a/packages/cli/binding/Cargo.toml b/packages/cli/binding/Cargo.toml index 76d3428314..5dc74e096a 100644 --- a/packages/cli/binding/Cargo.toml +++ b/packages/cli/binding/Cargo.toml @@ -10,6 +10,7 @@ rolldown = ["dep:rolldown_binding"] anyhow = { workspace = true } async-trait = { workspace = true } clap = { workspace = true, features = ["derive"] } +cow-utils = { workspace = true } fspy = { workspace = true } rustc-hash = { workspace = true } napi = { workspace = true } diff --git a/packages/cli/binding/src/cli.rs b/packages/cli/binding/src/cli.rs index a219a41949..efc444a3b6 100644 --- a/packages/cli/binding/src/cli.rs +++ b/packages/cli/binding/src/cli.rs @@ -12,6 +12,7 @@ use clap::{ Parser, Subcommand, error::{ContextKind, ContextValue, ErrorKind}, }; +use cow_utils::CowUtils; use owo_colors::OwoColorize; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -743,6 +744,33 @@ async fn resolve_and_execute_with_stdout_filter( Ok(ExitStatus(output.status.code().unwrap_or(1) as u8)) } +/// Like `resolve_and_execute`, but captures stderr, applies a text filter, +/// and writes the result to real stderr. Stdout remains inherited (streaming). +async fn resolve_and_execute_with_stderr_filter( + resolver: &SubcommandResolver, + subcommand: SynthesizableSubcommand, + resolved_vite_config: Option<&ResolvedUniversalViteConfig>, + envs: &Arc, Arc>>, + cwd: &AbsolutePathBuf, + cwd_arc: &Arc, + filter: impl Fn(&str) -> Cow<'_, str>, +) -> Result { + let mut cmd = + resolve_and_build_command(resolver, subcommand, resolved_vite_config, envs, cwd, cwd_arc) + .await?; + cmd.stderr(Stdio::piped()); + + let child = cmd.spawn().map_err(|e| Error::Anyhow(e.into()))?; + let output = child.wait_with_output().await.map_err(|e| Error::Anyhow(e.into()))?; + + use std::io::Write; + let stderr = String::from_utf8_lossy(&output.stderr); + let filtered = filter(&stderr); + let _ = std::io::stderr().lock().write_all(filtered.as_bytes()); + + Ok(ExitStatus(output.status.code().unwrap_or(1) as u8)) +} + struct CapturedCommandOutput { status: ExitStatus, stdout: String, @@ -1258,6 +1286,17 @@ async fn execute_direct_subcommand( |_| Cow::Borrowed(""), ) .await? + } else if matches!(&other, SynthesizableSubcommand::Fmt { .. }) { + resolve_and_execute_with_stderr_filter( + &resolver, + other, + None, + &envs, + cwd, + &cwd_arc, + |s| s.cow_replace("oxfmt --init", "vp fmt --init"), + ) + .await? } else { resolve_and_execute(&resolver, other, None, &envs, cwd, &cwd_arc).await? } diff --git a/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt b/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt index d066f83b35..da62de5026 100644 --- a/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt +++ b/packages/cli/snap-tests-global/create-missing-typecheck/snap.txt @@ -6,6 +6,7 @@ export default defineConfig({ staged: { "*": "vp check --fix", }, + fmt: {}, lint: { options: { typeAware: true, typeCheck: true } }, }); @@ -17,6 +18,7 @@ export default defineConfig({ staged: { "*": "vp check --fix", }, + fmt: {}, lint: { options: { typeAware: true, typeCheck: true } }, }); diff --git a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt index c25c8d0a00..e4f6d9a206 100644 --- a/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt +++ b/packages/cli/snap-tests-global/migration-baseurl-tsconfig/snap.txt @@ -15,6 +15,7 @@ export default defineConfig({ staged: { "*": "vp check --fix" }, + fmt: {}, lint: { "rules": { "no-unused-vars": "error" diff --git a/packages/cli/snap-tests-global/migration-chained-lint-staged-pre-commit/snap.txt b/packages/cli/snap-tests-global/migration-chained-lint-staged-pre-commit/snap.txt index 7ddd00bbd1..d249a630af 100644 --- a/packages/cli/snap-tests-global/migration-chained-lint-staged-pre-commit/snap.txt +++ b/packages/cli/snap-tests-global/migration-chained-lint-staged-pre-commit/snap.txt @@ -30,6 +30,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.js": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-env-prefix-lint-staged/snap.txt b/packages/cli/snap-tests-global/migration-env-prefix-lint-staged/snap.txt index 27492d2dba..8c4b64d41c 100644 --- a/packages/cli/snap-tests-global/migration-env-prefix-lint-staged/snap.txt +++ b/packages/cli/snap-tests-global/migration-env-prefix-lint-staged/snap.txt @@ -30,6 +30,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.js": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-eslint-lint-staged/snap.txt b/packages/cli/snap-tests-global/migration-eslint-lint-staged/snap.txt index 7516e0b42d..38daa0d79c 100644 --- a/packages/cli/snap-tests-global/migration-eslint-lint-staged/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-lint-staged/snap.txt @@ -30,6 +30,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: { "plugins": [ "oxc", diff --git a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt index 93ad9e3aa5..80df03be3d 100644 --- a/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint-lintstagedrc/snap.txt @@ -31,6 +31,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: { "plugins": [ "oxc", diff --git a/packages/cli/snap-tests-global/migration-eslint/snap.txt b/packages/cli/snap-tests-global/migration-eslint/snap.txt index c6d5113eae..6906eeaf5a 100644 --- a/packages/cli/snap-tests-global/migration-eslint/snap.txt +++ b/packages/cli/snap-tests-global/migration-eslint/snap.txt @@ -37,6 +37,7 @@ export default defineConfig({ staged: { "*": "vp check --fix" }, + fmt: {}, lint: { "plugins": [ "oxc", diff --git a/packages/cli/snap-tests-global/migration-existing-husky-lint-staged/snap.txt b/packages/cli/snap-tests-global/migration-existing-husky-lint-staged/snap.txt index 699480b009..b260ddbd02 100644 --- a/packages/cli/snap-tests-global/migration-existing-husky-lint-staged/snap.txt +++ b/packages/cli/snap-tests-global/migration-existing-husky-lint-staged/snap.txt @@ -30,6 +30,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.js": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt index 8ff2897af9..30a4b77cdb 100644 --- a/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-existing-lint-staged-config/snap.txt @@ -31,6 +31,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.ts": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-existing-pnpm-exec-lint-staged/snap.txt b/packages/cli/snap-tests-global/migration-existing-pnpm-exec-lint-staged/snap.txt index c13742dbc2..820a5041dd 100644 --- a/packages/cli/snap-tests-global/migration-existing-pnpm-exec-lint-staged/snap.txt +++ b/packages/cli/snap-tests-global/migration-existing-pnpm-exec-lint-staged/snap.txt @@ -30,6 +30,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.js": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt index dc8b844389..e959e77120 100644 --- a/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt +++ b/packages/cli/snap-tests-global/migration-from-tsdown-json-config/snap.txt @@ -22,6 +22,7 @@ export default defineConfig({ "cwd": "./src" } }, + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, server: { port: 3000, @@ -72,6 +73,7 @@ export default defineConfig({ "cwd": "./src" } }, + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, server: { port: 3000, diff --git a/packages/cli/snap-tests-global/migration-from-tsdown/snap.txt b/packages/cli/snap-tests-global/migration-from-tsdown/snap.txt index 143e5b1a29..bda47ba5a6 100644 --- a/packages/cli/snap-tests-global/migration-from-tsdown/snap.txt +++ b/packages/cli/snap-tests-global/migration-from-tsdown/snap.txt @@ -27,6 +27,7 @@ export default defineConfig({ "*": "vp check --fix" }, pack: tsdownConfig, + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, }); @@ -78,6 +79,7 @@ export default defineConfig({ "*": "vp check --fix" }, pack: tsdownConfig, + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, }); diff --git a/packages/cli/snap-tests-global/migration-lint-staged-in-scripts/snap.txt b/packages/cli/snap-tests-global/migration-lint-staged-in-scripts/snap.txt index 1bbeec19c6..7d0434118f 100644 --- a/packages/cli/snap-tests-global/migration-lint-staged-in-scripts/snap.txt +++ b/packages/cli/snap-tests-global/migration-lint-staged-in-scripts/snap.txt @@ -31,6 +31,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.js": "vp lint --fix" diff --git a/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt b/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt index 6e13bbaab0..238997d15c 100644 --- a/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt +++ b/packages/cli/snap-tests-global/migration-lintstagedrc-json/snap.txt @@ -104,6 +104,7 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { "*.@(js|ts|tsx|yml|yaml|md|json|html|toml)": [ diff --git a/packages/cli/snap-tests-global/migration-lintstagedrc-staged-exists/snap.txt b/packages/cli/snap-tests-global/migration-lintstagedrc-staged-exists/snap.txt index 06d6cf6606..3d1722081a 100644 --- a/packages/cli/snap-tests-global/migration-lintstagedrc-staged-exists/snap.txt +++ b/packages/cli/snap-tests-global/migration-lintstagedrc-staged-exists/snap.txt @@ -34,6 +34,7 @@ lintstagedrc.json still exists import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, staged: { '*.js': 'vp check --fix', diff --git a/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt b/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt index 99319a0c63..e300be0687 100644 --- a/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt +++ b/packages/cli/snap-tests-global/migration-merge-vite-config-js/snap.txt @@ -12,6 +12,7 @@ export default { staged: { "*": "vp check --fix" }, + fmt: {}, lint: { "rules": { "no-unused-vars": "error" diff --git a/packages/cli/snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector/snap.txt b/packages/cli/snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector/snap.txt index c39b26978d..b0e906cb6f 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector/snap.txt +++ b/packages/cli/snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector/snap.txt @@ -13,6 +13,7 @@ export default defineConfig({ staged: { "*": "vp check --fix" }, + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, plugins: [react()], }); diff --git a/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt b/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt index 64196416d0..408140e1b4 100644 --- a/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt +++ b/packages/cli/snap-tests-global/migration-monorepo-yarn4/snap.txt @@ -15,6 +15,7 @@ export default defineConfig({ staged: { "*": "vp check --fix" }, + fmt: {}, lint: { "rules": { "no-unused-vars": "error" diff --git a/packages/cli/snap-tests-global/migration-subpath/snap.txt b/packages/cli/snap-tests-global/migration-subpath/snap.txt index 7d281bcb96..64d8d29ae4 100644 --- a/packages/cli/snap-tests-global/migration-subpath/snap.txt +++ b/packages/cli/snap-tests-global/migration-subpath/snap.txt @@ -36,8 +36,8 @@ VITE+ - The Unified Toolchain for the Web import { defineConfig } from 'vite-plus'; export default defineConfig({ + fmt: {}, lint: {"options":{"typeAware":true,"typeCheck":true}}, - }); > git config --local core.hooksPath || echo 'core.hooksPath is not set' # should NOT be set diff --git a/packages/cli/snap-tests/fmt-no-config-message/package.json b/packages/cli/snap-tests/fmt-no-config-message/package.json new file mode 100644 index 0000000000..ac4dbcd6a4 --- /dev/null +++ b/packages/cli/snap-tests/fmt-no-config-message/package.json @@ -0,0 +1,6 @@ +{ + "name": "@test/fmt-no-config-message", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/packages/cli/snap-tests/fmt-no-config-message/snap.txt b/packages/cli/snap-tests/fmt-no-config-message/snap.txt new file mode 100644 index 0000000000..f56773690f --- /dev/null +++ b/packages/cli/snap-tests/fmt-no-config-message/snap.txt @@ -0,0 +1,16 @@ +> vp fmt 2>&1 # should show 'vp fmt --init' instead of 'oxfmt --init' +Finished in ms on 4 files using threads. +No config found, using defaults. Please add a config file or try `vp fmt --init` if needed. + +> vp fmt --init +Added 'fmt' to 'vite.config.ts'. + +> cat vite.config.ts # should have fmt config +export default { + fmt: { + ignorePatterns: [], + }, +}; + +> vp fmt 2>&1 # should no longer show 'No config found' message +Finished in ms on 4 files using threads. diff --git a/packages/cli/snap-tests/fmt-no-config-message/src/index.js b/packages/cli/snap-tests/fmt-no-config-message/src/index.js new file mode 100644 index 0000000000..b5c5375fad --- /dev/null +++ b/packages/cli/snap-tests/fmt-no-config-message/src/index.js @@ -0,0 +1,5 @@ +function hello() { + return 'world'; +} + +export { hello }; diff --git a/packages/cli/snap-tests/fmt-no-config-message/steps.json b/packages/cli/snap-tests/fmt-no-config-message/steps.json new file mode 100644 index 0000000000..4aae93fc94 --- /dev/null +++ b/packages/cli/snap-tests/fmt-no-config-message/steps.json @@ -0,0 +1,8 @@ +{ + "commands": [ + "vp fmt 2>&1 # should show 'vp fmt --init' instead of 'oxfmt --init'", + "vp fmt --init", + "cat vite.config.ts # should have fmt config", + "vp fmt 2>&1 # should no longer show 'No config found' message" + ] +} diff --git a/packages/cli/snap-tests/fmt-no-config-message/vite.config.ts b/packages/cli/snap-tests/fmt-no-config-message/vite.config.ts new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/packages/cli/snap-tests/fmt-no-config-message/vite.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 814546673e..d50f1a3634 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -726,6 +726,7 @@ export function rewriteStandaloneProject( } mergeViteConfigFiles(projectPath, silent, report); injectLintTypeCheckDefaults(projectPath, silent, report); + injectFmtDefaults(projectPath, silent, report); mergeTsdownConfigFile(projectPath, silent, report); // rewrite imports in all TypeScript/JavaScript files rewriteAllImports(projectPath, silent, report); @@ -771,6 +772,7 @@ export function rewriteMonorepo( } mergeViteConfigFiles(workspaceInfo.rootDir, silent, report); injectLintTypeCheckDefaults(workspaceInfo.rootDir, silent, report); + injectFmtDefaults(workspaceInfo.rootDir, silent, report); mergeTsdownConfigFile(workspaceInfo.rootDir, silent, report); // rewrite imports in all TypeScript/JavaScript files rewriteAllImports(workspaceInfo.rootDir, silent, report); @@ -1340,6 +1342,21 @@ export function injectLintTypeCheckDefaults( ); } +export function injectFmtDefaults( + projectPath: string, + silent = false, + report?: MigrationReport, +): void { + injectConfigDefaults( + projectPath, + 'fmt', + '.vite-plus-fmt-init.oxfmtrc.json', + JSON.stringify({}), + silent, + report, + ); +} + function injectConfigDefaults( projectPath: string, configKey: string,