Skip to content

Commit 2c3b693

Browse files
authored
Collect import helper needs during module info gathering (#21567)
* Collect import helper needs during module info gathering * Add tests for other forms that trigger import helpers
1 parent 567fba2 commit 2c3b693

File tree

7 files changed

+224
-26
lines changed

7 files changed

+224
-26
lines changed

src/compiler/factory.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4295,15 +4295,15 @@ namespace ts {
42954295
return emitNode && emitNode.externalHelpersModuleName;
42964296
}
42974297

4298-
export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean) {
4298+
export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) {
42994299
if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) {
43004300
const externalHelpersModuleName = getExternalHelpersModuleName(node);
43014301
if (externalHelpersModuleName) {
43024302
return externalHelpersModuleName;
43034303
}
43044304

43054305
const moduleKind = getEmitModuleKind(compilerOptions);
4306-
let create = hasExportStarsToExportValues
4306+
let create = (hasExportStarsToExportValues || (compilerOptions.esModuleInterop && hasImportStarOrImportDefault))
43074307
&& moduleKind !== ModuleKind.System
43084308
&& moduleKind !== ModuleKind.ES2015
43094309
&& moduleKind !== ModuleKind.ESNext;

src/compiler/transformers/module/module.ts

+2-23
Original file line numberDiff line numberDiff line change
@@ -690,36 +690,15 @@ namespace ts {
690690
return createCall(createPropertyAccess(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
691691
}
692692

693-
function getNamedImportCount(node: ImportDeclaration) {
694-
if (!(node.importClause && node.importClause.namedBindings)) return 0;
695-
const names = node.importClause.namedBindings;
696-
if (!names) return 0;
697-
if (!isNamedImports(names)) return 0;
698-
return names.elements.length;
699-
}
700-
701-
function containsDefaultReference(node: NamedImportBindings) {
702-
if (!node) return false;
703-
if (!isNamedImports(node)) return false;
704-
return some(node.elements, isNamedDefaultReference);
705-
}
706-
707-
function isNamedDefaultReference(e: ImportSpecifier) {
708-
return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default;
709-
}
710-
711693
function getHelperExpressionForImport(node: ImportDeclaration, innerExpr: Expression) {
712694
if (!compilerOptions.esModuleInterop || getEmitFlags(node) & EmitFlags.NeverApplyImportHelper) {
713695
return innerExpr;
714696
}
715-
const nameCount = getNamedImportCount(node);
716-
const hasDefault = nameCount > 0 ? containsDefaultReference(node.importClause.namedBindings) : undefined;
717-
718-
if (getNamespaceDeclarationNode(node) || (nameCount > 1 && hasDefault)) {
697+
if (getImportNeedsImportStarHelper(node)) {
719698
context.requestEmitHelper(importStarHelper);
720699
return createCall(getHelperName("__importStar"), /*typeArguments*/ undefined, [innerExpr]);
721700
}
722-
if (isDefaultImport(node) || (nameCount === 1 && hasDefault)) {
701+
if (getImportNeedsImportDefaultHelper(node)) {
723702
context.requestEmitHelper(importDefaultHelper);
724703
return createCall(getHelperName("__importDefault"), /*typeArguments*/ undefined, [innerExpr]);
725704
}

src/compiler/transformers/utilities.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@ namespace ts {
1515
hasExportStarsToExportValues: boolean; // whether this module contains export*
1616
}
1717

18+
function getNamedImportCount(node: ImportDeclaration) {
19+
if (!(node.importClause && node.importClause.namedBindings)) return 0;
20+
const names = node.importClause.namedBindings;
21+
if (!names) return 0;
22+
if (!isNamedImports(names)) return 0;
23+
return names.elements.length;
24+
}
25+
26+
function containsDefaultReference(node: NamedImportBindings) {
27+
if (!node) return false;
28+
if (!isNamedImports(node)) return false;
29+
return some(node.elements, isNamedDefaultReference);
30+
}
31+
32+
function isNamedDefaultReference(e: ImportSpecifier) {
33+
return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default;
34+
}
35+
36+
export function getImportNeedsImportStarHelper(node: ImportDeclaration) {
37+
return !!getNamespaceDeclarationNode(node) || (getNamedImportCount(node) > 1 && containsDefaultReference(node.importClause.namedBindings));
38+
}
39+
40+
export function getImportNeedsImportDefaultHelper(node: ImportDeclaration) {
41+
return isDefaultImport(node) || (getNamedImportCount(node) === 1 && containsDefaultReference(node.importClause.namedBindings));
42+
}
43+
1844
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo {
1945
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
2046
const exportSpecifiers = createMultiMap<ExportSpecifier>();
@@ -24,6 +50,7 @@ namespace ts {
2450
let hasExportDefault = false;
2551
let exportEquals: ExportAssignment = undefined;
2652
let hasExportStarsToExportValues = false;
53+
let hasImportStarOrImportDefault = false;
2754

2855
for (const node of sourceFile.statements) {
2956
switch (node.kind) {
@@ -33,6 +60,7 @@ namespace ts {
3360
// import * as x from "mod"
3461
// import { x, y } from "mod"
3562
externalImports.push(<ImportDeclaration>node);
63+
hasImportStarOrImportDefault = getImportNeedsImportStarHelper(<ImportDeclaration>node) || getImportNeedsImportDefaultHelper(<ImportDeclaration>node);
3664
break;
3765

3866
case SyntaxKind.ImportEqualsDeclaration:
@@ -135,7 +163,7 @@ namespace ts {
135163
}
136164
}
137165

138-
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions, hasExportStarsToExportValues);
166+
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStarOrImportDefault);
139167
const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration(
140168
/*decorators*/ undefined,
141169
/*modifiers*/ undefined,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/esModuleInteropTslibHelpers.ts] ////
2+
3+
//// [refs.d.ts]
4+
declare module "path";
5+
//// [file.ts]
6+
import path from "path";
7+
path.resolve("", "../");
8+
export class Foo { }
9+
//// [file2.ts]
10+
import * as path from "path";
11+
path.resolve("", "../");
12+
export class Foo2 { }
13+
//// [file3.ts]
14+
import {default as resolve} from "path";
15+
resolve("", "../");
16+
export class Foo3 { }
17+
//// [file4.ts]
18+
import {Bar, default as resolve} from "path";
19+
resolve("", "../");
20+
export { Bar }
21+
22+
//// [file.js]
23+
"use strict";
24+
exports.__esModule = true;
25+
var tslib_1 = require("tslib");
26+
var path_1 = tslib_1.__importDefault(require("path"));
27+
path_1["default"].resolve("", "../");
28+
var Foo = /** @class */ (function () {
29+
function Foo() {
30+
}
31+
return Foo;
32+
}());
33+
exports.Foo = Foo;
34+
//// [file2.js]
35+
"use strict";
36+
exports.__esModule = true;
37+
var tslib_1 = require("tslib");
38+
var path = tslib_1.__importStar(require("path"));
39+
path.resolve("", "../");
40+
var Foo2 = /** @class */ (function () {
41+
function Foo2() {
42+
}
43+
return Foo2;
44+
}());
45+
exports.Foo2 = Foo2;
46+
//// [file3.js]
47+
"use strict";
48+
exports.__esModule = true;
49+
var tslib_1 = require("tslib");
50+
var path_1 = tslib_1.__importDefault(require("path"));
51+
path_1["default"]("", "../");
52+
var Foo3 = /** @class */ (function () {
53+
function Foo3() {
54+
}
55+
return Foo3;
56+
}());
57+
exports.Foo3 = Foo3;
58+
//// [file4.js]
59+
"use strict";
60+
exports.__esModule = true;
61+
var tslib_1 = require("tslib");
62+
var path_1 = tslib_1.__importStar(require("path"));
63+
exports.Bar = path_1.Bar;
64+
path_1["default"]("", "../");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/refs.d.ts ===
2+
declare module "path";
3+
No type information for this code.=== tests/cases/compiler/file.ts ===
4+
import path from "path";
5+
>path : Symbol(path, Decl(file.ts, 0, 6))
6+
7+
path.resolve("", "../");
8+
>path : Symbol(path, Decl(file.ts, 0, 6))
9+
10+
export class Foo { }
11+
>Foo : Symbol(Foo, Decl(file.ts, 1, 24))
12+
13+
=== tests/cases/compiler/file2.ts ===
14+
import * as path from "path";
15+
>path : Symbol(path, Decl(file2.ts, 0, 6))
16+
17+
path.resolve("", "../");
18+
>path : Symbol(path, Decl(file2.ts, 0, 6))
19+
20+
export class Foo2 { }
21+
>Foo2 : Symbol(Foo2, Decl(file2.ts, 1, 24))
22+
23+
=== tests/cases/compiler/file3.ts ===
24+
import {default as resolve} from "path";
25+
>default : Symbol(resolve, Decl(file3.ts, 0, 8))
26+
>resolve : Symbol(resolve, Decl(file3.ts, 0, 8))
27+
28+
resolve("", "../");
29+
>resolve : Symbol(resolve, Decl(file3.ts, 0, 8))
30+
31+
export class Foo3 { }
32+
>Foo3 : Symbol(Foo3, Decl(file3.ts, 1, 19))
33+
34+
=== tests/cases/compiler/file4.ts ===
35+
import {Bar, default as resolve} from "path";
36+
>Bar : Symbol(Bar, Decl(file4.ts, 0, 8))
37+
>default : Symbol(resolve, Decl(file4.ts, 0, 12))
38+
>resolve : Symbol(resolve, Decl(file4.ts, 0, 12))
39+
40+
resolve("", "../");
41+
>resolve : Symbol(resolve, Decl(file4.ts, 0, 12))
42+
43+
export { Bar }
44+
>Bar : Symbol(Bar, Decl(file4.ts, 2, 8))
45+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/refs.d.ts ===
2+
declare module "path";
3+
No type information for this code.=== tests/cases/compiler/file.ts ===
4+
import path from "path";
5+
>path : any
6+
7+
path.resolve("", "../");
8+
>path.resolve("", "../") : any
9+
>path.resolve : any
10+
>path : any
11+
>resolve : any
12+
>"" : ""
13+
>"../" : "../"
14+
15+
export class Foo { }
16+
>Foo : Foo
17+
18+
=== tests/cases/compiler/file2.ts ===
19+
import * as path from "path";
20+
>path : any
21+
22+
path.resolve("", "../");
23+
>path.resolve("", "../") : any
24+
>path.resolve : any
25+
>path : any
26+
>resolve : any
27+
>"" : ""
28+
>"../" : "../"
29+
30+
export class Foo2 { }
31+
>Foo2 : Foo2
32+
33+
=== tests/cases/compiler/file3.ts ===
34+
import {default as resolve} from "path";
35+
>default : any
36+
>resolve : any
37+
38+
resolve("", "../");
39+
>resolve("", "../") : any
40+
>resolve : any
41+
>"" : ""
42+
>"../" : "../"
43+
44+
export class Foo3 { }
45+
>Foo3 : Foo3
46+
47+
=== tests/cases/compiler/file4.ts ===
48+
import {Bar, default as resolve} from "path";
49+
>Bar : any
50+
>default : any
51+
>resolve : any
52+
53+
resolve("", "../");
54+
>resolve("", "../") : any
55+
>resolve : any
56+
>"" : ""
57+
>"../" : "../"
58+
59+
export { Bar }
60+
>Bar : any
61+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @esModuleInterop: true
2+
// @importHelpers: true
3+
// @noEmitHelpers: true
4+
// @filename: refs.d.ts
5+
declare module "path";
6+
// @filename: file.ts
7+
import path from "path";
8+
path.resolve("", "../");
9+
export class Foo { }
10+
// @filename: file2.ts
11+
import * as path from "path";
12+
path.resolve("", "../");
13+
export class Foo2 { }
14+
// @filename: file3.ts
15+
import {default as resolve} from "path";
16+
resolve("", "../");
17+
export class Foo3 { }
18+
// @filename: file4.ts
19+
import {Bar, default as resolve} from "path";
20+
resolve("", "../");
21+
export { Bar }

0 commit comments

Comments
 (0)