From 0b93f55138bd2beba492ca7e3867ae5a2d4ed0f1 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 19 Mar 2020 17:46:19 +1100 Subject: [PATCH 1/8] feature(import/default): support default export in TSExportAssignment. --- package.json | 3 ++- src/ExportMap.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 28e45eeb56..5d77bf8080 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "minimatch": "^3.0.4", "object.values": "^1.1.0", "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "resolve": "^1.12.0", + "tsconfig-paths": "^3.9.0" }, "nyc": { "require": [ diff --git a/src/ExportMap.js b/src/ExportMap.js index 525f64a48a..0ccdb4ec5d 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -13,6 +13,8 @@ import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore' import { hashObject } from 'eslint-module-utils/hash' import * as unambiguous from 'eslint-module-utils/unambiguous' +import { loadConfig } from 'tsconfig-paths' + const log = debug('eslint-plugin-import:ExportMap') const exportCache = new Map() @@ -445,6 +447,14 @@ ExportMap.parse = function (path, content, context) { const source = makeSourceCode(content, ast) + function isEsModuleInterop() { + const configLoaderResult = loadConfig(context.parserOptions.tsconfigRootDir || process.cwd()) + if (configLoaderResult === 'success') { + return configLoaderResult.esModuleInterop + } + return false + } + ast.body.forEach(function (n) { if (n.type === 'ExportDefaultDeclaration') { @@ -530,6 +540,9 @@ ExportMap.parse = function (path, content, context) { // This doesn't declare anything, but changes what's being exported. if (n.type === 'TSExportAssignment') { + if (isEsModuleInterop()) { + m.namespace.set('default', {}) + } const moduleDecls = ast.body.filter((bodyNode) => bodyNode.type === 'TSModuleDeclaration' && bodyNode.id.name === n.expression.name ) From 8d662de4df9d5f76b7f37f7943b0099b622fe806 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 4 Jun 2020 19:54:27 +1000 Subject: [PATCH 2/8] add tests and fix for typescript-export-assign-default-namespace --- src/ExportMap.js | 21 +++++++++++-------- .../index.d.ts | 3 +++ .../tsconfig.json | 5 +++++ tests/src/rules/default.js | 21 +++++++++++++++++++ 4 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 tests/files/typescript-export-assign-default-namespace/index.d.ts create mode 100644 tests/files/typescript-export-assign-default-namespace/tsconfig.json diff --git a/src/ExportMap.js b/src/ExportMap.js index 9da5434521..179ad5dc91 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -13,7 +13,7 @@ import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore' import { hashObject } from 'eslint-module-utils/hash' import * as unambiguous from 'eslint-module-utils/unambiguous' -import { loadConfig } from 'tsconfig-paths' +import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader' const log = debug('eslint-plugin-import:ExportMap') @@ -448,9 +448,13 @@ ExportMap.parse = function (path, content, context) { const source = makeSourceCode(content, ast) function isEsModuleInterop() { - const configLoaderResult = loadConfig(context.parserOptions.tsconfigRootDir || process.cwd()) - if (configLoaderResult === 'success') { - return configLoaderResult.esModuleInterop + const tsConfig = tsConfigLoader({ + cwd: context.parserOptions.tsconfigRootDir || process.cwd(), + getEnv: (key) => process.env[key], + }) + if (tsConfig.tsConfigPath !== undefined) { + const json = fs.readFileSync(tsConfig.tsConfigPath) + return JSON.parse(json).compilerOptions.esModuleInterop } return false } @@ -540,11 +544,6 @@ ExportMap.parse = function (path, content, context) { // This doesn't declare anything, but changes what's being exported. if (n.type === 'TSExportAssignment') { - if (isEsModuleInterop()) { - m.namespace.set('default', {}) - } - const moduleDecls = ast.body.filter((bodyNode) => - bodyNode.type === 'TSModuleDeclaration' && bodyNode.id.name === n.expression.name const exportedName = n.expression.name const declTypes = [ 'VariableDeclaration', @@ -568,6 +567,10 @@ ExportMap.parse = function (path, content, context) { m.namespace.set('default', captureDoc(source, docStyleParsers, n)) return } + if (isEsModuleInterop()) { + console.log('isEsModuleInterop') + m.namespace.set('default', {}) + } exportedDecls.forEach((decl) => { if (decl.type === 'TSModuleDeclaration') { if (decl.body && decl.body.type === 'TSModuleDeclaration') { diff --git a/tests/files/typescript-export-assign-default-namespace/index.d.ts b/tests/files/typescript-export-assign-default-namespace/index.d.ts new file mode 100644 index 0000000000..2ad4822f7c --- /dev/null +++ b/tests/files/typescript-export-assign-default-namespace/index.d.ts @@ -0,0 +1,3 @@ +export = FooBar; + +declare namespace FooBar {} diff --git a/tests/files/typescript-export-assign-default-namespace/tsconfig.json b/tests/files/typescript-export-assign-default-namespace/tsconfig.json new file mode 100644 index 0000000000..ffe4e79f70 --- /dev/null +++ b/tests/files/typescript-export-assign-default-namespace/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 79d03e6c55..d0510f2dd1 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -1,3 +1,4 @@ +import path from 'path'; import { test, SYNTAX_CASES, getTSParsers } from '../utils' import { RuleTester } from 'eslint' @@ -189,6 +190,17 @@ context('TypeScript', function () { 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, }), + test({ + code: `import React from "./typescript-export-assign-default-namespace"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + parserOptions: { + tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-assign-default-namespace/') + } + }), ], invalid: [ @@ -201,6 +213,15 @@ context('TypeScript', function () { }, errors: ['No default export found in imported module "./typescript".'], }), + test({ + code: `import React from "./typescript-export-assign-default-namespace"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + errors: ['No default export found in imported module "./typescript-export-assign-default-namespace".'], + }), ], }) }) From 2698c1e3c81b1f5f0f3cd0e7eccd8dbcd1205aa1 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 4 Jun 2020 19:55:34 +1000 Subject: [PATCH 3/8] add final new line to tsconfig.json --- .../typescript-export-assign-default-namespace/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/files/typescript-export-assign-default-namespace/tsconfig.json b/tests/files/typescript-export-assign-default-namespace/tsconfig.json index ffe4e79f70..a72ee3e88b 100644 --- a/tests/files/typescript-export-assign-default-namespace/tsconfig.json +++ b/tests/files/typescript-export-assign-default-namespace/tsconfig.json @@ -2,4 +2,4 @@ "compilerOptions": { "esModuleInterop": true } -} \ No newline at end of file +} From dcb0ed269fe62b844b0e9efbe9922f31677e5d36 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 4 Jun 2020 19:57:52 +1000 Subject: [PATCH 4/8] rm console.log --- src/ExportMap.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ExportMap.js b/src/ExportMap.js index 179ad5dc91..1430f3fa70 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -568,7 +568,6 @@ ExportMap.parse = function (path, content, context) { return } if (isEsModuleInterop()) { - console.log('isEsModuleInterop') m.namespace.set('default', {}) } exportedDecls.forEach((decl) => { From 6ec0c0312a878c16399052a69e4959c8ced211aa Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 4 Jun 2020 19:59:45 +1000 Subject: [PATCH 5/8] eslint --fix --- tests/src/rules/default.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index d0510f2dd1..24e1bc2345 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'path' import { test, SYNTAX_CASES, getTSParsers } from '../utils' import { RuleTester } from 'eslint' @@ -198,8 +198,8 @@ context('TypeScript', function () { 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, parserOptions: { - tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-assign-default-namespace/') - } + tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-assign-default-namespace/'), + }, }), ], From e167cecfab2ac6ef27c0ef45fac395d5c836f2c3 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 4 Jun 2020 20:22:59 +1000 Subject: [PATCH 6/8] typescript-export-as-default-namespace --- src/ExportMap.js | 13 ++++++++---- .../index.d.ts | 3 +++ .../tsconfig.json | 5 +++++ tests/src/rules/default.js | 20 +++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 tests/files/typescript-export-as-default-namespace/index.d.ts create mode 100644 tests/files/typescript-export-as-default-namespace/tsconfig.json diff --git a/src/ExportMap.js b/src/ExportMap.js index 1430f3fa70..833ee958a7 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -449,7 +449,7 @@ ExportMap.parse = function (path, content, context) { function isEsModuleInterop() { const tsConfig = tsConfigLoader({ - cwd: context.parserOptions.tsconfigRootDir || process.cwd(), + cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(), getEnv: (key) => process.env[key], }) if (tsConfig.tsConfigPath !== undefined) { @@ -542,9 +542,14 @@ ExportMap.parse = function (path, content, context) { }) } + const isEsModuleInteropTrue = isEsModuleInterop() + + const exports = ['TSExportAssignment'] + isEsModuleInteropTrue && exports.push('TSNamespaceExportDeclaration') + // This doesn't declare anything, but changes what's being exported. - if (n.type === 'TSExportAssignment') { - const exportedName = n.expression.name + if (exports.includes(n.type)) { + const exportedName = n.expression && n.expression.name || n.id.name const declTypes = [ 'VariableDeclaration', 'ClassDeclaration', @@ -567,7 +572,7 @@ ExportMap.parse = function (path, content, context) { m.namespace.set('default', captureDoc(source, docStyleParsers, n)) return } - if (isEsModuleInterop()) { + if (isEsModuleInteropTrue) { m.namespace.set('default', {}) } exportedDecls.forEach((decl) => { diff --git a/tests/files/typescript-export-as-default-namespace/index.d.ts b/tests/files/typescript-export-as-default-namespace/index.d.ts new file mode 100644 index 0000000000..50e321cb56 --- /dev/null +++ b/tests/files/typescript-export-as-default-namespace/index.d.ts @@ -0,0 +1,3 @@ +export as namespace Foo + +export function bar(): void \ No newline at end of file diff --git a/tests/files/typescript-export-as-default-namespace/tsconfig.json b/tests/files/typescript-export-as-default-namespace/tsconfig.json new file mode 100644 index 0000000000..a72ee3e88b --- /dev/null +++ b/tests/files/typescript-export-as-default-namespace/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 24e1bc2345..d3d4aae4a7 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -201,6 +201,17 @@ context('TypeScript', function () { tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-assign-default-namespace/'), }, }), + test({ + code: `import Foo from "./typescript-export-as-default-namespace"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + parserOptions: { + tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-as-default-namespace/'), + }, + }), ], invalid: [ @@ -222,6 +233,15 @@ context('TypeScript', function () { }, errors: ['No default export found in imported module "./typescript-export-assign-default-namespace".'], }), + test({ + code: `import FooBar from "./typescript-export-as-default-namespace"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + errors: ['No default export found in imported module "./typescript-export-as-default-namespace".'], + }), ], }) }) From 38cd8eaae3cf3bff3d50cfcf59289ee26d8c508b Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Fri, 5 Jun 2020 20:55:46 +1000 Subject: [PATCH 7/8] add final new line --- tests/files/typescript-export-as-default-namespace/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/files/typescript-export-as-default-namespace/index.d.ts b/tests/files/typescript-export-as-default-namespace/index.d.ts index 50e321cb56..953c3410b1 100644 --- a/tests/files/typescript-export-as-default-namespace/index.d.ts +++ b/tests/files/typescript-export-as-default-namespace/index.d.ts @@ -1,3 +1,3 @@ export as namespace Foo -export function bar(): void \ No newline at end of file +export function bar(): void From 0be9bcd49f87e37e1fd87ef3d61d9fdaaa540270 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Fri, 5 Jun 2020 23:58:04 +1000 Subject: [PATCH 8/8] includes -> indexof for node 4 --- src/ExportMap.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ExportMap.js b/src/ExportMap.js index 833ee958a7..c3e95a5392 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -548,7 +548,7 @@ ExportMap.parse = function (path, content, context) { isEsModuleInteropTrue && exports.push('TSNamespaceExportDeclaration') // This doesn't declare anything, but changes what's being exported. - if (exports.includes(n.type)) { + if (exports.indexOf(n.type) !== -1) { const exportedName = n.expression && n.expression.name || n.id.name const declTypes = [ 'VariableDeclaration', @@ -561,7 +561,7 @@ ExportMap.parse = function (path, content, context) { 'TSModuleDeclaration', ] const exportedDecls = ast.body.filter(({ type, id, declarations }) => - declTypes.includes(type) && + declTypes.indexOf(type) !== -1 && ( (id && id.name === exportedName) || (declarations && declarations.find(d => d.id.name === exportedName))