Skip to content

Commit cc89dbf

Browse files
committed
fix(oxlint): Skip vite.config.ts w/o .lint field in auto-discovery (#20255)
The same as #20254 for Oxlint. The refactoring to move the logic to the JS side is being addressed in a subsequent PR. (Copilot review will be also addressed)
1 parent 11a2b45 commit cc89dbf

File tree

12 files changed

+93
-6
lines changed

12 files changed

+93
-6
lines changed

apps/oxlint/src/config_loader.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
1414

1515
use crate::{
1616
DEFAULT_JSONC_OXLINTRC_NAME, DEFAULT_OXLINTRC_NAME, DEFAULT_TS_OXLINTRC_NAME, VITE_CONFIG_NAME,
17+
VITE_OXLINT_CONFIG_FIELD,
1718
};
1819

1920
#[cfg(feature = "napi")]
@@ -490,9 +491,12 @@ impl<'a> ConfigLoader<'a> {
490491
}
491492

492493
// Fallback: check for vite.config.ts with .lint field (lowest priority)
493-
let vite_path = dir.join(VITE_CONFIG_NAME);
494-
if vite_path.is_file() {
495-
return self.load_root_js_config(&vite_path).map(Some);
494+
// If .lint field is missing, skip it and continue config search.
495+
let vite_config_path = dir.join(VITE_CONFIG_NAME);
496+
if vite_config_path.is_file()
497+
&& let Some(config) = self.try_load_root_vite_config(&vite_config_path)?
498+
{
499+
return Ok(Some(config));
496500
}
497501

498502
Ok(None)
@@ -556,6 +560,28 @@ impl<'a> ConfigLoader<'a> {
556560
Ok(Oxlintrc::default())
557561
}
558562

563+
/// Try to load vite.config.ts, returning `Ok(None)` if `.lint` field is missing.
564+
/// Other errors (e.g., JS runtime not available, parse errors) are propagated.
565+
fn try_load_root_vite_config(&self, path: &Path) -> Result<Option<Oxlintrc>, OxcDiagnostic> {
566+
match self.load_root_js_config(path) {
567+
Ok(config) => Ok(Some(config)),
568+
Err(diagnostic) => {
569+
let msg = diagnostic.message.to_string();
570+
// NOTE: This relies on matching the error message from `parse_js_config_response` in js_config.rs.
571+
// If that message changes, this match must be updated accordingly.
572+
if msg.contains(&format!("Expected a `{VITE_OXLINT_CONFIG_FIELD}` field")) {
573+
tracing::debug!(
574+
"Skipping {} (no `{VITE_OXLINT_CONFIG_FIELD}` field), continuing config search...",
575+
path.display(),
576+
);
577+
Ok(None)
578+
} else {
579+
Err(diagnostic)
580+
}
581+
}
582+
}
583+
}
584+
559585
fn load_root_js_config(&self, path: &Path) -> Result<Oxlintrc, OxcDiagnostic> {
560586
match self.load_js_configs(&[path.to_path_buf()]) {
561587
Ok(mut configs) => Ok(configs.pop().unwrap_or_default()),

apps/oxlint/src/js_config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ fn parse_js_config_response(json: &str) -> Result<Vec<JsConfigResult>, Vec<OxcDi
129129
if let Some(v) = entry.config.get(VITE_OXLINT_CONFIG_FIELD).cloned() {
130130
v
131131
} else {
132+
// NOTE: This error message is shown to users (explicit `--config`) and also
133+
// matched by `try_load_root_vite_config` in config_loader.rs (auto-discovery skip).
132134
errors.push(OxcDiagnostic::error(format!(
133135
"Expected a `{VITE_OXLINT_CONFIG_FIELD}` field in the default export of {}",
134136
entry.path
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
debugger;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"singleThread": true,
3+
"args": ["--config", "vite.config.ts"]
4+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Exit code
2+
1
3+
4+
# stdout
5+
```
6+
Failed to parse oxlint configuration file.
7+
8+
x Expected a `lint` field in the default export of <fixture>/vite.config.ts
9+
```
10+
11+
# stderr
12+
```
13+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
plugins: [],
3+
};

apps/oxlint/test/fixtures/vite_config_no_lint_field/output.snap.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# Exit code
2-
1
2+
0
33

44
# stdout
55
```
6-
Failed to parse oxlint configuration file.
6+
! eslint(no-debugger): `debugger` statement is not allowed
7+
,-[files/test.js:1:1]
8+
1 | debugger;
9+
: ^^^^^^^^^
10+
`----
11+
help: Remove the debugger statement
712
8-
x Expected a `lint` field in the default export of <fixture>/vite.config.ts
13+
Found 1 warning and 0 errors.
14+
Finished in Xms on 1 file with 93 rules using X threads.
915
```
1016

1117
# stderr

apps/oxlint/test/lsp/lint/__snapshots__/lint.test.ts.snap

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ unused-disable-directive-from-config/test.ts
140140
--------------------"
141141
`;
142142

143+
exports[`LSP linting > config options > should apply config from vite-config-skip-finds-parent/child/test.js 1`] = `
144+
"--- FILE -----------
145+
vite-config-skip-finds-parent/child/test.js
146+
--- Diagnostics ---------
147+
1 | debugger;
148+
> 2 | if (x == 1) {
149+
| ^^ Warning: Expected === and instead saw ==
150+
help: Prefer === operator
151+
3 | }
152+
4 |
153+
--------------------> 1 | debugger;
154+
| ^^^^^^^^^ Error: \`debugger\` statement is not allowed
155+
help: Remove the debugger statement
156+
2 | if (x == 1) {
157+
3 | }
158+
4 |
159+
--------------------"
160+
`;
161+
143162
exports[`LSP linting > initializationOptions > should use custom config path from configPath 1`] = `
144163
"--- FILE -----------
145164
custom-config-path/test.ts
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"rules": {
3+
"no-debugger": "error",
4+
"eqeqeq": "warn"
5+
}
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
debugger;
2+
if (x == 1) {
3+
}

0 commit comments

Comments
 (0)