Skip to content

Commit 5d508a4

Browse files
committedFeb 9, 2025·
feat(linter): support env and globals in overrides configuration (#8915)
This PR supports `env` and `globals` keys in `overrides[x]`. I added tests where I could. Snapshot CLI tests are working 🥳
1 parent ec601f2 commit 5d508a4

File tree

14 files changed

+413
-39
lines changed

14 files changed

+413
-39
lines changed
 

‎apps/oxlint/fixtures/overrides/.oxlintrc.json

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,25 @@
55
},
66
"overrides": [
77
{
8-
"files": ["*.js"],
8+
"files": [
9+
"*.js"
10+
],
911
"rules": {
1012
"no-console": "warn"
1113
}
1214
},
1315
{
14-
"files": ["*.{js,jsx}"],
16+
"files": [
17+
"*.{js,jsx}"
18+
],
1519
"rules": {
1620
"no-console": "off"
1721
}
1822
},
1923
{
20-
"files": ["*.ts"],
24+
"files": [
25+
"*.ts"
26+
],
2127
"rules": {
2228
"no-console": "warn"
2329
}

‎apps/oxlint/fixtures/overrides/directories-config.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
},
55
"overrides": [
66
{
7-
"files": ["lib/*.{js,ts}", "src/*"],
7+
"files": [
8+
"lib/*.{js,ts}",
9+
"src/*"
10+
],
811
"rules": {
912
"no-debugger": "error"
1013
}
1114
},
1215
{
13-
"files": ["**/tests/*"],
16+
"files": [
17+
"**/tests/*"
18+
],
1419
"rules": {
1520
"no-debugger": "warn"
1621
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"env": {
3+
"jquery": true
4+
},
5+
"globals": {
6+
"Foo": "readonly"
7+
},
8+
"overrides": [
9+
{
10+
"files": [
11+
"*.ts"
12+
],
13+
"env": {
14+
"jquery": false
15+
},
16+
"globals": {
17+
"Foo": "writeable"
18+
}
19+
},
20+
{
21+
"files": [
22+
"src/*"
23+
],
24+
"env": {
25+
"jquery": false
26+
},
27+
"globals": {
28+
"Foo": "writeable"
29+
}
30+
}
31+
]
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// for env detection
2+
globalThis = 'abc';
3+
$ = 'abc';
4+
5+
// for globals detection
6+
Foo = 'readable';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// for env detection
2+
globalThis = 'abc';
3+
$ = 'abc';
4+
5+
// for globals detection
6+
Foo = 'readable';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// for env detection
2+
globalThis = 'abc';
3+
$ = 'abc';
4+
5+
// for globals detection
6+
Foo = 'readable';

‎apps/oxlint/src/lint.rs

+6
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,12 @@ mod test {
755755
Tester::new().test_and_snapshot(args);
756756
}
757757

758+
#[test]
759+
fn test_overrides_envs_and_global() {
760+
let args = &["-c", ".oxlintrc.json", "."];
761+
Tester::new().with_cwd("fixtures/overrides_env_globals".into()).test_and_snapshot(args);
762+
}
763+
758764
#[test]
759765
fn test_ignore_patterns() {
760766
let args = &["-c", "./test/eslintrc.json", "--ignore-pattern", "*.ts", "."];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
source: apps/oxlint/src/tester.rs
3+
---
4+
##########
5+
arguments: -c .oxlintrc.json .
6+
working directory: fixtures/overrides_env_globals
7+
----------
8+
9+
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-global-assign.html\eslint(no-global-assign)]8;;\: Read-only global 'globalThis' should not be modified.
10+
,-[src/test.js:2:1]
11+
1 | // for env detection
12+
2 | globalThis = 'abc';
13+
: ^^^^^|^^^^
14+
: `-- Read-only global 'globalThis' should not be modified.
15+
3 | $ = 'abc';
16+
`----
17+
18+
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-global-assign.html\eslint(no-global-assign)]8;;\: Read-only global 'globalThis' should not be modified.
19+
,-[test.js:2:1]
20+
1 | // for env detection
21+
2 | globalThis = 'abc';
22+
: ^^^^^|^^^^
23+
: `-- Read-only global 'globalThis' should not be modified.
24+
3 | $ = 'abc';
25+
`----
26+
27+
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-global-assign.html\eslint(no-global-assign)]8;;\: Read-only global 'globalThis' should not be modified.
28+
,-[test.ts:2:1]
29+
1 | // for env detection
30+
2 | globalThis = 'abc';
31+
: ^^^^^|^^^^
32+
: `-- Read-only global 'globalThis' should not be modified.
33+
3 | $ = 'abc';
34+
`----
35+
36+
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-global-assign.html\eslint(no-global-assign)]8;;\: Read-only global '$' should not be modified.
37+
,-[test.js:3:1]
38+
2 | globalThis = 'abc';
39+
3 | $ = 'abc';
40+
: |
41+
: `-- Read-only global '$' should not be modified.
42+
4 |
43+
`----
44+
45+
! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-global-assign.html\eslint(no-global-assign)]8;;\: Read-only global 'Foo' should not be modified.
46+
,-[test.js:6:1]
47+
5 | // for globals detection
48+
6 | Foo = 'readable';
49+
: ^|^
50+
: `-- Read-only global 'Foo' should not be modified.
51+
`----
52+
53+
Found 5 warnings and 0 errors.
54+
Finished in <variable>ms on 3 files with 97 rules using 1 threads.
55+
----------
56+
CLI result: LintSucceeded
57+
----------

‎crates/oxc_linter/src/config/config_store.rs

+128-7
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,9 @@ impl ConfigStore {
8585
return config.base.clone();
8686
}
8787

