Skip to content

Commit

Permalink
feat(linter): support env and globals in overrides configuration (
Browse files Browse the repository at this point in the history
#8915)

This PR supports `env` and `globals` keys in `overrides[x]`.

I added tests where I could. Snapshot CLI tests are working 🥳
  • Loading branch information
Sysix committed Feb 9, 2025
1 parent ec601f2 commit 5d508a4
Show file tree
Hide file tree
Showing 14 changed files with 413 additions and 39 deletions.
12 changes: 9 additions & 3 deletions apps/oxlint/fixtures/overrides/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@
},
"overrides": [
{
"files": ["*.js"],
"files": [
"*.js"
],
"rules": {
"no-console": "warn"
}
},
{
"files": ["*.{js,jsx}"],
"files": [
"*.{js,jsx}"
],
"rules": {
"no-console": "off"
}
},
{
"files": ["*.ts"],
"files": [
"*.ts"
],
"rules": {
"no-console": "warn"
}
Expand Down
9 changes: 7 additions & 2 deletions apps/oxlint/fixtures/overrides/directories-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
},
"overrides": [
{
"files": ["lib/*.{js,ts}", "src/*"],
"files": [
"lib/*.{js,ts}",
"src/*"
],
"rules": {
"no-debugger": "error"
}
},
{
"files": ["**/tests/*"],
"files": [
"**/tests/*"
],
"rules": {
"no-debugger": "warn"
}
Expand Down
32 changes: 32 additions & 0 deletions apps/oxlint/fixtures/overrides_env_globals/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"env": {
"jquery": true
},
"globals": {
"Foo": "readonly"
},
"overrides": [
{
"files": [
"*.ts"
],
"env": {
"jquery": false
},
"globals": {
"Foo": "writeable"
}
},
{
"files": [
"src/*"
],
"env": {
"jquery": false
},
"globals": {
"Foo": "writeable"
}
}
]
}
6 changes: 6 additions & 0 deletions apps/oxlint/fixtures/overrides_env_globals/src/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// for env detection
globalThis = 'abc';
$ = 'abc';

// for globals detection
Foo = 'readable';
6 changes: 6 additions & 0 deletions apps/oxlint/fixtures/overrides_env_globals/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// for env detection
globalThis = 'abc';
$ = 'abc';

// for globals detection
Foo = 'readable';
6 changes: 6 additions & 0 deletions apps/oxlint/fixtures/overrides_env_globals/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// for env detection
globalThis = 'abc';
$ = 'abc';

// for globals detection
Foo = 'readable';
6 changes: 6 additions & 0 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,12 @@ mod test {
Tester::new().test_and_snapshot(args);
}

#[test]
fn test_overrides_envs_and_global() {
let args = &["-c", ".oxlintrc.json", "."];
Tester::new().with_cwd("fixtures/overrides_env_globals".into()).test_and_snapshot(args);
}

#[test]
fn test_ignore_patterns() {
let args = &["-c", "./test/eslintrc.json", "--ignore-pattern", "*.ts", "."];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments: -c .oxlintrc.json .
working directory: fixtures/overrides_env_globals
----------

! ]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.
,-[src/test.js:2:1]
1 | // for env detection
2 | globalThis = 'abc';
: ^^^^^|^^^^
: `-- Read-only global 'globalThis' should not be modified.
3 | $ = 'abc';
`----

! ]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.
,-[test.js:2:1]
1 | // for env detection
2 | globalThis = 'abc';
: ^^^^^|^^^^
: `-- Read-only global 'globalThis' should not be modified.
3 | $ = 'abc';
`----

! ]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.
,-[test.ts:2:1]
1 | // for env detection
2 | globalThis = 'abc';
: ^^^^^|^^^^
: `-- Read-only global 'globalThis' should not be modified.
3 | $ = 'abc';
`----

! ]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.
,-[test.js:3:1]
2 | globalThis = 'abc';
3 | $ = 'abc';
: |
: `-- Read-only global '$' should not be modified.
4 |
`----

! ]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.
,-[test.js:6:1]
5 | // for globals detection
6 | Foo = 'readable';
: ^|^
: `-- Read-only global 'Foo' should not be modified.
`----

Found 5 warnings and 0 errors.
Finished in <variable>ms on 3 files with 97 rules using 1 threads.
----------
CLI result: LintSucceeded
----------
135 changes: 128 additions & 7 deletions crates/oxc_linter/src/config/config_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,9 @@ impl ConfigStore {
return config.base.clone();
}

