From 712ee494407bf08c7e1ffcbc19b5d95c4ecf7655 Mon Sep 17 00:00:00 2001 From: Nathan Walters Date: Tue, 17 Aug 2021 14:29:19 -0700 Subject: [PATCH] [fix] `no-duplicates`: correctly handle case of mixed default/named type imports Co-authored-by: Nathan Walters Co-authored-by: Gord Pearson --- CHANGELOG.md | 6 ++- src/rules/no-duplicates.js | 22 ++++++++--- tests/src/rules/no-duplicates.js | 65 +++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e59d24626..a05937ab5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - `ExportMap`: Add default export when esModuleInterop is true and anything is exported ([#2184], thanks [@Maxim-Mazurok]) - [`named`], [`namespace`]: properly set reexports on `export * as … from` ([#1998], [#2161], thanks [@ljharb]) +- [`no-duplicates`]: correctly handle case of mixed default/named type imports ([#2149], thanks [@GoodForOneFare], [@nwalters512]) ### Changed - [Docs] `max-dependencies`: 📖 Document `ignoreTypeImports` option ([#2196], thanks [@himynameisdave]) @@ -897,6 +898,7 @@ for info on changes for earlier releases. [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 [#2158]: https://github.com/import-js/eslint-plugin-import/pull/2158 [#2156]: https://github.com/import-js/eslint-plugin-import/pull/2156 +[#2149]: https://github.com/benmosher/eslint-plugin-import/pull/2149 [#2146]: https://github.com/import-js/eslint-plugin-import/pull/2146 [#2140]: https://github.com/import-js/eslint-plugin-import/pull/2140 [#2138]: https://github.com/import-js/eslint-plugin-import/pull/2138 @@ -1404,6 +1406,7 @@ for info on changes for earlier releases. [@gavriguy]: https://github.com/gavriguy [@giodamelio]: https://github.com/giodamelio [@golopot]: https://github.com/golopot +[@GoodForOneFare]: https://github.com/GoodForOneFare [@graingert]: https://github.com/graingert [@grit96]: https://github.com/grit96 [@guillaumewuip]: https://github.com/guillaumewuip @@ -1471,6 +1474,7 @@ for info on changes for earlier releases. [@nicolashenry]: https://github.com/nicolashenry [@noelebrun]: https://github.com/noelebrun [@ntdb]: https://github.com/ntdb +[@nwalters512]: https://github.com/nwalters512 [@panrafal]: https://github.com/panrafal [@paztis]: https://github.com/paztis [@pcorpet]: https://github.com/pcorpet @@ -1533,4 +1537,4 @@ for info on changes for earlier releases. [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg [@xpl]: https://github.com/xpl [@yordis]: https://github.com/yordis -[@zloirock]: https://github.com/zloirock \ No newline at end of file +[@zloirock]: https://github.com/zloirock diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 43a29506b..634410197 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -276,13 +276,22 @@ module.exports = { const imported = new Map(); const nsImported = new Map(); - const typesImported = new Map(); + const defaultTypesImported = new Map(); + const namedTypesImported = new Map(); + + function getImportMap(n) { + if (n.importKind === 'type') { + return n.specifiers[0].type === 'ImportDefaultSpecifier' ? defaultTypesImported : namedTypesImported; + } + + return hasNamespace(n) ? nsImported : imported; + } + return { - 'ImportDeclaration': function (n) { + ImportDeclaration(n) { // resolved path will cover aliased duplicates const resolvedPath = resolver(n.source.value); - const importMap = n.importKind === 'type' ? typesImported : - (hasNamespace(n) ? nsImported : imported); + const importMap = getImportMap(n); if (importMap.has(resolvedPath)) { importMap.get(resolvedPath).push(n); @@ -291,10 +300,11 @@ module.exports = { } }, - 'Program:exit': function () { + 'Program:exit'() { checkImports(imported, context); checkImports(nsImported, context); - checkImports(typesImported, context); + checkImports(defaultTypesImported, context); + checkImports(namedTypesImported, context); }, }; }, diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index d1b9c7456..c6b355ab8 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -434,8 +434,71 @@ context('TypeScript', function() { code: "import type { x } from './foo'; import y from './foo'", ...parserConfig, }), + test({ + code: "import type x from './foo'; import type y from './bar'", + ...parserConfig, + }), + test({ + code: "import type {x} from './foo'; import type {y} from './bar'", + ...parserConfig, + }), + test({ + code: "import type x from './foo'; import type {y} from './foo'", + ...parserConfig, + }), + ], + invalid: [ + test({ + code: "import type x from './foo'; import type y from './foo'", + ...parserConfig, + errors: [ + { + line: 1, + column: 20, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 48, + message: "'./foo' imported multiple times.", + }, + ], + }), + test({ + code: "import type x from './foo'; import type x from './foo'", + output: "import type x from './foo'; ", + ...parserConfig, + errors: [ + { + line: 1, + column: 20, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 48, + message: "'./foo' imported multiple times.", + }, + ], + }), + test({ + code: "import type {x} from './foo'; import type {y} from './foo'", + ...parserConfig, + output: `import type {x,y} from './foo'; `, + errors: [ + { + line: 1, + column: 22, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 52, + message: "'./foo' imported multiple times.", + }, + ], + }), ], - invalid: [], }); }); });