diff --git a/packages/eslint-plugin/src/rules/no-restricted-imports.ts b/packages/eslint-plugin/src/rules/no-restricted-imports.ts index e8550856c2c2..d22f9b5d058f 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-imports.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-imports.ts @@ -267,24 +267,38 @@ export default createRule({ const restrictedPatterns = getRestrictedPatterns(options); const allowedImportTypeMatchers: Ignore[] = []; + const allowedImportTypeRegexMatchers: RegExp[] = []; for (const restrictedPattern of restrictedPatterns) { if ( typeof restrictedPattern === 'object' && restrictedPattern.allowTypeImports ) { // Following how ignore is configured in the base rule - allowedImportTypeMatchers.push( - ignore({ - allowRelativePaths: true, - ignoreCase: !restrictedPattern.caseSensitive, - }).add(restrictedPattern.group), - ); + if (restrictedPattern.group) { + allowedImportTypeMatchers.push( + ignore({ + allowRelativePaths: true, + ignoreCase: !restrictedPattern.caseSensitive, + }).add(restrictedPattern.group), + ); + } + if (restrictedPattern.regex) { + allowedImportTypeRegexMatchers.push( + new RegExp( + restrictedPattern.regex, + restrictedPattern.caseSensitive ? 'u' : 'iu', + ), + ); + } } } function isAllowedTypeImportPattern(importSource: string): boolean { return ( // As long as there's one matching pattern that allows type import - allowedImportTypeMatchers.some(matcher => matcher.ignores(importSource)) + allowedImportTypeMatchers.some(matcher => + matcher.ignores(importSource), + ) || + allowedImportTypeRegexMatchers.some(regex => regex.test(importSource)) ); } diff --git a/packages/eslint-plugin/tests/rules/no-restricted-imports.test.ts b/packages/eslint-plugin/tests/rules/no-restricted-imports.test.ts index 5640f285264f..b43a52ef8d71 100644 --- a/packages/eslint-plugin/tests/rules/no-restricted-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/no-restricted-imports.test.ts @@ -290,6 +290,43 @@ import type { foo } from 'import2/private/bar'; }, ], }, + { + code: ` +import type { foo } from 'import1/private/bar'; +import type { foo } from 'import2/private/bar'; + `, + options: [ + { + patterns: [ + { + allowTypeImports: true, + message: 'usage of import1 private modules not allowed.', + regex: 'import1/.*', + }, + { + allowTypeImports: true, + message: 'usage of import2 private modules not allowed.', + regex: 'import2/.*', + }, + ], + }, + ], + }, + { + code: "import { foo } from 'import1/private';", + options: [ + { + patterns: [ + { + allowTypeImports: true, + caseSensitive: true, + message: 'usage of import1 private modules not allowed.', + regex: 'import1/[A-Z]+', + }, + ], + }, + ], + }, { code: "import { type Bar } from 'import-foo';", options: [ @@ -722,6 +759,47 @@ import type { foo } from 'import2/private/bar'; }, ], }, + { + code: "export { foo } from 'import1/private/bar';", + errors: [ + { + messageId: 'patternWithCustomMessage', + type: AST_NODE_TYPES.ExportNamedDeclaration, + }, + ], + options: [ + { + patterns: [ + { + allowTypeImports: true, + message: 'usage of import1 private modules not allowed.', + regex: 'import1/.*', + }, + ], + }, + ], + }, + { + code: "import { foo } from 'import1/private-package';", + errors: [ + { + messageId: 'patternWithCustomMessage', + type: AST_NODE_TYPES.ImportDeclaration, + }, + ], + options: [ + { + patterns: [ + { + allowTypeImports: true, + caseSensitive: true, + message: 'usage of import1 private modules not allowed.', + regex: 'import1/private-[a-z]*', + }, + ], + }, + ], + }, { code: "export * from 'import1';", errors: [ diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 6632125e3b15..970839e19c01 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -558,7 +558,8 @@ declare module 'eslint/lib/rules/no-restricted-imports' { // extended allowTypeImports?: boolean; caseSensitive?: boolean; - group: string[]; + group?: string[]; + regex?: string; message?: string; }[] | string[];