diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/eslintrc.json b/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/eslintrc.json new file mode 100644 index 000000000..dab8e7c35 --- /dev/null +++ b/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/eslintrc.json @@ -0,0 +1,5 @@ +{ + "ignorePatterns": [ + "tests/**" + ] +} diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/main.js b/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/main.js new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/main.js @@ -0,0 +1 @@ + diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/tests/main.spec.js b/apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/tests/main.spec.js new file mode 100644 index 000000000..e69de29bb diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/eslintrc.json b/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/eslintrc.json new file mode 100644 index 000000000..91c51660d --- /dev/null +++ b/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/eslintrc.json @@ -0,0 +1,5 @@ +{ + "ignorePatterns": [ + "*.js" + ] +} diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.js b/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.js new file mode 100644 index 000000000..e69de29bb diff --git a/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.ts b/apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/oxlint/fixtures/print_config/ban_rules/expect.json b/apps/oxlint/fixtures/print_config/ban_rules/expect.json index bed2b9502..ee4c1a2e3 100644 --- a/apps/oxlint/fixtures/print_config/ban_rules/expect.json +++ b/apps/oxlint/fixtures/print_config/ban_rules/expect.json @@ -40,5 +40,6 @@ "env": { "builtin": true }, - "globals": {} + "globals": {}, + "ignorePatterns": [] } diff --git a/apps/oxlint/fixtures/print_config/normal/expect.json b/apps/oxlint/fixtures/print_config/normal/expect.json index cda666fbb..bd944f933 100644 --- a/apps/oxlint/fixtures/print_config/normal/expect.json +++ b/apps/oxlint/fixtures/print_config/normal/expect.json @@ -33,5 +33,6 @@ "env": { "builtin": true }, - "globals": {} + "globals": {}, + "ignorePatterns": [] } diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index faa2d501f..dc647a441 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -102,11 +102,6 @@ impl Runner for LintRunner { .copied() .collect::>(); - let paths = - Walk::new(&paths, &ignore_options).with_extensions(Extensions(extensions)).paths(); - - let number_of_files = paths.len(); - let config_search_result = Self::find_oxlint_config(&self.cwd, &basic_options.config); if let Err(err) = config_search_result { @@ -115,6 +110,17 @@ impl Runner for LintRunner { let mut oxlintrc = config_search_result.unwrap(); + let ignore_paths = oxlintrc + .ignore_patterns + .iter() + .map(|value| oxlintrc.path.parent().unwrap().join(value)) + .collect::>(); + let paths = Walk::new(&paths, &ignore_options, &ignore_paths) + .with_extensions(Extensions(extensions)) + .paths(); + + let number_of_files = paths.len(); + enable_plugins.apply_overrides(&mut oxlintrc.plugins); let oxlintrc_for_print = @@ -750,4 +756,24 @@ mod test { assert_eq!(result.number_of_warnings, 2); assert_eq!(result.number_of_errors, 2); } + + #[test] + fn test_config_ignore_patterns_extension() { + let result = test(&[ + "-c", + "fixtures/config_ignore_patterns/ignore_extension/eslintrc.json", + "fixtures/config_ignore_patterns/ignore_extension", + ]); + assert_eq!(result.number_of_files, 1); + } + + #[test] + fn test_config_ignore_patterns_directory() { + let result = test(&[ + "-c", + "fixtures/config_ignore_patterns/ignore_directory/eslintrc.json", + "fixtures/config_ignore_patterns/ignore_directory", + ]); + assert_eq!(result.number_of_files, 1); + } } diff --git a/apps/oxlint/src/walk.rs b/apps/oxlint/src/walk.rs index 7c10bacd0..b2f3160d6 100644 --- a/apps/oxlint/src/walk.rs +++ b/apps/oxlint/src/walk.rs @@ -70,7 +70,11 @@ impl ignore::ParallelVisitor for WalkCollector { impl Walk { /// Will not canonicalize paths. /// # Panics - pub fn new(paths: &[PathBuf], options: &IgnoreOptions) -> Self { + pub fn new( + paths: &[PathBuf], + options: &IgnoreOptions, + config_ignore_patterns: &[PathBuf], + ) -> Self { assert!(!paths.is_empty(), "At least one path must be provided to Walk::new"); let mut inner = ignore::WalkBuilder::new( @@ -89,17 +93,24 @@ impl Walk { if !options.no_ignore { inner.add_custom_ignore_filename(&options.ignore_path); + let mut override_builder = OverrideBuilder::new(Path::new("/")); if !options.ignore_pattern.is_empty() { - let mut override_builder = OverrideBuilder::new(Path::new("/")); for pattern in &options.ignore_pattern { // Meaning of ignore pattern is reversed // let pattern = format!("!{pattern}"); override_builder.add(&pattern).unwrap(); } - let overrides = override_builder.build().unwrap(); - inner.overrides(overrides); } + + if !config_ignore_patterns.is_empty() { + for pattern in config_ignore_patterns { + let pattern = format!("!{}", pattern.to_str().unwrap()); + override_builder.add(&pattern).unwrap(); + } + } + + inner.overrides(override_builder.build().unwrap()); } // Turning off `follow_links` because: // * following symlinks is a really slow syscall @@ -155,7 +166,7 @@ mod test { symlinks: false, }; - let mut paths = Walk::new(&fixtures, &ignore_options) + let mut paths = Walk::new(&fixtures, &ignore_options, &[]) .with_extensions(Extensions(["js", "vue"].to_vec())) .paths() .into_iter() diff --git a/crates/oxc_language_server/src/main.rs b/crates/oxc_language_server/src/main.rs index 6b32aa2da..8a4d63eb1 100644 --- a/crates/oxc_language_server/src/main.rs +++ b/crates/oxc_language_server/src/main.rs @@ -88,7 +88,6 @@ enum SyntheticRunLevel { impl LanguageServer for Backend { async fn initialize(&self, params: InitializeParams) -> Result { self.init(params.root_uri)?; - self.init_ignore_glob().await; let options = params.initialization_options.and_then(|mut value| { let settings = value.get_mut("settings")?.take(); serde_json::from_value::(settings).ok() @@ -117,7 +116,8 @@ impl LanguageServer for Backend { None }; - self.init_linter_config().await; + let oxlintrc = self.init_linter_config().await; + self.init_ignore_glob(oxlintrc).await; Ok(InitializeResult { server_info: Some(ServerInfo { name: "oxc".into(), version: None }), offset_encoding: None, @@ -411,7 +411,7 @@ impl Backend { Ok(()) } - async fn init_ignore_glob(&self) { + async fn init_ignore_glob(&self, oxlintrc: Option) { let uri = self .root_uri .get() @@ -447,6 +447,17 @@ impl Backend { } } } + + if let Some(oxlintrc) = oxlintrc { + if !oxlintrc.ignore_patterns.is_empty() { + let mut builder = + ignore::gitignore::GitignoreBuilder::new(oxlintrc.path.parent().unwrap()); + for entry in &oxlintrc.ignore_patterns { + builder.add_line(None, entry).expect("Failed to add ignore line"); + } + gitignore_globs.push(builder.build().unwrap()); + } + } } #[allow(clippy::ptr_arg)] @@ -470,12 +481,12 @@ impl Backend { .await; } - async fn init_linter_config(&self) { + async fn init_linter_config(&self) -> Option { let Some(Some(uri)) = self.root_uri.get() else { - return; + return None; }; let Ok(root_path) = uri.to_file_path() else { - return; + return None; }; let mut config_path = None; let config = root_path.join(self.options.lock().await.get_config_path().unwrap()); @@ -484,16 +495,17 @@ impl Backend { } if let Some(config_path) = config_path { let mut linter = self.server_linter.write().await; + let config = Oxlintrc::from_file(&config_path) + .expect("should have initialized linter with new options"); *linter = ServerLinter::new_with_linter( - LinterBuilder::from_oxlintrc( - true, - Oxlintrc::from_file(&config_path) - .expect("should have initialized linter with new options"), - ) - .with_fix(FixKind::SafeFix) - .build(), + LinterBuilder::from_oxlintrc(true, config.clone()) + .with_fix(FixKind::SafeFix) + .build(), ); + return Some(config); } + + None } async fn handle_file_update(&self, uri: Url, content: Option, version: Option) { diff --git a/crates/oxc_linter/src/builder.rs b/crates/oxc_linter/src/builder.rs index dfbe82727..9ba6a6e56 100644 --- a/crates/oxc_linter/src/builder.rs +++ b/crates/oxc_linter/src/builder.rs @@ -95,6 +95,7 @@ impl LinterBuilder { rules: oxlintrc_rules, overrides, path, + ignore_patterns: _, } = oxlintrc; let config = LintConfig { plugins, settings, env, globals, path: Some(path) }; diff --git a/crates/oxc_linter/src/config/oxlintrc.rs b/crates/oxc_linter/src/config/oxlintrc.rs index 1fc3f64e3..7e6d7d09c 100644 --- a/crates/oxc_linter/src/config/oxlintrc.rs +++ b/crates/oxc_linter/src/config/oxlintrc.rs @@ -88,6 +88,9 @@ pub struct Oxlintrc { /// Absolute path to the configuration file. #[serde(skip)] pub path: PathBuf, + /// Globs to ignore during linting. These are resolved from the configuration file path. + #[serde(rename = "ignorePatterns")] + pub ignore_patterns: Vec, } impl Oxlintrc { diff --git a/crates/oxc_linter/src/snapshots/schema_json.snap b/crates/oxc_linter/src/snapshots/schema_json.snap index 2e159c948..0ded97643 100644 --- a/crates/oxc_linter/src/snapshots/schema_json.snap +++ b/crates/oxc_linter/src/snapshots/schema_json.snap @@ -1,7 +1,6 @@ --- source: crates/oxc_linter/src/lib.rs expression: json -snapshot_kind: text --- { "$schema": "http://json-schema.org/draft-07/schema#", @@ -37,6 +36,14 @@ snapshot_kind: text } ] }, + "ignorePatterns": { + "description": "Globs to ignore during linting. These are resolved from the configuration file path.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, "overrides": { "description": "Add, remove, or otherwise reconfigure rules for specific files or groups of files.", "allOf": [ diff --git a/npm/oxlint/configuration_schema.json b/npm/oxlint/configuration_schema.json index 2a1452e8d..ba0f547ab 100644 --- a/npm/oxlint/configuration_schema.json +++ b/npm/oxlint/configuration_schema.json @@ -32,6 +32,14 @@ } ] }, + "ignorePatterns": { + "description": "Globs to ignore during linting. These are resolved from the configuration file path.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, "overrides": { "description": "Add, remove, or otherwise reconfigure rules for specific files or groups of files.", "allOf": [ diff --git a/tasks/website/src/linter/snapshots/schema_markdown.snap b/tasks/website/src/linter/snapshots/schema_markdown.snap index fcda4fbef..bc81c19b4 100644 --- a/tasks/website/src/linter/snapshots/schema_markdown.snap +++ b/tasks/website/src/linter/snapshots/schema_markdown.snap @@ -1,7 +1,6 @@ --- source: tasks/website/src/linter/json_schema.rs expression: snapshot -snapshot_kind: text --- # Oxlint Configuration File @@ -161,6 +160,15 @@ Globals can be disabled by setting their value to `"off"`. For example, in an en You may also use `"readable"` or `false` to represent `"readonly"`, and `"writeable"` or `true` to represent `"writable"`. +## ignorePatterns + +type: `string[]` + +default: `[]` + +Globs to ignore during linting. These are resolved from the configuration file path. + + ## overrides type: `array`