From e9518c4a1c6d7af54e145f2a6a8ed718a40ab902 Mon Sep 17 00:00:00 2001 From: Martin Garcia Date: Thu, 27 Jan 2022 17:10:07 +0100 Subject: [PATCH] [Fix] `export`: false positive for typescript namespace merging Fixes #1964. --- CHANGELOG.md | 7 ++++++- src/rules/export.js | 23 ++++++++++++++++++++++- tests/src/rules/export.js | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52777b296a..7d40bb7643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`, `namespace`, `no-unused-modules`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) - [`no-dynamic-require`]: support dynamic import with espree ([#2371], thanks [@sosukesuzuki]) +### Fixed +- [`export`]/TypeScript: false positive for typescript namespace merging ([#2375], thanks [@magarcia]) + ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) @@ -964,6 +967,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2375]: https://github.com/import-js/eslint-plugin-import/pull/2375 [#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 [#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 [#2332]: https://github.com/import-js/eslint-plugin-import/pull/2332 @@ -1571,6 +1575,7 @@ for info on changes for earlier releases. [@ludofischer]: https://github.com/ludofischer [@lukeapage]: https://github.com/lukeapage [@lydell]: https://github.com/lydell +[@magarcia]: https://github.com/magarcia [@Mairu]: https://github.com/Mairu [@malykhinvi]: https://github.com/malykhinvi [@manovotny]: https://github.com/manovotny @@ -1661,4 +1666,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/export.js b/src/rules/export.js index b9378f0910..b1f596dd17 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -45,6 +45,27 @@ function isTypescriptFunctionOverloads(nodes) { ); } +/** + * Detect merging Namespaces with Classes, Functions, or Enums like: + * ```ts + * export class Foo { } + * export namespace Foo { } + * ``` + * @param {Set} nodes + * @returns {boolean} + */ +function isTypescriptNamespaceMerging(nodes) { + const types = new Set(Array.from(nodes, (node) => node.parent.type)); + return ( + types.has('TSModuleDeclaration') && + (types.size === 1 || + (types.size === 2 && ( + types.has('ClassDeclaration') || types.has('FunctionDeclaration') || types.has('TSEnumDeclaration') + )) + ) + ); +} + module.exports = { meta: { type: 'problem', @@ -156,7 +177,7 @@ module.exports = { for (const [name, nodes] of named) { if (nodes.size <= 1) continue; - if (isTypescriptFunctionOverloads(nodes)) continue; + if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) continue; for (const node of nodes) { if (name === 'default') { diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index d075aea6bd..f446ddb7f9 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -219,6 +219,27 @@ context('TypeScript', function () { } `, }, parserConfig)), + test(Object.assign({ + code: ` + export class Foo { } + export namespace Foo { } + export namespace Foo { + export class Bar {} + } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export function Foo() { } + export namespace Foo { } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export enum Foo { } + export namespace Foo { } + `, + }, parserConfig)), test(Object.assign({ code: 'export * from "./file1.ts"', filename: testFilePath('typescript-d-ts/file-2.ts'),