From 4bdf61af182dc1793e229d6f0da2a0e7472f86e6 Mon Sep 17 00:00:00 2001 From: yesl-kim Date: Sun, 11 Aug 2024 17:34:29 +0900 Subject: [PATCH] [Fix] `no-duplicates`: Removing duplicates breaks in TypeScript Fixes #3016. Fixes #2792. --- CHANGELOG.md | 3 +++ src/rules/no-duplicates.js | 16 ++++++++++++++-- tests/src/rules/no-duplicates.js | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba781c23..667d9fffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-extraneous-dependencies`]: allow wrong path ([#3012], thanks [@chabb]) - [`no-cycle`]: use scc algorithm to optimize ([#2998], thanks [@soryy708]) +- [`no-duplicates`]: Removing duplicates breaks in TypeScript ([#3033], thanks [@yesl-kim]) ### Changed - [Docs] `no-extraneous-dependencies`: Make glob pattern description more explicit ([#2944], thanks [@mulztob]) @@ -1121,6 +1122,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#3033]: https://github.com/import-js/eslint-plugin-import/pull/3033 [#3012]: https://github.com/import-js/eslint-plugin-import/pull/3012 [#3011]: https://github.com/import-js/eslint-plugin-import/pull/3011 [#3004]: https://github.com/import-js/eslint-plugin-import/pull/3004 @@ -1962,6 +1964,7 @@ for info on changes for earlier releases. [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg [@xM8WVqaG]: https://github.com/xM8WVqaG [@xpl]: https://github.com/xpl +[@yesl-kim]: https://github.com/yesl-kim [@yndajas]: https://github.com/yndajas [@yordis]: https://github.com/yordis [@Zamiell]: https://github.com/Zamiell diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 033e854e0..d9fb1a130 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -132,6 +132,7 @@ function getFix(first, rest, sourceCode, context) { const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1; const shouldAddSpecifiers = specifiers.length > 0; const shouldRemoveUnnecessary = unnecessaryImports.length > 0; + const preferInline = context.options[0] && context.options[0]['prefer-inline']; if (!(shouldAddDefault || shouldAddSpecifiers || shouldRemoveUnnecessary)) { return undefined; @@ -157,8 +158,7 @@ function getFix(first, rest, sourceCode, context) { ([result, needsComma, existingIdentifiers], specifier) => { const isTypeSpecifier = specifier.importNode.importKind === 'type'; - const preferInline = context.options[0] && context.options[0]['prefer-inline']; - // a user might set prefer-inline but not have a supporting TypeScript version. Flow does not support inline types so this should fail in that case as well. + // a user might set prefer-inline but not have a supporting TypeScript version. Flow does not support inline types so this should fail in that case as well. if (preferInline && (!typescriptPkg || !semver.satisfies(typescriptPkg.version, '>= 4.5'))) { throw new Error('Your version of TypeScript does not support inline type imports.'); } @@ -186,6 +186,18 @@ function getFix(first, rest, sourceCode, context) { const fixes = []; + if (shouldAddSpecifiers && preferInline && first.importKind === 'type') { + // `import type {a} from './foo'` → `import {type a} from './foo'` + const typeIdentifierToken = tokens.find((token) => token.type === 'Identifier' && token.value === 'type'); + fixes.push(fixer.removeRange([typeIdentifierToken.range[0], typeIdentifierToken.range[1] + 1])); + + tokens + .filter((token) => firstExistingIdentifiers.has(token.value)) + .forEach((identifier) => { + fixes.push(fixer.replaceTextRange([identifier.range[0], identifier.range[1]], `type ${identifier.value}`)); + }); + } + if (shouldAddDefault && openBrace == null && shouldAddSpecifiers) { // `import './foo'` → `import def, {...} from './foo'` fixes.push( diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index f83221105..e682f2235 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -704,6 +704,24 @@ context('TypeScript', function () { }, ], }), + test({ + code: "import type {x} from 'foo'; import {type y} from 'foo'", + ...parserConfig, + options: [{ 'prefer-inline': true }], + output: `import {type x,type y} from 'foo'; `, + errors: [ + { + line: 1, + column: 22, + message: "'foo' imported multiple times.", + }, + { + line: 1, + column: 50, + message: "'foo' imported multiple times.", + }, + ], + }), test({ code: "import {type x} from 'foo'; import type {y} from 'foo'", ...parserConfig,