From 0c2ceac73ce61a7713203c20726083814235a285 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 24 Sep 2026 15:43:04 +0800 Subject: [PATCH 1/3] fix(cli): let lint and fmt discover configs from any directory --- .../oxc_config_auto_discovery/snapshots.toml | 4 +- .../discover_config_from_subdirectory.md | 20 +-- .../workspace_fmt_root_config/snapshots.toml | 14 +- ...md => check_fix_uses_discovered_format.md} | 6 +- ...md => discovered_format_options.global.md} | 23 +-- .../discovered_format_options.local.md | 40 +++++ .../snapshots/explicit_format_config.md | 2 +- .../snapshots/root_format_options.local.md | 49 ------ .../workspace_lint_root_config/snapshots.toml | 14 +- .../snapshots/conflicting_package_options.md | 44 +----- .../discovered_rules_and_typecheck.global.md | 70 +++++++++ .../discovered_rules_and_typecheck.local.md | 63 ++++++++ .../root_rules_and_typecheck.global.md | 105 ------------- .../root_rules_and_typecheck.local.md | 99 ------------ .../workspace_lint_subpackage/snapshots.toml | 2 +- .../snapshots/workspace_lint_subpackage.md | 14 +- docs/config/fmt.md | 2 +- docs/config/lint.md | 2 +- docs/guide/check.md | 2 +- docs/guide/fmt.md | 4 +- docs/guide/lint.md | 4 +- docs/guide/monorepo.md | 4 +- docs/guide/troubleshooting.md | 2 +- packages/cli/binding/src/cli/resolver.rs | 145 +++--------------- 24 files changed, 241 insertions(+), 493 deletions(-) rename crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/{check_fix_uses_root_format.md => check_fix_uses_discovered_format.md} (75%) rename crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/{root_format_options.global.md => discovered_format_options.global.md} (50%) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md delete mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.local.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md delete mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.global.md delete mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.local.md diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots.toml index 7868c3fcd2..0316c9e77a 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots.toml @@ -28,9 +28,9 @@ steps = [ { argv = ["vp", "fmt", "index.js"], cwd = "src" }, { argv = ["vpt", "print-file", "src/index.js"], comment = "The root fmt settings apply from a subdirectory too." }, { argv = ["vpt", "write-file", "src/vite.config.ts", "export default { lint: { rules: { 'no-console': 'off' } }, fmt: { singleQuote: false, semi: true } };\n"] }, - { argv = ["vp", "lint", "index.js"], cwd = "src", comment = "The root lint config still applies when the working directory has its own lint settings.", continue-on-failure = true }, + { argv = ["vp", "lint", "index.js"], cwd = "src", comment = "Oxlint discovers the lint settings in the working directory.", continue-on-failure = true }, { argv = ["vp", "fmt", "index.js"], cwd = "src" }, - { argv = ["vpt", "print-file", "src/index.js"], comment = "The root fmt settings still apply when the working directory has its own fmt block." }, + { argv = ["vpt", "print-file", "src/index.js"], comment = "Oxfmt discovers the fmt settings in the working directory." }, { argv = ["vp", "lint", "src/index.js"], comment = "Running from the root keeps per-file nested lint configs disabled.", continue-on-failure = true }, { argv = ["vp", "fmt", "src/index.js"] }, { argv = ["vpt", "print-file", "src/index.js"], comment = "Running from the root keeps per-file nested fmt configs disabled." }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots/discover_config_from_subdirectory.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots/discover_config_from_subdirectory.md index fe2c411a35..b15e387a33 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots/discover_config_from_subdirectory.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/oxc_config_auto_discovery/snapshots/discover_config_from_subdirectory.md @@ -47,20 +47,10 @@ console.log('hello') ## `cd src && vp lint index.js` -The root lint config still applies when the working directory has its own lint settings. - -**Exit code:** 1 +Oxlint discovers the lint settings in the working directory. ``` - - × eslint(no-console): Unexpected console statement. - ╭─[index.js:1:1] - 1 │ console.log('hello') - · ─────────── - ╰──── - help: Delete this console statement. - -Found 0 warnings and 1 error. +Found 0 warnings and 0 errors. Finished in on 1 file with rules using threads. ``` @@ -72,10 +62,10 @@ Finished in on 1 files using threads. ## `vpt print-file src/index.js` -The root fmt settings still apply when the working directory has its own fmt block. +Oxfmt discovers the fmt settings in the working directory. ``` -console.log('hello') +console.log("hello"); ``` ## `vp lint src/index.js` @@ -88,7 +78,7 @@ Running from the root keeps per-file nested lint configs disabled. × eslint(no-console): Unexpected console statement. ╭─[src/index.js:1:1] - 1 │ console.log('hello') + 1 │ console.log("hello"); · ─────────── ╰──── help: Delete this console statement. diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml index 8aab529fb9..1090427cca 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml @@ -1,12 +1,12 @@ [[case]] -name = "root_format_options" +name = "discovered_format_options" vp = ["local", "global"] cwd = "packages/app" steps = [ - { argv = ["vp", "fmt", "--check", "index.js"], comment = "The package format settings cannot replace the root settings.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-lint", "index.js"], comment = "The formatting phase of check uses the same root config.", continue-on-failure = true }, + { argv = ["vp", "fmt", "--check", "index.js"], comment = "Oxfmt discovers the package format settings.", continue-on-failure = true }, + { argv = ["vp", "check", "--no-lint", "index.js"], comment = "The formatting phase of check also discovers the package config.", continue-on-failure = true }, { argv = ["vp", "fmt", "index.js"] }, - { argv = ["vpt", "print-file", "index.js"], comment = "The root single-quote and semicolon settings apply without changing the package working directory." }, + { argv = ["vpt", "print-file", "index.js"], comment = "The package double-quote and semicolon settings apply with package-relative file paths." }, { argv = ["vp", "check", "--no-lint", "index.js"] }, ] @@ -15,18 +15,18 @@ name = "explicit_format_config" vp = "local" cwd = "packages/app" steps = [ - { argv = ["vp", "fmt", "-c", "vite.config.ts", "--check", "index.js"], comment = "An explicit package config takes precedence without a duplicate automatic config argument." }, + { argv = ["vp", "fmt", "-c", "vite.config.ts", "--check", "index.js"], comment = "An explicit package config matches native discovery." }, { argv = ["vp", "fmt", "--config", "vite.config.ts", "--check", "index.js"] }, { argv = ["vp", "fmt", "--config=vite.config.ts", "--check", "index.js"] }, { argv = ["vp", "fmt", "-c", "../../vite.config.ts", "--check", "index.js"], comment = "Selecting the root config explicitly detects the conflicting format.", continue-on-failure = true }, ] [[case]] -name = "check_fix_uses_root_format" +name = "check_fix_uses_discovered_format" vp = "local" cwd = "packages/app" steps = [ { argv = ["vp", "check", "--fix", "fix.js"] }, - { argv = ["vpt", "print-file", "fix.js"], comment = "Formatting after the curly lint fix also uses root quotes and semicolons." }, + { argv = ["vpt", "print-file", "fix.js"], comment = "Formatting after the root curly lint fix uses package quotes and semicolons." }, { argv = ["vp", "check", "fix.js"] }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md similarity index 75% rename from crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md rename to crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md index b73d211e0f..aa2b8e21f9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md @@ -1,4 +1,4 @@ -# check_fix_uses_root_format +# check_fix_uses_discovered_format ## `vp check --fix fix.js` @@ -9,12 +9,12 @@ pass: Found no warnings or lint errors in 1 file (, threads) ## `vpt print-file fix.js` -Formatting after the curly lint fix also uses root quotes and semicolons. +Formatting after the root curly lint fix uses package quotes and semicolons. ``` export function greet(show) { if (show) { - console.log('hello') + console.log("hello"); } } ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md similarity index 50% rename from crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.global.md rename to crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md index d14c234ca2..bb8ce84331 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md @@ -1,35 +1,26 @@ -# root_format_options +# discovered_format_options ## `vp fmt --check index.js` -The package format settings cannot replace the root settings. - -**Exit code:** 1 +Oxfmt discovers the package format settings. ``` VITE+ - The Unified Toolchain for the Web Checking formatting... -index.js () - -Format issues found in above 1 files. Run without `--check` to fix. +All matched files use the correct format. Finished in on 1 files using threads. ``` ## `vp check --no-lint index.js` -The formatting phase of check uses the same root config. - -**Exit code:** 1 +The formatting phase of check also discovers the package config. ``` VITE+ - The Unified Toolchain for the Web -error: Formatting issues found -index.js () - -Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. +pass: All 1 file are correctly formatted (, threads) ``` ## `vp fmt index.js` @@ -42,10 +33,10 @@ Finished in on 1 files using threads. ## `vpt print-file index.js` -The root single-quote and semicolon settings apply without changing the package working directory. +The package double-quote and semicolon settings apply with package-relative file paths. ``` -export const message = 'hello' +export const message = "hello"; ``` ## `vp check --no-lint index.js` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md new file mode 100644 index 0000000000..fb3ae25bbf --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md @@ -0,0 +1,40 @@ +# discovered_format_options + +## `vp fmt --check index.js` + +Oxfmt discovers the package format settings. + +``` +Checking formatting... + +All matched files use the correct format. +Finished in on 1 files using threads. +``` + +## `vp check --no-lint index.js` + +The formatting phase of check also discovers the package config. + +``` +pass: All 1 file are correctly formatted (, threads) +``` + +## `vp fmt index.js` + +``` +Finished in on 1 files using threads. +``` + +## `vpt print-file index.js` + +The package double-quote and semicolon settings apply with package-relative file paths. + +``` +export const message = "hello"; +``` + +## `vp check --no-lint index.js` + +``` +pass: All 1 file are correctly formatted (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/explicit_format_config.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/explicit_format_config.md index 07991244f9..da3fc6f82c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/explicit_format_config.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/explicit_format_config.md @@ -2,7 +2,7 @@ ## `vp fmt -c vite.config.ts --check index.js` -An explicit package config takes precedence without a duplicate automatic config argument. +An explicit package config matches native discovery. ``` Checking formatting... diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.local.md deleted file mode 100644 index 3107f85a3f..0000000000 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/root_format_options.local.md +++ /dev/null @@ -1,49 +0,0 @@ -# root_format_options - -## `vp fmt --check index.js` - -The package format settings cannot replace the root settings. - -**Exit code:** 1 - -``` -Checking formatting... - -index.js () - -Format issues found in above 1 files. Run without `--check` to fix. -Finished in on 1 files using threads. -``` - -## `vp check --no-lint index.js` - -The formatting phase of check uses the same root config. - -**Exit code:** 1 - -``` -error: Formatting issues found -index.js () - -Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. -``` - -## `vp fmt index.js` - -``` -Finished in on 1 files using threads. -``` - -## `vpt print-file index.js` - -The root single-quote and semicolon settings apply without changing the package working directory. - -``` -export const message = 'hello' -``` - -## `vp check --no-lint index.js` - -``` -pass: All 1 file are correctly formatted (, threads) -``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml index 14a20b9412..9312aa7387 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml @@ -1,12 +1,12 @@ [[case]] -name = "root_rules_and_typecheck" +name = "discovered_rules_and_typecheck" vp = ["local", "global"] cwd = "packages/app" steps = [ - { argv = ["vp", "lint", "index.ts"], comment = "The root lint rules and type-check options apply even when the package has its own lint block.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "The check wrapper and Oxlint both use the root config.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-fmt", "--no-lint", "index.ts"], comment = "Type-check-only mode reports the same type error without lint rules.", continue-on-failure = true }, - { argv = ["vp", "lint", "-c", "../../vite.config.ts", "index.ts"], comment = "Explicit root selection has the same result and preserves package-relative file paths.", continue-on-failure = true }, + { argv = ["vp", "lint", "index.ts"], comment = "Oxlint discovers the package lint block without the root rules or type-check options.", continue-on-failure = true }, + { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "The lint phase of check also lets Oxlint discover the package config.", continue-on-failure = true }, + { argv = ["vp", "check", "--no-fmt", "--no-lint", "index.ts"], comment = "Type-check-only mode still reports the type error without lint rules.", continue-on-failure = true }, + { argv = ["vp", "lint", "-c", "../../vite.config.ts", "index.ts"], comment = "Explicit root selection enables the root rules and type checking while preserving package-relative file paths.", continue-on-failure = true }, ] [[case]] @@ -15,9 +15,9 @@ vp = "local" cwd = "packages/app" steps = [ { argv = ["vpt", "write-file", "vite.config.ts", "export default { lint: { options: { typeAware: false, typeCheck: false }, rules: { 'no-console': 'off' } } };\n"], snapshot = false }, - { argv = ["vp", "lint", "index.ts"], comment = "Package options cannot disable the root rules or type checking.", continue-on-failure = true }, + { argv = ["vp", "lint", "index.ts"], comment = "Oxlint uses the package rules and type-check options.", continue-on-failure = true }, { argv = ["vp", "check", "--no-fmt", "index.ts"], continue-on-failure = true }, - { argv = ["vp", "lint", "-c", "vite.config.ts", "index.ts"], comment = "An explicit package config overrides the root config without a duplicate config argument." }, + { argv = ["vp", "lint", "-c", "vite.config.ts", "index.ts"], comment = "An explicit package config matches native discovery." }, { argv = ["vp", "lint", "--config", "vite.config.ts", "index.ts"] }, { argv = ["vp", "lint", "--config=vite.config.ts", "index.ts"] }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md index 83d42847a2..f33fa63a8f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md @@ -6,58 +6,22 @@ ## `vp lint index.ts` -Package options cannot disable the root rules or type checking. - -**Exit code:** 1 +Oxlint uses the package rules and type-check options. ``` - - × eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 0 warnings and 2 errors. +Found 0 warnings and 0 errors. Finished in on 1 file with rules using threads. ``` ## `vp check --no-fmt index.ts` -**Exit code:** 1 - ``` -error: Lint or type issues found -× eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 2 errors and 0 warnings in 1 file (, threads) +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) ``` ## `vp lint -c vite.config.ts index.ts` -An explicit package config overrides the root config without a duplicate config argument. +An explicit package config matches native discovery. ``` Found 0 warnings and 0 errors. diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md new file mode 100644 index 0000000000..cf825d8b24 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md @@ -0,0 +1,70 @@ +# discovered_rules_and_typecheck + +## `vp lint index.ts` + +Oxlint discovers the package lint block without the root rules or type-check options. + +``` +VITE+ - The Unified Toolchain for the Web + +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp check --no-fmt index.ts` + +The lint phase of check also lets Oxlint discover the package config. + +``` +VITE+ - The Unified Toolchain for the Web + +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +``` + +## `vp check --no-fmt --no-lint index.ts` + +Type-check-only mode still reports the type error without lint rules. + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + +error: Type errors found +× typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 1 error and 0 warnings in 1 file (, threads) +``` + +## `vp lint -c ../../vite.config.ts index.ts` + +Explicit root selection enables the root rules and type checking while preserving package-relative file paths. + +**Exit code:** 1 + +``` +VITE+ - The Unified Toolchain for the Web + + × eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + + × typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md new file mode 100644 index 0000000000..66d6ca7d69 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md @@ -0,0 +1,63 @@ +# discovered_rules_and_typecheck + +## `vp lint index.ts` + +Oxlint discovers the package lint block without the root rules or type-check options. + +``` +Found 0 warnings and 0 errors. +Finished in on 1 file with rules using threads. +``` + +## `vp check --no-fmt index.ts` + +The lint phase of check also lets Oxlint discover the package config. + +``` +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +``` + +## `vp check --no-fmt --no-lint index.ts` + +Type-check-only mode still reports the type error without lint rules. + +**Exit code:** 1 + +``` +error: Type errors found +× typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 1 error and 0 warnings in 1 file (, threads) +``` + +## `vp lint -c ../../vite.config.ts index.ts` + +Explicit root selection enables the root rules and type checking while preserving package-relative file paths. + +**Exit code:** 1 + +``` + + × eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + + × typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 0 warnings and 2 errors. +Finished in on 1 file with rules using threads. +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.global.md deleted file mode 100644 index e87a633c03..0000000000 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.global.md +++ /dev/null @@ -1,105 +0,0 @@ -# root_rules_and_typecheck - -## `vp lint index.ts` - -The root lint rules and type-check options apply even when the package has its own lint block. - -**Exit code:** 1 - -``` -VITE+ - The Unified Toolchain for the Web - - × eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 0 warnings and 2 errors. -Finished in on 1 file with rules using threads. -``` - -## `vp check --no-fmt index.ts` - -The check wrapper and Oxlint both use the root config. - -**Exit code:** 1 - -``` -VITE+ - The Unified Toolchain for the Web - -error: Lint or type issues found -× eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 2 errors and 0 warnings in 1 file (, threads) -``` - -## `vp check --no-fmt --no-lint index.ts` - -Type-check-only mode reports the same type error without lint rules. - -**Exit code:** 1 - -``` -VITE+ - The Unified Toolchain for the Web - -error: Type errors found -× typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 1 error and 0 warnings in 1 file (, threads) -``` - -## `vp lint -c ../../vite.config.ts index.ts` - -Explicit root selection has the same result and preserves package-relative file paths. - -**Exit code:** 1 - -``` -VITE+ - The Unified Toolchain for the Web - - × eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 0 warnings and 2 errors. -Finished in on 1 file with rules using threads. -``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.local.md deleted file mode 100644 index 2a74d87a15..0000000000 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/root_rules_and_typecheck.local.md +++ /dev/null @@ -1,99 +0,0 @@ -# root_rules_and_typecheck - -## `vp lint index.ts` - -The root lint rules and type-check options apply even when the package has its own lint block. - -**Exit code:** 1 - -``` - - × eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 0 warnings and 2 errors. -Finished in on 1 file with rules using threads. -``` - -## `vp check --no-fmt index.ts` - -The check wrapper and Oxlint both use the root config. - -**Exit code:** 1 - -``` -error: Lint or type issues found -× eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 2 errors and 0 warnings in 1 file (, threads) -``` - -## `vp check --no-fmt --no-lint index.ts` - -Type-check-only mode reports the same type error without lint rules. - -**Exit code:** 1 - -``` -error: Type errors found -× typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 1 error and 0 warnings in 1 file (, threads) -``` - -## `vp lint -c ../../vite.config.ts index.ts` - -Explicit root selection has the same result and preserves package-relative file paths. - -**Exit code:** 1 - -``` - - × eslint(no-console): Unexpected console statement. - ╭─[index.ts:2:1] - 1 │ export const value: number = "not a number"; - 2 │ console.log(value); - · ─────────── - ╰──── - help: Delete this console statement. - - × typescript(TS2322): Type 'string' is not assignable to type 'number'. - ╭─[index.ts:1:14] - 1 │ export const value: number = "not a number"; - · ───── - 2 │ console.log(value); - ╰──── - -Found 0 warnings and 2 errors. -Finished in on 1 file with rules using threads. -``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots.toml index 8de9e88048..e60b4292ea 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots.toml @@ -4,7 +4,7 @@ vp = "local" skip-platforms = ["windows"] steps = [ { argv = ["vp", "lint", "packages/app-a"], comment = "Running from the root uses the root no-console:warn rule.", continue-on-failure = true }, - { argv = ["vp", "lint"], cwd = "packages/app-a", comment = "Running from the package still uses the root no-console:warn rule.", continue-on-failure = true }, + { argv = ["vp", "lint"], cwd = "packages/app-a", comment = "Running from the package discovers its no-console:off rule.", continue-on-failure = true }, { argv = ["vp", "lint", "-c", "vite.config.ts"], cwd = "packages/app-a", comment = "Explicitly selecting the package config uses its no-console:off rule." }, { argv = ["vpt", "write-file", "packages/app-a/vite.config.ts", "export default {};\n"] }, { argv = ["vp", "lint"], cwd = "packages/app-a", comment = "A package config without lint settings also uses the root config.", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots/workspace_lint_subpackage.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots/workspace_lint_subpackage.md index 77bafb9afe..a283e7cb60 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots/workspace_lint_subpackage.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_subpackage/snapshots/workspace_lint_subpackage.md @@ -21,20 +21,10 @@ Finished in on 2 files with rules using threads. ## `cd packages/app-a && vp lint` -Running from the package still uses the root no-console:warn rule. +Running from the package discovers its no-console:off rule. ``` - - ⚠ eslint(no-console): Unexpected console statement. - ╭─[src/index.js:2:3] - 1 │ function hello() { - 2 │ console.log('hello from app-a'); - · ─────────── - 3 │ return 'hello'; - ╰──── - help: Delete this console statement. - -Found 1 warning and 0 errors. +Found 0 warnings and 0 errors. Finished in on 2 files with rules using threads. ``` diff --git a/docs/config/fmt.md b/docs/config/fmt.md index dca2264676..a5ad28bbe5 100644 --- a/docs/config/fmt.md +++ b/docs/config/fmt.md @@ -1,6 +1,6 @@ # Format Config -`vp fmt` and `vp check` use the workspace-root `fmt` block, including when run from a package directory. Package configs do not replace these format settings. Use `vp fmt -c ` or `vp fmt --config ` to select another config. If the root config has no `fmt` block, Oxfmt uses [native discovery](/guide/fmt#configuration). See [Oxfmt's configuration](https://oxc.rs/docs/guide/usage/formatter/config.html) for details. +`vp fmt` and the formatting phase of `vp check` use Oxfmt's [native config discovery](/guide/fmt#configuration) from the working directory. Use `vp fmt -c ` or `vp fmt --config ` to select another config. See [Oxfmt's configuration](https://oxc.rs/docs/guide/usage/formatter/config.html) for details. ## Example diff --git a/docs/config/lint.md b/docs/config/lint.md index a0e4e0192b..548afba7e9 100644 --- a/docs/config/lint.md +++ b/docs/config/lint.md @@ -1,6 +1,6 @@ # Lint Config -`vp lint` and `vp check` use the workspace-root `lint` block, including when run from a package directory. Package configs do not replace these lint settings. Use `vp lint -c ` or `vp lint --config ` to select another config. If the root config has no `lint` block, Oxlint uses [native discovery](/guide/lint#configuration). See [Oxlint's configuration](https://oxc.rs/docs/guide/usage/linter/config.html) for details. +`vp lint` and the lint phase of `vp check` use Oxlint's [native config discovery](/guide/lint#configuration) from the working directory. Use `vp lint -c ` or `vp lint --config ` to select another config. See [Oxlint's configuration](https://oxc.rs/docs/guide/usage/linter/config.html) for details. ## Example diff --git a/docs/guide/check.md b/docs/guide/check.md index f71225f9e4..21bd98638f 100644 --- a/docs/guide/check.md +++ b/docs/guide/check.md @@ -29,7 +29,7 @@ vp check --no-fmt --no-lint # Type-check only (requires `typeCheck` enabled). - [`fmt`](/guide/fmt#configuration) block in `vite.config.ts` - TypeScript project structure and tsconfig files for type-aware linting -When run from a workspace package, `vp check` uses the workspace-root `lint` and `fmt` blocks. Package configs cannot replace root format settings, lint rules, or type-check options. File paths and TypeScript projects are still resolved from the package working directory. +`vp check` lets Oxlint and Oxfmt discover their configurations from the working directory, including when run from a workspace package. File paths and TypeScript projects are also resolved from that directory. Recommended base `lint` config: diff --git a/docs/guide/fmt.md b/docs/guide/fmt.md index 6caa47ec27..520fecf7cd 100644 --- a/docs/guide/fmt.md +++ b/docs/guide/fmt.md @@ -20,9 +20,9 @@ vp fmt . --write Put formatting configuration directly in the `fmt` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `.oxfmtrc.json` with Vite+. -When the workspace-root config has a `fmt` block, `vp fmt` and `vp check` use it even when run from a package directory. Package configs cannot replace the root format settings. Commands keep the package working directory, so relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options. +`vp fmt` and the formatting phase of `vp check` let Oxfmt discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options. -An explicit `vp fmt -c ` or `vp fmt --config ` selects another config. Otherwise, when running from the workspace root or when the root config has no `fmt` block, Oxfmt discovers the nearest `vite.config.*` file with a `fmt` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. +An explicit `vp fmt -c ` or `vp fmt --config ` selects another config. Otherwise, Oxfmt discovers the nearest `vite.config.*` file with a `fmt` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. For editors, disable nested formatter configs to prevent per-file overrides: diff --git a/docs/guide/lint.md b/docs/guide/lint.md index b501309360..cc9dfd8d8c 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -20,9 +20,9 @@ vp lint --type-aware Put lint configuration directly in the `lint` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `oxlint.config.ts` or `.oxlintrc.json` with Vite+. -When the workspace-root config has a `lint` block, `vp lint` and `vp check` use it even when run from a package directory. Package configs cannot replace the root rules or disable its `typeAware` and `typeCheck` options. Commands keep the package working directory, so relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules. +`vp lint` and the lint phase of `vp check` let Oxlint discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules. -An explicit `vp lint -c ` or `vp lint --config ` selects another config. Otherwise, when running from the workspace root or when the root config has no `lint` block, Oxlint discovers the nearest `vite.config.*` file with a `lint` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. +An explicit `vp lint -c ` or `vp lint --config ` selects another config. Otherwise, Oxlint discovers the nearest `vite.config.*` file with a `lint` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. For the upstream rule set, options, and compatibility details, see the [Oxlint docs](https://oxc.rs/docs/guide/usage/linter.html). diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index a8731f75e6..d30baa0a37 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -8,9 +8,9 @@ Because `vite.config.ts` is just JavaScript, you can choose to put your entire c Run `vp lint`, `vp fmt`, and `vp check` from the workspace root to apply the root lint and format settings across packages. These commands do not apply nested configs to individual files. Define file- or package-specific settings with overrides in the root `vite.config.ts`. -When run from a package directory, `vp lint`, `vp fmt`, and `vp check` still use the root `lint` and `fmt` blocks. Package configs cannot replace root format settings, lint rules, or type-check options. File arguments remain relative to the package working directory. +When run from a package directory, `vp lint`, `vp fmt`, and `vp check` let Oxlint and Oxfmt discover configuration from that directory. A package config with a `lint` or `fmt` block can therefore change the settings for that run. File arguments remain relative to the package working directory. -Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config. If the root config has no block for that tool, the tool uses native discovery from the working directory. +Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. Use `lint.overrides` for Oxlint rules that only apply to some packages: diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index 1262a24d6d..c1e44f797c 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -26,7 +26,7 @@ The Oxlint type checker path powered by `tsgolint` does not support `baseUrl`. ` ## Nested lint or format config is not applied -When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. Commands run from a package directory still use the workspace-root `lint` and `fmt` blocks when they exist. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config. If the root config has no block for that tool, the tool uses native discovery from the working directory. +When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. Oxlint and Oxfmt discover configuration from the working directory, so running from a package directory can select that package's config. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. Keep lint and format settings in the root `vite.config.ts`. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) and [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific settings. You can also [import configuration objects](/guide/monorepo#composing-configuration-files) into the root config to keep settings in separate files. diff --git a/packages/cli/binding/src/cli/resolver.rs b/packages/cli/binding/src/cli/resolver.rs index 12255f7b5e..cc37b9b8bc 100644 --- a/packages/cli/binding/src/cli/resolver.rs +++ b/packages/cli/binding/src/cli/resolver.rs @@ -64,22 +64,6 @@ impl SubcommandResolver { })?) } - /// Resolve root settings for package runs unless the user selected another config. - async fn resolve_workspace_config( - &self, - cwd: &AbsolutePath, - args: &[String], - ) -> anyhow::Result> { - let explicit_config = args - .iter() - .take_while(|arg| arg.as_str() != "--") - .any(|arg| arg.starts_with("-c") || arg == "--config" || arg.starts_with("--config=")); - if cwd == self.workspace_path.as_ref() || explicit_config { - return Ok(None); - } - self.resolve_universal_vite_config().await.map(Some) - } - /// Resolve a synthesizable subcommand to a concrete program, args, cache config, and envs. pub(super) async fn resolve( &self, @@ -88,7 +72,7 @@ impl SubcommandResolver { cwd: &AbsolutePath, ) -> anyhow::Result { match subcommand { - SynthesizableSubcommand::Lint { mut args } => { + SynthesizableSubcommand::Lint { args } => { let cli_options = self.cli_options()?; let resolved = (cli_options.lint)(cwd, &args).await?; let js_path = resolved.bin_path; @@ -96,14 +80,6 @@ impl SubcommandResolver { .to_str() .ok_or_else(|| anyhow::anyhow!("lint JS path is not valid UTF-8"))?; - if let Some(config) = self.resolve_workspace_config(cwd, &args).await? - && config.lint.is_some() - && let Some(config_file) = config.config_file - { - args.insert(0, "-c".to_string()); - args.insert(1, config_file); - } - Ok(ResolvedSubcommand { program: Arc::clone(&cli_options.node_exec_path), args: [ @@ -122,7 +98,7 @@ impl SubcommandResolver { envs: merge_resolved_envs_with_version(envs, resolved.envs), }) } - SynthesizableSubcommand::Fmt { mut args } => { + SynthesizableSubcommand::Fmt { args } => { let cli_options = self.cli_options()?; let resolved = (cli_options.fmt)(cwd, &args).await?; let js_path = resolved.bin_path; @@ -130,14 +106,6 @@ impl SubcommandResolver { .to_str() .ok_or_else(|| anyhow::anyhow!("fmt JS path is not valid UTF-8"))?; - if let Some(config) = self.resolve_workspace_config(cwd, &args).await? - && config.fmt.is_some() - && let Some(config_file) = config.config_file - { - args.insert(0, "-c".to_string()); - args.insert(1, config_file); - } - Ok(ResolvedSubcommand { program: Arc::clone(&cli_options.node_exec_path), args: iter::once(Str::from(js_path_str)) @@ -412,113 +380,38 @@ mod tests { #[tokio::test] async fn lint_and_fmt_preserve_args_without_loading_config() { let temp = tempfile::tempdir().unwrap(); - let cwd = AbsolutePathBuf::new(temp.path().to_path_buf()).unwrap(); - let resolver = SubcommandResolver::new(cwd.clone().into()) + let root = AbsolutePathBuf::new(temp.path().to_path_buf()).unwrap(); + let resolver = SubcommandResolver::new(root.clone().into()) .with_cli_options(cli_options(Arc::from(OsStr::new("node")))); let envs = Arc::new(EnvMap::default()); - for args in [ - &["src"][..], - &["-c", "custom.json", "src"], - &["--config", "custom.json", "src"], - &["--config=custom.json", "src"], - &["--disable-nested-config", "src"], - ] { - let tool_args: Vec = args.iter().map(|arg| (*arg).to_string()).collect(); - for (command, prefix) in [ - ( - SynthesizableSubcommand::Lint { args: tool_args.clone() }, - &["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", "tool.js"][..], - ), - (SynthesizableSubcommand::Fmt { args: tool_args }, &["tool.js"]), + for cwd in [&root, &root.join("packages/app"), &root.join("packages/app/src")] { + for args in [ + &[][..], + &["src"], + &["-c", "custom.json", "src"], + &["-c./custom.json", "src"], + &["-c=custom.json", "src"], + &["--config", "custom.json", "src"], + &["--config=custom.json", "src"], + &["--disable-nested-config", "src"], + &["--", "--config"], ] { - let resolved = resolver.resolve(command, &envs, &cwd).await.unwrap(); - let actual_args: Vec<&str> = resolved.args.iter().map(|arg| arg.as_str()).collect(); - let expected_args = [prefix, args].concat(); - assert_eq!(actual_args, expected_args); - } - } - } - - #[tokio::test] - async fn lint_and_fmt_from_subdirectory_use_the_matching_root_config_block() { - let temp = tempfile::tempdir().unwrap(); - let root = AbsolutePathBuf::new(temp.path().to_path_buf()).unwrap(); - let cwd = root.join("packages/app"); - let config_file = root.join("vite config.mts").as_path().to_str().unwrap().to_string(); - let envs = Arc::new(EnvMap::default()); - - for (mut config, has_lint, has_fmt) in [ - (serde_json::json!({}), false, false), - (serde_json::json!({ "lint": {} }), true, false), - (serde_json::json!({ "fmt": {} }), false, true), - (serde_json::json!({ "lint": {}, "fmt": {} }), true, true), - ] { - config["configFile"] = serde_json::json!(config_file); - let config = config.to_string(); - let root_string = root.as_path().to_str().unwrap().to_string(); - let mut options = cli_options(Arc::from(OsStr::new("node"))); - options.resolve_universal_vite_config = Arc::new(move |path| { - assert_eq!(path, root_string); - let config = config.clone(); - Box::pin(async move { Ok(config) }) - }); - let resolver = SubcommandResolver::new(root.clone().into()).with_cli_options(options); - - // A config-looking filename after `--` must not suppress root selection. - for args in [&["index.ts"][..], &["--", "--config"]] { let tool_args: Vec = args.iter().map(|arg| (*arg).to_string()).collect(); - for (command, prefix, has_block) in [ + for (command, prefix) in [ ( SynthesizableSubcommand::Lint { args: tool_args.clone() }, &["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", "tool.js"][..], - has_lint, ), - (SynthesizableSubcommand::Fmt { args: tool_args }, &["tool.js"], has_fmt), + (SynthesizableSubcommand::Fmt { args: tool_args }, &["tool.js"]), ] { - let resolved = resolver.resolve(command, &envs, &cwd).await.unwrap(); + let resolved = resolver.resolve(command, &envs, cwd).await.unwrap(); let actual_args: Vec<&str> = resolved.args.iter().map(|arg| arg.as_str()).collect(); - let mut expected_args = prefix.to_vec(); - if has_block { - expected_args.extend(["-c", config_file.as_str()]); - } - expected_args.extend(args); + let expected_args = [prefix, args].concat(); assert_eq!(actual_args, expected_args); } } } } - - #[tokio::test] - async fn explicit_lint_and_fmt_config_from_subdirectory_skips_root_config_loading() { - let temp = tempfile::tempdir().unwrap(); - let root = AbsolutePathBuf::new(temp.path().to_path_buf()).unwrap(); - let cwd = root.join("packages/app"); - let resolver = SubcommandResolver::new(root.into()) - .with_cli_options(cli_options(Arc::from(OsStr::new("node")))); - let envs = Arc::new(EnvMap::default()); - - for args in [ - &["-c", "custom.json", "index.ts"][..], - &["-c./custom.json", "index.ts"], - &["-c=custom.json", "index.ts"], - &["--config", "custom.json", "index.ts"], - &["--config=custom.json", "index.ts"], - ] { - let tool_args: Vec = args.iter().map(|arg| (*arg).to_string()).collect(); - for (command, prefix) in [ - ( - SynthesizableSubcommand::Lint { args: tool_args.clone() }, - &["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", "tool.js"][..], - ), - (SynthesizableSubcommand::Fmt { args: tool_args }, &["tool.js"]), - ] { - let resolved = resolver.resolve(command, &envs, &cwd).await.unwrap(); - let actual_args: Vec<&str> = resolved.args.iter().map(|arg| arg.as_str()).collect(); - let expected_args = [prefix, args].concat(); - assert_eq!(actual_args, expected_args); - } - } - } } From bd6583fc3c5e9c1a8c37be5a7eb84fdf0b9a69c6 Mon Sep 17 00:00:00 2001 From: MK Date: Thu, 24 Sep 2026 15:55:33 +0800 Subject: [PATCH 2/3] fix(cli): preserve workspace config selection in vp check --- .../workspace_fmt_root_config/snapshots.toml | 29 ++++++++++++++++--- .../snapshots/check_explicit_format_config.md | 21 ++++++++++++++ ...ormat.md => check_fix_uses_root_format.md} | 6 ++-- .../snapshots/check_without_root_format.md | 21 ++++++++++++++ .../discovered_format_options.global.md | 18 ++++++++++-- .../discovered_format_options.local.md | 18 ++++++++++-- .../workspace_lint_root_config/snapshots.toml | 24 +++++++++++++-- .../snapshots/check_explicit_lint_config.md | 21 ++++++++++++++ .../snapshots/check_without_root_lint.md | 28 ++++++++++++++++++ .../snapshots/conflicting_package_options.md | 22 +++++++++++++- .../discovered_rules_and_typecheck.global.md | 22 ++++++++++++-- .../discovered_rules_and_typecheck.local.md | 22 ++++++++++++-- docs/config/fmt.md | 4 ++- docs/config/lint.md | 4 ++- docs/guide/check.md | 2 +- docs/guide/fmt.md | 4 ++- docs/guide/lint.md | 4 ++- docs/guide/monorepo.md | 4 ++- docs/guide/troubleshooting.md | 2 +- packages/cli/binding/src/check/mod.rs | 25 ++++++++++++++-- packages/cli/binding/src/cli/resolver.rs | 4 +++ 21 files changed, 275 insertions(+), 30 deletions(-) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_explicit_format_config.md rename crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/{check_fix_uses_discovered_format.md => check_fix_uses_root_format.md} (75%) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_without_root_format.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_explicit_lint_config.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_without_root_lint.md diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml index 1090427cca..bd1dc6d22c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots.toml @@ -4,10 +4,10 @@ vp = ["local", "global"] cwd = "packages/app" steps = [ { argv = ["vp", "fmt", "--check", "index.js"], comment = "Oxfmt discovers the package format settings.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-lint", "index.js"], comment = "The formatting phase of check also discovers the package config.", continue-on-failure = true }, + { argv = ["vp", "check", "--no-lint", "index.js"], comment = "The check command keeps the root format settings.", continue-on-failure = true }, { argv = ["vp", "fmt", "index.js"] }, { argv = ["vpt", "print-file", "index.js"], comment = "The package double-quote and semicolon settings apply with package-relative file paths." }, - { argv = ["vp", "check", "--no-lint", "index.js"] }, + { argv = ["vp", "check", "--no-lint", "index.js"], comment = "Direct formatting does not change the root settings used by check.", continue-on-failure = true }, ] [[case]] @@ -22,11 +22,32 @@ steps = [ ] [[case]] -name = "check_fix_uses_discovered_format" +name = "check_fix_uses_root_format" vp = "local" cwd = "packages/app" steps = [ { argv = ["vp", "check", "--fix", "fix.js"] }, - { argv = ["vpt", "print-file", "fix.js"], comment = "Formatting after the root curly lint fix uses package quotes and semicolons." }, + { argv = ["vpt", "print-file", "fix.js"], comment = "Formatting after the curly lint fix also uses root quotes and semicolons." }, { argv = ["vp", "check", "fix.js"] }, ] + +[[case]] +name = "check_without_root_format" +vp = "local" +cwd = "packages/app" +steps = [ + { argv = ["vpt", "write-file", "../../vite.config.ts", "export default { lint: {} };\n"], snapshot = false }, + { argv = ["vpt", "write-file", "vite.config.ts", "export default { fmt: { singleQuote: true, semi: false } };\n"], snapshot = false }, + { argv = ["vpt", "write-file", "index.js", "export const message = 'hello'\n"], snapshot = false }, + { argv = ["vp", "check", "--no-lint", "index.js"], comment = "Without a root fmt block, check lets Oxfmt discover the package format settings." }, +] + +[[case]] +name = "check_explicit_format_config" +vp = "local" +cwd = "packages/app" +steps = [ + { argv = ["vp", "check", "--no-lint", "--", "-c", "vite.config.ts", "index.js"], comment = "An explicit config passed through check still takes precedence over the root config." }, + { argv = ["vp", "check", "--no-lint", "--", "--config", "vite.config.ts", "index.js"] }, + { argv = ["vp", "check", "--no-lint", "--", "--config=vite.config.ts", "index.js"] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_explicit_format_config.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_explicit_format_config.md new file mode 100644 index 0000000000..7fb9ac04ff --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_explicit_format_config.md @@ -0,0 +1,21 @@ +# check_explicit_format_config + +## `vp check --no-lint -- -c vite.config.ts index.js` + +An explicit config passed through check still takes precedence over the root config. + +``` +pass: All 1 file are correctly formatted (, threads) +``` + +## `vp check --no-lint -- --config vite.config.ts index.js` + +``` +pass: All 1 file are correctly formatted (, threads) +``` + +## `vp check --no-lint -- --config=vite.config.ts index.js` + +``` +pass: All 1 file are correctly formatted (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md similarity index 75% rename from crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md rename to crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md index aa2b8e21f9..b73d211e0f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_discovered_format.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_fix_uses_root_format.md @@ -1,4 +1,4 @@ -# check_fix_uses_discovered_format +# check_fix_uses_root_format ## `vp check --fix fix.js` @@ -9,12 +9,12 @@ pass: Found no warnings or lint errors in 1 file (, threads) ## `vpt print-file fix.js` -Formatting after the root curly lint fix uses package quotes and semicolons. +Formatting after the curly lint fix also uses root quotes and semicolons. ``` export function greet(show) { if (show) { - console.log("hello"); + console.log('hello') } } ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_without_root_format.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_without_root_format.md new file mode 100644 index 0000000000..3d9da7a4a2 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/check_without_root_format.md @@ -0,0 +1,21 @@ +# check_without_root_format + +## `vpt write-file ../../vite.config.ts 'export default { lint: {} }; +'` + + +## `vpt write-file vite.config.ts 'export default { fmt: { singleQuote: true, semi: false } }; +'` + + +## `vpt write-file index.js 'export const message = '\''hello'\'' +'` + + +## `vp check --no-lint index.js` + +Without a root fmt block, check lets Oxfmt discover the package format settings. + +``` +pass: All 1 file are correctly formatted (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md index bb8ce84331..88ca3cac27 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.global.md @@ -15,12 +15,17 @@ Finished in on 1 files using threads. ## `vp check --no-lint index.js` -The formatting phase of check also discovers the package config. +The check command keeps the root format settings. + +**Exit code:** 1 ``` VITE+ - The Unified Toolchain for the Web -pass: All 1 file are correctly formatted (, threads) +error: Formatting issues found +index.js () + +Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. ``` ## `vp fmt index.js` @@ -41,8 +46,15 @@ export const message = "hello"; ## `vp check --no-lint index.js` +Direct formatting does not change the root settings used by check. + +**Exit code:** 1 + ``` VITE+ - The Unified Toolchain for the Web -pass: All 1 file are correctly formatted (, threads) +error: Formatting issues found +index.js () + +Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md index fb3ae25bbf..11a0a4019e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_fmt_root_config/snapshots/discovered_format_options.local.md @@ -13,10 +13,15 @@ Finished in on 1 files using threads. ## `vp check --no-lint index.js` -The formatting phase of check also discovers the package config. +The check command keeps the root format settings. + +**Exit code:** 1 ``` -pass: All 1 file are correctly formatted (, threads) +error: Formatting issues found +index.js () + +Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. ``` ## `vp fmt index.js` @@ -35,6 +40,13 @@ export const message = "hello"; ## `vp check --no-lint index.js` +Direct formatting does not change the root settings used by check. + +**Exit code:** 1 + ``` -pass: All 1 file are correctly formatted (, threads) +error: Formatting issues found +index.js () + +Found formatting issues in 1 file (, threads). Run `vp check --fix` to fix them. ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml index 9312aa7387..4d98ffb41c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots.toml @@ -4,7 +4,7 @@ vp = ["local", "global"] cwd = "packages/app" steps = [ { argv = ["vp", "lint", "index.ts"], comment = "Oxlint discovers the package lint block without the root rules or type-check options.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "The lint phase of check also lets Oxlint discover the package config.", continue-on-failure = true }, + { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "The check command keeps the root lint rules and type-check options.", continue-on-failure = true }, { argv = ["vp", "check", "--no-fmt", "--no-lint", "index.ts"], comment = "Type-check-only mode still reports the type error without lint rules.", continue-on-failure = true }, { argv = ["vp", "lint", "-c", "../../vite.config.ts", "index.ts"], comment = "Explicit root selection enables the root rules and type checking while preserving package-relative file paths.", continue-on-failure = true }, ] @@ -16,8 +16,28 @@ cwd = "packages/app" steps = [ { argv = ["vpt", "write-file", "vite.config.ts", "export default { lint: { options: { typeAware: false, typeCheck: false }, rules: { 'no-console': 'off' } } };\n"], snapshot = false }, { argv = ["vp", "lint", "index.ts"], comment = "Oxlint uses the package rules and type-check options.", continue-on-failure = true }, - { argv = ["vp", "check", "--no-fmt", "index.ts"], continue-on-failure = true }, + { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "Package options cannot disable the root rules or type checking in check.", continue-on-failure = true }, { argv = ["vp", "lint", "-c", "vite.config.ts", "index.ts"], comment = "An explicit package config matches native discovery." }, { argv = ["vp", "lint", "--config", "vite.config.ts", "index.ts"] }, { argv = ["vp", "lint", "--config=vite.config.ts", "index.ts"] }, ] + +[[case]] +name = "check_without_root_lint" +vp = "local" +cwd = "packages/app" +steps = [ + { argv = ["vpt", "write-file", "../../vite.config.ts", "export default { fmt: {} };\n"], snapshot = false }, + { argv = ["vpt", "write-file", "vite.config.ts", "export default { lint: { rules: { 'no-console': 'error' } } };\n"], snapshot = false }, + { argv = ["vp", "check", "--no-fmt", "index.ts"], comment = "Without a root lint block, check lets Oxlint discover the package lint rules.", continue-on-failure = true }, +] + +[[case]] +name = "check_explicit_lint_config" +vp = "local" +cwd = "packages/app" +steps = [ + { argv = ["vp", "check", "--no-fmt", "--", "-c", "vite.config.ts", "index.ts"], comment = "An explicit config passed through check still takes precedence over the root config." }, + { argv = ["vp", "check", "--no-fmt", "--", "--config", "vite.config.ts", "index.ts"] }, + { argv = ["vp", "check", "--no-fmt", "--", "--config=vite.config.ts", "index.ts"] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_explicit_lint_config.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_explicit_lint_config.md new file mode 100644 index 0000000000..9175a841f8 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_explicit_lint_config.md @@ -0,0 +1,21 @@ +# check_explicit_lint_config + +## `vp check --no-fmt -- -c vite.config.ts index.ts` + +An explicit config passed through check still takes precedence over the root config. + +``` +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +``` + +## `vp check --no-fmt -- --config vite.config.ts index.ts` + +``` +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +``` + +## `vp check --no-fmt -- --config=vite.config.ts index.ts` + +``` +pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_without_root_lint.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_without_root_lint.md new file mode 100644 index 0000000000..356b4af143 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/check_without_root_lint.md @@ -0,0 +1,28 @@ +# check_without_root_lint + +## `vpt write-file ../../vite.config.ts 'export default { fmt: {} }; +'` + + +## `vpt write-file vite.config.ts 'export default { lint: { rules: { '\''no-console'\'': '\''error'\'' } } }; +'` + + +## `vp check --no-fmt index.ts` + +Without a root lint block, check lets Oxlint discover the package lint rules. + +**Exit code:** 1 + +``` +error: Lint issues found +× eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + +Found 1 error and 0 warnings in 1 file (, threads) +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md index f33fa63a8f..14350f59b3 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/conflicting_package_options.md @@ -15,8 +15,28 @@ Finished in on 1 file with rules using threads. ## `vp check --no-fmt index.ts` +Package options cannot disable the root rules or type checking in check. + +**Exit code:** 1 + ``` -pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +error: Lint or type issues found +× eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + + × typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 2 errors and 0 warnings in 1 file (, threads) ``` ## `vp lint -c vite.config.ts index.ts` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md index cf825d8b24..3702fe5cdc 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.global.md @@ -13,12 +13,30 @@ Finished in on 1 file with rules using threads. ## `vp check --no-fmt index.ts` -The lint phase of check also lets Oxlint discover the package config. +The check command keeps the root lint rules and type-check options. + +**Exit code:** 1 ``` VITE+ - The Unified Toolchain for the Web -pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +error: Lint or type issues found +× eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + + × typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 2 errors and 0 warnings in 1 file (, threads) ``` ## `vp check --no-fmt --no-lint index.ts` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md index 66d6ca7d69..924f30ef40 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/workspace_lint_root_config/snapshots/discovered_rules_and_typecheck.local.md @@ -11,10 +11,28 @@ Finished in on 1 file with rules using threads. ## `vp check --no-fmt index.ts` -The lint phase of check also lets Oxlint discover the package config. +The check command keeps the root lint rules and type-check options. + +**Exit code:** 1 ``` -pass: Found no warnings, lint errors, or type errors in 1 file (, threads) +error: Lint or type issues found +× eslint(no-console): Unexpected console statement. + ╭─[index.ts:2:1] + 1 │ export const value: number = "not a number"; + 2 │ console.log(value); + · ─────────── + ╰──── + help: Delete this console statement. + + × typescript(TS2322): Type 'string' is not assignable to type 'number'. + ╭─[index.ts:1:14] + 1 │ export const value: number = "not a number"; + · ───── + 2 │ console.log(value); + ╰──── + +Found 2 errors and 0 warnings in 1 file (, threads) ``` ## `vp check --no-fmt --no-lint index.ts` diff --git a/docs/config/fmt.md b/docs/config/fmt.md index a5ad28bbe5..fbc1c2402a 100644 --- a/docs/config/fmt.md +++ b/docs/config/fmt.md @@ -1,6 +1,8 @@ # Format Config -`vp fmt` and the formatting phase of `vp check` use Oxfmt's [native config discovery](/guide/fmt#configuration) from the working directory. Use `vp fmt -c ` or `vp fmt --config ` to select another config. See [Oxfmt's configuration](https://oxc.rs/docs/guide/usage/formatter/config.html) for details. +`vp fmt` uses Oxfmt's [native config discovery](/guide/fmt#configuration) from the working directory. Use `vp fmt -c ` or `vp fmt --config ` to select another config. See [Oxfmt's configuration](https://oxc.rs/docs/guide/usage/formatter/config.html) for details. + +`vp check` uses the workspace-root `fmt` block when it exists, including from a package directory. Package configs do not replace these format settings in `vp check`. ## Example diff --git a/docs/config/lint.md b/docs/config/lint.md index 548afba7e9..02bb85cb7c 100644 --- a/docs/config/lint.md +++ b/docs/config/lint.md @@ -1,6 +1,8 @@ # Lint Config -`vp lint` and the lint phase of `vp check` use Oxlint's [native config discovery](/guide/lint#configuration) from the working directory. Use `vp lint -c ` or `vp lint --config ` to select another config. See [Oxlint's configuration](https://oxc.rs/docs/guide/usage/linter/config.html) for details. +`vp lint` uses Oxlint's [native config discovery](/guide/lint#configuration) from the working directory. Use `vp lint -c ` or `vp lint --config ` to select another config. See [Oxlint's configuration](https://oxc.rs/docs/guide/usage/linter/config.html) for details. + +`vp check` uses the workspace-root `lint` block when it exists, including from a package directory. Package configs do not replace these lint settings in `vp check`. ## Example diff --git a/docs/guide/check.md b/docs/guide/check.md index 21bd98638f..f71225f9e4 100644 --- a/docs/guide/check.md +++ b/docs/guide/check.md @@ -29,7 +29,7 @@ vp check --no-fmt --no-lint # Type-check only (requires `typeCheck` enabled). - [`fmt`](/guide/fmt#configuration) block in `vite.config.ts` - TypeScript project structure and tsconfig files for type-aware linting -`vp check` lets Oxlint and Oxfmt discover their configurations from the working directory, including when run from a workspace package. File paths and TypeScript projects are also resolved from that directory. +When run from a workspace package, `vp check` uses the workspace-root `lint` and `fmt` blocks. Package configs cannot replace root format settings, lint rules, or type-check options. File paths and TypeScript projects are still resolved from the package working directory. Recommended base `lint` config: diff --git a/docs/guide/fmt.md b/docs/guide/fmt.md index 520fecf7cd..48461744df 100644 --- a/docs/guide/fmt.md +++ b/docs/guide/fmt.md @@ -20,7 +20,9 @@ vp fmt . --write Put formatting configuration directly in the `fmt` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `.oxfmtrc.json` with Vite+. -`vp fmt` and the formatting phase of `vp check` let Oxfmt discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options. +`vp fmt` lets Oxfmt discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options. + +`vp check` uses the workspace-root `fmt` block when it exists, including from a package directory. Package configs cannot replace those format settings in `vp check`. An explicit `vp fmt -c ` or `vp fmt --config ` selects another config. Otherwise, Oxfmt discovers the nearest `vite.config.*` file with a `fmt` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. diff --git a/docs/guide/lint.md b/docs/guide/lint.md index cc9dfd8d8c..3619f343d9 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -20,7 +20,9 @@ vp lint --type-aware Put lint configuration directly in the `lint` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `oxlint.config.ts` or `.oxlintrc.json` with Vite+. -`vp lint` and the lint phase of `vp check` let Oxlint discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules. +`vp lint` lets Oxlint discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules. + +`vp check` uses the workspace-root `lint` block when it exists, including from a package directory. Package configs cannot replace those lint settings in `vp check`. An explicit `vp lint -c ` or `vp lint --config ` selects another config. Otherwise, Oxlint discovers the nearest `vite.config.*` file with a `lint` block. Supported extensions are `.js`, `.mjs`, `.ts`, `.cjs`, `.mts`, and `.cts`. Nested configs do not override settings for individual files. diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index d30baa0a37..2c18f14c22 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -8,7 +8,9 @@ Because `vite.config.ts` is just JavaScript, you can choose to put your entire c Run `vp lint`, `vp fmt`, and `vp check` from the workspace root to apply the root lint and format settings across packages. These commands do not apply nested configs to individual files. Define file- or package-specific settings with overrides in the root `vite.config.ts`. -When run from a package directory, `vp lint`, `vp fmt`, and `vp check` let Oxlint and Oxfmt discover configuration from that directory. A package config with a `lint` or `fmt` block can therefore change the settings for that run. File arguments remain relative to the package working directory. +When run from a package directory, `vp lint` and `vp fmt` let Oxlint and Oxfmt discover configuration from that directory. A package config with a `lint` or `fmt` block can therefore change the settings for that run. File arguments remain relative to the package working directory. + +`vp check` uses the root `lint` and `fmt` blocks when they exist. Package configs cannot replace root format settings, lint rules, or type-check options in `vp check`. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index c1e44f797c..8b5909f121 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -26,7 +26,7 @@ The Oxlint type checker path powered by `tsgolint` does not support `baseUrl`. ` ## Nested lint or format config is not applied -When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. Oxlint and Oxfmt discover configuration from the working directory, so running from a package directory can select that package's config. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. +When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. `vp lint` and `vp fmt` let Oxlint and Oxfmt discover configuration from the working directory, so running from a package directory can select that package's config. `vp check` selects the workspace-root `lint` and `fmt` blocks when they exist. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. Keep lint and format settings in the root `vite.config.ts`. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) and [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific settings. You can also [import configuration objects](/guide/monorepo#composing-configuration-files) into the root config to keep settings in separate files. diff --git a/packages/cli/binding/src/check/mod.rs b/packages/cli/binding/src/check/mod.rs index bad28e26cd..56387073a6 100644 --- a/packages/cli/binding/src/check/mod.rs +++ b/packages/cli/binding/src/check/mod.rs @@ -39,6 +39,22 @@ pub(crate) async fn execute_check( let mut deferred_lint_pass: Option<(String, String)> = None; let resolved_vite_config = resolver.resolve_universal_vite_config().await?; + // Keep package runs on the root settings selected by `vp check`. + // Direct `vp lint` and `vp fmt` leave config discovery to the tools. + let explicit_config = paths + .iter() + .take_while(|arg| arg.as_str() != "--") + .any(|arg| arg.starts_with("-c") || arg == "--config" || arg.starts_with("--config=")); + let root_config_file = resolved_vite_config + .config_file + .as_deref() + .filter(|_| !explicit_config && cwd.as_path() != resolver.workspace_path().as_path()); + let config_args = |has_block: bool| match root_config_file.filter(|_| has_block) { + Some(path) => vec!["-c".to_string(), path.to_string()], + None => Vec::new(), + }; + let fmt_config_args = config_args(resolved_vite_config.fmt.is_some()); + // A step is skipped when either the CLI flag is passed OR `check.fmt`/ // `check.lint` is disabled in vite.config.ts. The skip note is printed only // when CONFIG (not the CLI flag) turned a step off, so existing `--no-fmt` / @@ -67,7 +83,10 @@ pub(crate) async fn execute_check( } if !no_fmt { - let mut args = if fix { vec![] } else { vec!["--check".to_string()] }; + let mut args = fmt_config_args.clone(); + if !fix { + args.push("--check".to_string()); + } if suppress_unmatched { args.push("--no-error-on-unmatched-pattern".to_string()); } @@ -147,7 +166,7 @@ pub(crate) async fn execute_check( if run_lint_phase { let lint_message_kind = LintMessageKind::from_flags(lint_enabled, type_check_enabled); - let mut args = Vec::new(); + let mut args = config_args(resolved_vite_config.lint.is_some()); // oxlint cannot auto-fix type diagnostics, so `--fix` is dropped on the // type-check-only path. if fix && lint_enabled { @@ -248,7 +267,7 @@ pub(crate) async fn execute_check( // Re-run fmt after lint --fix, since lint fixes can break formatting // (e.g. the curly rule adding braces to if-statements). if fix && !no_fmt && lint_enabled { - let mut args = Vec::new(); + let mut args = fmt_config_args; if suppress_unmatched { args.push("--no-error-on-unmatched-pattern".to_string()); } diff --git a/packages/cli/binding/src/cli/resolver.rs b/packages/cli/binding/src/cli/resolver.rs index cc37b9b8bc..ce85f109fe 100644 --- a/packages/cli/binding/src/cli/resolver.rs +++ b/packages/cli/binding/src/cli/resolver.rs @@ -41,6 +41,10 @@ impl SubcommandResolver { self } + pub(crate) fn workspace_path(&self) -> &AbsolutePath { + &self.workspace_path + } + fn cli_options(&self) -> anyhow::Result<&CliOptions> { self.cli_options .as_ref() From bc528f946cfc5cac67ec2ee9758a4a4f543c9f80 Mon Sep 17 00:00:00 2001 From: wan9chi Date: Fri, 25 Sep 2026 20:51:38 +0800 Subject: [PATCH 3/3] docs: avoid presenting package-level lint and fmt blocks as supported --- docs/guide/fmt.md | 2 +- docs/guide/lint.md | 2 +- docs/guide/monorepo.md | 2 +- docs/guide/troubleshooting.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/fmt.md b/docs/guide/fmt.md index 48461744df..5a1581b2ac 100644 --- a/docs/guide/fmt.md +++ b/docs/guide/fmt.md @@ -20,7 +20,7 @@ vp fmt . --write Put formatting configuration directly in the `fmt` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `.oxfmtrc.json` with Vite+. -`vp fmt` lets Oxfmt discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options. +`vp fmt` finds its config from the working directory, so package directories without their own `fmt` block use the root config. Relative file arguments retain their meaning. Use [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific options instead of adding `fmt` blocks to package configs. `vp check` uses the workspace-root `fmt` block when it exists, including from a package directory. Package configs cannot replace those format settings in `vp check`. diff --git a/docs/guide/lint.md b/docs/guide/lint.md index 3619f343d9..211008821e 100644 --- a/docs/guide/lint.md +++ b/docs/guide/lint.md @@ -20,7 +20,7 @@ vp lint --type-aware Put lint configuration directly in the `lint` block in the root `vite.config.ts` so all your configuration stays in one place. We do not recommend using `oxlint.config.ts` or `.oxlintrc.json` with Vite+. -`vp lint` lets Oxlint discover configuration from the working directory, including when run from a workspace package. Relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules. +`vp lint` finds its config from the working directory, so package directories without their own `lint` block use the root config. Relative file arguments retain their meaning. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) for file- or package-specific rules instead of adding `lint` blocks to package configs. `vp check` uses the workspace-root `lint` block when it exists, including from a package directory. Package configs cannot replace those lint settings in `vp check`. diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index 2c18f14c22..256b53962f 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -8,7 +8,7 @@ Because `vite.config.ts` is just JavaScript, you can choose to put your entire c Run `vp lint`, `vp fmt`, and `vp check` from the workspace root to apply the root lint and format settings across packages. These commands do not apply nested configs to individual files. Define file- or package-specific settings with overrides in the root `vite.config.ts`. -When run from a package directory, `vp lint` and `vp fmt` let Oxlint and Oxfmt discover configuration from that directory. A package config with a `lint` or `fmt` block can therefore change the settings for that run. File arguments remain relative to the package working directory. +When run from a package directory, `vp lint` and `vp fmt` still use the root settings unless the package config defines its own `lint` or `fmt` block. We do not recommend package-level blocks; use overrides in the root config instead. File arguments remain relative to the package working directory. `vp check` uses the root `lint` and `fmt` blocks when they exist. Package configs cannot replace root format settings, lint rules, or type-check options in `vp check`. diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md index 8b5909f121..3f2a930fa2 100644 --- a/docs/guide/troubleshooting.md +++ b/docs/guide/troubleshooting.md @@ -26,7 +26,7 @@ The Oxlint type checker path powered by `tsgolint` does not support `baseUrl`. ` ## Nested lint or format config is not applied -When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. `vp lint` and `vp fmt` let Oxlint and Oxfmt discover configuration from the working directory, so running from a package directory can select that package's config. `vp check` selects the workspace-root `lint` and `fmt` blocks when they exist. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. +When running `vp lint`, `vp fmt`, or `vp check`, configs in subdirectories do not override settings for individual files. `vp lint` and `vp fmt` let Oxlint and Oxfmt discover configuration from the working directory, so a `lint` or `fmt` block in a package config takes effect when run from that package, which we do not recommend. `vp check` selects the workspace-root `lint` and `fmt` blocks when they exist. Use `-c ` or `--config ` with `vp lint` or `vp fmt` to select another config explicitly. Keep lint and format settings in the root `vite.config.ts`. Use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) and [`fmt.overrides`](/guide/monorepo#format-overrides) for file- or package-specific settings. You can also [import configuration objects](/guide/monorepo#composing-configuration-files) into the root config to keep settings in separate files.