fix(linter): rule no-restricted-imports: support option allowImportNames (#8002)

waiting for #7943
This commit is contained in:
Alexander S. 2024-12-20 13:56:05 +01:00 committed by GitHub
parent ac097e9160
commit b3f38aeab9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 205 additions and 168 deletions

View file

@ -36,10 +36,11 @@ struct NoRestrictedImportsConfig {
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RestrictedPath {
name: CompactStr,
#[serde(rename = "importNames")]
import_names: Option<Box<[CompactStr]>>,
allow_import_names: Option<Box<[CompactStr]>>,
message: Option<CompactStr>,
}
@ -110,6 +111,7 @@ fn add_configuration_from_string(paths: &mut Vec<RestrictedPath>, module_name: &
paths.push(RestrictedPath {
name: CompactStr::new(module_name),
import_names: None,
allow_import_names: None,
message: None,
});
}
@ -155,42 +157,11 @@ impl Rule for NoRestrictedImports {
continue;
}
if let Some(import_names) = &path.import_names {
match &entry.import_name {
ImportImportName::Name(import) => {
let name = CompactStr::new(import.name());
if import_names.contains(&name) {
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
}
ImportImportName::Default(_) => {
if import_names.contains(&CompactStr::new("default")) {
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
}
ImportImportName::NamespaceObject => {
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
}
} else {
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
if is_import_name_allowed(&entry.import_name, path) {
continue;
}
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
}
for (source, requests) in &module_record.requested_modules {
@ -228,31 +199,11 @@ impl Rule for NoRestrictedImports {
continue;
}
if let Some(import_names) = &path.import_names {
match &entry.import_name {
ExportImportName::Name(import_name)
if import_names.contains(&import_name.name) =>
{
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
ExportImportName::All | ExportImportName::AllButDefault => {
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
_ => (),
}
} else {
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
if is_export_name_allowed(&entry.import_name, path) {
continue;
}
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
}
}
@ -265,37 +216,95 @@ impl Rule for NoRestrictedImports {
continue;
}
if let Some(import_names) = &path.import_names {
match &entry.import_name {
ExportImportName::Name(import_name)
if import_names.contains(&import_name.name) =>
{
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
ExportImportName::All | ExportImportName::AllButDefault => {
no_restricted_imports_diagnostic(
ctx,
span,
path.message.clone(),
source,
);
}
_ => (),
}
} else {
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
if is_export_name_allowed(&entry.import_name, path) {
continue;
}
no_restricted_imports_diagnostic(ctx, span, path.message.clone(), source);
}
}
}
}
}
fn is_import_name_allowed(name: &ImportImportName, path: &RestrictedPath) -> bool {
match &name {
ImportImportName::Name(import) => {
// fast check if this name is allowed
if path
.allow_import_names
.as_ref()
.is_some_and(|allowed| allowed.contains(&import.name))
{
return true;
}
// when no importNames option is provided, no import in general is allowed
if path.import_names.as_ref().is_none() {
return false;
}
// the name is found is the importNames list
if path
.import_names
.as_ref()
.is_some_and(|disallowed| disallowed.contains(&import.name))
{
return false;
}
// we allow it
true
}
ImportImportName::Default(_) => {
if path
.import_names
.as_ref()
.is_some_and(|disallowed| disallowed.contains(&CompactStr::new("default")))
|| path.import_names.as_ref().is_none()
{
return false;
}
true
}
ImportImportName::NamespaceObject => false,
}
}
fn is_export_name_allowed(name: &ExportImportName, path: &RestrictedPath) -> bool {
match &name {
ExportImportName::Name(import_name) => {
// fast check if this name is allowed
if path
.allow_import_names
.as_ref()
.is_some_and(|allowed| allowed.contains(&import_name.name))
{
return true;
}
// when no importNames option is provided, no import in general is allowed
if path.import_names.as_ref().is_none() {
return false;
}
// the name is found is the importNames list
if path
.import_names
.as_ref()
.is_some_and(|disallowed| disallowed.contains(&import_name.name))
{
return false;
}
true
}
ExportImportName::All | ExportImportName::AllButDefault => false,
ExportImportName::Null => true,
}
}
#[test]
fn test() {
use crate::tester::Tester;
@ -627,52 +636,52 @@ fn test() {
}]
}])),
),
// (
// 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"]
// }]
// }])),
// ),
(
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!([{
@ -1507,25 +1516,25 @@ fn test() {
// }]
// }])),
// ),
// (
// r#"import { AllowedObject, DisallowedObject } from "foo";"#,
// Some(serde_json::json!([{
// "paths": [{
// "name": "foo",
// "allowImportNames": ["AllowedObject"]
// }]
// }])),
// ),
// (
// r#"import { AllowedObject, DisallowedObject } from "foo";"#,
// Some(serde_json::json!([{
// "paths": [{
// "name": "foo",
// "allowImportNames": ["AllowedObject"],
// "message": r#"Only "AllowedObject" is allowed to be imported from "foo"."#
// }]
// }])),
// ),
(
r#"import { AllowedObject, DisallowedObject } from "foo";"#,
Some(serde_json::json!([{
"paths": [{
"name": "foo",
"allowImportNames": ["AllowedObject"]
}]
}])),
),
(
r#"import { AllowedObject, DisallowedObject } from "foo";"#,
Some(serde_json::json!([{
"paths": [{
"name": "foo",
"allowImportNames": ["AllowedObject"],
"message": r#"Only "AllowedObject" is allowed to be imported from "foo"."#
}]
}])),
),
// (
// r#"import { AllowedObject, DisallowedObject } from "foo";"#,
// Some(serde_json::json!([{
@ -1545,25 +1554,25 @@ fn test() {
// }]
// }])),
// ),
// (
// r#"import * as AllowedObject from "foo";"#,
// Some(serde_json::json!([{
// "paths": [{
// "name": "foo",
// "allowImportNames": ["AllowedObject"]
// }]
// }])),
// ),
// (
// r#"import * as AllowedObject from "foo";"#,
// Some(serde_json::json!([{
// "paths": [{
// "name": "foo",
// "allowImportNames": ["AllowedObject"],
// "message": r#"Only "AllowedObject" is allowed to be imported from "foo"."#
// }]
// }])),
// ),
(
r#"import * as AllowedObject from "foo";"#,
Some(serde_json::json!([{
"paths": [{
"name": "foo",
"allowImportNames": ["AllowedObject"]
}]
}])),
),
(
r#"import * as AllowedObject from "foo";"#,
Some(serde_json::json!([{
"paths": [{
"name": "foo",
"allowImportNames": ["AllowedObject"],
"message": r#"Only "AllowedObject" is allowed to be imported from "foo"."#
}]
}])),
),
// (
// r#"import * as AllowedObject from "foo/bar";"#,
// Some(serde_json::json!([{

View file

@ -484,3 +484,31 @@ snapshot_kind: text
· ──────
╰────
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";
· ─────
╰────
help: Remove the import statement.
⚠ eslint(no-restricted-imports): Only "AllowedObject" is allowed to be imported from "foo".
╭─[no_restricted_imports.tsx:1:49]
1 │ import { AllowedObject, DisallowedObject } from "foo";
· ─────
╰────
help: Remove the import statement.
⚠ 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".
╭─[no_restricted_imports.tsx:1:32]
1 │ import * as AllowedObject from "foo";
· ─────
╰────
help: Remove the import statement.