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";