Skip to content

Commit e6debc8

Browse files
Maxim-Mazurokljharb
authored andcommitted
[New] import/default: support default export in TSExportAssignment
1 parent e1ed323 commit e6debc8

File tree

7 files changed

+92
-10
lines changed

7 files changed

+92
-10
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@
107107
"minimatch": "^3.0.4",
108108
"object.values": "^1.1.0",
109109
"read-pkg-up": "^2.0.0",
110-
"resolve": "^1.12.0"
110+
"resolve": "^1.12.0",
111+
"tsconfig-paths": "^3.9.0"
111112
},
112113
"nyc": {
113114
"require": [

src/ExportMap.js

+33-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore'
1313
import { hashObject } from 'eslint-module-utils/hash'
1414
import * as unambiguous from 'eslint-module-utils/unambiguous'
1515

16+
import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader'
17+
18+
import includes from 'array-includes'
19+
1620
const log = debug('eslint-plugin-import:ExportMap')
1721

1822
const exportCache = new Map()
@@ -445,6 +449,21 @@ ExportMap.parse = function (path, content, context) {
445449

446450
const source = makeSourceCode(content, ast)
447451

452+
function isEsModuleInterop() {
453+
const tsConfig = tsConfigLoader({
454+
cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(),
455+
getEnv: (key) => process.env[key],
456+
})
457+
try {
458+
if (tsConfig.tsConfigPath !== undefined) {
459+
const json = fs.readFileSync(tsConfig.tsConfigPath)
460+
return JSON.parse(json).compilerOptions.esModuleInterop
461+
}
462+
} catch (e) {
463+
return false
464+
}
465+
}
466+
448467
ast.body.forEach(function (n) {
449468

450469
if (n.type === 'ExportDefaultDeclaration') {
@@ -528,9 +547,14 @@ ExportMap.parse = function (path, content, context) {
528547
})
529548
}
530549

550+
const isEsModuleInteropTrue = isEsModuleInterop()
551+
552+
const exports = ['TSExportAssignment']
553+
isEsModuleInteropTrue && exports.push('TSNamespaceExportDeclaration')
554+
531555
// This doesn't declare anything, but changes what's being exported.
532-
if (n.type === 'TSExportAssignment') {
533-
const exportedName = n.expression.name
556+
if (includes(exports, n.type)) {
557+
const exportedName = n.expression && n.expression.name || n.id.name
534558
const declTypes = [
535559
'VariableDeclaration',
536560
'ClassDeclaration',
@@ -541,18 +565,18 @@ ExportMap.parse = function (path, content, context) {
541565
'TSAbstractClassDeclaration',
542566
'TSModuleDeclaration',
543567
]
544-
const exportedDecls = ast.body.filter(({ type, id, declarations }) =>
545-
declTypes.includes(type) &&
546-
(
547-
(id && id.name === exportedName) ||
548-
(declarations && declarations.find(d => d.id.name === exportedName))
549-
)
550-
)
568+
const exportedDecls = ast.body.filter(({ type, id, declarations }) => includes(declTypes, type) && (
569+
(id && id.name === exportedName) ||
570+
(declarations && declarations.find(d => d.id.name === exportedName))
571+
))
551572
if (exportedDecls.length === 0) {
552573
// Export is not referencing any local declaration, must be re-exporting
553574
m.namespace.set('default', captureDoc(source, docStyleParsers, n))
554575
return
555576
}
577+
if (isEsModuleInteropTrue) {
578+
m.namespace.set('default', {})
579+
}
556580
exportedDecls.forEach((decl) => {
557581
if (decl.type === 'TSModuleDeclaration') {
558582
if (decl.body && decl.body.type === 'TSModuleDeclaration') {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export as namespace Foo
2+
3+
export function bar(): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"esModuleInterop": true
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export = FooBar;
2+
3+
declare namespace FooBar {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"esModuleInterop": true
4+
}
5+
}

tests/src/rules/default.js

+41
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path'
12
import { test, SYNTAX_CASES, getTSParsers } from '../utils'
23
import { RuleTester } from 'eslint'
34

@@ -189,6 +190,28 @@ context('TypeScript', function () {
189190
'import/resolver': { 'eslint-import-resolver-typescript': true },
190191
},
191192
}),
193+
test({
194+
code: `import React from "./typescript-export-assign-default-namespace"`,
195+
parser: parser,
196+
settings: {
197+
'import/parsers': { [parser]: ['.ts'] },
198+
'import/resolver': { 'eslint-import-resolver-typescript': true },
199+
},
200+
parserOptions: {
201+
tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-assign-default-namespace/'),
202+
},
203+
}),
204+
test({
205+
code: `import Foo from "./typescript-export-as-default-namespace"`,
206+
parser: parser,
207+
settings: {
208+
'import/parsers': { [parser]: ['.ts'] },
209+
'import/resolver': { 'eslint-import-resolver-typescript': true },
210+
},
211+
parserOptions: {
212+
tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-as-default-namespace/'),
213+
},
214+
}),
192215
],
193216

194217
invalid: [
@@ -201,6 +224,24 @@ context('TypeScript', function () {
201224
},
202225
errors: ['No default export found in imported module "./typescript".'],
203226
}),
227+
test({
228+
code: `import React from "./typescript-export-assign-default-namespace"`,
229+
parser: parser,
230+
settings: {
231+
'import/parsers': { [parser]: ['.ts'] },
232+
'import/resolver': { 'eslint-import-resolver-typescript': true },
233+
},
234+
errors: ['No default export found in imported module "./typescript-export-assign-default-namespace".'],
235+
}),
236+
test({
237+
code: `import FooBar from "./typescript-export-as-default-namespace"`,
238+
parser: parser,
239+
settings: {
240+
'import/parsers': { [parser]: ['.ts'] },
241+
'import/resolver': { 'eslint-import-resolver-typescript': true },
242+
},
243+
errors: ['No default export found in imported module "./typescript-export-as-default-namespace".'],
244+
}),
204245
],
205246
})
206247
})

0 commit comments

Comments
 (0)