From c71f423b89bf034caf2a4f1bb3ed0389b72f3aa9 Mon Sep 17 00:00:00 2001 From: YeonJuan Date: Wed, 7 Oct 2020 02:12:59 +0900 Subject: [PATCH] fix(eslint-plugin): [no-duplicate-imports] distinguish member, default (#2637) --- .../src/rules/no-duplicate-imports.ts | 25 +++++++++++-- .../tests/rules/no-duplicate-imports.test.ts | 37 ++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts index d539845746f..a6f368902e3 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-imports.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-imports.ts @@ -35,7 +35,8 @@ export default util.createRule({ create(context, [option]) { const rules = baseRule.create(context); const includeExports = option.includeExports; - const typeImports = new Set(); + const typeMemberImports = new Set(); + const typeDefaultImports = new Set(); const typeExports = new Set(); function report( @@ -62,16 +63,32 @@ export default util.createRule({ ); } + function isAllMemberImport(node: TSESTree.ImportDeclaration): boolean { + return node.specifiers.every( + specifier => specifier.type === AST_NODE_TYPES.ImportSpecifier, + ); + } + function checkTypeImport(node: TSESTree.ImportDeclaration): void { if (isStringLiteral(node.source)) { const value = node.source.value; - if (typeImports.has(value)) { + const isMemberImport = isAllMemberImport(node); + if ( + isMemberImport + ? typeMemberImports.has(value) + : typeDefaultImports.has(value) + ) { report('importType', node, value); } + if (includeExports && typeExports.has(value)) { report('importTypeAs', node, value); } - typeImports.add(value); + if (isMemberImport) { + typeMemberImports.add(value); + } else { + typeDefaultImports.add(value); + } } } @@ -83,7 +100,7 @@ export default util.createRule({ if (typeExports.has(value)) { report('exportType', node, value); } - if (typeImports.has(value)) { + if (typeMemberImports.has(value) || typeDefaultImports.has(value)) { report('exportTypeAs', node, value); } typeExports.add(value); diff --git a/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts b/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts index d133a7aadd9..98e12fbeb7a 100644 --- a/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts @@ -5,7 +5,7 @@ const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', }); -ruleTester.run('no-dupe-class-members', rule, { +ruleTester.run('no-duplicate-imports', rule, { valid: [ { code: "import type foo from 'foo';", @@ -13,6 +13,24 @@ ruleTester.run('no-dupe-class-members', rule, { { code: "import type { foo } from 'foo';", }, + { + code: ` + import type { foo } from 'foo'; + import type Bar from 'foo'; + `, + }, + { + code: ` + import type Foo from 'foo'; + import type { bar } from 'foo'; + `, + }, + { + code: ` + import type Foo from 'foo'; + import type { bar as Bar } from 'foo'; + `, + }, { code: ` import foo from 'foo'; @@ -69,6 +87,14 @@ ruleTester.run('no-dupe-class-members', rule, { `, options: [{ includeExports: true }], }, + { + code: ` + import type Foo from 'foo'; + import type { bar } from 'foo'; + export type { bar }; + `, + options: [{ includeExports: true }], + }, ], invalid: [ { @@ -116,6 +142,15 @@ ruleTester.run('no-dupe-class-members', rule, { options: [{ includeExports: true }], errors: [{ messageId: 'exportTypeAs' }], }, + { + code: ` + import type Foo from 'foo'; + import type { bar } from 'foo'; + export type { bar } from 'foo'; + `, + options: [{ includeExports: true }], + errors: [{ messageId: 'exportTypeAs' }], + }, { code: ` export type * as foo from 'foo';