From e09d6ffd14f559ee5bc453e624cd548379a23928 Mon Sep 17 00:00:00 2001 From: Simon Buchan Date: Mon, 14 Jul 2025 19:23:50 +1200 Subject: [PATCH 1/3] fix(linter): Report implicit config parse errors (#12258) --- .../auto_config_parse_error/.oxlintrc.json | 5 ++ .../auto_config_parse_error/debugger.js | 1 + apps/oxlint/src/lint.rs | 62 +++++++++++-------- ...config_parse_error_debugger.js@oxlint.snap | 15 +++++ 4 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 apps/oxlint/fixtures/auto_config_parse_error/.oxlintrc.json create mode 100644 apps/oxlint/fixtures/auto_config_parse_error/debugger.js create mode 100644 apps/oxlint/src/snapshots/fixtures__auto_config_parse_error_debugger.js@oxlint.snap diff --git a/apps/oxlint/fixtures/auto_config_parse_error/.oxlintrc.json b/apps/oxlint/fixtures/auto_config_parse_error/.oxlintrc.json new file mode 100644 index 0000000000000..c0db87c7c0fea --- /dev/null +++ b/apps/oxlint/fixtures/auto_config_parse_error/.oxlintrc.json @@ -0,0 +1,5 @@ +{ + "rules" { + "no-debugger": "error" + } +} diff --git a/apps/oxlint/fixtures/auto_config_parse_error/debugger.js b/apps/oxlint/fixtures/auto_config_parse_error/debugger.js new file mode 100644 index 0000000000000..eab74692130a6 --- /dev/null +++ b/apps/oxlint/fixtures/auto_config_parse_error/debugger.js @@ -0,0 +1 @@ +debugger; diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index a4ee2647e74e8..62965fc388a8c 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -486,39 +486,43 @@ impl LintRunner { // when no config is provided, it will search for the default file names in the current working directory // when no file is found, the default configuration is returned fn find_oxlint_config(cwd: &Path, config: Option<&PathBuf>) -> Result { - if let Some(config_path) = config { - let full_path = match absolute(cwd.join(config_path)) { - Ok(path) => path, - Err(e) => { - let handler = GraphicalReportHandler::new(); - let mut err = String::new(); - handler - .render_report( - &mut err, - &OxcDiagnostic::error(format!( - "Failed to resolve config path {}: {e}", - config_path.display() - )), - ) - .unwrap(); - return Err(err); - } - }; - return match Oxlintrc::from_file(&full_path) { + let path: &Path = config.map_or(Self::DEFAULT_OXLINTRC.as_ref(), PathBuf::as_ref); + match (absolute(cwd.join(path)), config) { + // Config file exists, either explicitly provided or the default file name + // Parse the config file and return it, or report an error if parsing fails + (Ok(full_path), _) => match Oxlintrc::from_file(&full_path) { Ok(config) => Ok(config), Err(diagnostic) => { let handler = GraphicalReportHandler::new(); let mut err = String::new(); handler.render_report(&mut err, &diagnostic).unwrap(); - return Err(err); + // normalize the config path in the error message for simpler snapshot testing + let err = err.replace( + full_path.to_string_lossy().as_ref(), + &full_path.to_string_lossy().cow_replace('\\', "/"), + ); + Err(err) } - }; + }, + // Failed to resolve config, and it was explicitly provided + // Return that the config file could not be found + (Err(e), Some(config_path)) => { + let handler = GraphicalReportHandler::new(); + let mut err = String::new(); + handler + .render_report( + &mut err, + &OxcDiagnostic::error(format!( + "Failed to resolve config path {}: {e}", + config_path.display() + )), + ) + .unwrap(); + Err(err) + } + // Failed to resolve implicit path, return default config + _ => Ok(Oxlintrc::default()), } - // no config argument is provided, - // auto detect default config file from current work directory - // or return the default configuration, when no valid file is found - let config_path = cwd.join(Self::DEFAULT_OXLINTRC); - Oxlintrc::from_file(&config_path).or_else(|_| Ok(Oxlintrc::default())) } /// Looks in a directory for an oxlint config file, returns the oxlint config if it exists @@ -715,6 +719,12 @@ mod test { Tester::new().with_cwd("fixtures/auto_config_detection".into()).test_and_snapshot(args); } + #[test] + fn oxlint_config_auto_detection_parse_error() { + let args = &["debugger.js"]; + Tester::new().with_cwd("fixtures/auto_config_parse_error".into()).test_and_snapshot(args); + } + #[test] fn eslintrc_no_undef() { let args = &[ diff --git a/apps/oxlint/src/snapshots/fixtures__auto_config_parse_error_debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__auto_config_parse_error_debugger.js@oxlint.snap new file mode 100644 index 0000000000000..36af2fd528796 --- /dev/null +++ b/apps/oxlint/src/snapshots/fixtures__auto_config_parse_error_debugger.js@oxlint.snap @@ -0,0 +1,15 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: debugger.js +working directory: fixtures/auto_config_parse_error +---------- +Failed to parse configuration file. + + x Failed to parse eslint config /fixtures/auto_config_parse_error/.oxlintrc.json. + | expected `:` at line 2 column 11 + +---------- +CLI result: InvalidOptionConfig +---------- From bdb4af2f9b1805b5a0b9b908b42fc5535a16605a Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Mon, 14 Jul 2025 09:38:41 +0100 Subject: [PATCH 2/3] fix --- apps/oxlint/src/lint.rs | 53 +++++++++++++---------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 62965fc388a8c..6bc0455f448cb 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -82,6 +82,12 @@ impl Runner for LintRunner { } }; + let handler = if cfg!(any(test, feature = "force_test_reporter")) { + GraphicalReportHandler::new_themed(miette::GraphicalTheme::none()) + } else { + GraphicalReportHandler::new() + }; + let config_search_result = Self::find_oxlint_config(&self.cwd, basic_options.config.as_ref()); @@ -90,8 +96,12 @@ impl Runner for LintRunner { Err(err) => { print_and_flush_stdout( stdout, - &format!("Failed to parse configuration file.\n{err}\n"), + &format!( + "Failed to parse configuration file.\n{}\n", + render_report(&handler, &err) + ), ); + return CliRunResult::InvalidOptionConfig; } }; @@ -172,12 +182,6 @@ impl Runner for LintRunner { let paths = walker.paths(); let number_of_files = paths.len(); - let handler = if cfg!(any(test, feature = "force_test_reporter")) { - GraphicalReportHandler::new_themed(miette::GraphicalTheme::none()) - } else { - GraphicalReportHandler::new() - }; - let mut external_plugin_store = ExternalPluginStore::default(); let search_for_nested_configs = !disable_nested_config && @@ -485,41 +489,18 @@ impl LintRunner { // when config is provided, but not found, an String with the formatted error is returned, else the oxlintrc config file is returned // when no config is provided, it will search for the default file names in the current working directory // when no file is found, the default configuration is returned - fn find_oxlint_config(cwd: &Path, config: Option<&PathBuf>) -> Result { + fn find_oxlint_config(cwd: &Path, config: Option<&PathBuf>) -> Result { let path: &Path = config.map_or(Self::DEFAULT_OXLINTRC.as_ref(), PathBuf::as_ref); match (absolute(cwd.join(path)), config) { // Config file exists, either explicitly provided or the default file name // Parse the config file and return it, or report an error if parsing fails - (Ok(full_path), _) => match Oxlintrc::from_file(&full_path) { - Ok(config) => Ok(config), - Err(diagnostic) => { - let handler = GraphicalReportHandler::new(); - let mut err = String::new(); - handler.render_report(&mut err, &diagnostic).unwrap(); - // normalize the config path in the error message for simpler snapshot testing - let err = err.replace( - full_path.to_string_lossy().as_ref(), - &full_path.to_string_lossy().cow_replace('\\', "/"), - ); - Err(err) - } - }, + (Ok(full_path), _) => Oxlintrc::from_file(&full_path), // Failed to resolve config, and it was explicitly provided // Return that the config file could not be found - (Err(e), Some(config_path)) => { - let handler = GraphicalReportHandler::new(); - let mut err = String::new(); - handler - .render_report( - &mut err, - &OxcDiagnostic::error(format!( - "Failed to resolve config path {}: {e}", - config_path.display() - )), - ) - .unwrap(); - Err(err) - } + (Err(e), Some(config_path)) => Err(OxcDiagnostic::error(format!( + "Failed to resolve config path {}: {e}", + config_path.display() + ))), // Failed to resolve implicit path, return default config _ => Ok(Oxlintrc::default()), } From 42d6bd71a82efbe7a6fb81a8301c0de18aba7744 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Mon, 14 Jul 2025 09:48:52 +0100 Subject: [PATCH 3/3] u --- apps/oxlint/src/lint.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 6bc0455f448cb..de24e8972aca8 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -491,19 +491,12 @@ impl LintRunner { // when no file is found, the default configuration is returned fn find_oxlint_config(cwd: &Path, config: Option<&PathBuf>) -> Result { let path: &Path = config.map_or(Self::DEFAULT_OXLINTRC.as_ref(), PathBuf::as_ref); - match (absolute(cwd.join(path)), config) { - // Config file exists, either explicitly provided or the default file name - // Parse the config file and return it, or report an error if parsing fails - (Ok(full_path), _) => Oxlintrc::from_file(&full_path), - // Failed to resolve config, and it was explicitly provided - // Return that the config file could not be found - (Err(e), Some(config_path)) => Err(OxcDiagnostic::error(format!( - "Failed to resolve config path {}: {e}", - config_path.display() - ))), - // Failed to resolve implicit path, return default config - _ => Ok(Oxlintrc::default()), + let full_path = cwd.join(path); + + if config.is_some() || full_path.exists() { + return Oxlintrc::from_file(&full_path); } + Ok(Oxlintrc::default()) } /// Looks in a directory for an oxlint config file, returns the oxlint config if it exists