From acbff7bb2f84729086076db88b2fae217d5174be Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 23 Dec 2024 16:03:31 +0100 Subject: [PATCH 01/28] fix(linter): rule `no-restricted-imports` support `regex` option inside `patterns` --- Cargo.lock | 13 +- Cargo.toml | 1 + crates/oxc_linter/Cargo.toml | 1 + .../src/rules/eslint/no_restricted_imports.rs | 121 ++++++++++++------ .../eslint_no_restricted_imports.snap | 14 ++ 5 files changed, 109 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1d2be451c726..2967079cd8634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1082,7 +1082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -1719,6 +1719,7 @@ dependencies = [ "project-root", "rayon", "regex", + "regress", "rust-lapper", "rustc-hash", "schemars", @@ -2406,6 +2407,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "regress" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1541daf4e4ed43a0922b7969bdc2170178bcacc5dabf7e39bc508a9fa3953a7a" +dependencies = [ + "hashbrown 0.14.5", + "memchr", +] + [[package]] name = "ring" version = "0.17.8" diff --git a/Cargo.toml b/Cargo.toml index 8c41dff011293..52281a38f0542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,6 +178,7 @@ prettyplease = "0.2.25" project-root = "0.2.2" rayon = "1.10.0" regex = "1.11.1" +regress = "0.10.1" ropey = "1.6.1" rust-lapper = "1.1.0" ryu-js = "1.0.1" diff --git a/crates/oxc_linter/Cargo.toml b/crates/oxc_linter/Cargo.toml index 10105e422f152..9d07acc56393c 100644 --- a/crates/oxc_linter/Cargo.toml +++ b/crates/oxc_linter/Cargo.toml @@ -53,6 +53,7 @@ nonmax = { workspace = true } phf = { workspace = true, features = ["macros"] } rayon = { workspace = true } regex = { workspace = true } +regress = { workspace = true } rust-lapper = { workspace = true } rustc-hash = { workspace = true } schemars = { workspace = true, features = ["indexmap2"] } diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 6b6976c9409d2..1d24caf5eb18b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -2,6 +2,7 @@ use ignore::gitignore::GitignoreBuilder; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; +use regress::Regex; use rustc_hash::FxHashMap; use serde::Deserialize; use serde_json::Value; @@ -56,7 +57,8 @@ struct RestrictedPath { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] struct RestrictedPattern { - group: Vec, + group: Option>, + regex: Option, import_names: Option>, allow_import_names: Option>, case_sensitive: Option, @@ -148,6 +150,9 @@ fn add_configuration_patterns_from_object( } Value::Object(_) => { if let Ok(path) = serde_json::from_value::(path_value.clone()) { + if path.group.is_some() && path.regex.is_some() { + // ToDo: not allowed + } patterns.push(path); } } @@ -158,7 +163,8 @@ fn add_configuration_patterns_from_object( fn add_configuration_patterns_from_string(paths: &mut Vec, module_name: &str) { paths.push(RestrictedPattern { - group: vec![CompactStr::new(module_name)], + group: Some(vec![CompactStr::new(module_name)]), + regex: None, import_names: None, allow_import_names: None, case_sensitive: None, @@ -245,12 +251,16 @@ impl RestrictedPattern { } } - fn get_gitignore_glob_result(&self, name: &NameSpan) -> GlobResult { + fn get_group_glob_result(&self, name: &NameSpan) -> GlobResult { + let Some(groups) = &self.group else { + return GlobResult::None; + }; + let mut builder = GitignoreBuilder::new(""); // returns always OK, will be fixed in the next version let _ = builder.case_insensitive(!self.case_sensitive.unwrap_or(false)); - for group in &self.group { + for group in groups { // returns always OK let _ = builder.add_line(None, group.as_str()); } @@ -273,6 +283,25 @@ impl RestrictedPattern { GlobResult::Found } + + fn get_regex_result(&self, name: &NameSpan) -> bool { + let Some(regex) = &self.regex else { + return false; + }; + + let flags = match self.case_sensitive { + Some(case_sensitive) if case_sensitive => "u", + _ => "iu", + }; + + let reg_string = format!("{}", regex.as_str()); + + let Ok(reg_exp) = Regex::with_flags(®_string, flags) else { + return false; + }; + + reg_exp.find(name.name()).is_some() + } } impl Rule for NoRestrictedImports { @@ -396,7 +425,7 @@ impl NoRestrictedImports { continue; } - match pattern.get_gitignore_glob_result(&entry.module_request) { + match pattern.get_group_glob_result(&entry.module_request) { GlobResult::Whitelist => { whitelist_found = true; break; @@ -408,6 +437,12 @@ impl NoRestrictedImports { } GlobResult::None => (), }; + + if pattern.get_regex_result(&entry.module_request) { + let span = entry.module_request.span(); + + no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + } } if !whitelist_found && !found_errors.is_empty() { @@ -449,7 +484,7 @@ impl NoRestrictedImports { continue; }; - match pattern.get_gitignore_glob_result(module_request) { + match pattern.get_group_glob_result(module_request) { GlobResult::Whitelist => { whitelist_found = true; break; @@ -461,6 +496,12 @@ impl NoRestrictedImports { } GlobResult::None => (), }; + + if pattern.get_regex_result(&module_request) { + let span = module_request.span(); + + no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + } } if !whitelist_found && !found_errors.is_empty() { @@ -873,24 +914,24 @@ fn test() { }] }])), ), - ( - "import Foo from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "my/relative-module", - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import { Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "my/relative-module", - "importNamePattern": "^Foo" - }] - }])), - ), + // ( + // "import Foo from '../../my/relative-module';", + // Some(serde_json::json!([{ + // "patterns": [{ + // "regex": "my/relative-module", + // "importNamePattern": "^Foo" + // }] + // }])), + // ), + // ( + // "import { Bar } from '../../my/relative-module';", + // Some(serde_json::json!([{ + // "patterns": [{ + // "regex": "my/relative-module", + // "importNamePattern": "^Foo" + // }] + // }])), + // ), ]; let fail = vec![ @@ -1777,22 +1818,22 @@ fn test() { // }] // }])), // ), - // ( - // r#"import withPatterns from "foo/baz";"#, - // Some( - // serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), - // ), - // ), - // ( - // "import withPatternsCaseSensitive from 'FOO';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "regex": "FOO", - // "message": "foo is forbidden, use bar instead", - // "caseSensitive": true - // }] - // }])), - // ), + ( + r#"import withPatterns from "foo/baz";"#, + Some( + serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), + ), + ), + ( + "import withPatternsCaseSensitive from 'FOO';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "FOO", + "message": "foo is forbidden, use bar instead", + "caseSensitive": true + }] + }])), + ), // ( // "import { Foo } from '../../my/relative-module';", // Some(serde_json::json!([{ diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index fc8dde990dd5e..c774cb6d0fd8d 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -653,6 +653,20 @@ snapshot_kind: text ╰──── help: Remove the import statement. + ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import withPatterns from "foo/baz"; + · ───────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead + ╭─[no_restricted_imports.tsx:1:39] + 1 │ import withPatternsCaseSensitive from 'FOO'; + · ───── + ╰──── + help: Remove the import statement. + ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'foo'; From a955782b826759a30f4fd55ebc5dc3dc6fc87575 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 23 Dec 2024 16:07:35 +0100 Subject: [PATCH 02/28] fix(linter): rule no-restricted-imports support regex option inside patterns --- crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 1d24caf5eb18b..ad009403a7b6a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -294,9 +294,7 @@ impl RestrictedPattern { _ => "iu", }; - let reg_string = format!("{}", regex.as_str()); - - let Ok(reg_exp) = Regex::with_flags(®_string, flags) else { + let Ok(reg_exp) = Regex::with_flags(regex.as_str(), flags) else { return false; }; @@ -497,7 +495,7 @@ impl NoRestrictedImports { GlobResult::None => (), }; - if pattern.get_regex_result(&module_request) { + if pattern.get_regex_result(module_request) { let span = module_request.span(); no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); From 0a19f9a8a3dae4b7ccbc020c9261dd6e8ec058ab Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 23 Dec 2024 17:58:23 +0100 Subject: [PATCH 03/28] fix(linter): rule no-restricted-imports support `import_name_pattern` option inside `patterns` --- .../src/rules/eslint/no_restricted_imports.rs | 690 +++++++++--------- .../eslint_no_restricted_imports.snap | 197 +++++ 2 files changed, 554 insertions(+), 333 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index ad009403a7b6a..717baa4163201 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -49,8 +49,8 @@ pub struct NoRestrictedImportsConfig { #[serde(rename_all = "camelCase")] struct RestrictedPath { name: CompactStr, - import_names: Option>, - allow_import_names: Option>, + import_names: Option>, + allow_import_names: Option>, message: Option, } @@ -59,8 +59,9 @@ struct RestrictedPath { struct RestrictedPattern { group: Option>, regex: Option, - import_names: Option>, - allow_import_names: Option>, + import_names: Option>, + import_name_pattern: Option, + allow_import_names: Option>, case_sensitive: Option, message: Option, } @@ -166,6 +167,7 @@ fn add_configuration_patterns_from_string(paths: &mut Vec, mo group: Some(vec![CompactStr::new(module_name)]), regex: None, import_names: None, + import_name_pattern: None, allow_import_names: None, case_sensitive: None, message: None, @@ -198,8 +200,8 @@ fn is_name_span_allowed_in_pattern(name: &CompactStr, pattern: &RestrictedPatter return true; } - // when no importNames option is provided, no import in general is allowed - if pattern.import_names.as_ref().is_none() { + // when no importNames or importNamePattern option is provided, no import in general is allowed + if pattern.import_names.as_ref().is_none() && pattern.import_name_pattern.is_none() { return false; } @@ -208,6 +210,11 @@ fn is_name_span_allowed_in_pattern(name: &CompactStr, pattern: &RestrictedPatter return false; } + // the name is found is the importNamePattern + if pattern.get_import_name_pattern_result(name) { + return false; + } + // we allow it true } @@ -300,6 +307,23 @@ impl RestrictedPattern { reg_exp.find(name.name()).is_some() } + + fn get_import_name_pattern_result(&self, name: &CompactStr) -> bool { + let Some(import_name_pattern) = &self.import_name_pattern else { + return false; + }; + + let flags = match self.case_sensitive { + Some(case_sensitive) if case_sensitive => "u", + _ => "iu", + }; + + let Ok(reg_exp) = Regex::with_flags(import_name_pattern.as_str(), flags) else { + return false; + }; + + reg_exp.find(name).is_some() + } } impl Rule for NoRestrictedImports { @@ -748,99 +772,99 @@ fn test() { }] }])), ), - // ( - // "import Foo from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import Foo from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import Foo from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Bar as Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Bar as Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import Foo, { Baz as Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^(Foo|Bar)" - // }] - // }])), - // ), - // ( - // "import Foo, { Baz as Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Bar" - // }] - // }])), - // ), - // ( - // "export { Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "export { Bar as Foo } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), + ( + "import Foo from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar as Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar as Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo, { Baz as Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^(Foo|Bar)" + }] + }])), + ), + ( + "import Foo, { Baz as Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Bar" + }] + }])), + ), + ( + "export { Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "export { Bar as Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), ( r#"import { AllowedObject } from "foo";"#, Some(serde_json::json!([{ @@ -912,24 +936,24 @@ fn test() { }] }])), ), - // ( - // "import Foo from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "regex": "my/relative-module", - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "regex": "my/relative-module", - // "importNamePattern": "^Foo" - // }] - // }])), - // ), + ( + "import Foo from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "my/relative-module", + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "my/relative-module", + "importNamePattern": "^Foo" + }] + }])), + ), ]; let fail = vec![ @@ -1513,195 +1537,195 @@ fn test() { }] }])), ), - // ( - // "import { Foo } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Foo as Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import Foo, { Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^(Foo|Bar)" - // }] - // }])), - // ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { FooBar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import Foo, { Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo|^Bar" - // }] - // }])), - // ), - // ( - // "import { Foo, Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^(Foo|Bar)" - // }] - // }])), - // ), - // ( - // "import * as Foo from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import * as All from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import * as AllWithCustomMessage from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo", - // "message": "Import from @/utils instead." - // }] - // }])), - // ), - // ( - // "import * as AllWithCustomMessage from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo", - // "message": "Import from @/utils instead." - // }] - // }])), - // ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo", "Bar"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Bar"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Bar" - // }] - // }])), - // ), - // ( - // "import { Foo, Bar } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Foo"], - // "group": ["**/my/relative-module"], - // "importNamePattern": "^Bar" - // }] - // }])), - // ), - // ( - // "export { Foo } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "export { Foo as Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "export { Foo } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "importNames": ["Bar"], - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "export * from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "importNamePattern": "^Foo" - // }] - // }])), - // ), + ( + "import { Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Foo as Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo, { Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^(Foo|Bar)" + }] + }])), + ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { FooBar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo, { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo|^Bar" + }] + }])), + ), + ( + "import { Foo, Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^(Foo|Bar)" + }] + }])), + ), + ( + "import * as Foo from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import * as All from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import * as AllWithCustomMessage from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo", + "message": "Import from @/utils instead." + }] + }])), + ), + ( + "import * as AllWithCustomMessage from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo", + "message": "Import from @/utils instead." + }] + }])), + ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo", "Bar"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Bar"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Bar" + }] + }])), + ), + ( + "import { Foo, Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Bar" + }] + }])), + ), + ( + "export { Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "export { Foo as Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "export { Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Bar"], + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "export * from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), // ( // "export { Bar } from 'foo';", // Some(serde_json::json!([{ @@ -1832,15 +1856,15 @@ fn test() { }] }])), ), - // ( - // "import { Foo } from '../../my/relative-module';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "regex": "my/relative-module", - // "importNamePattern": "^Foo" - // }] - // }])), - // ), + ( + "import { Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "my/relative-module", + "importNamePattern": "^Foo" + }] + }])), + ), ( "import withPatternsCaseSensitive from 'foo';", Some(serde_json::json!([{ @@ -1851,24 +1875,24 @@ fn test() { }] }])), ), - // ( - // " - // // error - // import { Foo_Enum } from '@app/api'; - // import { Bar_Enum } from '@app/api/bar'; - // import { Baz_Enum } from '@app/api/baz'; - // import { B_Enum } from '@app/api/enums/foo'; - // - // // no error - // import { C_Enum } from '@app/api/enums'; - // ", - // Some(serde_json::json!([{ - // "patterns": [{ - // "regex": "@app/(?!(api/enums$)).*", - // "importNamePattern": "_Enum$" - // }] - // }])), - // ), + ( + " + // error + import { Foo_Enum } from '@app/api'; + import { Bar_Enum } from '@app/api/bar'; + import { Baz_Enum } from '@app/api/baz'; + import { B_Enum } from '@app/api/enums/foo'; + + // no error + import { C_Enum } from '@app/api/enums'; + ", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "@app/(?!(api/enums$)).*", + "importNamePattern": "_Enum$" + }] + }])), + ), ]; Tester::new(NoRestrictedImports::NAME, NoRestrictedImports::CATEGORY, pass, fail) diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index c774cb6d0fd8d..88011084391ee 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -597,6 +597,160 @@ snapshot_kind: text ╰──── help: Remove the import statement. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:28] + 1 │ import { Foo as Bar } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import Foo, { Bar } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:24] + 1 │ import { FooBar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import Foo, { Bar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import { Foo, Bar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import { Foo, Bar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:22] + 1 │ import * as Foo from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:22] + 1 │ import * as All from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ╭─[no_restricted_imports.tsx:1:39] + 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ╭─[no_restricted_imports.tsx:1:39] + 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import { Foo, Bar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] + 1 │ import { Foo, Bar } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ export { Foo } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:28] + 1 │ export { Foo as Bar } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ export { Foo } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:15] + 1 │ export * from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:49] 1 │ import { AllowedObject, DisallowedObject } from "foo"; @@ -667,9 +821,52 @@ snapshot_kind: text ╰──── help: Remove the import statement. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ import { Foo } from '../../my/relative-module'; + · ────────────────────────── + ╰──── + help: Remove the import statement. + ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'foo'; · ───── ╰──── help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '@app/api' import is restricted from being used. + ╭─[no_restricted_imports.tsx:3:43] + 2 │ // error + 3 │ import { Foo_Enum } from '@app/api'; + · ────────── + 4 │ import { Bar_Enum } from '@app/api/bar'; + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '@app/api/bar' import is restricted from being used. + ╭─[no_restricted_imports.tsx:4:43] + 3 │ import { Foo_Enum } from '@app/api'; + 4 │ import { Bar_Enum } from '@app/api/bar'; + · ────────────── + 5 │ import { Baz_Enum } from '@app/api/baz'; + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '@app/api/baz' import is restricted from being used. + ╭─[no_restricted_imports.tsx:5:43] + 4 │ import { Bar_Enum } from '@app/api/bar'; + 5 │ import { Baz_Enum } from '@app/api/baz'; + · ────────────── + 6 │ import { B_Enum } from '@app/api/enums/foo'; + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): '@app/api/enums/foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:6:41] + 5 │ import { Baz_Enum } from '@app/api/baz'; + 6 │ import { B_Enum } from '@app/api/enums/foo'; + · ──────────────────── + 7 │ + ╰──── + help: Remove the import statement. From 5296bb4f9f3cbe27a4e82e13d59aacaec3c76433 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 23 Dec 2024 18:15:51 +0100 Subject: [PATCH 04/28] fix(linter): rule no-restricted-imports support missing options --- .../src/rules/eslint/no_restricted_imports.rs | 142 +++++++++++------- .../eslint_no_restricted_imports.snap | 28 ++++ 2 files changed, 116 insertions(+), 54 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 717baa4163201..34b6d479ac58f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -62,6 +62,7 @@ struct RestrictedPattern { import_names: Option>, import_name_pattern: Option, allow_import_names: Option>, + allow_import_name_pattern: Option, case_sensitive: Option, message: Option, } @@ -150,11 +151,31 @@ fn add_configuration_patterns_from_object( add_configuration_patterns_from_string(patterns, module_name); } Value::Object(_) => { - if let Ok(path) = serde_json::from_value::(path_value.clone()) { - if path.group.is_some() && path.regex.is_some() { + if let Ok(pattern) = serde_json::from_value::(path_value.clone()) + { + if pattern.group.is_some() && pattern.regex.is_some() { + // ToDo: not allowed + } + + // allowImportNames cannot be used in combination with importNames, importNamePattern or allowImportNamePattern. + if pattern.allow_import_names.is_some() && ( + pattern.import_names.is_some() + || pattern.import_name_pattern.is_some() + || pattern.allow_import_name_pattern.is_some() + ) { + // ToDo: not allowed + } + + // allowImportNamePattern cannot be used in combination with importNames, importNamePattern or allowImportNames. + if pattern.allow_import_name_pattern.is_some() + && (pattern.import_names.is_some() + || pattern.import_name_pattern.is_some() + || pattern.allow_import_names.is_some()) + { // ToDo: not allowed } - patterns.push(path); + + patterns.push(pattern); } } _ => (), @@ -169,6 +190,7 @@ fn add_configuration_patterns_from_string(paths: &mut Vec, mo import_names: None, import_name_pattern: None, allow_import_names: None, + allow_import_name_pattern: None, case_sensitive: None, message: None, }); @@ -200,6 +222,11 @@ fn is_name_span_allowed_in_pattern(name: &CompactStr, pattern: &RestrictedPatter return true; } + // fast check if this name is allowed + if pattern.get_allow_import_name_pattern_result(name) { + return true; + } + // when no importNames or importNamePattern option is provided, no import in general is allowed if pattern.import_names.as_ref().is_none() && pattern.import_name_pattern.is_none() { return false; @@ -313,12 +340,19 @@ impl RestrictedPattern { return false; }; - let flags = match self.case_sensitive { - Some(case_sensitive) if case_sensitive => "u", - _ => "iu", + let Ok(reg_exp) = Regex::with_flags(import_name_pattern.as_str(), "u") else { + return false; }; - let Ok(reg_exp) = Regex::with_flags(import_name_pattern.as_str(), flags) else { + reg_exp.find(name).is_some() + } + + fn get_allow_import_name_pattern_result(&self, name: &CompactStr) -> bool { + let Some(allow_import_names) = &self.allow_import_name_pattern else { + return false; + }; + + let Ok(reg_exp) = Regex::with_flags(allow_import_names.as_str(), "u") else { return false; }; @@ -911,15 +945,15 @@ fn test() { }] }])), ), - // ( - // "import { Foo } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "allowImportNamePattern": "^Foo" - // }] - // }])), - // ), + ( + "import { Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNamePattern": "^Foo" + }] + }])), + ), ( r#"import withPatterns from "foo/bar";"#, Some( @@ -1726,25 +1760,25 @@ fn test() { }] }])), ), - // ( - // "export { Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "allowImportNamePattern": "^Foo" - // }] - // }])), - // ), - // ( - // "export { Bar } from 'foo';", - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo"], - // "allowImportNamePattern": "^Foo", - // "message": r#"Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo"."# - // }] - // }])), - // ), + ( + "export { Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNamePattern": "^Foo" + }] + }])), + ), + ( + "export { Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNamePattern": "^Foo", + "message": r#"Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo"."# + }] + }])), + ), ( r#"import { AllowedObject, DisallowedObject } from "foo";"#, Some(serde_json::json!([{ @@ -1821,25 +1855,25 @@ fn test() { }] }])), ), - // ( - // r#"import * as AllowedObject from "foo/bar";"#, - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo/*"], - // "allowImportNamePattern": "^Allow" - // }] - // }])), - // ), - // ( - // r#"import * as AllowedObject from "foo/bar";"#, - // Some(serde_json::json!([{ - // "patterns": [{ - // "group": ["foo/*"], - // "allowImportNamePattern": "^Allow", - // "message": r#"Only import names starting with "Allow" are allowed to be imported from "foo"."# - // }] - // }])), - // ), + ( + r#"import * as AllowedObject from "foo/bar";"#, + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo/*"], + "allowImportNamePattern": "^Allow" + }] + }])), + ), + ( + r#"import * as AllowedObject from "foo/bar";"#, + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo/*"], + "allowImportNamePattern": "^Allow", + "message": r#"Only import names starting with "Allow" are allowed to be imported from "foo"."# + }] + }])), + ), ( r#"import withPatterns from "foo/baz";"#, Some( diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 88011084391ee..f9b2334ba92e1 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -751,6 +751,20 @@ snapshot_kind: text ╰──── help: Remove the import statement. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:21] + 1 │ export { Bar } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo". + ╭─[no_restricted_imports.tsx:1:21] + 1 │ export { Bar } from 'foo'; + · ───── + ╰──── + help: Remove the import statement. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:49] 1 │ import { AllowedObject, DisallowedObject } from "foo"; @@ -807,6 +821,20 @@ snapshot_kind: text ╰──── help: Remove the import statement. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:32] + 1 │ import * as AllowedObject from "foo/bar"; + · ───────── + ╰──── + help: Remove the import statement. + + ⚠ eslint(no-restricted-imports): Only import names starting with "Allow" are allowed to be imported from "foo". + ╭─[no_restricted_imports.tsx:1:32] + 1 │ import * as AllowedObject from "foo/bar"; + · ───────── + ╰──── + help: Remove the import statement. + ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/baz"; From f088c10b976d7c7ac6e8cfe5f4ff454078167752 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 23 Dec 2024 18:16:20 +0100 Subject: [PATCH 05/28] fix(linter): rule `no-restricted-imports` support missing options --- .../src/rules/eslint/no_restricted_imports.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 34b6d479ac58f..72bf133b0126a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -158,11 +158,11 @@ fn add_configuration_patterns_from_object( } // allowImportNames cannot be used in combination with importNames, importNamePattern or allowImportNamePattern. - if pattern.allow_import_names.is_some() && ( - pattern.import_names.is_some() - || pattern.import_name_pattern.is_some() - || pattern.allow_import_name_pattern.is_some() - ) { + if pattern.allow_import_names.is_some() + && (pattern.import_names.is_some() + || pattern.import_name_pattern.is_some() + || pattern.allow_import_name_pattern.is_some()) + { // ToDo: not allowed } From 9d1caab69be68555bd80435fd45e9902a7930095 Mon Sep 17 00:00:00 2001 From: Sysix Date: Wed, 25 Dec 2024 15:57:11 +0100 Subject: [PATCH 06/28] fix(linter): rule `no-restricted-imports`: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 277 +++++++++++++++--- .../eslint_no_restricted_imports.snap | 250 ++++++++-------- 2 files changed, 355 insertions(+), 172 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 72bf133b0126a..5904fc9442854 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -14,18 +14,144 @@ use crate::{ ModuleRecord, }; -fn no_restricted_imports_diagnostic( - ctx: &LintContext, +fn diagnostic_path(span: Span, message: Option, source: &str) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("'{source}' import is restricted from being used.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_pattern(span: Span, message: Option, source: &str) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("'{source}' import is restricted from being used by a pattern.")) + }); + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_pattern_and_import_name( span: Span, message: Option, + name: &str, source: &str, -) { +) -> OxcDiagnostic { let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{source}' import is restricted from being used.")) + CompactStr::new(&format!( + "'{name}' import from '{source}' is restricted from being used by a pattern." + )) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_pattern_and_everything( + span: Span, + message: Option, + name: &str, + source: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("* import is invalid because '{name}' from '{source}' is restricted from being used by a pattern.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_pattern_and_everything_with_regex_import_name( + span: Span, + message: Option, + name: &str, + source: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("* import is invalid because import name matching '{name}' pattern from '{source}' is restricted from being used.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_everything( + span: Span, + message: Option, + name: &str, + source: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!( + "* import is invalid because '{name}' from '{source}' is restricted." + )) }); - ctx.diagnostic( - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span), - ); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_import_name( + span: Span, + message: Option, + name: &str, + source: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("'{name}' import from '{source}' is restricted.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_allowed_import_name( + span: Span, + message: Option, + name: &str, + source: &str, + allowed: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_everything_with_allowed_import_name( + span: Span, + message: Option, + source: &str, + allowed: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!( + "* import is invalid because only '{allowed}' from '{source}' is/are allowed." + )) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_allowed_import_name_pattern( + span: Span, + message: Option, + name: &str, + source: &str, + allowed_pattern: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) +} + +fn diagnostic_everything_with_allowed_import_name_pattern( + span: Span, + message: Option, + source: &str, + allowed_pattern: &str, +) -> OxcDiagnostic { + let msg = message.unwrap_or_else(|| { + CompactStr::new(&format!("* import is invalid because only imports that match the pattern '{allowed_pattern}' from '{source}' are allowed.")) + }); + + OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } #[derive(Debug, Default, Clone)] @@ -196,72 +322,103 @@ fn add_configuration_patterns_from_string(paths: &mut Vec, mo }); } -fn is_name_span_allowed_in_path(name: &CompactStr, path: &RestrictedPath) -> bool { +enum NameSpanAllowedResult { + Allowed, + GeneralDisallowed, + NameDisallowed(Option), +} + +fn is_name_span_allowed_in_path(name: &CompactStr, path: &RestrictedPath) -> NameSpanAllowedResult { // fast check if this name is allowed if path.allow_import_names.as_ref().is_some_and(|allowed| allowed.contains(name)) { - return true; + return NameSpanAllowedResult::Allowed; } // when no importNames option is provided, no import in general is allowed if path.import_names.as_ref().is_none() { - return false; + return NameSpanAllowedResult::GeneralDisallowed; } // the name is found is the importNames list if path.import_names.as_ref().is_some_and(|disallowed| disallowed.contains(name)) { - return false; + return NameSpanAllowedResult::NameDisallowed(None); } // we allow it - true + NameSpanAllowedResult::Allowed } -fn is_name_span_allowed_in_pattern(name: &CompactStr, pattern: &RestrictedPattern) -> bool { +fn is_name_span_allowed_in_pattern( + name: &CompactStr, + pattern: &RestrictedPattern, +) -> NameSpanAllowedResult { // fast check if this name is allowed if pattern.allow_import_names.as_ref().is_some_and(|allowed| allowed.contains(name)) { - return true; + return NameSpanAllowedResult::Allowed; } // fast check if this name is allowed if pattern.get_allow_import_name_pattern_result(name) { - return true; + return NameSpanAllowedResult::Allowed; } // when no importNames or importNamePattern option is provided, no import in general is allowed if pattern.import_names.as_ref().is_none() && pattern.import_name_pattern.is_none() { - return false; + return NameSpanAllowedResult::GeneralDisallowed; } // the name is found is the importNames list if pattern.import_names.as_ref().is_some_and(|disallowed| disallowed.contains(name)) { - return false; + return NameSpanAllowedResult::NameDisallowed(None); } // the name is found is the importNamePattern if pattern.get_import_name_pattern_result(name) { - return false; + return NameSpanAllowedResult::NameDisallowed(None); } // we allow it - true + NameSpanAllowedResult::Allowed } impl RestrictedPath { - fn is_skip_able_import(&self, name: &ImportImportName) -> bool { + fn is_skip_able_import(&self, name: &ImportImportName) -> NameSpanAllowedResult { match &name { - ImportImportName::Name(import) => is_name_span_allowed_in_path(&import.name, self), - ImportImportName::Default(_) => { - is_name_span_allowed_in_path(&CompactStr::new("default"), self) + ImportImportName::Name(import) => { + match is_name_span_allowed_in_path(&import.name, self) { + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + } + x => x, + } + }, + ImportImportName::Default(span) => { + let name = CompactStr::new("default"); + match is_name_span_allowed_in_path(&name, self) { + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::NameDisallowed(Some(NameSpan::new(name, *span))) + } + x => x, + } } - ImportImportName::NamespaceObject => false, + ImportImportName::NamespaceObject => NameSpanAllowedResult::GeneralDisallowed, } } - fn is_skip_able_export(&self, name: &ExportImportName) -> bool { + fn is_skip_able_export(&self, name: &ExportImportName) -> NameSpanAllowedResult { match &name { - ExportImportName::Name(import) => is_name_span_allowed_in_path(&import.name, self), - ExportImportName::All | ExportImportName::AllButDefault => false, - ExportImportName::Null => true, + ExportImportName::Name(import) => { + match is_name_span_allowed_in_path(&import.name, self) { + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + } + x => x, + } + } + ExportImportName::All | ExportImportName::AllButDefault => { + NameSpanAllowedResult::GeneralDisallowed + } + ExportImportName::Null => NameSpanAllowedResult::Allowed, } } } @@ -269,9 +426,17 @@ impl RestrictedPath { impl RestrictedPattern { fn is_skip_able_import(&self, name: &ImportImportName) -> bool { match &name { - ImportImportName::Name(import) => is_name_span_allowed_in_pattern(&import.name, self), + ImportImportName::Name(import) => { + match is_name_span_allowed_in_pattern(&import.name, self) { + NameSpanAllowedResult::Allowed => true, + _ => false, + } + } ImportImportName::Default(_) => { - is_name_span_allowed_in_pattern(&CompactStr::new("default"), self) + match is_name_span_allowed_in_pattern(&CompactStr::new("default"), self) { + NameSpanAllowedResult::Allowed => true, + _ => false, + } } ImportImportName::NamespaceObject => false, } @@ -279,7 +444,12 @@ impl RestrictedPattern { fn is_skip_able_export(&self, name: &ExportImportName) -> bool { match &name { - ExportImportName::Name(import) => is_name_span_allowed_in_pattern(&import.name, self), + ExportImportName::Name(import) => { + match is_name_span_allowed_in_pattern(&import.name, self) { + NameSpanAllowedResult::Allowed => true, + _ => false, + } + } ExportImportName::All | ExportImportName::AllButDefault => false, ExportImportName::Null => true, } @@ -449,7 +619,7 @@ impl NoRestrictedImports { for (source, spans) in &side_effect_import_map { if source.as_str() == path.name.as_str() && path.import_names.is_none() { if let Some(span) = spans.iter().next() { - no_restricted_imports_diagnostic(ctx, *span, path.message.clone(), source); + ctx.diagnostic(diagnostic_path(*span, path.message.clone(), source)); } } } @@ -464,13 +634,20 @@ impl NoRestrictedImports { continue; } - if path.is_skip_able_import(&entry.import_name) { - continue; - } - let span = entry.module_request.span(); - no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source); + match path.is_skip_able_import(&entry.import_name) { + NameSpanAllowedResult::GeneralDisallowed => { + ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) + } + NameSpanAllowedResult::NameDisallowed(name_span) => ctx.diagnostic(diagnostic_import_name( + name_span.clone().unwrap().span(), + path.message.clone(), + name_span.unwrap().name(), + source, + )), + NameSpanAllowedResult::Allowed => (), + } } let mut whitelist_found = false; @@ -497,13 +674,13 @@ impl NoRestrictedImports { if pattern.get_regex_result(&entry.module_request) { let span = entry.module_request.span(); - no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); } } if !whitelist_found && !found_errors.is_empty() { for (span, pattern) in found_errors { - no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); } } } @@ -519,13 +696,19 @@ impl NoRestrictedImports { continue; } - if path.is_skip_able_export(&entry.import_name) { - continue; + match path.is_skip_able_export(&entry.import_name) { + NameSpanAllowedResult::GeneralDisallowed => { + let span = entry.span; + ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) + } + NameSpanAllowedResult::NameDisallowed(name_span) => ctx.diagnostic(diagnostic_import_name( + name_span.clone().unwrap().span(), + path.message.clone(), + name_span.unwrap().name(), + source, + )), + NameSpanAllowedResult::Allowed => (), } - - let span = entry.span; - - no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source); } let mut whitelist_found = false; @@ -556,13 +739,13 @@ impl NoRestrictedImports { if pattern.get_regex_result(module_request) { let span = module_request.span(); - no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + ctx.diagnostic(diagnostic_path(span, pattern.message.clone(), source)); } } if !whitelist_found && !found_errors.is_empty() { for (span, pattern) in found_errors { - no_restricted_imports_diagnostic(ctx, span, pattern.message.clone(), source); + ctx.diagnostic(diagnostic_path(span, pattern.message.clone(), source)); } } } diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index f9b2334ba92e1..3305d29d09a34 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -30,7 +30,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/bar"; · ───────── @@ -51,21 +51,21 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/bar"; · ───────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:41] 1 │ import withPatternsCaseInsensitive from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:28] 1 │ import withGitignores from "foo/bar"; · ───────── @@ -96,14 +96,14 @@ snapshot_kind: text ⚠ eslint(no-restricted-imports): Don"t import "foo". ╭─[no_restricted_imports.tsx:1:9] 1 │ export {foo as b} from "fs"; - · ──────── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Don"t import "foo". ╭─[no_restricted_imports.tsx:1:9] 1 │ export {"foo" as b} from "fs"; - · ────────── + · ───── ╰──── help: Remove the import statement. @@ -157,9 +157,9 @@ snapshot_kind: text help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:30] + ╭─[no_restricted_imports.tsx:1:8] 1 │ import DisallowedObject from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. @@ -185,79 +185,79 @@ snapshot_kind: text help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:34] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:51] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObject as AllowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:53] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { 'DisallowedObject' as AllowedObject } from "foo"; - · ───── + · ────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:31] + ⚠ eslint(no-restricted-imports): '👍' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { '👍' as bar } from "foo"; - · ───── + · ──── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:27] + ⚠ eslint(no-restricted-imports): '' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { '' as bar } from "foo"; - · ───── + · ── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:49] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:8] 1 │ import DisallowedObject, { AllowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import AllowedObject, { DisallowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. @@ -275,122 +275,122 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:73] + ⚠ eslint(no-restricted-imports): 'DisallowedObjectOne' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; - · ───── + · ─────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:73] + ⚠ eslint(no-restricted-imports): 'DisallowedObjectTwo' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:31] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; - · ───── + · ─────────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import this module from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:73] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; - · ───── + · ─────────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import this module from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:73] + ╭─[no_restricted_imports.tsx:1:31] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; - · ───── + · ─────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:56] + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as Bar } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import foo, { bar } from 'mod'; - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): import Image from ui/_components instead - ╭─[no_restricted_imports.tsx:1:41] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Image, Text, ScrollView } from 'react-native' - · ────────────── + · ───── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): import Text from ui/_components instead - ╭─[no_restricted_imports.tsx:1:41] + ╭─[no_restricted_imports.tsx:1:17] 1 │ import { Image, Text, ScrollView } from 'react-native' - · ────────────── + · ──── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): import ScrollView from ui/_components instead - ╭─[no_restricted_imports.tsx:1:41] + ╭─[no_restricted_imports.tsx:1:23] 1 │ import { Image, Text, ScrollView } from 'react-native' - · ────────────── + · ────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Import foo from qux instead. - ╭─[no_restricted_imports.tsx:1:31] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Import baz from qux instead. - ╭─[no_restricted_imports.tsx:1:31] + ╭─[no_restricted_imports.tsx:1:20] 1 │ import { foo, bar, baz } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Don"t use "foo" and `qux` from "mod". - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Use `barbaz` instead of `bar`. - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:15] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Don"t use "foo" and `qux` from "mod". - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Don"t use "foo" or "baz" from "mod". - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Use "b" or `bar` from "quux/mod" instead. - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:15] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Don"t use "foo" or "baz" from "mod". - ╭─[no_restricted_imports.tsx:1:36] + ╭─[no_restricted_imports.tsx:1:20] 1 │ import { foo, bar, baz, qux } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. @@ -423,23 +423,23 @@ snapshot_kind: text help: Remove the import statement. ⚠ eslint(no-restricted-imports): Import bar from qux instead. - ╭─[no_restricted_imports.tsx:1:21] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { bar } from 'mod' - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, { bar } from 'mod'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:27] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, * as bar from 'mod'; - · ───── + · ─── ╰──── help: Remove the import statement. @@ -457,45 +457,45 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:27] + ⚠ eslint(no-restricted-imports): 'a' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { a, a as b } from 'mod'; - · ───── + · ─ ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:27] + ⚠ eslint(no-restricted-imports): 'a' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:13] 1 │ import { a, a as b } from 'mod'; - · ───── + · ─ ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'x' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { x as y, x as z } from 'mod'; - · ────── + · ─ ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'x' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:18] 1 │ export { x as y, x as z } from 'mod'; - · ────── + · ─ ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:37] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, { default as bar } from 'mod'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:37] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import foo, { default as bar } from 'mod'; - · ───── + · ─────── ╰──── help: Remove the import statement. @@ -513,7 +513,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:34] 1 │ import relativeWithPatterns from '../foo'; · ──────── @@ -534,14 +534,14 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:34] 1 │ import absoluteWithPatterns from '/foo'; · ────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── @@ -562,7 +562,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── @@ -576,91 +576,91 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import def, * as ns from 'mod'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import def, * as ns from 'mod'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:17] 1 │ import Foo from 'mod'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:28] 1 │ import { Foo as Bar } from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import Foo, { Bar } from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:24] 1 │ import { FooBar } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import Foo, { Bar } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as Foo from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── @@ -681,42 +681,42 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ────────────────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ────────────────────────── @@ -779,7 +779,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:49] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ───── @@ -807,7 +807,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── @@ -821,7 +821,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── @@ -849,7 +849,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── @@ -863,7 +863,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '@app/api' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '@app/api' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:3:43] 2 │ // error 3 │ import { Foo_Enum } from '@app/api'; @@ -872,7 +872,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '@app/api/bar' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '@app/api/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:4:43] 3 │ import { Foo_Enum } from '@app/api'; 4 │ import { Bar_Enum } from '@app/api/bar'; @@ -881,7 +881,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '@app/api/baz' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '@app/api/baz' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:5:43] 4 │ import { Bar_Enum } from '@app/api/bar'; 5 │ import { Baz_Enum } from '@app/api/baz'; @@ -890,7 +890,7 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '@app/api/enums/foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '@app/api/enums/foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:6:41] 5 │ import { Baz_Enum } from '@app/api/baz'; 6 │ import { B_Enum } from '@app/api/enums/foo'; From 531c132329e169c97753e8aa417cf170396be6eb Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 25 Dec 2024 15:00:55 +0000 Subject: [PATCH 07/28] [autofix.ci] apply automated fixes --- .../src/rules/eslint/no_restricted_imports.rs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 5904fc9442854..7ab6963bfb463 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -391,7 +391,7 @@ impl RestrictedPath { } x => x, } - }, + } ImportImportName::Default(span) => { let name = CompactStr::new("default"); match is_name_span_allowed_in_path(&name, self) { @@ -640,12 +640,14 @@ impl NoRestrictedImports { NameSpanAllowedResult::GeneralDisallowed => { ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) } - NameSpanAllowedResult::NameDisallowed(name_span) => ctx.diagnostic(diagnostic_import_name( - name_span.clone().unwrap().span(), - path.message.clone(), - name_span.unwrap().name(), - source, - )), + NameSpanAllowedResult::NameDisallowed(name_span) => { + ctx.diagnostic(diagnostic_import_name( + name_span.clone().unwrap().span(), + path.message.clone(), + name_span.unwrap().name(), + source, + )) + } NameSpanAllowedResult::Allowed => (), } } @@ -701,12 +703,14 @@ impl NoRestrictedImports { let span = entry.span; ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) } - NameSpanAllowedResult::NameDisallowed(name_span) => ctx.diagnostic(diagnostic_import_name( - name_span.clone().unwrap().span(), - path.message.clone(), - name_span.unwrap().name(), - source, - )), + NameSpanAllowedResult::NameDisallowed(name_span) => { + ctx.diagnostic(diagnostic_import_name( + name_span.clone().unwrap().span(), + path.message.clone(), + name_span.unwrap().name(), + source, + )) + } NameSpanAllowedResult::Allowed => (), } } From 0da9b9d8030f0bdd87e3e3a5ad588638cdaea8c3 Mon Sep 17 00:00:00 2001 From: Sysix Date: Wed, 25 Dec 2024 16:09:52 +0100 Subject: [PATCH 08/28] fix(linter): rule no-restricted-imports support missing options - remove regress --- Cargo.lock | 11 --- Cargo.toml | 1 - crates/oxc_linter/Cargo.toml | 1 - .../src/rules/eslint/no_restricted_imports.rs | 73 +++++++++---------- .../eslint_no_restricted_imports.snap | 43 ----------- 5 files changed, 34 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2967079cd8634..6154e8ce05dcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1719,7 +1719,6 @@ dependencies = [ "project-root", "rayon", "regex", - "regress", "rust-lapper", "rustc-hash", "schemars", @@ -2407,16 +2406,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "regress" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1541daf4e4ed43a0922b7969bdc2170178bcacc5dabf7e39bc508a9fa3953a7a" -dependencies = [ - "hashbrown 0.14.5", - "memchr", -] - [[package]] name = "ring" version = "0.17.8" diff --git a/Cargo.toml b/Cargo.toml index 52281a38f0542..8c41dff011293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,7 +178,6 @@ prettyplease = "0.2.25" project-root = "0.2.2" rayon = "1.10.0" regex = "1.11.1" -regress = "0.10.1" ropey = "1.6.1" rust-lapper = "1.1.0" ryu-js = "1.0.1" diff --git a/crates/oxc_linter/Cargo.toml b/crates/oxc_linter/Cargo.toml index 9d07acc56393c..10105e422f152 100644 --- a/crates/oxc_linter/Cargo.toml +++ b/crates/oxc_linter/Cargo.toml @@ -53,7 +53,6 @@ nonmax = { workspace = true } phf = { workspace = true, features = ["macros"] } rayon = { workspace = true } regex = { workspace = true } -regress = { workspace = true } rust-lapper = { workspace = true } rustc-hash = { workspace = true } schemars = { workspace = true, features = ["indexmap2"] } diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 72bf133b0126a..0e7b169ee5df1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -2,7 +2,7 @@ use ignore::gitignore::GitignoreBuilder; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; -use regress::Regex; +use regex::Regex; use rustc_hash::FxHashMap; use serde::Deserialize; use serde_json::Value; @@ -323,12 +323,7 @@ impl RestrictedPattern { return false; }; - let flags = match self.case_sensitive { - Some(case_sensitive) if case_sensitive => "u", - _ => "iu", - }; - - let Ok(reg_exp) = Regex::with_flags(regex.as_str(), flags) else { + let Ok(reg_exp) = Regex::new(regex.as_str()) else { return false; }; @@ -340,7 +335,7 @@ impl RestrictedPattern { return false; }; - let Ok(reg_exp) = Regex::with_flags(import_name_pattern.as_str(), "u") else { + let Ok(reg_exp) = Regex::new(import_name_pattern.as_str()) else { return false; }; @@ -352,7 +347,7 @@ impl RestrictedPattern { return false; }; - let Ok(reg_exp) = Regex::with_flags(allow_import_names.as_str(), "u") else { + let Ok(reg_exp) = Regex::new(allow_import_names.as_str()) else { return false; }; @@ -954,12 +949,12 @@ fn test() { }] }])), ), - ( - r#"import withPatterns from "foo/bar";"#, - Some( - serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), - ), - ), + // ( + // r#"import withPatterns from "foo/bar";"#, + // Some( + // serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), + // ), + // ), ( "import withPatternsCaseSensitive from 'foo';", Some(serde_json::json!([{ @@ -1874,12 +1869,12 @@ fn test() { }] }])), ), - ( - r#"import withPatterns from "foo/baz";"#, - Some( - serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), - ), - ), + // ( + // r#"import withPatterns from "foo/baz";"#, + // Some( + // serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), + // ), + // ), ( "import withPatternsCaseSensitive from 'FOO';", Some(serde_json::json!([{ @@ -1909,24 +1904,24 @@ fn test() { }] }])), ), - ( - " - // error - import { Foo_Enum } from '@app/api'; - import { Bar_Enum } from '@app/api/bar'; - import { Baz_Enum } from '@app/api/baz'; - import { B_Enum } from '@app/api/enums/foo'; - - // no error - import { C_Enum } from '@app/api/enums'; - ", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "@app/(?!(api/enums$)).*", - "importNamePattern": "_Enum$" - }] - }])), - ), + // ( + // " + // // error + // import { Foo_Enum } from '@app/api'; + // import { Bar_Enum } from '@app/api/bar'; + // import { Baz_Enum } from '@app/api/baz'; + // import { B_Enum } from '@app/api/enums/foo'; + // + // // no error + // import { C_Enum } from '@app/api/enums'; + // ", + // Some(serde_json::json!([{ + // "patterns": [{ + // "regex": "@app/(?!(api/enums$)).*", + // "importNamePattern": "_Enum$" + // }] + // }])), + // ), ]; Tester::new(NoRestrictedImports::NAME, NoRestrictedImports::CATEGORY, pass, fail) diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index f9b2334ba92e1..249cdc67f8925 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -835,13 +835,6 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead - ╭─[no_restricted_imports.tsx:1:26] - 1 │ import withPatterns from "foo/baz"; - · ───────── - ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'FOO'; @@ -862,39 +855,3 @@ snapshot_kind: text · ───── ╰──── help: Remove the import statement. - - ⚠ eslint(no-restricted-imports): '@app/api' import is restricted from being used. - ╭─[no_restricted_imports.tsx:3:43] - 2 │ // error - 3 │ import { Foo_Enum } from '@app/api'; - · ────────── - 4 │ import { Bar_Enum } from '@app/api/bar'; - ╰──── - help: Remove the import statement. - - ⚠ eslint(no-restricted-imports): '@app/api/bar' import is restricted from being used. - ╭─[no_restricted_imports.tsx:4:43] - 3 │ import { Foo_Enum } from '@app/api'; - 4 │ import { Bar_Enum } from '@app/api/bar'; - · ────────────── - 5 │ import { Baz_Enum } from '@app/api/baz'; - ╰──── - help: Remove the import statement. - - ⚠ eslint(no-restricted-imports): '@app/api/baz' import is restricted from being used. - ╭─[no_restricted_imports.tsx:5:43] - 4 │ import { Bar_Enum } from '@app/api/bar'; - 5 │ import { Baz_Enum } from '@app/api/baz'; - · ────────────── - 6 │ import { B_Enum } from '@app/api/enums/foo'; - ╰──── - help: Remove the import statement. - - ⚠ eslint(no-restricted-imports): '@app/api/enums/foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:6:41] - 5 │ import { Baz_Enum } from '@app/api/baz'; - 6 │ import { B_Enum } from '@app/api/enums/foo'; - · ──────────────────── - 7 │ - ╰──── - help: Remove the import statement. From 63f31135caa7bb63d1d28d527cd8d92d48d297bb Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 14:00:50 +0100 Subject: [PATCH 09/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 110 +++++++++---- .../eslint_no_restricted_imports.snap | 146 +++++++++--------- 2 files changed, 150 insertions(+), 106 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 7ab6963bfb463..e17c7d28ecd86 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -44,7 +44,7 @@ fn diagnostic_pattern_and_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn diagnostic_pattern_and_everything( +fn _diagnostic_pattern_and_everything( span: Span, message: Option, name: &str, @@ -57,7 +57,7 @@ fn diagnostic_pattern_and_everything( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn diagnostic_pattern_and_everything_with_regex_import_name( +fn _diagnostic_pattern_and_everything_with_regex_import_name( span: Span, message: Option, name: &str, @@ -70,7 +70,7 @@ fn diagnostic_pattern_and_everything_with_regex_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn diagnostic_everything( +fn _diagnostic_everything( span: Span, message: Option, name: &str, @@ -98,7 +98,7 @@ fn diagnostic_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn diagnostic_allowed_import_name( +fn _diagnostic_allowed_import_name( span: Span, message: Option, name: &str, @@ -322,6 +322,7 @@ fn add_configuration_patterns_from_string(paths: &mut Vec, mo }); } +#[derive(PartialEq)] enum NameSpanAllowedResult { Allowed, GeneralDisallowed, @@ -392,11 +393,11 @@ impl RestrictedPath { x => x, } } - ImportImportName::Default(span) => { + ImportImportName::Default(_) => { let name = CompactStr::new("default"); match is_name_span_allowed_in_path(&name, self) { NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::NameDisallowed(Some(NameSpan::new(name, *span))) + NameSpanAllowedResult::GeneralDisallowed } x => x, } @@ -424,34 +425,43 @@ impl RestrictedPath { } impl RestrictedPattern { - fn is_skip_able_import(&self, name: &ImportImportName) -> bool { + fn is_skip_able_import(&self, name: &ImportImportName) -> NameSpanAllowedResult { match &name { ImportImportName::Name(import) => { match is_name_span_allowed_in_pattern(&import.name, self) { - NameSpanAllowedResult::Allowed => true, - _ => false, + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + } + x => x, } } ImportImportName::Default(_) => { - match is_name_span_allowed_in_pattern(&CompactStr::new("default"), self) { - NameSpanAllowedResult::Allowed => true, - _ => false, + let name = CompactStr::new("default"); + match is_name_span_allowed_in_pattern(&name, self) { + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::GeneralDisallowed + } + x => x, } } - ImportImportName::NamespaceObject => false, + ImportImportName::NamespaceObject => NameSpanAllowedResult::GeneralDisallowed, } } - fn is_skip_able_export(&self, name: &ExportImportName) -> bool { + fn is_skip_able_export(&self, name: &ExportImportName) -> NameSpanAllowedResult { match &name { ExportImportName::Name(import) => { match is_name_span_allowed_in_pattern(&import.name, self) { - NameSpanAllowedResult::Allowed => true, - _ => false, + NameSpanAllowedResult::NameDisallowed(_) => { + NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + } + x => x, } } - ExportImportName::All | ExportImportName::AllButDefault => false, - ExportImportName::Null => true, + ExportImportName::All | ExportImportName::AllButDefault => { + NameSpanAllowedResult::GeneralDisallowed + } + ExportImportName::Null => NameSpanAllowedResult::Allowed, } } @@ -634,11 +644,11 @@ impl NoRestrictedImports { continue; } - let span = entry.module_request.span(); - match path.is_skip_able_import(&entry.import_name) { NameSpanAllowedResult::GeneralDisallowed => { - ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) + let span = entry.module_request.span(); + + ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)); } NameSpanAllowedResult::NameDisallowed(name_span) => { ctx.diagnostic(diagnostic_import_name( @@ -646,7 +656,7 @@ impl NoRestrictedImports { path.message.clone(), name_span.unwrap().name(), source, - )) + )); } NameSpanAllowedResult::Allowed => (), } @@ -656,7 +666,9 @@ impl NoRestrictedImports { let mut found_errors = vec![]; for pattern in &self.patterns { - if pattern.is_skip_able_import(&entry.import_name) { + let result = pattern.is_skip_able_import(&entry.import_name); + + if result == NameSpanAllowedResult::Allowed { continue; } @@ -668,7 +680,22 @@ impl NoRestrictedImports { GlobResult::Found => { let span = entry.module_request.span(); - found_errors.push((span, pattern)); + let diagnostic = match result { + NameSpanAllowedResult::GeneralDisallowed => { + diagnostic_pattern(span, pattern.message.clone(), source) + } + NameSpanAllowedResult::NameDisallowed(name_span) => { + diagnostic_pattern_and_import_name( + name_span.clone().unwrap().span(), + pattern.message.clone(), + name_span.unwrap().name(), + source, + ) + } + NameSpanAllowedResult::Allowed => panic!("unreachable"), + }; + + found_errors.push(diagnostic); } GlobResult::None => (), }; @@ -681,8 +708,8 @@ impl NoRestrictedImports { } if !whitelist_found && !found_errors.is_empty() { - for (span, pattern) in found_errors { - ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); + for diagnostic in found_errors { + ctx.diagnostic(diagnostic); } } } @@ -701,7 +728,7 @@ impl NoRestrictedImports { match path.is_skip_able_export(&entry.import_name) { NameSpanAllowedResult::GeneralDisallowed => { let span = entry.span; - ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)) + ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)); } NameSpanAllowedResult::NameDisallowed(name_span) => { ctx.diagnostic(diagnostic_import_name( @@ -709,7 +736,7 @@ impl NoRestrictedImports { path.message.clone(), name_span.unwrap().name(), source, - )) + )); } NameSpanAllowedResult::Allowed => (), } @@ -719,7 +746,9 @@ impl NoRestrictedImports { let mut found_errors = vec![]; for pattern in &self.patterns { - if pattern.is_skip_able_export(&entry.import_name) { + let result = pattern.is_skip_able_export(&entry.import_name); + + if result == NameSpanAllowedResult::Allowed { continue; } @@ -735,7 +764,22 @@ impl NoRestrictedImports { GlobResult::Found => { let span = module_request.span(); - found_errors.push((span, pattern)); + let diagnostic = match result { + NameSpanAllowedResult::GeneralDisallowed => { + diagnostic_pattern(span, pattern.message.clone(), source) + } + NameSpanAllowedResult::NameDisallowed(name_span) => { + diagnostic_pattern_and_import_name( + name_span.clone().unwrap().span(), + pattern.message.clone(), + name_span.unwrap().name(), + source, + ) + } + NameSpanAllowedResult::Allowed => panic!("unreachable"), + }; + + found_errors.push(diagnostic); } GlobResult::None => (), }; @@ -743,13 +787,13 @@ impl NoRestrictedImports { if pattern.get_regex_result(module_request) { let span = module_request.span(); - ctx.diagnostic(diagnostic_path(span, pattern.message.clone(), source)); + ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); } } if !whitelist_found && !found_errors.is_empty() { - for (span, pattern) in found_errors { - ctx.diagnostic(diagnostic_path(span, pattern.message.clone(), source)); + for diagnostic in found_errors { + ctx.diagnostic(diagnostic); } } } diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 3305d29d09a34..186dcaa389726 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -157,9 +157,9 @@ snapshot_kind: text help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:8] + ╭─[no_restricted_imports.tsx:1:30] 1 │ import DisallowedObject from "foo"; - · ──────────────── + · ───── ╰──── help: Remove the import statement. @@ -248,9 +248,9 @@ snapshot_kind: text help: Remove the import statement. ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. - ╭─[no_restricted_imports.tsx:1:8] + ╭─[no_restricted_imports.tsx:1:69] 1 │ import DisallowedObject, { AllowedObject as AllowedObjectTwo } from "foo"; - · ──────────────── + · ───── ╰──── help: Remove the import statement. @@ -429,17 +429,17 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:8] + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:26] 1 │ import foo, { bar } from 'mod'; - · ─── + · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:8] + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:27] 1 │ import foo, * as bar from 'mod'; - · ─── + · ───── ╰──── help: Remove the import statement. @@ -485,10 +485,10 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:8] + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ╭─[no_restricted_imports.tsx:1:37] 1 │ import foo, { default as bar } from 'mod'; - · ─── + · ───── ╰──── help: Remove the import statement. @@ -541,24 +541,24 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Import from @/utils instead. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Import from @/utils instead. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. @@ -597,59 +597,59 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:28] + ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo as Bar } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import Foo, { Bar } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:24] + ⚠ eslint(no-restricted-imports): 'FooBar' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { FooBar } from '../../my/relative-module'; - · ────────────────────────── + · ────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import Foo, { Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. @@ -681,77 +681,77 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; - · ────────────────────────── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:28] + ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo as Bar } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ export * from 'foo'; · ───── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ export { Bar } from 'foo'; · ───── From 0152d1061b2d1cc3b5a7ebbf02b9a4007edfbed3 Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 16:45:42 +0100 Subject: [PATCH 10/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 232 ++++++++++++------ .../eslint_no_restricted_imports.snap | 30 +-- 2 files changed, 166 insertions(+), 96 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index e17c7d28ecd86..fd14930c21c39 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -44,7 +44,7 @@ fn diagnostic_pattern_and_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn _diagnostic_pattern_and_everything( +fn diagnostic_pattern_and_everything( span: Span, message: Option, name: &str, @@ -57,7 +57,7 @@ fn _diagnostic_pattern_and_everything( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn _diagnostic_pattern_and_everything_with_regex_import_name( +fn diagnostic_pattern_and_everything_with_regex_import_name( span: Span, message: Option, name: &str, @@ -70,7 +70,7 @@ fn _diagnostic_pattern_and_everything_with_regex_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn _diagnostic_everything( +fn diagnostic_everything( span: Span, message: Option, name: &str, @@ -98,7 +98,7 @@ fn diagnostic_import_name( OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) } -fn _diagnostic_allowed_import_name( +fn diagnostic_allowed_import_name( span: Span, message: Option, name: &str, @@ -326,7 +326,7 @@ fn add_configuration_patterns_from_string(paths: &mut Vec, mo enum NameSpanAllowedResult { Allowed, GeneralDisallowed, - NameDisallowed(Option), + NameDisallowed, } fn is_name_span_allowed_in_path(name: &CompactStr, path: &RestrictedPath) -> NameSpanAllowedResult { @@ -335,14 +335,18 @@ fn is_name_span_allowed_in_path(name: &CompactStr, path: &RestrictedPath) -> Nam return NameSpanAllowedResult::Allowed; } - // when no importNames option is provided, no import in general is allowed if path.import_names.as_ref().is_none() { + // when no importNames and no allowImportNames option is provided, no import in general is allowed + if path.allow_import_names.is_some() { + return NameSpanAllowedResult::NameDisallowed; + } + return NameSpanAllowedResult::GeneralDisallowed; } // the name is found is the importNames list if path.import_names.as_ref().is_some_and(|disallowed| disallowed.contains(name)) { - return NameSpanAllowedResult::NameDisallowed(None); + return NameSpanAllowedResult::NameDisallowed; } // we allow it @@ -365,103 +369,130 @@ fn is_name_span_allowed_in_pattern( // when no importNames or importNamePattern option is provided, no import in general is allowed if pattern.import_names.as_ref().is_none() && pattern.import_name_pattern.is_none() { + if pattern.allow_import_names.is_some() || pattern.allow_import_name_pattern.is_some() { + return NameSpanAllowedResult::NameDisallowed; + } + return NameSpanAllowedResult::GeneralDisallowed; } // the name is found is the importNames list if pattern.import_names.as_ref().is_some_and(|disallowed| disallowed.contains(name)) { - return NameSpanAllowedResult::NameDisallowed(None); + return NameSpanAllowedResult::NameDisallowed; } // the name is found is the importNamePattern if pattern.get_import_name_pattern_result(name) { - return NameSpanAllowedResult::NameDisallowed(None); + return NameSpanAllowedResult::NameDisallowed; } // we allow it NameSpanAllowedResult::Allowed } +#[derive(PartialEq, Debug)] +enum IsSkipAbleResult { + Allowed, + GeneralDisallowed(NameSpan), + GeneralDisallowedWithoutSpan, + NameDisallowed(NameSpan), +} + impl RestrictedPath { - fn is_skip_able_import(&self, name: &ImportImportName) -> NameSpanAllowedResult { + fn is_skip_able_import(&self, name: &ImportImportName) -> IsSkipAbleResult { match &name { ImportImportName::Name(import) => { match is_name_span_allowed_in_path(&import.name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(import.clone()) + } + NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(import.clone()) } - x => x, + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::Default(_) => { + ImportImportName::Default(span) => { let name = CompactStr::new("default"); + match is_name_span_allowed_in_path(&name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::GeneralDisallowed + NameSpanAllowedResult::NameDisallowed + | NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(NameSpan::new(name, *span)) } - x => x, + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::NamespaceObject => NameSpanAllowedResult::GeneralDisallowed, + ImportImportName::NamespaceObject => IsSkipAbleResult::GeneralDisallowedWithoutSpan, } } - fn is_skip_able_export(&self, name: &ExportImportName) -> NameSpanAllowedResult { + fn is_skip_able_export(&self, name: &ExportImportName) -> IsSkipAbleResult { match &name { ExportImportName::Name(import) => { match is_name_span_allowed_in_path(&import.name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(import.clone()) + } + NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(import.clone()) } - x => x, + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } ExportImportName::All | ExportImportName::AllButDefault => { - NameSpanAllowedResult::GeneralDisallowed + IsSkipAbleResult::GeneralDisallowedWithoutSpan } - ExportImportName::Null => NameSpanAllowedResult::Allowed, + ExportImportName::Null => IsSkipAbleResult::Allowed, } } } impl RestrictedPattern { - fn is_skip_able_import(&self, name: &ImportImportName) -> NameSpanAllowedResult { + fn is_skip_able_import(&self, name: &ImportImportName) -> IsSkipAbleResult { match &name { ImportImportName::Name(import) => { match is_name_span_allowed_in_pattern(&import.name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(import.clone()) } - x => x, + NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(import.clone()) + } + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::Default(_) => { - let name = CompactStr::new("default"); + ImportImportName::Default(span) => { + let name: CompactStr = CompactStr::new("default"); match is_name_span_allowed_in_pattern(&name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::GeneralDisallowed + NameSpanAllowedResult::NameDisallowed + | NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(NameSpan::new(name, *span)) } - x => x, + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::NamespaceObject => NameSpanAllowedResult::GeneralDisallowed, + ImportImportName::NamespaceObject => IsSkipAbleResult::GeneralDisallowedWithoutSpan, } } - fn is_skip_able_export(&self, name: &ExportImportName) -> NameSpanAllowedResult { + fn is_skip_able_export(&self, name: &ExportImportName) -> IsSkipAbleResult { match &name { ExportImportName::Name(import) => { match is_name_span_allowed_in_pattern(&import.name, self) { - NameSpanAllowedResult::NameDisallowed(_) => { - NameSpanAllowedResult::NameDisallowed(Some(import.clone())) + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(import.clone()) } - x => x, + NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(import.clone()) + } + NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } ExportImportName::All | ExportImportName::AllButDefault => { - NameSpanAllowedResult::GeneralDisallowed + IsSkipAbleResult::GeneralDisallowedWithoutSpan } - ExportImportName::Null => NameSpanAllowedResult::Allowed, + ExportImportName::Null => IsSkipAbleResult::Allowed, } } @@ -645,20 +676,33 @@ impl NoRestrictedImports { } match path.is_skip_able_import(&entry.import_name) { - NameSpanAllowedResult::GeneralDisallowed => { - let span = entry.module_request.span(); - - ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)); - } - NameSpanAllowedResult::NameDisallowed(name_span) => { - ctx.diagnostic(diagnostic_import_name( - name_span.clone().unwrap().span(), + IsSkipAbleResult::GeneralDisallowed(_) + | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + ctx.diagnostic(diagnostic_path( + entry.module_request.span(), path.message.clone(), - name_span.unwrap().name(), source, )); } - NameSpanAllowedResult::Allowed => (), + IsSkipAbleResult::NameDisallowed(name_span) => { + if let Some(import_names) = &path.allow_import_names { + ctx.diagnostic(diagnostic_allowed_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + import_names.join(", ").as_str(), + )); + } else { + ctx.diagnostic(diagnostic_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + )); + }; + } + IsSkipAbleResult::Allowed => (), } } @@ -668,7 +712,7 @@ impl NoRestrictedImports { for pattern in &self.patterns { let result = pattern.is_skip_able_import(&entry.import_name); - if result == NameSpanAllowedResult::Allowed { + if result == IsSkipAbleResult::Allowed { continue; } @@ -678,21 +722,36 @@ impl NoRestrictedImports { break; } GlobResult::Found => { - let span = entry.module_request.span(); + let diagnostic: OxcDiagnostic = match result { + IsSkipAbleResult::GeneralDisallowed(_) + | IsSkipAbleResult::GeneralDisallowedWithoutSpan => diagnostic_pattern( + entry.module_request.span(), + pattern.message.clone(), + source, + ), + IsSkipAbleResult::NameDisallowed(name_span) => { + let diagnostic = if let Some(allow_import_name_pattern) = + &pattern.allow_import_name_pattern + { + diagnostic_allowed_import_name_pattern( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + allow_import_name_pattern.as_str(), + ) + } else { + diagnostic_pattern_and_import_name( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + ) + }; - let diagnostic = match result { - NameSpanAllowedResult::GeneralDisallowed => { - diagnostic_pattern(span, pattern.message.clone(), source) - } - NameSpanAllowedResult::NameDisallowed(name_span) => { - diagnostic_pattern_and_import_name( - name_span.clone().unwrap().span(), - pattern.message.clone(), - name_span.unwrap().name(), - source, - ) + diagnostic } - NameSpanAllowedResult::Allowed => panic!("unreachable"), + IsSkipAbleResult::Allowed => panic!("unreachable"), }; found_errors.push(diagnostic); @@ -726,19 +785,29 @@ impl NoRestrictedImports { } match path.is_skip_able_export(&entry.import_name) { - NameSpanAllowedResult::GeneralDisallowed => { - let span = entry.span; - ctx.diagnostic(diagnostic_path(span, path.message.clone(), source)); + IsSkipAbleResult::GeneralDisallowed(_) + | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + ctx.diagnostic(diagnostic_path(entry.span, path.message.clone(), source)); } - NameSpanAllowedResult::NameDisallowed(name_span) => { - ctx.diagnostic(diagnostic_import_name( - name_span.clone().unwrap().span(), - path.message.clone(), - name_span.unwrap().name(), - source, - )); + IsSkipAbleResult::NameDisallowed(name_span) => { + if let Some(allow_import_names) = &path.allow_import_names { + ctx.diagnostic(diagnostic_allowed_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + allow_import_names.join(", ").as_str(), + )); + } else { + ctx.diagnostic(diagnostic_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + )); + }; } - NameSpanAllowedResult::Allowed => (), + IsSkipAbleResult::Allowed => (), } } @@ -748,7 +817,7 @@ impl NoRestrictedImports { for pattern in &self.patterns { let result = pattern.is_skip_able_export(&entry.import_name); - if result == NameSpanAllowedResult::Allowed { + if result == IsSkipAbleResult::Allowed { continue; } @@ -765,18 +834,19 @@ impl NoRestrictedImports { let span = module_request.span(); let diagnostic = match result { - NameSpanAllowedResult::GeneralDisallowed => { + IsSkipAbleResult::GeneralDisallowed(_) + | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { diagnostic_pattern(span, pattern.message.clone(), source) } - NameSpanAllowedResult::NameDisallowed(name_span) => { + IsSkipAbleResult::NameDisallowed(name_span) => { diagnostic_pattern_and_import_name( - name_span.clone().unwrap().span(), + name_span.clone().span(), pattern.message.clone(), - name_span.unwrap().name(), + name_span.name(), source, ) } - NameSpanAllowedResult::Allowed => panic!("unreachable"), + IsSkipAbleResult::Allowed => panic!("unreachable"), }; found_errors.push(diagnostic); diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 186dcaa389726..81a0de8b118e2 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -751,45 +751,45 @@ snapshot_kind: text ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo". - ╭─[no_restricted_imports.tsx:1:21] + ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; - · ───── + · ─── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:49] + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted because only AllowedObject import(s) is/are allowed. + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". - ╭─[no_restricted_imports.tsx:1:49] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:49] + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". - ╭─[no_restricted_imports.tsx:1:49] + ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; - · ───── + · ──────────────── ╰──── help: Remove the import statement. From b6b185a3ededbb892b7ff72774b4fea6877a6fc3 Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 17:09:29 +0100 Subject: [PATCH 11/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 487 ++---------------- .../eslint_no_restricted_imports.snap | 292 ++++------- 2 files changed, 158 insertions(+), 621 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index fd14930c21c39..0965cbd747d5a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -15,18 +15,25 @@ use crate::{ }; fn diagnostic_path(span: Span, message: Option, source: &str) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{source}' import is restricted from being used.")) - }); + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{source}' import is restricted from being used.")) + .with_help(message) + .with_label(span); + } - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + OxcDiagnostic::warn(format!("'{source}' import is restricted from being used.")) + .with_label(span) } fn diagnostic_pattern(span: Span, message: Option, source: &str) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{source}' import is restricted from being used by a pattern.")) - }); - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{source}' import is restricted from being used by a pattern.")) + .with_help(message) + .with_label(span); + } + + OxcDiagnostic::warn(format!("'{source}' import is restricted from being used by a pattern.")) + .with_label(span) } fn diagnostic_pattern_and_import_name( @@ -35,13 +42,14 @@ fn diagnostic_pattern_and_import_name( name: &str, source: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!( - "'{name}' import from '{source}' is restricted from being used by a pattern." - )) - }); - - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted from being used by a pattern.")) + .with_help(message) + .with_label(span); + } + + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted from being used by a pattern.")) + .with_label(span) } fn diagnostic_pattern_and_everything( @@ -91,11 +99,14 @@ fn diagnostic_import_name( name: &str, source: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{name}' import from '{source}' is restricted.")) - }); - - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")) + .with_help(message) + .with_label(span); + } + + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")) + .with_label(span) } fn diagnostic_allowed_import_name( @@ -105,11 +116,14 @@ fn diagnostic_allowed_import_name( source: &str, allowed: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) - }); - - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) + .with_help(message) + .with_label(span); + } + + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) + .with_label(span) } fn diagnostic_everything_with_allowed_import_name( @@ -134,11 +148,14 @@ fn diagnostic_allowed_import_name_pattern( source: &str, allowed_pattern: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) - }); - - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + if let Some(message) = message { + return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) + .with_help(message) + .with_label(span); + } + + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) + .with_label(span) } fn diagnostic_everything_with_allowed_import_name_pattern( @@ -881,415 +898,7 @@ fn test() { }] }]); - let pass = vec![ - (r#"import os from "os";"#, None), - (r#"import os from "os";"#, Some(serde_json::json!(["osx"]))), - (r#"import fs from "fs";"#, Some(serde_json::json!(["crypto"]))), - (r#"import path from "path";"#, Some(serde_json::json!(["crypto", "stream", "os"]))), - (r#"import async from "async";"#, None), - (r#"import "foo""#, Some(serde_json::json!(["crypto"]))), - (r#"import "foo/bar";"#, Some(serde_json::json!(["foo"]))), - ( - r#"import withPaths from "foo/bar";"#, - Some(serde_json::json!([{ "paths": ["foo", "bar"] }])), - ), - ( - r#"import withPatterns from "foo/bar";"#, - Some(serde_json::json!([{ "patterns": ["foo/c*"] }])), - ), - ("import foo from 'foo';", Some(serde_json::json!(["../foo"]))), - ("import foo from 'foo';", Some(serde_json::json!([{ "paths": ["../foo"] }]))), - ("import foo from 'foo';", Some(serde_json::json!([{ "patterns": ["../foo"] }]))), - ("import foo from 'foo';", Some(serde_json::json!(["/foo"]))), - ("import foo from 'foo';", Some(serde_json::json!([{ "paths": ["/foo"] }]))), - ("import relative from '../foo';", None), - ("import relative from '../foo';", Some(serde_json::json!(["../notFoo"]))), - ( - "import relativeWithPaths from '../foo';", - Some(serde_json::json!([{ "paths": ["../notFoo"] }])), - ), - ( - "import relativeWithPatterns from '../foo';", - Some(serde_json::json!([{ "patterns": ["notFoo"] }])), - ), - ("import absolute from '/foo';", None), - ("import absolute from '/foo';", Some(serde_json::json!(["/notFoo"]))), - ( - "import absoluteWithPaths from '/foo';", - Some(serde_json::json!([{ "paths": ["/notFoo"] }])), - ), - ( - "import absoluteWithPatterns from '/foo';", - Some(serde_json::json!([{ "patterns": ["notFoo"] }])), - ), - ( - r#"import withPatternsAndPaths from "foo/bar";"#, - Some(serde_json::json!([{ "paths": ["foo"], "patterns": ["foo/c*"] }])), - ), - ( - r#"import withGitignores from "foo/bar";"#, - Some(serde_json::json!([{ "patterns": ["foo/*", "!foo/bar"] }])), - ), - ( - r#"import withPatterns from "foo/bar";"#, - Some( - serde_json::json!([{ "patterns": [{ "group": ["foo/*", "!foo/bar"], "message": "foo is forbidden, use bar instead" }] }]), - ), - ), - ( - "import withPatternsCaseSensitive from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["FOO"], - "message": "foo is forbidden, use bar instead", - "caseSensitive": true - }] - }])), - ), - ( - r#"import AllowedObject from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject"] - }] - }])), - ), - ( - r#"import DisallowedObject from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject"] - }] - }])), - ), - ( - r#"import * as DisallowedObject from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "bar", - "importNames": ["DisallowedObject"], - "message": r#"Please import "DisallowedObject" from /bar/ instead."# - }] - }])), - ), - ( - r#"import { AllowedObject } from "foo";"#, - Some(serde_json::json!(pass_disallowed_object_foo.clone())), - ), - ( - r#"import { 'AllowedObject' as bar } from "foo";"#, - Some(serde_json::json!(pass_disallowed_object_foo.clone())), - ), - ( - r#"import { ' ' as bar } from "foo";"#, - Some(serde_json::json!([{"paths": [{"name": "foo","importNames": [""]}]}])), - ), - ( - r#"import { '' as bar } from "foo";"#, - Some(serde_json::json!([{"paths": [{"name": "foo","importNames": [" "]}]}])), - ), - ( - r#"import { DisallowedObject } from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "bar", - "importNames": ["DisallowedObject"], - "message": r#"Please import "DisallowedObject" from /bar/ instead."# - }] - }])), - ), - ( - r#"import { AllowedObject as DisallowedObject } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import { 'AllowedObject' as DisallowedObject } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import { AllowedObject, AllowedObjectTwo } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import { AllowedObject, AllowedObjectTwo as DisallowedObject } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import AllowedObjectThree, { AllowedObject as AllowedObjectTwo } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import AllowedObject, { AllowedObjectTwo as DisallowedObject } from "foo";"#, - Some(pass_disallowed_object_foo.clone()), - ), - ( - r#"import AllowedObject, { AllowedObjectTwo as DisallowedObject } from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject", "DisallowedObjectTwo"], - "message": r#"Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead."# - }] - }])), - ), - ( - r#"import AllowedObject, * as DisallowedObject from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "bar", - "importNames": ["DisallowedObject"], - "message": r#"Please import "DisallowedObject" from /bar/ instead."# - }] - }])), - ), - ( - r#"import "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject", "DisallowedObjectTwo"], - "message": r#"Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead."# - }] - }])), - ), - // ( - // r#"import { - // AllowedObject, - // DisallowedObject, // eslint-disable-line - // } from "foo";"#, - // Some( - // serde_json::json!([{ "paths": [{ "name": "foo", "importNames": ["DisallowedObject"] }] }]), - // ), - // ), - (r#"export * from "foo";"#, Some(serde_json::json!(["bar"]))), - ( - r#"export * from "foo";"#, - Some(serde_json::json!([{ - "name": "bar", - "importNames": ["DisallowedObject"] - }])), - ), - ( - r#"export { 'AllowedObject' } from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject"] - }] - }])), - ), - ( - r#"export { 'AllowedObject' as DisallowedObject } from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "importNames": ["DisallowedObject"] - }] - }])), - ), - ( - "import { Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNames": ["Foo"] - }] - }])), - ), - ( - "import Foo from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNames": ["Foo"] - }] - }])), - ), - ( - "import Foo from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import Foo from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "importNames": ["Foo"], - "group": ["foo"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import Foo from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import { Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import { Bar as Foo } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import { Bar as Foo } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "importNames": ["Foo"], - "group": ["**/my/relative-module"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import Foo, { Baz as Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["**/my/relative-module"], - "importNamePattern": "^(Foo|Bar)" - }] - }])), - ), - ( - "import Foo, { Baz as Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "importNames": ["Foo"], - "group": ["**/my/relative-module"], - "importNamePattern": "^Bar" - }] - }])), - ), - ( - "export { Bar } from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "export { Bar as Foo } from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "importNamePattern": "^Foo" - }] - }])), - ), - ( - r#"import { AllowedObject } from "foo";"#, - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "allowImportNames": ["AllowedObject"], - "message": r#"Please import anything except "AllowedObject" from /bar/ instead."# - }] - }])), - ), - ( - "import { foo } from 'foo';", - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "allowImportNames": ["foo"] - }] - }])), - ), - ( - "import { foo } from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "allowImportNames": ["foo"] - }] - }])), - ), - ( - "export { bar } from 'foo';", - Some(serde_json::json!([{ - "paths": [{ - "name": "foo", - "allowImportNames": ["bar"] - }] - }])), - ), - ( - "export { bar } from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "allowImportNames": ["bar"] - }] - }])), - ), - ( - "import { Foo } from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "group": ["foo"], - "allowImportNamePattern": "^Foo" - }] - }])), - ), - ( - r#"import withPatterns from "foo/bar";"#, - Some( - serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), - ), - ), - ( - "import withPatternsCaseSensitive from 'foo';", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "FOO", - "message": "foo is forbidden, use bar instead", - "caseSensitive": true - }] - }])), - ), - ( - "import Foo from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "my/relative-module", - "importNamePattern": "^Foo" - }] - }])), - ), - ( - "import { Bar } from '../../my/relative-module';", - Some(serde_json::json!([{ - "patterns": [{ - "regex": "my/relative-module", - "importNamePattern": "^Foo" - }] - }])), - ), - ]; + let pass = vec![]; let fail = vec![ (r#"import "fs""#, Some(serde_json::json!(["fs"]))), diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 81a0de8b118e2..48c2ba5a93fad 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -7,861 +7,793 @@ snapshot_kind: text 1 │ import "fs" · ──── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'os' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:16] 1 │ import os from "os"; · ──── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:8] 1 │ import "foo/bar"; · ───────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:23] 1 │ import withPaths from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): foo is forbidden, use foo/bar instead + ⚠ eslint(no-restricted-imports): 'foo/baz' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/baz"; · ───────── ╰──── - help: Remove the import statement. + help: foo is forbidden, use foo/bar instead - ⚠ eslint(no-restricted-imports): some foo subimports are restricted + ⚠ eslint(no-restricted-imports): 'foo/baz' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/baz"; · ───────── ╰──── - help: Remove the import statement. + help: some foo subimports are restricted ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:41] 1 │ import withPatternsCaseInsensitive from 'foo'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:28] 1 │ import withGitignores from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from "fs"; · ─────────────────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * as ns from "fs"; · ───────────────────────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {a} from "fs"; · ─ ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Don"t import "foo". + ⚠ eslint(no-restricted-imports): 'foo' import from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {foo as b} from "fs"; · ─── ╰──── - help: Remove the import statement. + help: Don"t import "foo". - ⚠ eslint(no-restricted-imports): Don"t import "foo". + ⚠ eslint(no-restricted-imports): 'foo' import from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {"foo" as b} from "fs"; · ───── ╰──── - help: Remove the import statement. + help: Don"t import "foo". - ⚠ eslint(no-restricted-imports): Don"t import "foo". + ⚠ eslint(no-restricted-imports): 'foo' import from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {"foo"} from "fs"; · ───── ╰──── - help: Remove the import statement. + help: Don"t import "foo". - ⚠ eslint(no-restricted-imports): Don"t import "👍". + ⚠ eslint(no-restricted-imports): '👍' import from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {'👍'} from "fs"; · ──── ╰──── - help: Remove the import statement. + help: Don"t import "👍". - ⚠ eslint(no-restricted-imports): Don"t import "". + ⚠ eslint(no-restricted-imports): '' import from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:9] 1 │ export {''} from "fs"; · ── ╰──── - help: Remove the import statement. + help: Don"t import "". - ⚠ eslint(no-restricted-imports): Don"t import "foo". + ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * as ns from "fs"; · ───────────────────────── ╰──── - help: Remove the import statement. + help: Don"t import "foo". - ⚠ eslint(no-restricted-imports): Please import from "bar" instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:28] 1 │ import withGitignores from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import from "bar" instead. - ⚠ eslint(no-restricted-imports): Please import from "baz" instead. + ⚠ eslint(no-restricted-imports): 'bar' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:28] 1 │ import withGitignores from "bar"; · ───── ╰──── - help: Remove the import statement. + help: Please import from "baz" instead. - ⚠ eslint(no-restricted-imports): Please import from "bar" instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:28] 1 │ import withGitignores from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import from "bar" instead. - ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:30] 1 │ import DisallowedObject from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import the default import of "foo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from "foo"; · ──────────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from "foo"; · ──────────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObject as AllowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { 'DisallowedObject' as AllowedObject } from "foo"; · ────────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. ⚠ eslint(no-restricted-imports): '👍' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { '👍' as bar } from "foo"; · ──── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { '' as bar } from "foo"; · ── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as AllowedObjectTwo } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import the default import of "foo" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:69] 1 │ import DisallowedObject, { AllowedObject as AllowedObjectTwo } from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import the default import of "foo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import AllowedObject, { DisallowedObject as AllowedObjectTwo } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:50] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:50] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. ⚠ eslint(no-restricted-imports): 'DisallowedObjectOne' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; · ─────────────────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'DisallowedObjectTwo' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:31] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; · ─────────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Please import this module from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObjectOne' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; · ─────────────────── ╰──── - help: Remove the import statement. + help: Please import this module from /bar/ instead. - ⚠ eslint(no-restricted-imports): Please import this module from /bar/ instead. + ⚠ eslint(no-restricted-imports): 'DisallowedObjectTwo' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:31] 1 │ import { DisallowedObjectOne, DisallowedObjectTwo, AllowedObject } from "foo"; · ─────────────────── ╰──── - help: Remove the import statement. + help: Please import this module from /bar/ instead. ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject as Bar } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:15] 1 │ import foo, { bar } from 'mod'; · ─── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): import Image from ui/_components instead + ⚠ eslint(no-restricted-imports): 'Image' import from 'react-native' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Image, Text, ScrollView } from 'react-native' · ───── ╰──── - help: Remove the import statement. + help: import Image from ui/_components instead - ⚠ eslint(no-restricted-imports): import Text from ui/_components instead + ⚠ eslint(no-restricted-imports): 'Text' import from 'react-native' is restricted. ╭─[no_restricted_imports.tsx:1:17] 1 │ import { Image, Text, ScrollView } from 'react-native' · ──── ╰──── - help: Remove the import statement. + help: import Text from ui/_components instead - ⚠ eslint(no-restricted-imports): import ScrollView from ui/_components instead + ⚠ eslint(no-restricted-imports): 'ScrollView' import from 'react-native' is restricted. ╭─[no_restricted_imports.tsx:1:23] 1 │ import { Image, Text, ScrollView } from 'react-native' · ────────── ╰──── - help: Remove the import statement. + help: import ScrollView from ui/_components instead - ⚠ eslint(no-restricted-imports): Import foo from qux instead. + ⚠ eslint(no-restricted-imports): 'foo' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Import foo from qux instead. - ⚠ eslint(no-restricted-imports): Import baz from qux instead. + ⚠ eslint(no-restricted-imports): 'baz' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:20] 1 │ import { foo, bar, baz } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Import baz from qux instead. - ⚠ eslint(no-restricted-imports): Don"t use "foo" and `qux` from "mod". + ⚠ eslint(no-restricted-imports): 'foo' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Don"t use "foo" and `qux` from "mod". - ⚠ eslint(no-restricted-imports): Use `barbaz` instead of `bar`. + ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:15] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Use `barbaz` instead of `bar`. - ⚠ eslint(no-restricted-imports): Don"t use "foo" and `qux` from "mod". + ⚠ eslint(no-restricted-imports): 'qux' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Don"t use "foo" and `qux` from "mod". - ⚠ eslint(no-restricted-imports): Don"t use "foo" or "baz" from "mod". + ⚠ eslint(no-restricted-imports): 'foo' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Don"t use "foo" or "baz" from "mod". - ⚠ eslint(no-restricted-imports): Use "b" or `bar` from "quux/mod" instead. + ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:15] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Use "b" or `bar` from "quux/mod" instead. - ⚠ eslint(no-restricted-imports): Don"t use "foo" or "baz" from "mod". + ⚠ eslint(no-restricted-imports): 'baz' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:20] 1 │ import { foo, bar, baz, qux } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Don"t use "foo" or "baz" from "mod". - ⚠ eslint(no-restricted-imports): Import foo from qux instead. + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as mod from 'mod' · ───── ╰──── - help: Remove the import statement. + help: Import foo from qux instead. - ⚠ eslint(no-restricted-imports): Import bar from qux instead. + ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as mod from 'mod' · ───── ╰──── - help: Remove the import statement. + help: Import bar from qux instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { foo } from 'mod' · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { bar } from 'mod' · ───── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Import bar from qux instead. + ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { bar } from 'mod' · ─── ╰──── - help: Remove the import statement. + help: Import bar from qux instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:26] 1 │ import foo, { bar } from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:27] 1 │ import foo, * as bar from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:27] 1 │ import foo, * as bar from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as bar from 'foo'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'a' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { a, a as b } from 'mod'; · ─ ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'a' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:13] 1 │ import { a, a as b } from 'mod'; · ─ ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'x' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { x as y, x as z } from 'mod'; · ─ ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'x' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:18] 1 │ export { x as y, x as z } from 'mod'; · ─ ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:37] 1 │ import foo, { default as bar } from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:15] 1 │ import foo, { default as bar } from 'mod'; · ─────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import relative from '../foo'; · ──────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:31] 1 │ import relativeWithPaths from '../foo'; · ──────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:34] 1 │ import relativeWithPatterns from '../foo'; · ──────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import absolute from '/foo'; · ────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:31] 1 │ import absoluteWithPaths from '/foo'; · ────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:34] 1 │ import absoluteWithPatterns from '/foo'; · ────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. + help: Import from @/utils instead. - ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. + help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. + help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import def, * as ns from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import def, * as ns from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:17] 1 │ import Foo from 'mod'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo as Bar } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ import Foo, { Bar } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'FooBar' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { FooBar } from '../../my/relative-module'; · ────── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ import Foo, { Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as Foo from 'foo'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. + help: Import from @/utils instead. - ⚠ eslint(no-restricted-imports): Import from @/utils instead. + ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. + help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Bar' import from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ import { Foo, Bar } from '../../my/relative-module'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo as Bar } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Foo } from 'foo'; · ─── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:15] 1 │ export * from 'foo'; · ───── ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; · ─── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; · ─── ╰──── - help: Remove the import statement. + help: Only imports that match the pattern "/^Foo/u" are allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted because only AllowedObject import(s) is/are allowed. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted because only AllowedObject import(s) is/are allowed. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - help: Remove the import statement. + help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo"; · ───── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo"; · ───── ╰──── - help: Remove the import statement. + help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. + help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): Only import names starting with "Allow" are allowed to be imported from "foo". + ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - help: Remove the import statement. + help: Only import names starting with "Allow" are allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead + ⚠ eslint(no-restricted-imports): 'foo/baz' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import withPatterns from "foo/baz"; · ───────── ╰──── - help: Remove the import statement. + help: foo is forbidden, use bar instead - ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead + ⚠ eslint(no-restricted-imports): 'FOO' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'FOO'; · ───── ╰──── - help: Remove the import statement. + help: foo is forbidden, use bar instead ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:21] 1 │ import { Foo } from '../../my/relative-module'; · ────────────────────────── ╰──── - help: Remove the import statement. - ⚠ eslint(no-restricted-imports): foo is forbidden, use bar instead + ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'foo'; · ───── ╰──── - help: Remove the import statement. + help: foo is forbidden, use bar instead ⚠ eslint(no-restricted-imports): '@app/api' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:3:43] @@ -870,7 +802,6 @@ snapshot_kind: text · ────────── 4 │ import { Bar_Enum } from '@app/api/bar'; ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '@app/api/bar' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:4:43] @@ -879,7 +810,6 @@ snapshot_kind: text · ────────────── 5 │ import { Baz_Enum } from '@app/api/baz'; ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '@app/api/baz' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:5:43] @@ -888,7 +818,6 @@ snapshot_kind: text · ────────────── 6 │ import { B_Enum } from '@app/api/enums/foo'; ╰──── - help: Remove the import statement. ⚠ eslint(no-restricted-imports): '@app/api/enums/foo' import is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:6:41] @@ -897,4 +826,3 @@ snapshot_kind: text · ──────────────────── 7 │ ╰──── - help: Remove the import statement. From e069fcbb03fcc47b1df261c0033d98b37d68dffb Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 17:21:40 +0100 Subject: [PATCH 12/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 477 +++++++++++++++++- .../eslint_no_restricted_imports.snap | 12 +- 2 files changed, 461 insertions(+), 28 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 0965cbd747d5a..03919b8fde7d6 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -27,11 +27,13 @@ fn diagnostic_path(span: Span, message: Option, source: &str) -> Oxc fn diagnostic_pattern(span: Span, message: Option, source: &str) -> OxcDiagnostic { if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{source}' import is restricted from being used by a pattern.")) - .with_help(message) - .with_label(span); + return OxcDiagnostic::warn(format!( + "'{source}' import is restricted from being used by a pattern." + )) + .with_help(message) + .with_label(span); } - + OxcDiagnostic::warn(format!("'{source}' import is restricted from being used by a pattern.")) .with_label(span) } @@ -43,13 +45,17 @@ fn diagnostic_pattern_and_import_name( source: &str, ) -> OxcDiagnostic { if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted from being used by a pattern.")) - .with_help(message) - .with_label(span); + return OxcDiagnostic::warn(format!( + "'{name}' import from '{source}' is restricted from being used by a pattern." + )) + .with_help(message) + .with_label(span); } - - OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted from being used by a pattern.")) - .with_label(span) + + OxcDiagnostic::warn(format!( + "'{name}' import from '{source}' is restricted from being used by a pattern." + )) + .with_label(span) } fn diagnostic_pattern_and_everything( @@ -84,13 +90,18 @@ fn diagnostic_everything( name: &str, source: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!( + if let Some(message) = message { + return OxcDiagnostic::warn(format!( "* import is invalid because '{name}' from '{source}' is restricted." )) - }); + .with_help(message) + .with_label(span); + } - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + OxcDiagnostic::warn(format!( + "* import is invalid because '{name}' from '{source}' is restricted." + )) + .with_label(span) } fn diagnostic_import_name( @@ -104,9 +115,8 @@ fn diagnostic_import_name( .with_help(message) .with_label(span); } - - OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")) - .with_label(span) + + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")).with_label(span) } fn diagnostic_allowed_import_name( @@ -121,7 +131,7 @@ fn diagnostic_allowed_import_name( .with_help(message) .with_label(span); } - + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) .with_label(span) } @@ -153,7 +163,7 @@ fn diagnostic_allowed_import_name_pattern( .with_help(message) .with_label(span); } - + OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) .with_label(span) } @@ -693,14 +703,29 @@ impl NoRestrictedImports { } match path.is_skip_able_import(&entry.import_name) { - IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + IsSkipAbleResult::GeneralDisallowed(_) => { ctx.diagnostic(diagnostic_path( entry.module_request.span(), path.message.clone(), source, )); } + IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + if let Some(import_names) = &path.import_names { + ctx.diagnostic(diagnostic_everything( + entry.module_request.span(), + path.message.clone(), + import_names.join(", ").as_str(), + source, + )); + } else { + ctx.diagnostic(diagnostic_path( + entry.module_request.span(), + path.message.clone(), + source, + )); + } + } IsSkipAbleResult::NameDisallowed(name_span) => { if let Some(import_names) = &path.allow_import_names { ctx.diagnostic(diagnostic_allowed_import_name( @@ -898,7 +923,415 @@ fn test() { }] }]); - let pass = vec![]; + let pass = vec![ + (r#"import os from "os";"#, None), + (r#"import os from "os";"#, Some(serde_json::json!(["osx"]))), + (r#"import fs from "fs";"#, Some(serde_json::json!(["crypto"]))), + (r#"import path from "path";"#, Some(serde_json::json!(["crypto", "stream", "os"]))), + (r#"import async from "async";"#, None), + (r#"import "foo""#, Some(serde_json::json!(["crypto"]))), + (r#"import "foo/bar";"#, Some(serde_json::json!(["foo"]))), + ( + r#"import withPaths from "foo/bar";"#, + Some(serde_json::json!([{ "paths": ["foo", "bar"] }])), + ), + ( + r#"import withPatterns from "foo/bar";"#, + Some(serde_json::json!([{ "patterns": ["foo/c*"] }])), + ), + ("import foo from 'foo';", Some(serde_json::json!(["../foo"]))), + ("import foo from 'foo';", Some(serde_json::json!([{ "paths": ["../foo"] }]))), + ("import foo from 'foo';", Some(serde_json::json!([{ "patterns": ["../foo"] }]))), + ("import foo from 'foo';", Some(serde_json::json!(["/foo"]))), + ("import foo from 'foo';", Some(serde_json::json!([{ "paths": ["/foo"] }]))), + ("import relative from '../foo';", None), + ("import relative from '../foo';", Some(serde_json::json!(["../notFoo"]))), + ( + "import relativeWithPaths from '../foo';", + Some(serde_json::json!([{ "paths": ["../notFoo"] }])), + ), + ( + "import relativeWithPatterns from '../foo';", + Some(serde_json::json!([{ "patterns": ["notFoo"] }])), + ), + ("import absolute from '/foo';", None), + ("import absolute from '/foo';", Some(serde_json::json!(["/notFoo"]))), + ( + "import absoluteWithPaths from '/foo';", + Some(serde_json::json!([{ "paths": ["/notFoo"] }])), + ), + ( + "import absoluteWithPatterns from '/foo';", + Some(serde_json::json!([{ "patterns": ["notFoo"] }])), + ), + ( + r#"import withPatternsAndPaths from "foo/bar";"#, + Some(serde_json::json!([{ "paths": ["foo"], "patterns": ["foo/c*"] }])), + ), + ( + r#"import withGitignores from "foo/bar";"#, + Some(serde_json::json!([{ "patterns": ["foo/*", "!foo/bar"] }])), + ), + ( + r#"import withPatterns from "foo/bar";"#, + Some( + serde_json::json!([{ "patterns": [{ "group": ["foo/*", "!foo/bar"], "message": "foo is forbidden, use bar instead" }] }]), + ), + ), + ( + "import withPatternsCaseSensitive from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["FOO"], + "message": "foo is forbidden, use bar instead", + "caseSensitive": true + }] + }])), + ), + ( + r#"import AllowedObject from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject"] + }] + }])), + ), + ( + r#"import DisallowedObject from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject"] + }] + }])), + ), + ( + r#"import * as DisallowedObject from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "bar", + "importNames": ["DisallowedObject"], + "message": r#"Please import "DisallowedObject" from /bar/ instead."# + }] + }])), + ), + ( + r#"import { AllowedObject } from "foo";"#, + Some(serde_json::json!(pass_disallowed_object_foo.clone())), + ), + ( + r#"import { 'AllowedObject' as bar } from "foo";"#, + Some(serde_json::json!(pass_disallowed_object_foo.clone())), + ), + ( + r#"import { ' ' as bar } from "foo";"#, + Some(serde_json::json!([{"paths": [{"name": "foo","importNames": [""]}]}])), + ), + ( + r#"import { '' as bar } from "foo";"#, + Some(serde_json::json!([{"paths": [{"name": "foo","importNames": [" "]}]}])), + ), + ( + r#"import { DisallowedObject } from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "bar", + "importNames": ["DisallowedObject"], + "message": r#"Please import "DisallowedObject" from /bar/ instead."# + }] + }])), + ), + ( + r#"import { AllowedObject as DisallowedObject } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import { 'AllowedObject' as DisallowedObject } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import { AllowedObject, AllowedObjectTwo } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import { AllowedObject, AllowedObjectTwo as DisallowedObject } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import AllowedObjectThree, { AllowedObject as AllowedObjectTwo } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import AllowedObject, { AllowedObjectTwo as DisallowedObject } from "foo";"#, + Some(pass_disallowed_object_foo.clone()), + ), + ( + r#"import AllowedObject, { AllowedObjectTwo as DisallowedObject } from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject", "DisallowedObjectTwo"], + "message": r#"Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead."# + }] + }])), + ), + ( + r#"import AllowedObject, * as DisallowedObject from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "bar", + "importNames": ["DisallowedObject"], + "message": r#"Please import "DisallowedObject" from /bar/ instead."# + }] + }])), + ), + ( + r#"import "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject", "DisallowedObjectTwo"], + "message": r#"Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead."# + }] + }])), + ), + // ( + // r#"import { + // AllowedObject, + // DisallowedObject, // eslint-disable-line + // } from "foo";"#, + // Some( + // serde_json::json!([{ "paths": [{ "name": "foo", "importNames": ["DisallowedObject"] }] }]), + // ), + // ), + (r#"export * from "foo";"#, Some(serde_json::json!(["bar"]))), + ( + r#"export * from "foo";"#, + Some(serde_json::json!([{ + "name": "bar", + "importNames": ["DisallowedObject"] + }])), + ), + ( + r#"export { 'AllowedObject' } from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject"] + }] + }])), + ), + ( + r#"export { 'AllowedObject' as DisallowedObject } from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "importNames": ["DisallowedObject"] + }] + }])), + ), + ( + "import { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNames": ["Foo"] + }] + }])), + ), + ( + "import Foo from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNames": ["Foo"] + }] + }])), + ), + ( + "import Foo from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar as Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar as Foo } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import Foo, { Baz as Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["**/my/relative-module"], + "importNamePattern": "^(Foo|Bar)" + }] + }])), + ), + ( + "import Foo, { Baz as Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "importNames": ["Foo"], + "group": ["**/my/relative-module"], + "importNamePattern": "^Bar" + }] + }])), + ), + ( + "export { Bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "export { Bar as Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "importNamePattern": "^Foo" + }] + }])), + ), + ( + r#"import { AllowedObject } from "foo";"#, + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "allowImportNames": ["AllowedObject"], + "message": r#"Please import anything except "AllowedObject" from /bar/ instead."# + }] + }])), + ), + ( + "import { foo } from 'foo';", + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "allowImportNames": ["foo"] + }] + }])), + ), + ( + "import { foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNames": ["foo"] + }] + }])), + ), + ( + "export { bar } from 'foo';", + Some(serde_json::json!([{ + "paths": [{ + "name": "foo", + "allowImportNames": ["bar"] + }] + }])), + ), + ( + "export { bar } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNames": ["bar"] + }] + }])), + ), + ( + "import { Foo } from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "group": ["foo"], + "allowImportNamePattern": "^Foo" + }] + }])), + ), + ( + r#"import withPatterns from "foo/bar";"#, + Some( + serde_json::json!([{ "patterns": [{ "regex": "foo/(?!bar)", "message": "foo is forbidden, use bar instead" }] }]), + ), + ), + ( + "import withPatternsCaseSensitive from 'foo';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "FOO", + "message": "foo is forbidden, use bar instead", + "caseSensitive": true + }] + }])), + ), + ( + "import Foo from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "my/relative-module", + "importNamePattern": "^Foo" + }] + }])), + ), + ( + "import { Bar } from '../../my/relative-module';", + Some(serde_json::json!([{ + "patterns": [{ + "regex": "my/relative-module", + "importNamePattern": "^Foo" + }] + }])), + ), + ]; let fail = vec![ (r#"import "fs""#, Some(serde_json::json!(["fs"]))), diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 48c2ba5a93fad..dd75207cbf373 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -152,7 +152,7 @@ snapshot_kind: text ╰──── help: Please import the default import of "foo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject' from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from "foo"; · ───── @@ -247,14 +247,14 @@ snapshot_kind: text ╰──── help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject' from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:50] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; · ───── ╰──── help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject, DisallowedObjectTwo' from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:50] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; · ───── @@ -376,14 +376,14 @@ snapshot_kind: text ╰──── help: Don"t use "foo" or "baz" from "mod". - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'foo' from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as mod from 'mod' · ───── ╰──── help: Import foo from qux instead. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'bar' from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as mod from 'mod' · ───── @@ -421,7 +421,7 @@ snapshot_kind: text · ───── ╰──── - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted. ╭─[no_restricted_imports.tsx:1:27] 1 │ import foo, * as bar from 'mod'; · ───── From 6567e393a73a927a1b2df2ff74b7c0879478800e Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 17:34:26 +0100 Subject: [PATCH 13/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 58 ++++++++++++++----- .../eslint_no_restricted_imports.snap | 10 ++-- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 03919b8fde7d6..5aaf1239ef7b7 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -142,13 +142,18 @@ fn diagnostic_everything_with_allowed_import_name( source: &str, allowed: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!( + if let Some(message) = message { + return OxcDiagnostic::warn(format!( "* import is invalid because only '{allowed}' from '{source}' is/are allowed." )) - }); + .with_help(message) + .with_label(span); + } - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + OxcDiagnostic::warn(format!( + "'* import is invalid because only '{allowed}' from '{source}' is/are allowed." + )) + .with_label(span) } fn diagnostic_allowed_import_name_pattern( @@ -421,7 +426,7 @@ fn is_name_span_allowed_in_pattern( enum IsSkipAbleResult { Allowed, GeneralDisallowed(NameSpan), - GeneralDisallowedWithoutSpan, + DefaultDisallowed, NameDisallowed(NameSpan), } @@ -450,7 +455,7 @@ impl RestrictedPath { NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::NamespaceObject => IsSkipAbleResult::GeneralDisallowedWithoutSpan, + ImportImportName::NamespaceObject => IsSkipAbleResult::DefaultDisallowed, } } @@ -468,7 +473,7 @@ impl RestrictedPath { } } ExportImportName::All | ExportImportName::AllButDefault => { - IsSkipAbleResult::GeneralDisallowedWithoutSpan + IsSkipAbleResult::DefaultDisallowed } ExportImportName::Null => IsSkipAbleResult::Allowed, } @@ -499,7 +504,7 @@ impl RestrictedPattern { NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, } } - ImportImportName::NamespaceObject => IsSkipAbleResult::GeneralDisallowedWithoutSpan, + ImportImportName::NamespaceObject => IsSkipAbleResult::DefaultDisallowed, } } @@ -517,7 +522,7 @@ impl RestrictedPattern { } } ExportImportName::All | ExportImportName::AllButDefault => { - IsSkipAbleResult::GeneralDisallowedWithoutSpan + IsSkipAbleResult::DefaultDisallowed } ExportImportName::Null => IsSkipAbleResult::Allowed, } @@ -710,7 +715,7 @@ impl NoRestrictedImports { source, )); } - IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + IsSkipAbleResult::DefaultDisallowed => { if let Some(import_names) = &path.import_names { ctx.diagnostic(diagnostic_everything( entry.module_request.span(), @@ -718,6 +723,13 @@ impl NoRestrictedImports { import_names.join(", ").as_str(), source, )); + } else if let Some(allowed_import_names) = &path.allow_import_names { + ctx.diagnostic(diagnostic_everything_with_allowed_import_name( + entry.module_request.span(), + path.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + )); } else { ctx.diagnostic(diagnostic_path( entry.module_request.span(), @@ -766,7 +778,7 @@ impl NoRestrictedImports { GlobResult::Found => { let diagnostic: OxcDiagnostic = match result { IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::GeneralDisallowedWithoutSpan => diagnostic_pattern( + | IsSkipAbleResult::DefaultDisallowed => diagnostic_pattern( entry.module_request.span(), pattern.message.clone(), source, @@ -827,10 +839,28 @@ impl NoRestrictedImports { } match path.is_skip_able_export(&entry.import_name) { - IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + IsSkipAbleResult::GeneralDisallowed(_) => { ctx.diagnostic(diagnostic_path(entry.span, path.message.clone(), source)); } + IsSkipAbleResult::DefaultDisallowed => { + if let Some(import_names) = &path.import_names { + ctx.diagnostic(diagnostic_everything( + entry.span, + path.message.clone(), + import_names.join(", ").as_str(), + source, + )); + } else if let Some(allowed_import_names) = &path.allow_import_names { + ctx.diagnostic(diagnostic_everything_with_allowed_import_name( + entry.span, + path.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + )); + } else { + ctx.diagnostic(diagnostic_path(entry.span, path.message.clone(), source)); + } + } IsSkipAbleResult::NameDisallowed(name_span) => { if let Some(allow_import_names) = &path.allow_import_names { ctx.diagnostic(diagnostic_allowed_import_name( @@ -877,7 +907,7 @@ impl NoRestrictedImports { let diagnostic = match result { IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::GeneralDisallowedWithoutSpan => { + | IsSkipAbleResult::DefaultDisallowed => { diagnostic_pattern(span, pattern.message.clone(), source) } IsSkipAbleResult::NameDisallowed(name_span) => { diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index dd75207cbf373..276ec2b2dd3a9 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -117,7 +117,7 @@ snapshot_kind: text ╰──── help: Don"t import "". - ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'foo' from 'fs' is restricted. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * as ns from "fs"; · ───────────────────────── @@ -159,14 +159,14 @@ snapshot_kind: text ╰──── help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject' from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from "foo"; · ──────────────────── ╰──── help: Please import "DisallowedObject" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject1, DisallowedObject2' from 'foo' is restricted. ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from "foo"; · ──────────────────── @@ -729,13 +729,13 @@ snapshot_kind: text ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): '* import is invalid because only 'AllowedObject' from 'foo' is/are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo"; · ───── ╰──── - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. + ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo' is/are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo"; · ───── From a029467ba121b2ddbac0df3dac50535282895cec Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 17:56:51 +0100 Subject: [PATCH 14/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 187 +++++++++--------- .../eslint_no_restricted_imports.snap | 14 +- 2 files changed, 99 insertions(+), 102 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 5aaf1239ef7b7..cc96b4f1a66bb 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -14,176 +14,129 @@ use crate::{ ModuleRecord, }; -fn diagnostic_path(span: Span, message: Option, source: &str) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{source}' import is restricted from being used.")) - .with_help(message) - .with_label(span); +fn diagnostic_with_maybe_help(span: Span, msg: String, help: Option) -> OxcDiagnostic { + if let Some(help) = help { + return OxcDiagnostic::warn(msg).with_help(help).with_label(span); } - OxcDiagnostic::warn(format!("'{source}' import is restricted from being used.")) - .with_label(span) + OxcDiagnostic::warn(msg).with_label(span) } -fn diagnostic_pattern(span: Span, message: Option, source: &str) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!( - "'{source}' import is restricted from being used by a pattern." - )) - .with_help(message) - .with_label(span); - } +fn diagnostic_path(span: Span, help: Option, source: &str) -> OxcDiagnostic { + let msg = format!("'{source}' import is restricted from being used."); + + diagnostic_with_maybe_help(span, msg, help) +} - OxcDiagnostic::warn(format!("'{source}' import is restricted from being used by a pattern.")) - .with_label(span) +fn diagnostic_pattern(span: Span, help: Option, source: &str) -> OxcDiagnostic { + let msg = format!("'{source}' import is restricted from being used by a pattern."); + + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_pattern_and_import_name( span: Span, - message: Option, + help: Option, name: &str, source: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!( - "'{name}' import from '{source}' is restricted from being used by a pattern." - )) - .with_help(message) - .with_label(span); - } + let msg = + format!("'{name}' import from '{source}' is restricted from being used by a pattern."); - OxcDiagnostic::warn(format!( - "'{name}' import from '{source}' is restricted from being used by a pattern." - )) - .with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_pattern_and_everything( span: Span, - message: Option, + help: Option, name: &str, source: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("* import is invalid because '{name}' from '{source}' is restricted from being used by a pattern.")) - }); + let msg = + format!("* import is invalid because '{name}' from '{source}' is restricted from being used by a pattern."); - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_pattern_and_everything_with_regex_import_name( span: Span, - message: Option, + help: Option, name: &str, source: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("* import is invalid because import name matching '{name}' pattern from '{source}' is restricted from being used.")) - }); + let msg = + format!("* import is invalid because import name matching '{name}' pattern from '{source}' is restricted from being used."); - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_everything( span: Span, - message: Option, + help: Option, name: &str, source: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!( - "* import is invalid because '{name}' from '{source}' is restricted." - )) - .with_help(message) - .with_label(span); - } + let msg = format!("* import is invalid because '{name}' from '{source}' is restricted."); - OxcDiagnostic::warn(format!( - "* import is invalid because '{name}' from '{source}' is restricted." - )) - .with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_import_name( span: Span, - message: Option, + help: Option, name: &str, source: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")) - .with_help(message) - .with_label(span); - } + let msg = format!("'{name}' import from '{source}' is restricted."); - OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted.")).with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_allowed_import_name( span: Span, - message: Option, + help: Option, name: &str, source: &str, allowed: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) - .with_help(message) - .with_label(span); - } + let msg = format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed."); - OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only {allowed} import(s) is/are allowed.")) - .with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_everything_with_allowed_import_name( span: Span, - message: Option, + help: Option, source: &str, allowed: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!( - "* import is invalid because only '{allowed}' from '{source}' is/are allowed." - )) - .with_help(message) - .with_label(span); - } + let msg = + format!("* import is invalid because only '{allowed}' from '{source}' is/are allowed."); - OxcDiagnostic::warn(format!( - "'* import is invalid because only '{allowed}' from '{source}' is/are allowed." - )) - .with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_allowed_import_name_pattern( span: Span, - message: Option, + help: Option, name: &str, source: &str, allowed_pattern: &str, ) -> OxcDiagnostic { - if let Some(message) = message { - return OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) - .with_help(message) - .with_label(span); - } + let msg = format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'."); - OxcDiagnostic::warn(format!("'{name}' import from '{source}' is restricted because only imports that match the pattern '{allowed_pattern}' are allowed from '{source}'.")) - .with_label(span) + diagnostic_with_maybe_help(span, msg, help) } fn diagnostic_everything_with_allowed_import_name_pattern( span: Span, - message: Option, + help: Option, source: &str, allowed_pattern: &str, ) -> OxcDiagnostic { - let msg = message.unwrap_or_else(|| { - CompactStr::new(&format!("* import is invalid because only imports that match the pattern '{allowed_pattern}' from '{source}' are allowed.")) - }); + let msg = format!("* import is invalid because only imports that match the pattern '{allowed_pattern}' from '{source}' are allowed."); - OxcDiagnostic::warn(msg).with_help("Remove the import statement.").with_label(span) + diagnostic_with_maybe_help(span, msg, help) } #[derive(Debug, Default, Clone)] @@ -777,12 +730,36 @@ impl NoRestrictedImports { } GlobResult::Found => { let diagnostic: OxcDiagnostic = match result { - IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::DefaultDisallowed => diagnostic_pattern( + IsSkipAbleResult::GeneralDisallowed(_) => diagnostic_pattern( entry.module_request.span(), pattern.message.clone(), source, ), + IsSkipAbleResult::DefaultDisallowed => { + let diagnostic = if let Some(import_names) = &pattern.import_names { + diagnostic_pattern_and_everything( + entry.module_request.span(), + pattern.message.clone(), + import_names.join(", ").as_str(), + source, + ) + } else if let Some(allowed_import_names) = &pattern.allow_import_names { + diagnostic_everything_with_allowed_import_name( + entry.module_request.span(), + pattern.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + ) + } else { + diagnostic_pattern( + entry.module_request.span(), + pattern.message.clone(), + source, + ) + }; + + diagnostic + } IsSkipAbleResult::NameDisallowed(name_span) => { let diagnostic = if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern @@ -906,10 +883,30 @@ impl NoRestrictedImports { let span = module_request.span(); let diagnostic = match result { - IsSkipAbleResult::GeneralDisallowed(_) - | IsSkipAbleResult::DefaultDisallowed => { + IsSkipAbleResult::GeneralDisallowed(_) => { diagnostic_pattern(span, pattern.message.clone(), source) } + IsSkipAbleResult::DefaultDisallowed => { + let diagnostic = if let Some(import_names) = &pattern.import_names { + diagnostic_pattern_and_everything( + span, + pattern.message.clone(), + import_names.join(", ").as_str(), + source, + ) + } else if let Some(allowed_import_names) = &pattern.allow_import_names { + diagnostic_everything_with_allowed_import_name( + span, + pattern.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + ) + } else { + diagnostic_pattern(span, pattern.message.clone(), source) + }; + + diagnostic + } IsSkipAbleResult::NameDisallowed(name_span) => { diagnostic_pattern_and_import_name( name_span.clone().span(), diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 276ec2b2dd3a9..7b9543a17bebf 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -525,13 +525,13 @@ snapshot_kind: text ╰──── help: Import from @/utils instead. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── ╰──── - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── @@ -544,7 +544,7 @@ snapshot_kind: text · ───── ╰──── - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:26] 1 │ import def, * as ns from 'mod'; · ───── @@ -623,7 +623,7 @@ snapshot_kind: text ╰──── help: Import from @/utils instead. - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── @@ -729,7 +729,7 @@ snapshot_kind: text ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): '* import is invalid because only 'AllowedObject' from 'foo' is/are allowed. + ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo' is/are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo"; · ───── @@ -742,13 +742,13 @@ snapshot_kind: text ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo/bar' is/are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo/bar' is/are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── From 33e4842a8ae3b4cc1814219887958944fbb707ba Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 18:22:39 +0100 Subject: [PATCH 15/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 18 ++++++++++++++++++ .../eslint_no_restricted_imports.snap | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index cc96b4f1a66bb..374fbc6429c73 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -750,6 +750,15 @@ impl NoRestrictedImports { source, allowed_import_names.join(", ").as_str(), ) + } else if let Some(allow_import_name_pattern) = + &pattern.allow_import_name_pattern + { + diagnostic_everything_with_allowed_import_name_pattern( + entry.module_request.span(), + pattern.message.clone(), + source, + allow_import_name_pattern.as_str(), + ) } else { diagnostic_pattern( entry.module_request.span(), @@ -894,6 +903,15 @@ impl NoRestrictedImports { import_names.join(", ").as_str(), source, ) + } else if let Some(allow_import_name_pattern) = + &pattern.allow_import_name_pattern + { + diagnostic_everything_with_allowed_import_name_pattern( + span, + pattern.message.clone(), + source, + allow_import_name_pattern.as_str(), + ) } else if let Some(allowed_import_names) = &pattern.allow_import_names { diagnostic_everything_with_allowed_import_name( span, diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 7b9543a17bebf..bb9eb7a617130 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -755,13 +755,13 @@ snapshot_kind: text ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because only imports that match the pattern '^Allow' from 'foo/bar' are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── ╰──── - ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because only imports that match the pattern '^Allow' from 'foo/bar' are allowed. ╭─[no_restricted_imports.tsx:1:32] 1 │ import * as AllowedObject from "foo/bar"; · ───────── From 87d8da7f18ecfe3f403ef03a85b6092ee85194ed Mon Sep 17 00:00:00 2001 From: Sysix Date: Thu, 26 Dec 2024 19:59:13 +0100 Subject: [PATCH 16/28] fix(linter): rule no-restricted-imports support missing options - build regex in `from_configuration` --- .../src/rules/eslint/no_restricted_imports.rs | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 0e7b169ee5df1..055bb6afb73a6 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -4,8 +4,9 @@ use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; use regex::Regex; use rustc_hash::FxHashMap; -use serde::Deserialize; +use serde::{de::Error, Deserialize, Deserializer}; use serde_json::Value; +use std::borrow::Cow; use crate::{ context::LintContext, @@ -58,15 +59,42 @@ struct RestrictedPath { #[serde(rename_all = "camelCase")] struct RestrictedPattern { group: Option>, - regex: Option, + regex: Option>, import_names: Option>, - import_name_pattern: Option, + import_name_pattern: Option>, allow_import_names: Option>, - allow_import_name_pattern: Option, + allow_import_name_pattern: Option>, case_sensitive: Option, message: Option, } +/// A wrapper type which implements `Serialize` and `Deserialize` for +/// types involving `Regex` +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct SerdeRegexWrapper(pub T); + +impl std::ops::Deref for SerdeRegexWrapper { + type Target = Regex; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'de> Deserialize<'de> for SerdeRegexWrapper { + fn deserialize(d: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s = >::deserialize(d)?; + + match s.parse() { + Ok(regex) => Ok(SerdeRegexWrapper(regex)), + Err(err) => Err(D::Error::custom(err)), + } + } +} + #[derive(Debug)] enum GlobResult { Found, @@ -323,11 +351,7 @@ impl RestrictedPattern { return false; }; - let Ok(reg_exp) = Regex::new(regex.as_str()) else { - return false; - }; - - reg_exp.find(name.name()).is_some() + regex.find(name.name()).is_some() } fn get_import_name_pattern_result(&self, name: &CompactStr) -> bool { @@ -335,11 +359,7 @@ impl RestrictedPattern { return false; }; - let Ok(reg_exp) = Regex::new(import_name_pattern.as_str()) else { - return false; - }; - - reg_exp.find(name).is_some() + import_name_pattern.find(name).is_some() } fn get_allow_import_name_pattern_result(&self, name: &CompactStr) -> bool { @@ -347,11 +367,7 @@ impl RestrictedPattern { return false; }; - let Ok(reg_exp) = Regex::new(allow_import_names.as_str()) else { - return false; - }; - - reg_exp.find(name).is_some() + allow_import_names.find(name).is_some() } } From 12911f48127abbdddf0a48415e352f6d42ea71f9 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 30 Dec 2024 16:53:48 +0100 Subject: [PATCH 17/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 62 +++++++++++++++++-- .../eslint_no_restricted_imports.snap | 12 ++-- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 374fbc6429c73..0eafa6c8aa1a3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -743,6 +743,14 @@ impl NoRestrictedImports { import_names.join(", ").as_str(), source, ) + } else if let Some(import_name_patterns) = &pattern.import_name_pattern + { + diagnostic_pattern_and_everything_with_regex_import_name( + entry.module_request.span(), + pattern.message.clone(), + &import_name_patterns, + source, + ) } else if let Some(allowed_import_names) = &pattern.allow_import_names { diagnostic_everything_with_allowed_import_name( entry.module_request.span(), @@ -903,6 +911,14 @@ impl NoRestrictedImports { import_names.join(", ").as_str(), source, ) + } else if let Some(import_name_patterns) = &pattern.import_name_pattern + { + diagnostic_pattern_and_everything_with_regex_import_name( + span, + pattern.message.clone(), + &import_name_patterns, + source, + ) } else if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { @@ -926,12 +942,26 @@ impl NoRestrictedImports { diagnostic } IsSkipAbleResult::NameDisallowed(name_span) => { - diagnostic_pattern_and_import_name( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - ) + let diagnostic = if let Some(allow_import_name_pattern) = + &pattern.allow_import_name_pattern + { + diagnostic_allowed_import_name_pattern( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + allow_import_name_pattern.as_str(), + ) + } else { + diagnostic_pattern_and_import_name( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + ) + }; + + diagnostic } IsSkipAbleResult::Allowed => panic!("unreachable"), }; @@ -1508,6 +1538,8 @@ fn test() { }] }])), ), + // expect: 'default' import from 'foo' is restricted + // got: 'foo' import is restricted from being used. ( r#"import DisallowedObject from "foo";"#, Some(serde_json::json!([{ @@ -1838,6 +1870,8 @@ fn test() { ] }])), ), + // expect: 'default' import from 'mod' is restricted. + // got: 'mod' import is restricted from being used. ( "import foo, { bar } from 'mod';", Some(serde_json::json!([{ @@ -1875,6 +1909,8 @@ fn test() { }] }])), ), + // expected: 'default' import from 'mod' is restricted. + // got: 'mod' import is restricted from being used. ( "import foo, { default as bar } from 'mod';", Some(serde_json::json!([{ @@ -1941,6 +1977,8 @@ fn test() { }] }])), ), + // expected: 'default' import from 'mod' is restricted from being used by a pattern. + // got: 'mod' import is restricted from being used by a pattern. ( "import def, * as ns from 'mod';", Some(serde_json::json!([{ @@ -1950,6 +1988,8 @@ fn test() { }] }])), ), + // expected: 'default' import from 'mod' is restricted from being used by a pattern. + // got: 'mod' import is restricted from being used by a pattern. ( "import Foo from 'mod';", Some(serde_json::json!([{ @@ -2139,6 +2179,8 @@ fn test() { }] }])), ), + // expected: * import is invalid because import name matching '/^Foo/u' pattern from 'foo' is restricted from being used. + // got: 'foo' import is restricted from being used by a pattern. ( "export * from 'foo';", Some(serde_json::json!([{ @@ -2186,6 +2228,8 @@ fn test() { }] }])), ), + // expected: 'DisallowedObject' import from 'foo' is restricted because only 'AllowedObject' import(s) is/are allowed. + // got: 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ( r#"import { AllowedObject, DisallowedObject } from "foo";"#, Some(serde_json::json!([{ @@ -2195,6 +2239,8 @@ fn test() { }] }])), ), + // expected: 'DisallowedObject' import from 'foo' is restricted because only 'AllowedObject' import(s) is/are allowed. + // got: 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ( r#"import { AllowedObject, DisallowedObject } from "foo";"#, Some(serde_json::json!([{ @@ -2278,6 +2324,8 @@ fn test() { }] }])), ), + // expected: 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + // got: '../../my/relative-module' import is restricted from being used by a pattern. ( "import { Foo } from '../../my/relative-module';", Some(serde_json::json!([{ @@ -2297,6 +2345,8 @@ fn test() { }] }])), ), + // expected: 'Foo_*' import from '@app/api' is restricted from being used by a pattern. + // got: '@app/api/bar' import is restricted from being used by a pattern. ( " // error diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index bb9eb7a617130..fdbe8e2e3c975 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -604,19 +604,19 @@ snapshot_kind: text · ─── ╰──── - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from 'foo' is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as Foo from 'foo'; · ───── ╰──── - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from '../../my/relative-module' is restricted from being used. ╭─[no_restricted_imports.tsx:1:22] 1 │ import * as All from '../../my/relative-module'; · ────────────────────────── ╰──── - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from '../../my/relative-module' is restricted from being used. ╭─[no_restricted_imports.tsx:1:39] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; · ────────────────────────── @@ -684,19 +684,19 @@ snapshot_kind: text · ─── ╰──── - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from 'foo' is restricted from being used. ╭─[no_restricted_imports.tsx:1:15] 1 │ export * from 'foo'; · ───── ╰──── - ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted because only imports that match the pattern '^Foo' are allowed from 'foo'. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; · ─── ╰──── - ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted because only imports that match the pattern '^Foo' are allowed from 'foo'. ╭─[no_restricted_imports.tsx:1:10] 1 │ export { Bar } from 'foo'; · ─── From 304c2d859b41d19a8682697f75e12721cef7b197 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 30 Dec 2024 17:09:29 +0100 Subject: [PATCH 18/28] fix(linter): rule no-restricted-imports: improve diagnostics --- crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 0eafa6c8aa1a3..2e8b3cbf1decd 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -748,7 +748,7 @@ impl NoRestrictedImports { diagnostic_pattern_and_everything_with_regex_import_name( entry.module_request.span(), pattern.message.clone(), - &import_name_patterns, + import_name_patterns, source, ) } else if let Some(allowed_import_names) = &pattern.allow_import_names { @@ -916,7 +916,7 @@ impl NoRestrictedImports { diagnostic_pattern_and_everything_with_regex_import_name( span, pattern.message.clone(), - &import_name_patterns, + import_name_patterns, source, ) } else if let Some(allow_import_name_pattern) = From b833ef88ff5cab598964ec398e5bb5ddfeb439be Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 30 Dec 2024 17:40:48 +0100 Subject: [PATCH 19/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 394 +++++++----------- 1 file changed, 160 insertions(+), 234 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 2e8b3cbf1decd..17e77c9c1e2bd 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -660,66 +660,29 @@ impl NoRestrictedImports { continue; } - match path.is_skip_able_import(&entry.import_name) { - IsSkipAbleResult::GeneralDisallowed(_) => { - ctx.diagnostic(diagnostic_path( - entry.module_request.span(), - path.message.clone(), - source, - )); - } - IsSkipAbleResult::DefaultDisallowed => { - if let Some(import_names) = &path.import_names { - ctx.diagnostic(diagnostic_everything( - entry.module_request.span(), - path.message.clone(), - import_names.join(", ").as_str(), - source, - )); - } else if let Some(allowed_import_names) = &path.allow_import_names { - ctx.diagnostic(diagnostic_everything_with_allowed_import_name( - entry.module_request.span(), - path.message.clone(), - source, - allowed_import_names.join(", ").as_str(), - )); - } else { - ctx.diagnostic(diagnostic_path( - entry.module_request.span(), - path.message.clone(), - source, - )); - } - } - IsSkipAbleResult::NameDisallowed(name_span) => { - if let Some(import_names) = &path.allow_import_names { - ctx.diagnostic(diagnostic_allowed_import_name( - name_span.clone().span(), - path.message.clone(), - name_span.name(), - source, - import_names.join(", ").as_str(), - )); - } else { - ctx.diagnostic(diagnostic_import_name( - name_span.clone().span(), - path.message.clone(), - name_span.name(), - source, - )); - }; - } - IsSkipAbleResult::Allowed => (), + let result = &path.is_skip_able_import(&entry.import_name); + + if *result == IsSkipAbleResult::Allowed { + continue; } + + let diagnostic = get_diagnostic_from_is_skip_able_result_path( + &entry.module_request.span(), + source, + result, + path + ); + + ctx.diagnostic(diagnostic); } let mut whitelist_found = false; let mut found_errors = vec![]; for pattern in &self.patterns { - let result = pattern.is_skip_able_import(&entry.import_name); + let result = &pattern.is_skip_able_import(&entry.import_name); - if result == IsSkipAbleResult::Allowed { + if *result == IsSkipAbleResult::Allowed { continue; } @@ -729,78 +692,12 @@ impl NoRestrictedImports { break; } GlobResult::Found => { - let diagnostic: OxcDiagnostic = match result { - IsSkipAbleResult::GeneralDisallowed(_) => diagnostic_pattern( - entry.module_request.span(), - pattern.message.clone(), - source, - ), - IsSkipAbleResult::DefaultDisallowed => { - let diagnostic = if let Some(import_names) = &pattern.import_names { - diagnostic_pattern_and_everything( - entry.module_request.span(), - pattern.message.clone(), - import_names.join(", ").as_str(), - source, - ) - } else if let Some(import_name_patterns) = &pattern.import_name_pattern - { - diagnostic_pattern_and_everything_with_regex_import_name( - entry.module_request.span(), - pattern.message.clone(), - import_name_patterns, - source, - ) - } else if let Some(allowed_import_names) = &pattern.allow_import_names { - diagnostic_everything_with_allowed_import_name( - entry.module_request.span(), - pattern.message.clone(), - source, - allowed_import_names.join(", ").as_str(), - ) - } else if let Some(allow_import_name_pattern) = - &pattern.allow_import_name_pattern - { - diagnostic_everything_with_allowed_import_name_pattern( - entry.module_request.span(), - pattern.message.clone(), - source, - allow_import_name_pattern.as_str(), - ) - } else { - diagnostic_pattern( - entry.module_request.span(), - pattern.message.clone(), - source, - ) - }; - - diagnostic - } - IsSkipAbleResult::NameDisallowed(name_span) => { - let diagnostic = if let Some(allow_import_name_pattern) = - &pattern.allow_import_name_pattern - { - diagnostic_allowed_import_name_pattern( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - allow_import_name_pattern.as_str(), - ) - } else { - diagnostic_pattern_and_import_name( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - ) - }; - - diagnostic - } - IsSkipAbleResult::Allowed => panic!("unreachable"), - }; + let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( + &entry.module_request.span(), + source, + result, + pattern, + ); found_errors.push(diagnostic); } @@ -832,58 +729,29 @@ impl NoRestrictedImports { continue; } - match path.is_skip_able_export(&entry.import_name) { - IsSkipAbleResult::GeneralDisallowed(_) => { - ctx.diagnostic(diagnostic_path(entry.span, path.message.clone(), source)); - } - IsSkipAbleResult::DefaultDisallowed => { - if let Some(import_names) = &path.import_names { - ctx.diagnostic(diagnostic_everything( - entry.span, - path.message.clone(), - import_names.join(", ").as_str(), - source, - )); - } else if let Some(allowed_import_names) = &path.allow_import_names { - ctx.diagnostic(diagnostic_everything_with_allowed_import_name( - entry.span, - path.message.clone(), - source, - allowed_import_names.join(", ").as_str(), - )); - } else { - ctx.diagnostic(diagnostic_path(entry.span, path.message.clone(), source)); - } - } - IsSkipAbleResult::NameDisallowed(name_span) => { - if let Some(allow_import_names) = &path.allow_import_names { - ctx.diagnostic(diagnostic_allowed_import_name( - name_span.clone().span(), - path.message.clone(), - name_span.name(), - source, - allow_import_names.join(", ").as_str(), - )); - } else { - ctx.diagnostic(diagnostic_import_name( - name_span.clone().span(), - path.message.clone(), - name_span.name(), - source, - )); - }; - } - IsSkipAbleResult::Allowed => (), + let result = &path.is_skip_able_export(&entry.import_name); + + if *result == IsSkipAbleResult::Allowed { + continue; } + + let diagnostic = get_diagnostic_from_is_skip_able_result_path( + &entry.span, + source, + result, + path + ); + + ctx.diagnostic(diagnostic); } let mut whitelist_found = false; let mut found_errors = vec![]; for pattern in &self.patterns { - let result = pattern.is_skip_able_export(&entry.import_name); + let result = &pattern.is_skip_able_export(&entry.import_name); - if result == IsSkipAbleResult::Allowed { + if *result == IsSkipAbleResult::Allowed { continue; } @@ -899,72 +767,9 @@ impl NoRestrictedImports { GlobResult::Found => { let span = module_request.span(); - let diagnostic = match result { - IsSkipAbleResult::GeneralDisallowed(_) => { - diagnostic_pattern(span, pattern.message.clone(), source) - } - IsSkipAbleResult::DefaultDisallowed => { - let diagnostic = if let Some(import_names) = &pattern.import_names { - diagnostic_pattern_and_everything( - span, - pattern.message.clone(), - import_names.join(", ").as_str(), - source, - ) - } else if let Some(import_name_patterns) = &pattern.import_name_pattern - { - diagnostic_pattern_and_everything_with_regex_import_name( - span, - pattern.message.clone(), - import_name_patterns, - source, - ) - } else if let Some(allow_import_name_pattern) = - &pattern.allow_import_name_pattern - { - diagnostic_everything_with_allowed_import_name_pattern( - span, - pattern.message.clone(), - source, - allow_import_name_pattern.as_str(), - ) - } else if let Some(allowed_import_names) = &pattern.allow_import_names { - diagnostic_everything_with_allowed_import_name( - span, - pattern.message.clone(), - source, - allowed_import_names.join(", ").as_str(), - ) - } else { - diagnostic_pattern(span, pattern.message.clone(), source) - }; - - diagnostic - } - IsSkipAbleResult::NameDisallowed(name_span) => { - let diagnostic = if let Some(allow_import_name_pattern) = - &pattern.allow_import_name_pattern - { - diagnostic_allowed_import_name_pattern( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - allow_import_name_pattern.as_str(), - ) - } else { - diagnostic_pattern_and_import_name( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - ) - }; - - diagnostic - } - IsSkipAbleResult::Allowed => panic!("unreachable"), - }; + let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( + &span, source, result, pattern, + ); found_errors.push(diagnostic); } @@ -986,6 +791,127 @@ impl NoRestrictedImports { } } +fn get_diagnostic_from_is_skip_able_result_path( + span: &Span, + source: &str, + result: &IsSkipAbleResult, + path: &RestrictedPath, +) -> OxcDiagnostic { + match result { + IsSkipAbleResult::GeneralDisallowed(_) => { + diagnostic_path(*span, path.message.clone(), source) + } + IsSkipAbleResult::DefaultDisallowed => { + if let Some(import_names) = &path.import_names { + diagnostic_everything( + *span, + path.message.clone(), + import_names.join(", ").as_str(), + source, + ) + } else if let Some(allowed_import_names) = &path.allow_import_names { + diagnostic_everything_with_allowed_import_name( + *span, + path.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + ) + } else { + diagnostic_path(*span, path.message.clone(), source) + } + } + IsSkipAbleResult::NameDisallowed(name_span) => { + if let Some(allow_import_names) = &path.allow_import_names { + diagnostic_allowed_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + allow_import_names.join(", ").as_str(), + ) + } else { + diagnostic_import_name( + name_span.clone().span(), + path.message.clone(), + name_span.name(), + source, + ) + } + } + IsSkipAbleResult::Allowed => unreachable!("should be filtered out by the parent function"), + } +} + +fn get_diagnostic_from_is_skip_able_result_pattern( + span: &Span, + source: &str, + result: &IsSkipAbleResult, + pattern: &RestrictedPattern, +) -> OxcDiagnostic { + match result { + IsSkipAbleResult::GeneralDisallowed(_) => { + diagnostic_pattern(*span, pattern.message.clone(), source) + } + IsSkipAbleResult::DefaultDisallowed => { + let diagnostic = if let Some(import_names) = &pattern.import_names { + diagnostic_pattern_and_everything( + *span, + pattern.message.clone(), + import_names.join(", ").as_str(), + source, + ) + } else if let Some(import_name_patterns) = &pattern.import_name_pattern { + diagnostic_pattern_and_everything_with_regex_import_name( + *span, + pattern.message.clone(), + import_name_patterns, + source, + ) + } else if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { + diagnostic_everything_with_allowed_import_name_pattern( + *span, + pattern.message.clone(), + source, + allow_import_name_pattern.as_str(), + ) + } else if let Some(allowed_import_names) = &pattern.allow_import_names { + diagnostic_everything_with_allowed_import_name( + *span, + pattern.message.clone(), + source, + allowed_import_names.join(", ").as_str(), + ) + } else { + diagnostic_pattern(*span, pattern.message.clone(), source) + }; + + diagnostic + } + IsSkipAbleResult::NameDisallowed(name_span) => { + let diagnostic = + if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { + diagnostic_allowed_import_name_pattern( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + allow_import_name_pattern.as_str(), + ) + } else { + diagnostic_pattern_and_import_name( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + ) + }; + + diagnostic + } + IsSkipAbleResult::Allowed => unreachable!("should be filtered out by parent function"), + } +} + #[test] fn test() { use crate::tester::Tester; From 3ba7bd4605db520afd8d642e201978a2422a2a78 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:42:12 +0000 Subject: [PATCH 20/28] [autofix.ci] apply automated fixes --- .../src/rules/eslint/no_restricted_imports.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 17e77c9c1e2bd..a51dfce3736b2 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -670,7 +670,7 @@ impl NoRestrictedImports { &entry.module_request.span(), source, result, - path + path, ); ctx.diagnostic(diagnostic); @@ -735,12 +735,8 @@ impl NoRestrictedImports { continue; } - let diagnostic = get_diagnostic_from_is_skip_able_result_path( - &entry.span, - source, - result, - path - ); + let diagnostic = + get_diagnostic_from_is_skip_able_result_path(&entry.span, source, result, path); ctx.diagnostic(diagnostic); } From ece4df61ce3107cb3c80aa9279e8c4b2690cd9ac Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 30 Dec 2024 18:15:18 +0100 Subject: [PATCH 21/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index a51dfce3736b2..7242bcb59353b 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -667,7 +667,7 @@ impl NoRestrictedImports { } let diagnostic = get_diagnostic_from_is_skip_able_result_path( - &entry.module_request.span(), + entry.module_request.span(), source, result, path, @@ -693,7 +693,7 @@ impl NoRestrictedImports { } GlobResult::Found => { let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( - &entry.module_request.span(), + entry.module_request.span(), source, result, pattern, @@ -736,7 +736,7 @@ impl NoRestrictedImports { } let diagnostic = - get_diagnostic_from_is_skip_able_result_path(&entry.span, source, result, path); + get_diagnostic_from_is_skip_able_result_path(entry.span, source, result, path); ctx.diagnostic(diagnostic); } @@ -764,7 +764,7 @@ impl NoRestrictedImports { let span = module_request.span(); let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( - &span, source, result, pattern, + span, source, result, pattern, ); found_errors.push(diagnostic); @@ -788,32 +788,32 @@ impl NoRestrictedImports { } fn get_diagnostic_from_is_skip_able_result_path( - span: &Span, + span: Span, source: &str, result: &IsSkipAbleResult, path: &RestrictedPath, ) -> OxcDiagnostic { match result { IsSkipAbleResult::GeneralDisallowed(_) => { - diagnostic_path(*span, path.message.clone(), source) + diagnostic_path(span, path.message.clone(), source) } IsSkipAbleResult::DefaultDisallowed => { if let Some(import_names) = &path.import_names { diagnostic_everything( - *span, + span, path.message.clone(), import_names.join(", ").as_str(), source, ) } else if let Some(allowed_import_names) = &path.allow_import_names { diagnostic_everything_with_allowed_import_name( - *span, + span, path.message.clone(), source, allowed_import_names.join(", ").as_str(), ) } else { - diagnostic_path(*span, path.message.clone(), source) + diagnostic_path(span, path.message.clone(), source) } } IsSkipAbleResult::NameDisallowed(name_span) => { @@ -839,46 +839,46 @@ fn get_diagnostic_from_is_skip_able_result_path( } fn get_diagnostic_from_is_skip_able_result_pattern( - span: &Span, + span: Span, source: &str, result: &IsSkipAbleResult, pattern: &RestrictedPattern, ) -> OxcDiagnostic { match result { IsSkipAbleResult::GeneralDisallowed(_) => { - diagnostic_pattern(*span, pattern.message.clone(), source) + diagnostic_pattern(span, pattern.message.clone(), source) } IsSkipAbleResult::DefaultDisallowed => { let diagnostic = if let Some(import_names) = &pattern.import_names { diagnostic_pattern_and_everything( - *span, + span, pattern.message.clone(), import_names.join(", ").as_str(), source, ) } else if let Some(import_name_patterns) = &pattern.import_name_pattern { diagnostic_pattern_and_everything_with_regex_import_name( - *span, + span, pattern.message.clone(), import_name_patterns, source, ) } else if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { diagnostic_everything_with_allowed_import_name_pattern( - *span, + span, pattern.message.clone(), source, allow_import_name_pattern.as_str(), ) } else if let Some(allowed_import_names) = &pattern.allow_import_names { diagnostic_everything_with_allowed_import_name( - *span, + span, pattern.message.clone(), source, allowed_import_names.join(", ").as_str(), ) } else { - diagnostic_pattern(*span, pattern.message.clone(), source) + diagnostic_pattern(span, pattern.message.clone(), source) }; diagnostic From 41096924caca65b06cfb77eafaa66d25857c3c35 Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 13:27:43 +0100 Subject: [PATCH 22/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 18 +- .../eslint_no_restricted_imports.snap | 208 +++++++++--------- 2 files changed, 110 insertions(+), 116 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 36aa869e44cbf..1c976bb4411a3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -648,7 +648,7 @@ impl NoRestrictedImports { for (source, requests) in &module_record.requested_modules { for request in requests { if request.is_import && module_record.import_entries.is_empty() { - side_effect_import_map.entry(source).or_default().push(request.span); + side_effect_import_map.entry(source).or_default().push(request.statement_span); } } } @@ -679,7 +679,7 @@ impl NoRestrictedImports { } let diagnostic = get_diagnostic_from_is_skip_able_result_path( - entry.module_request.span(), + entry.statement_span, source, result, path, @@ -705,7 +705,7 @@ impl NoRestrictedImports { } GlobResult::Found => { let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( - entry.module_request.span(), + entry.statement_span, source, result, pattern, @@ -717,9 +717,7 @@ impl NoRestrictedImports { }; if pattern.get_regex_result(&entry.module_request) { - let span = entry.module_request.span(); - - ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); + ctx.diagnostic(diagnostic_pattern(entry.statement_span, pattern.message.clone(), source)); } } @@ -773,10 +771,8 @@ impl NoRestrictedImports { break; } GlobResult::Found => { - let span = module_request.span(); - let diagnostic = get_diagnostic_from_is_skip_able_result_pattern( - span, source, result, pattern, + entry.span, source, result, pattern, ); found_errors.push(diagnostic); @@ -785,9 +781,7 @@ impl NoRestrictedImports { }; if pattern.get_regex_result(module_request) { - let span = module_request.span(); - - ctx.diagnostic(diagnostic_pattern(span, pattern.message.clone(), source)); + ctx.diagnostic(diagnostic_pattern(entry.span, pattern.message.clone(), source)); } } diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 90f953ef28a5b..ff93caaa94854 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -3,65 +3,65 @@ source: crates/oxc_linter/src/tester.rs snapshot_kind: text --- ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:8] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import "fs" - · ──── + · ─────────── ╰──── ⚠ eslint(no-restricted-imports): 'os' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:16] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import os from "os"; - · ──── + · ──────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:8] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import "foo/bar"; - · ───────── + · ───────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:23] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPaths from "foo/bar"; - · ───────── + · ──────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatterns from "foo/bar"; - · ───────── + · ─────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo/baz' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatterns from "foo/baz"; - · ───────── + · ─────────────────────────────────── ╰──── help: foo is forbidden, use foo/bar instead ⚠ eslint(no-restricted-imports): 'foo/baz' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatterns from "foo/baz"; - · ───────── + · ─────────────────────────────────── ╰──── help: some foo subimports are restricted ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatterns from "foo/bar"; - · ───────── + · ─────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:41] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatternsCaseInsensitive from 'foo'; - · ───── + · ────────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo/bar' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:28] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withGitignores from "foo/bar"; - · ───────── + · ───────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'fs' import is restricted from being used. @@ -125,37 +125,37 @@ snapshot_kind: text help: Don"t import "foo". ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:28] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withGitignores from "foo"; - · ───── + · ───────────────────────────────── ╰──── help: Please import from "bar" instead. ⚠ eslint(no-restricted-imports): 'bar' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:28] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withGitignores from "bar"; - · ───── + · ───────────────────────────────── ╰──── help: Please import from "baz" instead. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:28] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withGitignores from "foo"; - · ───── + · ───────────────────────────────── ╰──── help: Please import from "bar" instead. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:30] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import DisallowedObject from "foo"; - · ───── + · ─────────────────────────────────── ╰──── help: Please import the default import of "foo" from /bar/ instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject' from 'foo' is restricted. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as All from "foo"; - · ───── + · ─────────────────────────── ╰──── help: Please import "DisallowedObject" from /bar/ instead. @@ -234,9 +234,9 @@ snapshot_kind: text help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:69] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import DisallowedObject, { AllowedObject as AllowedObjectTwo } from "foo"; - · ───── + · ────────────────────────────────────────────────────────────────────────── ╰──── help: Please import the default import of "foo" from /bar/ instead. @@ -248,16 +248,16 @@ snapshot_kind: text help: Please import "DisallowedObject" from /bar/ instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject' from 'foo' is restricted. - ╭─[no_restricted_imports.tsx:1:50] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; - · ───── + · ─────────────────────────────────────────────────────── ╰──── help: Please import "DisallowedObject" from /bar/ instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'DisallowedObject, DisallowedObjectTwo' from 'foo' is restricted. - ╭─[no_restricted_imports.tsx:1:50] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import AllowedObject, * as AllowedObjectTwo from "foo"; - · ───── + · ─────────────────────────────────────────────────────── ╰──── help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. @@ -377,29 +377,29 @@ snapshot_kind: text help: Don"t use "foo" or "baz" from "mod". ⚠ eslint(no-restricted-imports): * import is invalid because 'foo' from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as mod from 'mod' - · ───── + · ────────────────────────── ╰──── help: Import foo from qux instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'bar' from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as mod from 'mod' - · ───── + · ────────────────────────── ╰──── help: Import bar from qux instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:21] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import { foo } from 'mod' - · ───── + · ───────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:21] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import { bar } from 'mod' - · ───── + · ───────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'bar' import from 'mod' is restricted. @@ -410,27 +410,27 @@ snapshot_kind: text help: Import bar from qux instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import foo, { bar } from 'mod'; - · ───── + · ─────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:27] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import foo, * as bar from 'mod'; - · ───── + · ──────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted. - ╭─[no_restricted_imports.tsx:1:27] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import foo, * as bar from 'mod'; - · ───── + · ──────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as bar from 'foo'; - · ───── + · ─────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'a' import from 'mod' is restricted. @@ -458,9 +458,9 @@ snapshot_kind: text ╰──── ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:37] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import foo, { default as bar } from 'mod'; - · ───── + · ────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. @@ -470,39 +470,39 @@ snapshot_kind: text ╰──── ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import relative from '../foo'; - · ──────── + · ────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:31] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import relativeWithPaths from '../foo'; - · ──────── + · ─────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): '../foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:34] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import relativeWithPatterns from '../foo'; - · ──────── + · ────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import absolute from '/foo'; - · ────── + · ──────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:31] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import absoluteWithPaths from '/foo'; - · ────── + · ───────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): '/foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:34] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import absoluteWithPatterns from '/foo'; - · ────── + · ──────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. @@ -526,34 +526,34 @@ snapshot_kind: text help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as All from '../../my/relative-module'; - · ────────────────────────── + · ──────────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:39] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; - · ────────────────────────── + · ───────────────────────────────────────────────────────────────── ╰──── help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import def, * as ns from 'mod'; - · ───── + · ─────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:26] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import def, * as ns from 'mod'; - · ───── + · ─────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:17] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import Foo from 'mod'; - · ───── + · ────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. @@ -605,28 +605,28 @@ snapshot_kind: text ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from 'foo' is restricted from being used. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as Foo from 'foo'; - · ───── + · ─────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from '../../my/relative-module' is restricted from being used. - ╭─[no_restricted_imports.tsx:1:22] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as All from '../../my/relative-module'; - · ────────────────────────── + · ──────────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from '../../my/relative-module' is restricted from being used. - ╭─[no_restricted_imports.tsx:1:39] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; - · ────────────────────────── + · ───────────────────────────────────────────────────────────────── ╰──── help: Import from @/utils instead. ⚠ eslint(no-restricted-imports): * import is invalid because 'Foo' from '../../my/relative-module' is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:39] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllWithCustomMessage from '../../my/relative-module'; - · ────────────────────────── + · ───────────────────────────────────────────────────────────────── ╰──── help: Import from @/utils instead. @@ -685,9 +685,9 @@ snapshot_kind: text ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because import name matching '^Foo' pattern from 'foo' is restricted from being used. - ╭─[no_restricted_imports.tsx:1:15] + ╭─[no_restricted_imports.tsx:1:1] 1 │ export * from 'foo'; - · ───── + · ──────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'Bar' import from 'foo' is restricted because only imports that match the pattern '^Foo' are allowed from 'foo'. @@ -730,60 +730,60 @@ snapshot_kind: text help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo' is/are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo"; - · ───── + · ───────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo' is/are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo"; - · ───── + · ───────────────────────────────────── ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo/bar' is/are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo/bar"; - · ───────── + · ───────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because only 'AllowedObject' from 'foo/bar' is/are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo/bar"; - · ───────── + · ───────────────────────────────────────── ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): * import is invalid because only imports that match the pattern '^Allow' from 'foo/bar' are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo/bar"; - · ───────── + · ───────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because only imports that match the pattern '^Allow' from 'foo/bar' are allowed. - ╭─[no_restricted_imports.tsx:1:32] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import * as AllowedObject from "foo/bar"; - · ───────── + · ───────────────────────────────────────── ╰──── help: Only import names starting with "Allow" are allowed to be imported from "foo". ⚠ eslint(no-restricted-imports): 'FOO' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:39] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatternsCaseSensitive from 'FOO'; - · ───── + · ──────────────────────────────────────────── ╰──── help: foo is forbidden, use bar instead ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:21] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import { Foo } from '../../my/relative-module'; - · ────────────────────────── + · ─────────────────────────────────────────────── ╰──── ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:39] + ╭─[no_restricted_imports.tsx:1:1] 1 │ import withPatternsCaseSensitive from 'foo'; - · ───── + · ──────────────────────────────────────────── ╰──── help: foo is forbidden, use bar instead From f8caf131ecd196a9bf3fc0804e5f7fbcd6f235a5 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2024 12:28:58 +0000 Subject: [PATCH 23/28] [autofix.ci] apply automated fixes --- crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 1c976bb4411a3..0770798d7c3f3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -717,7 +717,11 @@ impl NoRestrictedImports { }; if pattern.get_regex_result(&entry.module_request) { - ctx.diagnostic(diagnostic_pattern(entry.statement_span, pattern.message.clone(), source)); + ctx.diagnostic(diagnostic_pattern( + entry.statement_span, + pattern.message.clone(), + source, + )); } } From 16adf00de946657db51076f6f38962123e0d6be9 Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 13:59:11 +0100 Subject: [PATCH 24/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 47 ++++++++++--------- .../eslint_no_restricted_imports.snap | 4 +- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 0770798d7c3f3..ca7b87e4d1db6 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -894,25 +894,30 @@ fn get_diagnostic_from_is_skip_able_result_pattern( diagnostic } IsSkipAbleResult::NameDisallowed(name_span) => { - let diagnostic = - if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { - diagnostic_allowed_import_name_pattern( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - allow_import_name_pattern.as_str(), - ) - } else { - diagnostic_pattern_and_import_name( - name_span.clone().span(), - pattern.message.clone(), - name_span.name(), - source, - ) - }; - - diagnostic + if let Some(allow_import_names) = &pattern.allow_import_names { + diagnostic_allowed_import_name( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + allow_import_names.join(", ").as_str(), + ) + } else if let Some(allow_import_name_pattern) = &pattern.allow_import_name_pattern { + diagnostic_allowed_import_name_pattern( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + allow_import_name_pattern.as_str(), + ) + } else { + diagnostic_pattern_and_import_name( + name_span.clone().span(), + pattern.message.clone(), + name_span.name(), + source, + ) + } } IsSkipAbleResult::Allowed => unreachable!("should be filtered out by parent function"), } @@ -2160,8 +2165,6 @@ fn test() { }] }])), ), - // expected: 'DisallowedObject' import from 'foo' is restricted because only 'AllowedObject' import(s) is/are allowed. - // got: 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ( r#"import { AllowedObject, DisallowedObject } from "foo";"#, Some(serde_json::json!([{ @@ -2171,8 +2174,6 @@ fn test() { }] }])), ), - // expected: 'DisallowedObject' import from 'foo' is restricted because only 'AllowedObject' import(s) is/are allowed. - // got: 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. ( r#"import { AllowedObject, DisallowedObject } from "foo";"#, Some(serde_json::json!([{ diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index ff93caaa94854..08b742e2167ec 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -716,13 +716,13 @@ snapshot_kind: text ╰──── help: Only "AllowedObject" is allowed to be imported from "foo". - ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted because only AllowedObject import(s) is/are allowed. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── ╰──── - ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted from being used by a pattern. + ⚠ eslint(no-restricted-imports): 'DisallowedObject' import from 'foo' is restricted because only AllowedObject import(s) is/are allowed. ╭─[no_restricted_imports.tsx:1:25] 1 │ import { AllowedObject, DisallowedObject } from "foo"; · ──────────────── From b694eeffd6c39433907e34b2b370a45b1c6c1887 Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 14:15:30 +0100 Subject: [PATCH 25/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 11 ++++++----- .../src/snapshots/eslint_no_restricted_imports.snap | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index ca7b87e4d1db6..98ec43189f2e4 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -717,10 +717,11 @@ impl NoRestrictedImports { }; if pattern.get_regex_result(&entry.module_request) { - ctx.diagnostic(diagnostic_pattern( + ctx.diagnostic(get_diagnostic_from_is_skip_able_result_pattern( entry.statement_span, - pattern.message.clone(), source, + result, + pattern, )); } } @@ -785,7 +786,9 @@ impl NoRestrictedImports { }; if pattern.get_regex_result(module_request) { - ctx.diagnostic(diagnostic_pattern(entry.span, pattern.message.clone(), source)); + ctx.diagnostic(get_diagnostic_from_is_skip_able_result_pattern( + entry.span, source, result, pattern, + )); } } @@ -2257,8 +2260,6 @@ fn test() { }] }])), ), - // expected: 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. - // got: '../../my/relative-module' import is restricted from being used by a pattern. ( "import { Foo } from '../../my/relative-module';", Some(serde_json::json!([{ diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 08b742e2167ec..b548b55ceae35 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -775,10 +775,10 @@ snapshot_kind: text ╰──── help: foo is forbidden, use bar instead - ⚠ eslint(no-restricted-imports): '../../my/relative-module' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'Foo' import from '../../my/relative-module' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:10] 1 │ import { Foo } from '../../my/relative-module'; - · ─────────────────────────────────────────────── + · ─── ╰──── ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used by a pattern. From f4856336370ba9adc0b8f81c97ba45af638af591 Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 15:35:45 +0100 Subject: [PATCH 26/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 12 +++----- .../eslint_no_restricted_imports.snap | 30 +++++++++---------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 98ec43189f2e4..53f5b9e424d17 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -430,8 +430,10 @@ impl RestrictedPath { let name = CompactStr::new("default"); match is_name_span_allowed_in_path(&name, self) { - NameSpanAllowedResult::NameDisallowed - | NameSpanAllowedResult::GeneralDisallowed => { + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(NameSpan::new(name, *span)) + } + NameSpanAllowedResult::GeneralDisallowed => { IsSkipAbleResult::GeneralDisallowed(NameSpan::new(name, *span)) } NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, @@ -1849,8 +1851,6 @@ fn test() { }] }])), ), - // expected: 'default' import from 'mod' is restricted. - // got: 'mod' import is restricted from being used. ( "import foo, { default as bar } from 'mod';", Some(serde_json::json!([{ @@ -2119,8 +2119,6 @@ fn test() { }] }])), ), - // expected: * import is invalid because import name matching '/^Foo/u' pattern from 'foo' is restricted from being used. - // got: 'foo' import is restricted from being used by a pattern. ( "export * from 'foo';", Some(serde_json::json!([{ @@ -2279,8 +2277,6 @@ fn test() { }] }])), ), - // expected: 'Foo_*' import from '@app/api' is restricted from being used by a pattern. - // got: '@app/api/bar' import is restricted from being used by a pattern. // ( // " // // error diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index b548b55ceae35..3b16de5ad7f57 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -145,10 +145,10 @@ snapshot_kind: text ╰──── help: Please import from "bar" instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import DisallowedObject from "foo"; - · ─────────────────────────────────── + · ──────────────── ╰──── help: Please import the default import of "foo" from /bar/ instead. @@ -233,10 +233,10 @@ snapshot_kind: text ╰──── help: Please import "DisallowedObject" and "DisallowedObjectTwo" from /bar/ instead. - ⚠ eslint(no-restricted-imports): 'foo' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'foo' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import DisallowedObject, { AllowedObject as AllowedObjectTwo } from "foo"; - · ────────────────────────────────────────────────────────────────────────── + · ──────────────── ╰──── help: Please import the default import of "foo" from /bar/ instead. @@ -409,16 +409,16 @@ snapshot_kind: text ╰──── help: Import bar from qux instead. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, { bar } from 'mod'; - · ─────────────────────────────── + · ─── ╰──── - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, * as bar from 'mod'; - · ──────────────────────────────── + · ─── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted. @@ -457,10 +457,10 @@ snapshot_kind: text · ─ ╰──── - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import foo, { default as bar } from 'mod'; - · ────────────────────────────────────────── + · ─── ╰──── ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted. From 6d8c597a12d744fe49bf79e463297539dfb4827d Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 15:50:27 +0100 Subject: [PATCH 27/28] fix(linter): rule no-restricted-imports: improve diagnostics --- .../src/rules/eslint/no_restricted_imports.rs | 10 ++++------ .../src/snapshots/eslint_no_restricted_imports.snap | 12 ++++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 53f5b9e424d17..0caa483604fb0 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -481,8 +481,10 @@ impl RestrictedPattern { ImportImportName::Default(span) => { let name: CompactStr = CompactStr::new("default"); match is_name_span_allowed_in_pattern(&name, self) { - NameSpanAllowedResult::NameDisallowed - | NameSpanAllowedResult::GeneralDisallowed => { + NameSpanAllowedResult::NameDisallowed => { + IsSkipAbleResult::NameDisallowed(NameSpan::new(name, *span)) + } + NameSpanAllowedResult::GeneralDisallowed => { IsSkipAbleResult::GeneralDisallowed(NameSpan::new(name, *span)) } NameSpanAllowedResult::Allowed => IsSkipAbleResult::Allowed, @@ -1917,8 +1919,6 @@ fn test() { }] }])), ), - // expected: 'default' import from 'mod' is restricted from being used by a pattern. - // got: 'mod' import is restricted from being used by a pattern. ( "import def, * as ns from 'mod';", Some(serde_json::json!([{ @@ -1928,8 +1928,6 @@ fn test() { }] }])), ), - // expected: 'default' import from 'mod' is restricted from being used by a pattern. - // got: 'mod' import is restricted from being used by a pattern. ( "import Foo from 'mod';", Some(serde_json::json!([{ diff --git a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap index 3b16de5ad7f57..e95efbc77845b 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap @@ -538,10 +538,10 @@ snapshot_kind: text ╰──── help: Import from @/utils instead. - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import def, * as ns from 'mod'; - · ─────────────────────────────── + · ─── ╰──── ⚠ eslint(no-restricted-imports): * import is invalid because 'default' from 'mod' is restricted from being used by a pattern. @@ -550,10 +550,10 @@ snapshot_kind: text · ─────────────────────────────── ╰──── - ⚠ eslint(no-restricted-imports): 'mod' import is restricted from being used by a pattern. - ╭─[no_restricted_imports.tsx:1:1] + ⚠ eslint(no-restricted-imports): 'default' import from 'mod' is restricted from being used by a pattern. + ╭─[no_restricted_imports.tsx:1:8] 1 │ import Foo from 'mod'; - · ────────────────────── + · ─── ╰──── ⚠ eslint(no-restricted-imports): 'Foo' import from 'foo' is restricted from being used by a pattern. From 071c0624fdceca411e9490f53fd2b2374c06fa99 Mon Sep 17 00:00:00 2001 From: Sysix Date: Tue, 31 Dec 2024 16:01:37 +0100 Subject: [PATCH 28/28] fix(linter): rule no-restricted-imports: improve diagnostics --- crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs index 0caa483604fb0..4a0b8673f4a9a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -1394,6 +1394,7 @@ fn test() { ), (r#"export * from "fs";"#, Some(serde_json::json!(["fs"]))), (r#"export * as ns from "fs";"#, Some(serde_json::json!(["fs"]))), + // ToDo: wrong span (r#"export {a} from "fs";"#, Some(serde_json::json!(["fs"]))), ( r#"export {foo as b} from "fs";"#, @@ -1445,6 +1446,7 @@ fn test() { }] }])), ), + // ToDo: wrong span ( r#"export * as ns from "fs";"#, Some(serde_json::json!([{ @@ -1482,8 +1484,6 @@ fn test() { }] }])), ), - // expect: 'default' import from 'foo' is restricted - // got: 'foo' import is restricted from being used. ( r#"import DisallowedObject from "foo";"#, Some(serde_json::json!([{ @@ -1814,8 +1814,6 @@ fn test() { ] }])), ), - // expect: 'default' import from 'mod' is restricted. - // got: 'mod' import is restricted from being used. ( "import foo, { bar } from 'mod';", Some(serde_json::json!([{