diff --git a/Cargo.lock b/Cargo.lock index d1d2be451c726..6154e8ce05dcd 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]] 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..055bb6afb73a6 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs @@ -2,9 +2,11 @@ use ignore::gitignore::GitignoreBuilder; use oxc_diagnostics::OxcDiagnostic; 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, @@ -48,21 +50,51 @@ 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, } #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] struct RestrictedPattern { - group: Vec, - import_names: Option>, - allow_import_names: Option>, + group: Option>, + regex: Option>, + import_names: Option>, + import_name_pattern: Option>, + allow_import_names: 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, @@ -147,8 +179,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()) { - patterns.push(path); + 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(pattern); } } _ => (), @@ -158,9 +213,12 @@ 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, + import_name_pattern: None, allow_import_names: None, + allow_import_name_pattern: None, case_sensitive: None, message: None, }); @@ -192,8 +250,13 @@ 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() { + // 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; } @@ -202,6 +265,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 } @@ -245,12 +313,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 +345,30 @@ impl RestrictedPattern { GlobResult::Found } + + fn get_regex_result(&self, name: &NameSpan) -> bool { + let Some(regex) = &self.regex else { + return false; + }; + + regex.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; + }; + + import_name_pattern.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; + }; + + allow_import_names.find(name).is_some() + } } impl Rule for NoRestrictedImports { @@ -396,7 +492,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 +504,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 +551,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 +563,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() { @@ -709,99 +817,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!([{ @@ -848,21 +956,21 @@ fn test() { }] }])), ), - // ( - // "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 { 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!([{ @@ -1474,214 +1582,214 @@ 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" - // }] - // }])), - // ), - // ( - // "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"."# - // }] - // }])), - // ), + ( + "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!([{ + "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!([{ @@ -1758,50 +1866,50 @@ 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( // 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 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 withPatternsCaseSensitive 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 fc8dde990dd5e..249cdc67f8925 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,174 @@ 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: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"; @@ -653,6 +821,34 @@ 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:39] + 1 │ import withPatternsCaseSensitive 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): foo is forbidden, use bar instead ╭─[no_restricted_imports.tsx:1:39] 1 │ import withPatternsCaseSensitive from 'foo';