let mut env = config.base.config.env.clone();
let mut globals = config.base.config.globals.clone();
let mut plugins = config.base.config.plugins;
let all_rules = RULES
.iter()
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
.cloned()
.collect::<Vec<_>>();
let mut rules = config
.base
.rules
Expand All @@ -99,6 +96,12 @@ impl ConfigStore {
.cloned()
.collect::<FxHashSet<_>>();

let all_rules = RULES
.iter()
.filter(|rule| plugins.contains(LintPlugins::from(rule.plugin_name())))
.cloned()
.collect::<Vec<_>>();

for override_config in overrides_to_apply {
if !override_config.rules.is_empty() {
override_config.rules.override_rules(&mut rules, &all_rules);
Expand All @@ -107,18 +110,31 @@ impl ConfigStore {
if let Some(override_plugins) = override_config.plugins {
plugins |= override_plugins;
}

if let Some(override_env) = &override_config.env {
override_env.override_envs(&mut env);
}

if let Some(override_globals) = &override_config.globals {
override_globals.override_globals(&mut globals);
}
}

let rules = rules.into_iter().collect::<Vec<_>>();
let config = if plugins == config.base.config.plugins {
let config = if plugins == config.base.config.plugins
&& env == config.base.config.env
&& globals == config.base.config.globals
{
Arc::clone(&config.base.config)
} else {
let mut config = (*config.base.config).clone();

config.plugins = plugins;
config.env = env;
config.globals = globals;
Arc::new(config)
};

let rules = rules.into_iter().collect::<Vec<_>>();
ResolvedLinterState { rules: Arc::from(rules.into_boxed_slice()), config }
}
}
Expand Down Expand Up @@ -282,4 +298,109 @@ mod test {
let app = store.resolve("App.tsx".as_ref()).config;
assert_eq!(app.plugins, LintPlugins::IMPORT | LintPlugins::REACT | LintPlugins::TYPESCRIPT);
}

#[test]
fn test_add_env() {
let base_config = LintConfig {
env: OxlintEnv::default(),
plugins: LintPlugins::ESLINT,
settings: OxlintSettings::default(),
globals: OxlintGlobals::default(),
path: None,
};

let overrides = from_json!([{
"files": ["*.tsx"],
"env": {
"es2024": true
},
}]);

let store = ConfigStore::new(vec![], base_config, overrides);
assert!(!store.base.base.config.env.contains("React"));

let app = store.resolve("App.tsx".as_ref()).config;
assert!(app.env.contains("es2024"));
}

#[test]
fn test_replace_env() {
let base_config = LintConfig {
env: OxlintEnv::from_iter(["es2024".into()]),
plugins: LintPlugins::ESLINT,
settings: OxlintSettings::default(),
globals: OxlintGlobals::default(),
path: None,
};

let overrides = from_json!([{
"files": ["*.tsx"],
"env": {
"es2024": false
},
}]);

let store = ConfigStore::new(vec![], base_config, overrides);
assert!(store.base.base.config.env.contains("es2024"));

let app = store.resolve("App.tsx".as_ref()).config;
assert!(!app.env.contains("es2024"));
}

#[test]
fn test_add_globals() {
let base_config = LintConfig {
env: OxlintEnv::default(),
plugins: LintPlugins::ESLINT,
settings: OxlintSettings::default(),
globals: OxlintGlobals::default(),
path: None,
};

let overrides = from_json!([{
"files": ["*.tsx"],
"globals": {
"React": "readonly",
"Secret": "writeable"
},
}]);

let store = ConfigStore::new(vec![], base_config, overrides);
assert!(!store.base.base.config.globals.is_enabled("React"));
assert!(!store.base.base.config.globals.is_enabled("Secret"));

let app = store.resolve("App.tsx".as_ref()).config;
assert!(app.globals.is_enabled("React"));
assert!(app.globals.is_enabled("Secret"));
}

#[test]
fn test_replace_globals() {
let base_config = LintConfig {
env: OxlintEnv::default(),
plugins: LintPlugins::ESLINT,
settings: OxlintSettings::default(),
globals: from_json!({
"React": "readonly",
"Secret": "writeable"
}),
path: None,
};

let overrides = from_json!([{
"files": ["*.tsx"],
"globals": {
"React": "off",
"Secret": "off"
},
}]);

let store = ConfigStore::new(vec![], base_config, overrides);
assert!(store.base.base.config.globals.is_enabled("React"));
assert!(store.base.base.config.globals.is_enabled("Secret"));

let app = store.resolve("App.tsx".as_ref()).config;
assert!(!app.globals.is_enabled("React"));
assert!(!app.globals.is_enabled("Secret"));
}
}
Loading

0 comments on commit 5d508a4

Please sign in to comment.