88+
let mut env = config.base.config.env.clone();
89+
let mut globals = config.base.config.globals.clone();
8890
let mut plugins = config.base.config.plugins;
89-
let all_rules = RULES
90-
.iter()
91-
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
92-
.cloned()
93-
.collect::<Vec<_>>();
9491
let mut rules = config
9592
.base
9693
.rules
@@ -99,6 +96,12 @@ impl ConfigStore {
9996
.cloned()
10097
.collect::<FxHashSet<_>>();
10198

99+
let all_rules = RULES
100+
.iter()
101+
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
102+
.cloned()
103+
.collect::<Vec<_>>();
104+
102105
for override_config in overrides_to_apply {
103106
if !override_config.rules.is_empty() {
104107
override_config.rules.override_rules(&mut rules, &all_rules);
@@ -107,18 +110,31 @@ impl ConfigStore {
107110
if let Some(override_plugins) = override_config.plugins {
108111
plugins |= override_plugins;
109112
}
113+
114+
if let Some(override_env) = &override_config.env {
115+
override_env.override_envs(&mut env);
116+
}
117+
118+
if let Some(override_globals) = &override_config.globals {
119+
override_globals.override_globals(&mut globals);
120+
}
110121
}
111122

112-
let rules = rules.into_iter().collect::<Vec<_>>();
113-
let config = if plugins == config.base.config.plugins {
123+
let config = if plugins == config.base.config.plugins
124+
&& env == config.base.config.env
125+
&& globals == config.base.config.globals
126+
{
114127
Arc::clone(&config.base.config)
115128
} else {
116129
let mut config = (*config.base.config).clone();
117130

118131
config.plugins = plugins;
132+
config.env = env;
133+
config.globals = globals;
119134
Arc::new(config)
120135
};
121136

137+
let rules = rules.into_iter().collect::<Vec<_>>();
122138
ResolvedLinterState { rules: Arc::from(rules.into_boxed_slice()), config }
123139
}
124140
}
@@ -282,4 +298,109 @@ mod test {
282298
let app = store.resolve("App.tsx".as_ref()).config;
283299
assert_eq!(app.plugins, LintPlugins::IMPORT | LintPlugins::REACT | LintPlugins::TYPESCRIPT);
284300
}
301+
302+
#[test]
303+
fn test_add_env() {
304+
let base_config = LintConfig {
305+
env: OxlintEnv::default(),
306+
plugins: LintPlugins::ESLINT,
307+
settings: OxlintSettings::default(),
308+
globals: OxlintGlobals::default(),
309+
path: None,
310+
};
311+
312+
let overrides = from_json!([{
313+
"files": ["*.tsx"],
314+
"env": {
315+
"es2024": true
316+
},
317+
}]);
318+
319+
let store = ConfigStore::new(vec![], base_config, overrides);
320+
assert!(!store.base.base.config.env.contains("React"));
321+
322+
let app = store.resolve("App.tsx".as_ref()).config;
323+
assert!(app.env.contains("es2024"));
324+
}
325+
326+
#[test]
327+
fn test_replace_env() {
328+
let base_config = LintConfig {
329+
env: OxlintEnv::from_iter(["es2024".into()]),
330+
plugins: LintPlugins::ESLINT,
331+
settings: OxlintSettings::default(),
332+
globals: OxlintGlobals::default(),
333+
path: None,
334+
};
335+
336+
let overrides = from_json!([{
337+
"files": ["*.tsx"],
338+
"env": {
339+
"es2024": false
340+
},
341+
}]);
342+
343+
let store = ConfigStore::new(vec![], base_config, overrides);
344+
assert!(store.base.base.config.env.contains("es2024"));
345+
346+
let app = store.resolve("App.tsx".as_ref()).config;
347+
assert!(!app.env.contains("es2024"));
348+
}
349+
350+
#[test]
351+
fn test_add_globals() {
352+
let base_config = LintConfig {
353+
env: OxlintEnv::default(),
354+
plugins: LintPlugins::ESLINT,
355+
settings: OxlintSettings::default(),
356+
globals: OxlintGlobals::default(),
357+
path: None,
358+
};
359+
360+
let overrides = from_json!([{
361+
"files": ["*.tsx"],
362+
"globals": {
363+
"React": "readonly",
364+
"Secret": "writeable"
365+
},
366+
}]);
367+
368+
let store = ConfigStore::new(vec![], base_config, overrides);
369+
assert!(!store.base.base.config.globals.is_enabled("React"));
370+
assert!(!store.base.base.config.globals.is_enabled("Secret"));
371+
372+
let app = store.resolve("App.tsx".as_ref()).config;
373+
assert!(app.globals.is_enabled("React"));
374+
assert!(app.globals.is_enabled("Secret"));
375+
}
376+
377+
#[test]
378+
fn test_replace_globals() {
379+
let base_config = LintConfig {
380+
env: OxlintEnv::default(),
381+
plugins: LintPlugins::ESLINT,
382+
settings: OxlintSettings::default(),
383+
globals: from_json!({
384+
"React": "readonly",
385+
"Secret": "writeable"
386+
}),
387+
path: None,
388+
};
389+
390+
let overrides = from_json!([{
391+
"files": ["*.tsx"],
392+
"globals": {
393+
"React": "off",
394+
"Secret": "off"
395+
},
396+
}]);
397+
398+
let store = ConfigStore::new(vec![], base_config, overrides);
399+
assert!(store.base.base.config.globals.is_enabled("React"));
400+
assert!(store.base.base.config.globals.is_enabled("Secret"));
401+
402+
let app = store.resolve("App.tsx".as_ref()).config;
403+
assert!(!app.globals.is_enabled("React"));
404+
assert!(!app.globals.is_enabled("Secret"));
405+
}
285406
}

0 commit comments

Comments
 (0)
Please sign in to comment.