Skip to content

Commit

Permalink
fix(linter): rule no-restricted-imports support regex option insi…
Browse files Browse the repository at this point in the history
…de `patterns`
  • Loading branch information
Sysix committed Dec 23, 2024
1 parent d84d60a commit acbff7b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 41 deletions.
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
121 changes: 81 additions & 40 deletions crates/oxc_linter/src/rules/eslint/no_restricted_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,7 +57,8 @@ struct RestrictedPath {
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RestrictedPattern {
group: Vec<CompactStr>,
group: Option<Vec<CompactStr>>,
regex: Option<CompactStr>,
import_names: Option<Box<[CompactStr]>>,
allow_import_names: Option<Box<[CompactStr]>>,
case_sensitive: Option<bool>,
Expand Down Expand Up @@ -148,6 +150,9 @@ fn add_configuration_patterns_from_object(
}
Value::Object(_) => {
if let Ok(path) = serde_json::from_value::<RestrictedPattern>(path_value.clone()) {
if path.group.is_some() && path.regex.is_some() {
// ToDo: not allowed
}
patterns.push(path);
}
}
Expand All @@ -158,7 +163,8 @@ fn add_configuration_patterns_from_object(

fn add_configuration_patterns_from_string(paths: &mut Vec<RestrictedPattern>, 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,
Expand Down Expand Up @@ -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());
}
Expand All @@ -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(&reg_string, flags) else {
return false;
};

reg_exp.find(name.name()).is_some()
}
}

impl Rule for NoRestrictedImports {
Expand Down Expand Up @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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![
Expand Down Expand Up @@ -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!([{
Expand Down
14 changes: 14 additions & 0 deletions crates/oxc_linter/src/snapshots/eslint_no_restricted_imports.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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]
1import 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]
1import withPatternsCaseSensitive from 'FOO';
· ─────
╰────
help: Remove the import statement.

eslint(no-restricted-imports): foo is forbidden, use bar instead
╭─[no_restricted_imports.tsx:1:39]
1import withPatternsCaseSensitive from 'foo';
Expand Down

0 comments on commit acbff7b

Please sign in to comment.