Skip to content

Commit 381267a

Browse files
joaovieiraljharb
authored andcommitted
[New] import/default: support default export in TSExportAssignment
Fixes #1527
1 parent 9516152 commit 381267a

10 files changed

+112
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
66

77
## [Unreleased]
88

9+
### Added
10+
- [`import/default`]: support default export in TSExportAssignment ([#1528], thanks [@joaovieira])
11+
912
### Fixed
1013
- [`group-exports`]: Flow type export awareness ([#1702], thanks [@ernestostifano])
1114
- [`order`]: Recognize pathGroup config for first group ([#1719], [#1724], thanks [@forivall], [@xpl])
@@ -697,6 +700,7 @@ for info on changes for earlier releases.
697700
[#1560]: https://github.com/benmosher/eslint-plugin-import/pull/1560
698701
[#1551]: https://github.com/benmosher/eslint-plugin-import/pull/1551
699702
[#1542]: https://github.com/benmosher/eslint-plugin-import/pull/1542
703+
[#1528]: https://github.com/benmosher/eslint-plugin-import/pull/1528
700704
[#1526]: https://github.com/benmosher/eslint-plugin-import/pull/1526
701705
[#1521]: https://github.com/benmosher/eslint-plugin-import/pull/1521
702706
[#1519]: https://github.com/benmosher/eslint-plugin-import/pull/1519

src/ExportMap.js

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -530,30 +530,50 @@ ExportMap.parse = function (path, content, context) {
530530

531531
// This doesn't declare anything, but changes what's being exported.
532532
if (n.type === 'TSExportAssignment') {
533-
const moduleDecls = ast.body.filter((bodyNode) =>
534-
bodyNode.type === 'TSModuleDeclaration' && bodyNode.id.name === n.expression.name
533+
const exportedName = n.expression.name
534+
const declTypes = [
535+
'VariableDeclaration',
536+
'ClassDeclaration',
537+
'TSDeclareFunction',
538+
'TSEnumDeclaration',
539+
'TSTypeAliasDeclaration',
540+
'TSInterfaceDeclaration',
541+
'TSAbstractClassDeclaration',
542+
'TSModuleDeclaration',
543+
]
544+
const exportedDecls = ast.body.filter(({ type, id, declarations }) =>
545+
declTypes.includes(type) &&
546+
(id && id.name === exportedName || declarations.find(d => d.id.name === exportedName))
535547
)
536-
moduleDecls.forEach((moduleDecl) => {
537-
if (moduleDecl && moduleDecl.body && moduleDecl.body.body) {
538-
moduleDecl.body.body.forEach((moduleBlockNode) => {
548+
if (exportedDecls.length === 0) {
549+
// Export is not referencing any local declaration, must be re-exporting
550+
m.namespace.set('default', captureDoc(source, docStyleParsers, n))
551+
return
552+
}
553+
exportedDecls.forEach((decl) => {
554+
if (decl.type === 'TSModuleDeclaration' && decl && decl.body && decl.body.body) {
555+
decl.body.body.forEach((moduleBlockNode) => {
539556
// Export-assignment exports all members in the namespace, explicitly exported or not.
540-
const exportedDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ?
557+
const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ?
541558
moduleBlockNode.declaration :
542559
moduleBlockNode
543560

544-
if (exportedDecl.type === 'VariableDeclaration') {
545-
exportedDecl.declarations.forEach((decl) =>
546-
recursivePatternCapture(decl.id,(id) => m.namespace.set(
561+
if (namespaceDecl.type === 'VariableDeclaration') {
562+
namespaceDecl.declarations.forEach((d) =>
563+
recursivePatternCapture(d.id, (id) => m.namespace.set(
547564
id.name,
548-
captureDoc(source, docStyleParsers, decl, exportedDecl, moduleBlockNode))
565+
captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode))
549566
)
550567
)
551568
} else {
552569
m.namespace.set(
553-
exportedDecl.id.name,
570+
namespaceDecl.id.name,
554571
captureDoc(source, docStyleParsers, moduleBlockNode))
555572
}
556573
})
574+
} else {
575+
// Export as default
576+
m.namespace.set('default', captureDoc(source, docStyleParsers, decl))
557577
}
558578
})
559579
}

tests/files/typescript-default.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default function foobar() {};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import { getFoo } from './typescript';
2+
export = getFoo;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export = foobar;
2+
3+
declare const foobar: number;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export = foobar;
2+
3+
declare function foobar(): void;
4+
declare namespace foobar {
5+
type MyType = string
6+
enum MyEnum {
7+
Foo,
8+
Bar,
9+
Baz
10+
}
11+
}

tests/src/rules/default.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test, SYNTAX_CASES } from '../utils'
1+
import { test, SYNTAX_CASES, getTSParsers } from '../utils'
22
import { RuleTester } from 'eslint'
33

44
import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'
@@ -152,3 +152,56 @@ if (!CASE_SENSITIVE_FS) {
152152
],
153153
})
154154
}
155+
156+
context('TypeScript', function () {
157+
getTSParsers().forEach((parser) => {
158+
ruleTester.run(`default`, rule, {
159+
valid: [
160+
test({
161+
code: `import foobar from "./typescript-default"`,
162+
parser: parser,
163+
settings: {
164+
'import/parsers': { [parser]: ['.ts'] },
165+
'import/resolver': { 'eslint-import-resolver-typescript': true },
166+
},
167+
}),
168+
test({
169+
code: `import foobar from "./typescript-export-assign-default"`,
170+
parser: parser,
171+
settings: {
172+
'import/parsers': { [parser]: ['.ts'] },
173+
'import/resolver': { 'eslint-import-resolver-typescript': true },
174+
},
175+
}),
176+
test({
177+
code: `import foobar from "./typescript-export-assign-mixed"`,
178+
parser: parser,
179+
settings: {
180+
'import/parsers': { [parser]: ['.ts'] },
181+
'import/resolver': { 'eslint-import-resolver-typescript': true },
182+
},
183+
}),
184+
test({
185+
code: `import foobar from "./typescript-export-assign-default-reexport"`,
186+
parser: parser,
187+
settings: {
188+
'import/parsers': { [parser]: ['.ts'] },
189+
'import/resolver': { 'eslint-import-resolver-typescript': true },
190+
},
191+
}),
192+
],
193+
194+
invalid: [
195+
test({
196+
code: `import foobar from "./typescript"`,
197+
parser: parser,
198+
settings: {
199+
'import/parsers': { [parser]: ['.ts'] },
200+
'import/resolver': { 'eslint-import-resolver-typescript': true },
201+
},
202+
errors: ['No default export found in imported module "./typescript".'],
203+
}),
204+
],
205+
})
206+
})
207+
})

tests/src/rules/named.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,12 @@ ruleTester.run('named (export *)', rule, {
285285

286286
context('TypeScript', function () {
287287
getTSParsers().forEach((parser) => {
288-
['typescript', 'typescript-declare', 'typescript-export-assign', 'typescript-export-assign-merged'].forEach((source) => {
288+
[
289+
'typescript',
290+
'typescript-declare',
291+
'typescript-export-assign-namespace',
292+
'typescript-export-assign-namespace-merged',
293+
].forEach((source) => {
289294
ruleTester.run(`named`, rule, {
290295
valid: [
291296
test({

0 commit comments

Comments
 (0)