From c14c9bd665525865f023b7214bbaf3d0124563dd Mon Sep 17 00:00:00 2001 From: Ben Munro Date: Mon, 7 Oct 2019 18:03:41 +0200 Subject: [PATCH] [Fix] `named`/`ExportMap`: Fix ExportMap for a merged typescript namespace A typescript namespace may be declared multiple times, it is then merged together and considered to be a single declaration from consuming code. In the case where a merged namespace is assigned as the export from a module then the declarations from all the instances of the namespace in the AST need to be considered as exported. --- CHANGELOG.md | 3 ++ src/ExportMap.js | 44 ++++++++++--------- .../typescript-export-assign-merged.d.ts | 41 +++++++++++++++++ tests/src/rules/named.js | 2 +- 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 tests/files/typescript-export-assign-merged.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1fcd225..7a9c6519f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - `default`: make error message less confusing ([#1470], thanks [@golopot]) +- Support export of a merged typescript namespace declaration ([#1495], thanks [@benmunro]) ## [2.18.2] - 2019-07-19 - Skip warning on type interfaces ([#1425], thanks [@lencioni]) @@ -609,6 +610,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#1495]: https://github.com/benmosher/eslint-plugin-import/pull/1495 [#1472]: https://github.com/benmosher/eslint-plugin-import/pull/1472 [#1470]: https://github.com/benmosher/eslint-plugin-import/pull/1470 [#1436]: https://github.com/benmosher/eslint-plugin-import/pull/1436 @@ -992,3 +994,4 @@ for info on changes for earlier releases. [@atikenny]: https://github.com/atikenny [@schmidsi]: https://github.com/schmidsi [@TrevorBurnham]: https://github.com/TrevorBurnham +[@benmunro]: https://github.com/benmunro diff --git a/src/ExportMap.js b/src/ExportMap.js index ebeb4fad7..c9544c9c8 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -514,30 +514,32 @@ ExportMap.parse = function (path, content, context) { // This doesn't declare anything, but changes what's being exported. if (n.type === 'TSExportAssignment') { - const moduleDecl = ast.body.find((bodyNode) => + const moduleDecls = ast.body.filter((bodyNode) => bodyNode.type === 'TSModuleDeclaration' && bodyNode.id.name === n.expression.name ) - if (moduleDecl && moduleDecl.body && moduleDecl.body.body) { - moduleDecl.body.body.forEach((moduleBlockNode) => { - // Export-assignment exports all members in the namespace, explicitly exported or not. - const exportedDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ? - moduleBlockNode.declaration : - moduleBlockNode - - if (exportedDecl.type === 'VariableDeclaration') { - exportedDecl.declarations.forEach((decl) => - recursivePatternCapture(decl.id,(id) => m.namespace.set( - id.name, - captureDoc(source, docStyleParsers, decl, exportedDecl, moduleBlockNode)) + moduleDecls.forEach((moduleDecl) => { + if (moduleDecl && moduleDecl.body && moduleDecl.body.body) { + moduleDecl.body.body.forEach((moduleBlockNode) => { + // Export-assignment exports all members in the namespace, explicitly exported or not. + const exportedDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ? + moduleBlockNode.declaration : + moduleBlockNode + + if (exportedDecl.type === 'VariableDeclaration') { + exportedDecl.declarations.forEach((decl) => + recursivePatternCapture(decl.id,(id) => m.namespace.set( + id.name, + captureDoc(source, docStyleParsers, decl, exportedDecl, moduleBlockNode)) + ) ) - ) - } else { - m.namespace.set( - exportedDecl.id.name, - captureDoc(source, docStyleParsers, moduleBlockNode)) - } - }) - } + } else { + m.namespace.set( + exportedDecl.id.name, + captureDoc(source, docStyleParsers, moduleBlockNode)) + } + }) + } + }) } }) diff --git a/tests/files/typescript-export-assign-merged.d.ts b/tests/files/typescript-export-assign-merged.d.ts new file mode 100644 index 000000000..377a10d20 --- /dev/null +++ b/tests/files/typescript-export-assign-merged.d.ts @@ -0,0 +1,41 @@ +export = AssignedNamespace; + +declare namespace AssignedNamespace { + type MyType = string + enum MyEnum { + Foo, + Bar, + Baz + } +} + +declare namespace AssignedNamespace { + interface Foo { + native: string | number + typedef: MyType + enum: MyEnum + } + + abstract class Bar { + abstract foo(): Foo + + method(); + } + + export function getFoo() : MyType; + + export module MyModule { + export function ModuleFunction(); + } + + export namespace MyNamespace { + export function NamespaceFunction(); + + export module NSModule { + export function NSModuleFunction(); + } + } + + // Export-assignment exports all members in the namespace, explicitly exported or not. + // interface NotExported {} +} diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 9e15a34b0..90e07ba39 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -284,7 +284,7 @@ ruleTester.run('named (export *)', rule, { context('Typescript', function () { getTSParsers().forEach((parser) => { - ['typescript', 'typescript-declare', 'typescript-export-assign'].forEach((source) => { + ['typescript', 'typescript-declare', 'typescript-export-assign', 'typescript-export-assign-merged'].forEach((source) => { ruleTester.run(`named`, rule, { valid: [ test({