Collect import helper needs during module info gathering (#21567) (#21586)

* Collect import helper needs during module info gathering

* Add tests for other forms that trigger import helpers
This commit is contained in:
Wesley Wigham 2018-02-02 18:19:28 -08:00 committed by GitHub
parent efcb22ae09
commit 3305baf5eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 224 additions and 26 deletions

View File

@ -4293,7 +4293,7 @@ namespace ts {
return emitNode && emitNode.externalHelpersModuleName;
}
export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean) {
export function getOrCreateExternalHelpersModuleNameIfNeeded(node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) {
if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) {
const externalHelpersModuleName = getExternalHelpersModuleName(node);
if (externalHelpersModuleName) {
@ -4301,7 +4301,7 @@ namespace ts {
}
const moduleKind = getEmitModuleKind(compilerOptions);
let create = hasExportStarsToExportValues
let create = (hasExportStarsToExportValues || (compilerOptions.esModuleInterop && hasImportStarOrImportDefault))
&& moduleKind !== ModuleKind.System
&& moduleKind !== ModuleKind.ES2015
&& moduleKind !== ModuleKind.ESNext;

View File

@ -690,36 +690,15 @@ namespace ts {
return createCall(createPropertyAccess(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
}
function getNamedImportCount(node: ImportDeclaration) {
if (!(node.importClause && node.importClause.namedBindings)) return 0;
const names = node.importClause.namedBindings;
if (!names) return 0;
if (!isNamedImports(names)) return 0;
return names.elements.length;
}
function containsDefaultReference(node: NamedImportBindings) {
if (!node) return false;
if (!isNamedImports(node)) return false;
return some(node.elements, isNamedDefaultReference);
}
function isNamedDefaultReference(e: ImportSpecifier) {
return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default;
}
function getHelperExpressionForImport(node: ImportDeclaration, innerExpr: Expression) {
if (!compilerOptions.esModuleInterop || getEmitFlags(node) & EmitFlags.NeverApplyImportHelper) {
return innerExpr;
}
const nameCount = getNamedImportCount(node);
const hasDefault = nameCount > 0 ? containsDefaultReference(node.importClause.namedBindings) : undefined;
if (getNamespaceDeclarationNode(node) || (nameCount > 1 && hasDefault)) {
if (getImportNeedsImportStarHelper(node)) {
context.requestEmitHelper(importStarHelper);
return createCall(getHelperName("__importStar"), /*typeArguments*/ undefined, [innerExpr]);
}
if (isDefaultImport(node) || (nameCount === 1 && hasDefault)) {
if (getImportNeedsImportDefaultHelper(node)) {
context.requestEmitHelper(importDefaultHelper);
return createCall(getHelperName("__importDefault"), /*typeArguments*/ undefined, [innerExpr]);
}

View File

@ -15,6 +15,32 @@ namespace ts {
hasExportStarsToExportValues: boolean; // whether this module contains export*
}
function getNamedImportCount(node: ImportDeclaration) {
if (!(node.importClause && node.importClause.namedBindings)) return 0;
const names = node.importClause.namedBindings;
if (!names) return 0;
if (!isNamedImports(names)) return 0;
return names.elements.length;
}
function containsDefaultReference(node: NamedImportBindings) {
if (!node) return false;
if (!isNamedImports(node)) return false;
return some(node.elements, isNamedDefaultReference);
}
function isNamedDefaultReference(e: ImportSpecifier) {
return e.propertyName && e.propertyName.escapedText === InternalSymbolName.Default;
}
export function getImportNeedsImportStarHelper(node: ImportDeclaration) {
return !!getNamespaceDeclarationNode(node) || (getNamedImportCount(node) > 1 && containsDefaultReference(node.importClause.namedBindings));
}
export function getImportNeedsImportDefaultHelper(node: ImportDeclaration) {
return isDefaultImport(node) || (getNamedImportCount(node) === 1 && containsDefaultReference(node.importClause.namedBindings));
}
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo {
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
const exportSpecifiers = createMultiMap<ExportSpecifier>();
@ -24,6 +50,7 @@ namespace ts {
let hasExportDefault = false;
let exportEquals: ExportAssignment = undefined;
let hasExportStarsToExportValues = false;
let hasImportStarOrImportDefault = false;
for (const node of sourceFile.statements) {
switch (node.kind) {
@ -33,6 +60,7 @@ namespace ts {
// import * as x from "mod"
// import { x, y } from "mod"
externalImports.push(<ImportDeclaration>node);
hasImportStarOrImportDefault = getImportNeedsImportStarHelper(<ImportDeclaration>node) || getImportNeedsImportDefaultHelper(<ImportDeclaration>node);
break;
case SyntaxKind.ImportEqualsDeclaration:
@ -135,7 +163,7 @@ namespace ts {
}
}
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions, hasExportStarsToExportValues);
const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStarOrImportDefault);
const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,

View File

@ -0,0 +1,64 @@
//// [tests/cases/compiler/esModuleInteropTslibHelpers.ts] ////
//// [refs.d.ts]
declare module "path";
//// [file.ts]
import path from "path";
path.resolve("", "../");
export class Foo { }
//// [file2.ts]
import * as path from "path";
path.resolve("", "../");
export class Foo2 { }
//// [file3.ts]
import {default as resolve} from "path";
resolve("", "../");
export class Foo3 { }
//// [file4.ts]
import {Bar, default as resolve} from "path";
resolve("", "../");
export { Bar }
//// [file.js]
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var path_1 = tslib_1.__importDefault(require("path"));
path_1["default"].resolve("", "../");
var Foo = /** @class */ (function () {
function Foo() {
}
return Foo;
}());
exports.Foo = Foo;
//// [file2.js]
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var path = tslib_1.__importStar(require("path"));
path.resolve("", "../");
var Foo2 = /** @class */ (function () {
function Foo2() {
}
return Foo2;
}());
exports.Foo2 = Foo2;
//// [file3.js]
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var path_1 = tslib_1.__importDefault(require("path"));
path_1["default"]("", "../");
var Foo3 = /** @class */ (function () {
function Foo3() {
}
return Foo3;
}());
exports.Foo3 = Foo3;
//// [file4.js]
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var path_1 = tslib_1.__importStar(require("path"));
exports.Bar = path_1.Bar;
path_1["default"]("", "../");

View File

@ -0,0 +1,45 @@
=== tests/cases/compiler/refs.d.ts ===
declare module "path";
No type information for this code.=== tests/cases/compiler/file.ts ===
import path from "path";
>path : Symbol(path, Decl(file.ts, 0, 6))
path.resolve("", "../");
>path : Symbol(path, Decl(file.ts, 0, 6))
export class Foo { }
>Foo : Symbol(Foo, Decl(file.ts, 1, 24))
=== tests/cases/compiler/file2.ts ===
import * as path from "path";
>path : Symbol(path, Decl(file2.ts, 0, 6))
path.resolve("", "../");
>path : Symbol(path, Decl(file2.ts, 0, 6))
export class Foo2 { }
>Foo2 : Symbol(Foo2, Decl(file2.ts, 1, 24))
=== tests/cases/compiler/file3.ts ===
import {default as resolve} from "path";
>default : Symbol(resolve, Decl(file3.ts, 0, 8))
>resolve : Symbol(resolve, Decl(file3.ts, 0, 8))
resolve("", "../");
>resolve : Symbol(resolve, Decl(file3.ts, 0, 8))
export class Foo3 { }
>Foo3 : Symbol(Foo3, Decl(file3.ts, 1, 19))
=== tests/cases/compiler/file4.ts ===
import {Bar, default as resolve} from "path";
>Bar : Symbol(Bar, Decl(file4.ts, 0, 8))
>default : Symbol(resolve, Decl(file4.ts, 0, 12))
>resolve : Symbol(resolve, Decl(file4.ts, 0, 12))
resolve("", "../");
>resolve : Symbol(resolve, Decl(file4.ts, 0, 12))
export { Bar }
>Bar : Symbol(Bar, Decl(file4.ts, 2, 8))

View File

@ -0,0 +1,61 @@
=== tests/cases/compiler/refs.d.ts ===
declare module "path";
No type information for this code.=== tests/cases/compiler/file.ts ===
import path from "path";
>path : any
path.resolve("", "../");
>path.resolve("", "../") : any
>path.resolve : any
>path : any
>resolve : any
>"" : ""
>"../" : "../"
export class Foo { }
>Foo : Foo
=== tests/cases/compiler/file2.ts ===
import * as path from "path";
>path : any
path.resolve("", "../");
>path.resolve("", "../") : any
>path.resolve : any
>path : any
>resolve : any
>"" : ""
>"../" : "../"
export class Foo2 { }
>Foo2 : Foo2
=== tests/cases/compiler/file3.ts ===
import {default as resolve} from "path";
>default : any
>resolve : any
resolve("", "../");
>resolve("", "../") : any
>resolve : any
>"" : ""
>"../" : "../"
export class Foo3 { }
>Foo3 : Foo3
=== tests/cases/compiler/file4.ts ===
import {Bar, default as resolve} from "path";
>Bar : any
>default : any
>resolve : any
resolve("", "../");
>resolve("", "../") : any
>resolve : any
>"" : ""
>"../" : "../"
export { Bar }
>Bar : any

View File

@ -0,0 +1,21 @@
// @esModuleInterop: true
// @importHelpers: true
// @noEmitHelpers: true
// @filename: refs.d.ts
declare module "path";
// @filename: file.ts
import path from "path";
path.resolve("", "../");
export class Foo { }
// @filename: file2.ts
import * as path from "path";
path.resolve("", "../");
export class Foo2 { }
// @filename: file3.ts
import {default as resolve} from "path";
resolve("", "../");
export class Foo3 { }
// @filename: file4.ts
import {Bar, default as resolve} from "path";
resolve("", "../");
export { Bar }