Skip to content

Commit

Permalink
Fix ExportMap for a merged typescript namespace
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
benmunro committed Oct 7, 2019
1 parent 5e143b2 commit 6b81bd9
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,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])
Expand Down Expand Up @@ -608,6 +609,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
Expand Down Expand Up @@ -989,3 +991,4 @@ for info on changes for earlier releases.
[@JounQin]: https://github.com/JounQin
[@atikenny]: https://github.com/atikenny
[@schmidsi]: https://github.com/schmidsi
[@benmunro]: https://github.com/benmunro
44 changes: 23 additions & 21 deletions src/ExportMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
})
}
})
}
})

Expand Down
41 changes: 41 additions & 0 deletions tests/files/typescript-export-assign-merged.d.ts
Original file line number Diff line number Diff line change
@@ -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 {}
}
2 changes: 1 addition & 1 deletion tests/src/rules/named.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down

0 comments on commit 6b81bd9

Please sign in to comment.