diff --git a/src/ExportMap.js b/src/ExportMap.js index 5a36b220b8..fb4f7afcb4 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -551,26 +551,44 @@ ExportMap.parse = function (path, content, context) { return } exportedDecls.forEach((decl) => { - if (decl.type === 'TSModuleDeclaration' && decl && decl.body && decl.body.body) { - decl.body.body.forEach((moduleBlockNode) => { - // Export-assignment exports all members in the namespace, explicitly exported or not. - const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ? - moduleBlockNode.declaration : - moduleBlockNode - - if (namespaceDecl.type === 'VariableDeclaration') { - namespaceDecl.declarations.forEach((d) => - recursivePatternCapture(d.id, (id) => m.namespace.set( - id.name, - captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode)) + if (decl.type === 'TSModuleDeclaration') { + let currentDecl = decl + let moduleDecls = [decl] + + // Find recursive TSModuleDeclaration + while (currentDecl.body && currentDecl.body.type === 'TSModuleDeclaration') { + currentDecl = currentDecl.body + moduleDecls.push(currentDecl) + } + + if (currentDecl.body && currentDecl.body.body) { + currentDecl.body.body.forEach((moduleBlockNode) => { + // Export-assignment exports all members in the namespace, + // explicitly exported or not. + const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ? + moduleBlockNode.declaration : + moduleBlockNode + + if (namespaceDecl.type === 'VariableDeclaration') { + namespaceDecl.declarations.forEach((d) => + recursivePatternCapture(d.id, (id) => m.namespace.set( + id.name, + captureDoc( + source, + docStyleParsers, + ...moduleDecls, + namespaceDecl, + moduleBlockNode + ) + )) ) - ) - } else { - m.namespace.set( - namespaceDecl.id.name, - captureDoc(source, docStyleParsers, moduleBlockNode)) - } - }) + } else { + m.namespace.set( + namespaceDecl.id.name, + captureDoc(source, docStyleParsers, moduleBlockNode)) + } + }) + } } else { // Export as default m.namespace.set('default', captureDoc(source, docStyleParsers, decl)) diff --git a/tests/files/typescript-declare-nested.d.ts b/tests/files/typescript-declare-nested.d.ts new file mode 100644 index 0000000000..b88c66158a --- /dev/null +++ b/tests/files/typescript-declare-nested.d.ts @@ -0,0 +1,13 @@ +declare namespace foo { + interface SomeInterface { + a: string; + } +} + +declare namespace foo.bar { + interface SomeOtherInterface { + b: string; + } +} + +export = foo; diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index cfc6305d5a..bbc0cc798b 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES } from '../utils' +import { test, SYNTAX_CASES, getTSParsers } from '../utils' import { RuleTester } from 'eslint' var ruleTester = new RuleTester({ env: { es6: true }}) @@ -120,6 +120,16 @@ const valid = [ }, }), + // Typescript + ...getTSParsers().map((parser) => test({ + code: `import * as foo from "./typescript-declare-nested"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + })), + ...SYNTAX_CASES, ]