@@ -9,7 +9,7 @@ use std::{
99
1010use cow_utils:: CowUtils ;
1111use ignore:: { gitignore:: Gitignore , overrides:: OverrideBuilder } ;
12- use rustc_hash:: { FxBuildHasher , FxHashMap , FxHashSet } ;
12+ use rustc_hash:: { FxBuildHasher , FxHashMap } ;
1313
1414use oxc_diagnostics:: { DiagnosticSender , DiagnosticService , GraphicalReportHandler , OxcDiagnostic } ;
1515use oxc_linter:: {
@@ -20,7 +20,7 @@ use oxc_linter::{
2020use crate :: {
2121 DEFAULT_OXLINTRC_NAME ,
2222 cli:: { CliRunResult , LintCommand , MiscOptions , ReportUnusedDirectives , WarningOptions } ,
23- config_loader:: { ConfigLoadError , ConfigLoader } ,
23+ config_loader:: { ConfigLoadError , ConfigLoader , discover_configs_in_ancestors } ,
2424 output_formatter:: { LintCommandInfo , OutputFormatter } ,
2525 walk:: Walk ,
2626} ;
@@ -474,40 +474,18 @@ impl CliRunner {
474474 external_plugin_store : & mut ExternalPluginStore ,
475475 nested_ignore_patterns : & mut Vec < ( Vec < String > , PathBuf ) > ,
476476 ) -> Result < FxHashMap < PathBuf , Config > , CliRunResult > {
477- // TODO(perf): benchmark whether or not it is worth it to store the configurations on a
478- // per-file or per-directory basis, to avoid calling `.parent()` on every path.
479- let mut nested_oxlintrc = FxHashSet :: < PathBuf > :: default ( ) ;
480- // get all of the unique directories among the paths to use for search for
481- // oxlint config files in those directories and their ancestors
482- // e.g. `/some/file.js` will check `/some` and `/`
483- // `/some/other/file.js` will check `/some/other`, `/some`, and `/`
484- let mut directories = FxHashSet :: default ( ) ;
485- for path in paths {
486- let path = Path :: new ( path) ;
487- // Start from the file's parent directory and walk up the tree
488- let mut current = path. parent ( ) ;
489- while let Some ( dir) = current {
490- // NOTE: Initial benchmarking showed that it was faster to iterate over the directories twice
491- // rather than constructing the configs in one iteration. It's worth re-benchmarking that though.
492- let inserted = directories. insert ( dir) ;
493- if !inserted {
494- break ;
495- }
496- current = dir. parent ( ) ;
497- }
498- }
499- for directory in directories {
500- if let Some ( path) = Self :: find_oxlint_config_path_in_directory ( directory) {
501- nested_oxlintrc. insert ( path) ;
502- }
503- }
477+ // Discover config files by walking up from each file's directory
478+ let config_paths: Vec < _ > =
479+ paths. iter ( ) . map ( |p| Path :: new ( p. as_ref ( ) ) . to_path_buf ( ) ) . collect ( ) ;
480+ let discovered_configs = discover_configs_in_ancestors ( & config_paths) ;
504481
505482 // Load all discovered configs
506483 let mut loader = ConfigLoader :: new ( external_linter, external_plugin_store, filters) ;
507- let ( configs, errors) = loader. load_many ( nested_oxlintrc ) ;
484+ let ( configs, errors) = loader. load_many ( discovered_configs ) ;
508485
509- if let Some ( error) = errors. first ( ) {
510- let message = match error {
486+ // Fail on first error (CLI requires all configs to be valid)
487+ if let Some ( error) = errors. into_iter ( ) . next ( ) {
488+ let message = match & error {
511489 ConfigLoadError :: Parse { path, error } => {
512490 format ! (
513491 "Failed to parse oxlint configuration file at {}.\n {}\n " ,
@@ -517,17 +495,17 @@ impl CliRunner {
517495 }
518496 ConfigLoadError :: Build { path, error } => {
519497 format ! (
520- "Failed to build oxlint configuration file at {}.\n {}\n " ,
498+ "Failed to build configuration from {}.\n {}\n " ,
521499 path. to_string_lossy( ) . cow_replace( '\\' , "/" ) ,
522500 render_report( handler, & OxcDiagnostic :: error( error. clone( ) ) )
523501 )
524502 }
525503 } ;
526-
527504 print_and_flush_stdout ( stdout, & message) ;
528505 return Err ( CliRunResult :: InvalidOptionConfig ) ;
529506 }
530507
508+ // Convert loaded configs to nested config format
531509 let mut nested_configs =
532510 FxHashMap :: < PathBuf , Config > :: with_capacity_and_hasher ( configs. len ( ) , FxBuildHasher ) ;
533511 for loaded in configs {
@@ -551,13 +529,6 @@ impl CliRunner {
551529 }
552530 Ok ( Oxlintrc :: default ( ) )
553531 }
554-
555- /// Looks in a directory for an oxlint config file and returns the path if it exists.
556- /// Does not validate the file or apply the default config file.
557- fn find_oxlint_config_path_in_directory ( dir : & Path ) -> Option < PathBuf > {
558- let possible_config_path = dir. join ( DEFAULT_OXLINTRC_NAME ) ;
559- if possible_config_path. is_file ( ) { Some ( possible_config_path) } else { None }
560- }
561532}
562533
563534pub fn print_and_flush_stdout ( stdout : & mut dyn Write , message : & str ) {
0 commit comments