Skip to content

Commit 9adf236

Browse files
committed
refactor(linter): use fast-glob instead of globset for GlobSet
1 parent 9b9249d commit 9adf236

File tree

5 files changed

+47
-81
lines changed

5 files changed

+47
-81
lines changed

crates/oxc_linter/src/config/config_store.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,9 @@ impl Config {
144144
})
145145
.unwrap_or(path);
146146

147+
let path = relative_path.to_string_lossy();
147148
let overrides_to_apply =
148-
self.overrides.iter().filter(|config| config.files.is_match(relative_path));
149+
self.overrides.iter().filter(|config| config.files.is_match(path.as_ref()));
149150

150151
let mut overrides_to_apply = overrides_to_apply.peekable();
151152

@@ -351,7 +352,7 @@ mod test {
351352
let base_rules = vec![no_explicit_any()];
352353
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
353354
env: None,
354-
files: GlobSet::new(vec!["*.test.{ts,tsx}"]).unwrap(),
355+
files: GlobSet::new(vec!["*.test.{ts,tsx}"]),
355356
plugins: None,
356357
globals: None,
357358
rules: ResolvedOxlintOverrideRules { builtin_rules: vec![], external_rules: vec![] },
@@ -382,7 +383,7 @@ mod test {
382383
let base_rules = vec![no_explicit_any()];
383384
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
384385
env: None,
385-
files: GlobSet::new(vec!["*.test.{ts,tsx}"]).unwrap(),
386+
files: GlobSet::new(vec!["*.test.{ts,tsx}"]),
386387
plugins: Some(LintPlugins::new(
387388
BuiltinLintPlugins::REACT
388389
.union(BuiltinLintPlugins::TYPESCRIPT)
@@ -419,7 +420,7 @@ mod test {
419420
let base_rules = vec![no_explicit_any()];
420421
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
421422
env: None,
422-
files: GlobSet::new(vec!["*.test.{ts,tsx}"]).unwrap(),
423+
files: GlobSet::new(vec!["*.test.{ts,tsx}"]),
423424
plugins: None,
424425
globals: None,
425426
rules: ResolvedOxlintOverrideRules {
@@ -456,7 +457,7 @@ mod test {
456457
let base_rules = vec![no_explicit_any()];
457458
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
458459
env: None,
459-
files: GlobSet::new(vec!["src/**/*.{ts,tsx}"]).unwrap(),
460+
files: GlobSet::new(vec!["src/**/*.{ts,tsx}"]),
460461
plugins: None,
461462
globals: None,
462463
rules: ResolvedOxlintOverrideRules {
@@ -493,7 +494,7 @@ mod test {
493494
let base_rules = vec![no_explicit_any()];
494495
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
495496
env: None,
496-
files: GlobSet::new(vec!["src/**/*.{ts,tsx}"]).unwrap(),
497+
files: GlobSet::new(vec!["src/**/*.{ts,tsx}"]),
497498
plugins: None,
498499
globals: None,
499500
rules: ResolvedOxlintOverrideRules {
@@ -539,7 +540,7 @@ mod test {
539540
let overrides = ResolvedOxlintOverrides::new(vec![
540541
ResolvedOxlintOverride {
541542
env: None,
542-
files: GlobSet::new(vec!["*.jsx", "*.tsx"]).unwrap(),
543+
files: GlobSet::new(vec!["*.jsx", "*.tsx"]),
543544
plugins: Some(LintPlugins::new(BuiltinLintPlugins::REACT, FxHashSet::default())),
544545
globals: None,
545546
rules: ResolvedOxlintOverrideRules {
@@ -549,7 +550,7 @@ mod test {
549550
},
550551
ResolvedOxlintOverride {
551552
env: None,
552-
files: GlobSet::new(vec!["*.ts", "*.tsx"]).unwrap(),
553+
files: GlobSet::new(vec!["*.ts", "*.tsx"]),
553554
plugins: Some(LintPlugins::new(
554555
BuiltinLintPlugins::TYPESCRIPT,
555556
FxHashSet::default(),
@@ -600,7 +601,7 @@ mod test {
600601
};
601602
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
602603
env: Some(OxlintEnv::from_iter(["es2024".to_string()])),
603-
files: GlobSet::new(vec!["*.tsx"]).unwrap(),
604+
files: GlobSet::new(vec!["*.tsx"]),
604605
plugins: None,
605606
globals: None,
606607
rules: ResolvedOxlintOverrideRules { builtin_rules: vec![], external_rules: vec![] },
@@ -627,7 +628,7 @@ mod test {
627628
path: None,
628629
};
629630
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
630-
files: GlobSet::new(vec!["*.tsx"]).unwrap(),
631+
files: GlobSet::new(vec!["*.tsx"]),
631632
env: Some(from_json!({ "es2024": false })),
632633
plugins: None,
633634
globals: None,
@@ -656,7 +657,7 @@ mod test {
656657
};
657658

658659
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
659-
files: GlobSet::new(vec!["*.tsx"]).unwrap(),
660+
files: GlobSet::new(vec!["*.tsx"]),
660661
env: None,
661662
plugins: None,
662663
globals: Some(from_json!({ "React": "readonly", "Secret": "writeable" })),
@@ -690,7 +691,7 @@ mod test {
690691
};
691692

692693
let overrides = ResolvedOxlintOverrides::new(vec![ResolvedOxlintOverride {
693-
files: GlobSet::new(vec!["*.tsx"]).unwrap(),
694+
files: GlobSet::new(vec!["*.tsx"]),
694695
env: None,
695696
plugins: None,
696697
globals: Some(from_json!({ "React": "off", "Secret": "off" })),

crates/oxc_linter/src/config/overrides.rs

Lines changed: 31 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::{
22
borrow::Cow,
33
ops::{Deref, DerefMut},
4-
path::Path,
54
};
65

76
use schemars::{JsonSchema, r#gen, schema::Schema};
8-
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
7+
use serde::{Deserialize, Deserializer, Serialize};
98

109
use crate::{LintPlugins, OxlintEnv, OxlintGlobals, config::OxlintRules};
1110

@@ -97,74 +96,38 @@ pub struct OxlintOverride {
9796
pub rules: OxlintRules,
9897
}
9998

100-
/// A glob pattern.
101-
///
102-
/// Thin wrapper around [`globset::GlobSet`] because that struct doesn't implement Serialize or schemars
103-
/// traits.
104-
#[derive(Clone, Default)]
105-
pub struct GlobSet {
106-
/// Raw patterns from the config. Inefficient, but required for [serialization](Serialize),
107-
/// which in turn is required for `--print-config`.
108-
raw: Vec<String>,
109-
globs: globset::GlobSet,
110-
}
111-
112-
impl GlobSet {
113-
pub fn new<S: AsRef<str>, I: IntoIterator<Item = S>>(
114-
patterns: I,
115-
) -> Result<Self, globset::Error> {
116-
let patterns = patterns.into_iter();
117-
let size_hint = patterns.size_hint();
118-
119-
let mut builder = globset::GlobSetBuilder::new();
120-
let mut raw = Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0));
121-
122-
for pattern in patterns {
123-
let pattern = pattern.as_ref();
124-
let glob = globset::Glob::new(pattern)?;
125-
builder.add(glob);
126-
raw.push(pattern.to_string());
127-
}
128-
129-
let globs = builder.build()?;
130-
Ok(Self { raw, globs })
131-
}
132-
133-
pub fn is_match<P: AsRef<Path>>(&self, path: P) -> bool {
134-
self.globs.is_match(path)
135-
}
136-
}
137-
138-
impl std::fmt::Debug for GlobSet {
139-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140-
f.debug_tuple("GlobSet").field(&self.raw).finish()
141-
}
142-
}
143-
144-
impl Serialize for GlobSet {
145-
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
146-
self.raw.serialize(serializer)
147-
}
148-
}
99+
/// A set of glob patterns.
100+
#[derive(Debug, Default, Clone, Serialize, JsonSchema)]
101+
pub struct GlobSet(Vec<String>);
149102

150103
impl<'de> Deserialize<'de> for GlobSet {
151104
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
152-
let globs = Vec::<String>::deserialize(deserializer)?;
153-
Self::new(globs).map_err(de::Error::custom)
105+
Ok(Self::new(Vec::<String>::deserialize(deserializer)?))
154106
}
155107
}
156108

157-
impl JsonSchema for GlobSet {
158-
fn schema_name() -> String {
159-
Self::schema_id().into()
160-
}
161-
162-
fn schema_id() -> Cow<'static, str> {
163-
Cow::Borrowed("GlobSet")
164-
}
165-
166-
fn json_schema(r#gen: &mut r#gen::SchemaGenerator) -> Schema {
167-
r#gen.subschema_for::<Vec<String>>()
109+
impl GlobSet {
110+
pub fn new<S: AsRef<str>, I: IntoIterator<Item = S>>(patterns: I) -> Self {
111+
Self(
112+
patterns
113+
.into_iter()
114+
.map(|pat| {
115+
let pattern = pat.as_ref();
116+
if pattern.contains('/') {
117+
pattern.to_owned()
118+
} else {
119+
let mut s = String::with_capacity(pattern.len() + 3);
120+
s.push_str("**/");
121+
s.push_str(pattern);
122+
s
123+
}
124+
})
125+
.collect::<Vec<_>>(),
126+
)
127+
}
128+
129+
pub fn is_match(&self, path: &str) -> bool {
130+
self.0.iter().any(|glob| fast_glob::glob_match(glob, path))
168131
}
169132
}
170133

@@ -182,15 +145,15 @@ mod test {
182145
"files": ["*.tsx",],
183146
}))
184147
.unwrap();
185-
assert!(config.files.globs.is_match("/some/path/foo.tsx"));
186-
assert!(!config.files.globs.is_match("/some/path/foo.ts"));
148+
assert!(config.files.is_match("/some/path/foo.tsx"));
149+
assert!(!config.files.is_match("/some/path/foo.ts"));
187150

188151
let config: OxlintOverride = from_value(json!({
189152
"files": ["lib/*.ts",],
190153
}))
191154
.unwrap();
192-
assert!(config.files.globs.is_match("lib/foo.ts"));
193-
assert!(!config.files.globs.is_match("src/foo.ts"));
155+
assert!(config.files.is_match("lib/foo.ts"));
156+
assert!(!config.files.is_match("src/foo.ts"));
194157
}
195158

196159
#[test]

crates/oxc_linter/src/snapshots/schema_json.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ expression: json
193193
}
194194
},
195195
"GlobSet": {
196+
"description": "A set of glob patterns.",
196197
"type": "array",
197198
"items": {
198199
"type": "string"

npm/oxlint/configuration_schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
}
190190
},
191191
"GlobSet": {
192+
"description": "A set of glob patterns.",
192193
"type": "array",
193194
"items": {
194195
"type": "string"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ type: `object`
214214
type: `string[]`
215215

216216

217-
217+
A set of glob patterns.
218218

219219

220220
#### overrides[n].rules

0 commit comments

Comments
 (0)