Skip to content

Commit 2717bf2

Browse files
committed
feat(linter): Add support for ignorePatterns property within config file
1 parent ef62b9d commit 2717bf2

File tree

16 files changed

+116
-27
lines changed

16 files changed

+116
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"ignorePatterns": [
3+
"tests/**"
4+
]
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

apps/oxlint/fixtures/config_ignore_patterns/ignore_directory/tests/main.spec.js

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"ignorePatterns": [
3+
"*.js"
4+
]
5+
}

apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.js

Whitespace-only changes.

apps/oxlint/fixtures/config_ignore_patterns/ignore_extension/main.ts

Whitespace-only changes.

apps/oxlint/fixtures/print_config/ban_rules/expect.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
"env": {
4141
"builtin": true
4242
},
43-
"globals": {}
43+
"globals": {},
44+
"ignorePatterns": []
4445
}

apps/oxlint/fixtures/print_config/normal/expect.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
"env": {
3434
"builtin": true
3535
},
36-
"globals": {}
36+
"globals": {},
37+
"ignorePatterns": []
3738
}

apps/oxlint/src/lint.rs

+31-5
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,6 @@ impl Runner for LintRunner {
102102
.copied()
103103
.collect::<Vec<&'static str>>();
104104

105-
let paths =
106-
Walk::new(&paths, &ignore_options).with_extensions(Extensions(extensions)).paths();
107-
108-
let number_of_files = paths.len();
109-
110105
let config_search_result = Self::find_oxlint_config(&self.cwd, &basic_options.config);
111106

112107
if let Err(err) = config_search_result {
@@ -115,6 +110,17 @@ impl Runner for LintRunner {
115110

116111
let mut oxlintrc = config_search_result.unwrap();
117112

113+
let ignore_paths = oxlintrc
114+
.ignore_patterns
115+
.iter()
116+
.map(|value| oxlintrc.path.parent().unwrap().join(value))
117+
.collect::<Vec<_>>();
118+
let paths = Walk::new(&paths, &ignore_options, &ignore_paths)
119+
.with_extensions(Extensions(extensions))
120+
.paths();
121+
122+
let number_of_files = paths.len();
123+
118124
enable_plugins.apply_overrides(&mut oxlintrc.plugins);
119125

120126
let oxlintrc_for_print =
@@ -750,4 +756,24 @@ mod test {
750756
assert_eq!(result.number_of_warnings, 2);
751757
assert_eq!(result.number_of_errors, 2);
752758
}
759+
760+
#[test]
761+
fn test_config_ignore_patterns_extension() {
762+
let result = test(&[
763+
"-c",
764+
"fixtures/config_ignore_patterns/ignore_extension/eslintrc.json",
765+
"fixtures/config_ignore_patterns/ignore_extension",
766+
]);
767+
assert_eq!(result.number_of_files, 1);
768+
}
769+
770+
#[test]
771+
fn test_config_ignore_patterns_directory() {
772+
let result = test(&[
773+
"-c",
774+
"fixtures/config_ignore_patterns/ignore_directory/eslintrc.json",
775+
"fixtures/config_ignore_patterns/ignore_directory",
776+
]);
777+
assert_eq!(result.number_of_files, 1);
778+
}
753779
}

apps/oxlint/src/walk.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ impl ignore::ParallelVisitor for WalkCollector {
7070
impl Walk {
7171
/// Will not canonicalize paths.
7272
/// # Panics
73-
pub fn new(paths: &[PathBuf], options: &IgnoreOptions) -> Self {
73+
pub fn new(
74+
paths: &[PathBuf],
75+
options: &IgnoreOptions,
76+
config_ignore_patterns: &[PathBuf],
77+
) -> Self {
7478
assert!(!paths.is_empty(), "At least one path must be provided to Walk::new");
7579

7680
let mut inner = ignore::WalkBuilder::new(
@@ -89,17 +93,24 @@ impl Walk {
8993
if !options.no_ignore {
9094
inner.add_custom_ignore_filename(&options.ignore_path);
9195

96+
let mut override_builder = OverrideBuilder::new(Path::new("/"));
9297
if !options.ignore_pattern.is_empty() {
93-
let mut override_builder = OverrideBuilder::new(Path::new("/"));
9498
for pattern in &options.ignore_pattern {
9599
// Meaning of ignore pattern is reversed
96100
// <https://docs.rs/ignore/latest/ignore/overrides/struct.OverrideBuilder.html#method.add>
97101
let pattern = format!("!{pattern}");
98102
override_builder.add(&pattern).unwrap();
99103
}
100-
let overrides = override_builder.build().unwrap();
101-
inner.overrides(overrides);
102104
}
105+
106+
if !config_ignore_patterns.is_empty() {
107+
for pattern in config_ignore_patterns {
108+
let pattern = format!("!{}", pattern.to_str().unwrap());
109+
override_builder.add(&pattern).unwrap();
110+
}
111+
}
112+
113+
inner.overrides(override_builder.build().unwrap());
103114
}
104115
// Turning off `follow_links` because:
105116
// * following symlinks is a really slow syscall
@@ -155,7 +166,7 @@ mod test {
155166
symlinks: false,
156167
};
157168

158-
let mut paths = Walk::new(&fixtures, &ignore_options)
169+
let mut paths = Walk::new(&fixtures, &ignore_options, &[])
159170
.with_extensions(Extensions(["js", "vue"].to_vec()))
160171
.paths()
161172
.into_iter()

crates/oxc_language_server/src/main.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ enum SyntheticRunLevel {
8888
impl LanguageServer for Backend {
8989
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
9090
self.init(params.root_uri)?;
91-
self.init_ignore_glob().await;
9291
let options = params.initialization_options.and_then(|mut value| {
9392
let settings = value.get_mut("settings")?.take();
9493
serde_json::from_value::<Options>(settings).ok()
@@ -117,7 +116,8 @@ impl LanguageServer for Backend {
117116
None
118117
};
119118

120-
self.init_linter_config().await;
119+
let oxlintrc = self.init_linter_config().await;
120+
self.init_ignore_glob(oxlintrc).await;
121121
Ok(InitializeResult {
122122
server_info: Some(ServerInfo { name: "oxc".into(), version: None }),
123123
offset_encoding: None,
@@ -330,7 +330,7 @@ impl Backend {
330330
Ok(())
331331
}
332332

333-
async fn init_ignore_glob(&self) {
333+
async fn init_ignore_glob(&self, oxlintrc: Option<Oxlintrc>) {
334334
let uri = self
335335
.root_uri
336336
.get()
@@ -366,6 +366,17 @@ impl Backend {
366366
}
367367
}
368368
}
369+
370+
if let Some(oxlintrc) = oxlintrc {
371+
if !oxlintrc.ignore_patterns.is_empty() {
372+
let mut builder =
373+
ignore::gitignore::GitignoreBuilder::new(oxlintrc.path.parent().unwrap());
374+
for entry in &oxlintrc.ignore_patterns {
375+
builder.add_line(None, entry).expect("Failed to add ignore line");
376+
}
377+
gitignore_globs.push(builder.build().unwrap());
378+
}
379+
}
369380
}
370381

371382
#[allow(clippy::ptr_arg)]
@@ -389,12 +400,12 @@ impl Backend {
389400
.await;
390401
}
391402

392-
async fn init_linter_config(&self) {
403+
async fn init_linter_config(&self) -> Option<Oxlintrc> {
393404
let Some(Some(uri)) = self.root_uri.get() else {
394-
return;
405+
return None;
395406
};
396407
let Ok(root_path) = uri.to_file_path() else {
397-
return;
408+
return None;
398409
};
399410
let mut config_path = None;
400411
let config = root_path.join(self.options.lock().await.get_config_path().unwrap());
@@ -403,16 +414,17 @@ impl Backend {
403414
}
404415
if let Some(config_path) = config_path {
405416
let mut linter = self.server_linter.write().await;
417+
let config = Oxlintrc::from_file(&config_path)
418+
.expect("should have initialized linter with new options");
406419
*linter = ServerLinter::new_with_linter(
407-
LinterBuilder::from_oxlintrc(
408-
true,
409-
Oxlintrc::from_file(&config_path)
410-
.expect("should have initialized linter with new options"),
411-
)
412-
.with_fix(FixKind::SafeFix)
413-
.build(),
420+
LinterBuilder::from_oxlintrc(true, config.clone())
421+
.with_fix(FixKind::SafeFix)
422+
.build(),
414423
);
424+
return Some(config);
415425
}
426+
427+
None
416428
}
417429

418430
async fn handle_file_update(&self, uri: Url, content: Option<String>, version: Option<i32>) {

crates/oxc_linter/src/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl LinterBuilder {
9595
rules: oxlintrc_rules,
9696
overrides,
9797
path,
98+
ignore_patterns: _,
9899
} = oxlintrc;
99100

100101
let config = LintConfig { plugins, settings, env, globals, path: Some(path) };

crates/oxc_linter/src/config/oxlintrc.rs

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ pub struct Oxlintrc {
8888
/// Absolute path to the configuration file.
8989
#[serde(skip)]
9090
pub path: PathBuf,
91+
/// Globs to ignore during linting. These are resolved from the configuration file path.
92+
#[serde(rename = "ignorePatterns")]
93+
pub ignore_patterns: Vec<String>,
9194
}
9295

9396
impl Oxlintrc {

crates/oxc_linter/src/snapshots/schema_json.snap

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
source: crates/oxc_linter/src/lib.rs
33
expression: json
4-
snapshot_kind: text
54
---
65
{
76
"$schema": "http://json-schema.org/draft-07/schema#",
@@ -37,6 +36,14 @@ snapshot_kind: text
3736
}
3837
]
3938
},
39+
"ignorePatterns": {
40+
"description": "Globs to ignore during linting. These are resolved from the configuration file path.",
41+
"default": [],
42+
"type": "array",
43+
"items": {
44+
"type": "string"
45+
}
46+
},
4047
"overrides": {
4148
"description": "Add, remove, or otherwise reconfigure rules for specific files or groups of files.",
4249
"allOf": [

npm/oxlint/configuration_schema.json

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
}
3333
]
3434
},
35+
"ignorePatterns": {
36+
"description": "Globs to ignore during linting. These are resolved from the configuration file path.",
37+
"default": [],
38+
"type": "array",
39+
"items": {
40+
"type": "string"
41+
}
42+
},
3543
"overrides": {
3644
"description": "Add, remove, or otherwise reconfigure rules for specific files or groups of files.",
3745
"allOf": [

tasks/website/src/linter/snapshots/schema_markdown.snap

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
---
22
source: tasks/website/src/linter/json_schema.rs
33
expression: snapshot
4-
snapshot_kind: text
54
---
65
# Oxlint Configuration File
76

@@ -161,6 +160,15 @@ Globals can be disabled by setting their value to `"off"`. For example, in an en
161160
You may also use `"readable"` or `false` to represent `"readonly"`, and `"writeable"` or `true` to represent `"writable"`.
162161

163162

163+
## ignorePatterns
164+
165+
type: `string[]`
166+
167+
default: `[]`
168+
169+
Globs to ignore during linting. These are resolved from the configuration file path.
170+
171+
164172
## overrides
165173

166174
type: `array`

0 commit comments

Comments
 (0)