Fix 'this' capturing for dynamic import
This commit is contained in:
parent
1db762356e
commit
dc607c29b4
|
@ -2699,6 +2699,12 @@ namespace ts {
|
|||
|
||||
if (expression.kind === SyntaxKind.ImportKeyword) {
|
||||
transformFlags |= TransformFlags.ContainsDynamicImport;
|
||||
|
||||
// A dynamic 'import()' call that contains a lexical 'this' will
|
||||
// require a captured 'this' when emitting down-level.
|
||||
if (subtreeFlags & TransformFlags.ContainsLexicalThis) {
|
||||
transformFlags |= TransformFlags.ContainsCapturedLexicalThis;
|
||||
}
|
||||
}
|
||||
|
||||
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
|
||||
|
|
|
@ -561,46 +561,89 @@ namespace ts {
|
|||
// });
|
||||
const resolve = createUniqueName("resolve");
|
||||
const reject = createUniqueName("reject");
|
||||
return createNew(
|
||||
createIdentifier("Promise"),
|
||||
/*typeArguments*/ undefined,
|
||||
[createFunctionExpression(
|
||||
const parameters = [
|
||||
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve),
|
||||
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ reject)
|
||||
];
|
||||
const body = createBlock([
|
||||
createStatement(
|
||||
createCall(
|
||||
createIdentifier("require"),
|
||||
/*typeArguments*/ undefined,
|
||||
[createArrayLiteral([firstOrUndefined(node.arguments) || createOmittedExpression()]), resolve, reject]
|
||||
)
|
||||
)
|
||||
]);
|
||||
|
||||
let func: FunctionExpression | ArrowFunction;
|
||||
if (languageVersion >= ScriptTarget.ES2015) {
|
||||
func = createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
parameters,
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
body);
|
||||
}
|
||||
else {
|
||||
func = createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve),
|
||||
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ reject)],
|
||||
parameters,
|
||||
/*type*/ undefined,
|
||||
createBlock([createStatement(
|
||||
createCall(
|
||||
createIdentifier("require"),
|
||||
/*typeArguments*/ undefined,
|
||||
[createArrayLiteral([firstOrUndefined(node.arguments) || createOmittedExpression()]), resolve, reject]
|
||||
))])
|
||||
)]);
|
||||
body);
|
||||
|
||||
// if there is a lexical 'this' in the import call arguments, ensure we indicate
|
||||
// that this new function expression indicates it captures 'this' so that the
|
||||
// es2015 transformer will properly substitute 'this' with '_this'.
|
||||
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
|
||||
setEmitFlags(func, EmitFlags.CapturesThis);
|
||||
}
|
||||
}
|
||||
|
||||
return createNew(createIdentifier("Promise"), /*typeArguments*/ undefined, [func]);
|
||||
}
|
||||
|
||||
function transformImportCallExpressionCommonJS(node: ImportCall): Expression {
|
||||
function transformImportCallExpressionCommonJS(node: ImportCall): Expression {
|
||||
// import("./blah")
|
||||
// emit as
|
||||
// Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/
|
||||
// We have to wrap require in then callback so that require is done in asynchronously
|
||||
// if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately
|
||||
return createCall(
|
||||
createPropertyAccess(
|
||||
createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []),
|
||||
"then"),
|
||||
/*typeArguments*/ undefined,
|
||||
[createFunctionExpression(
|
||||
const promiseResolveCall = createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []);
|
||||
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, node.arguments);
|
||||
|
||||
let func: FunctionExpression | ArrowFunction;
|
||||
if (languageVersion >= ScriptTarget.ES2015) {
|
||||
func = createArrowFunction(
|
||||
/*modifiers*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
/*parameters*/ [],
|
||||
/*type*/ undefined,
|
||||
/*equalsGreaterThanToken*/ undefined,
|
||||
requireCall);
|
||||
}
|
||||
else {
|
||||
func = createFunctionExpression(
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
/*parameters*/ undefined,
|
||||
/*parameters*/ [],
|
||||
/*type*/ undefined,
|
||||
createBlock([createReturn(createCall(createIdentifier("require"), /*typeArguments*/ undefined, node.arguments))])
|
||||
)]);
|
||||
createBlock([createReturn(requireCall)]));
|
||||
|
||||
// if there is a lexical 'this' in the import call arguments, ensure we indicate
|
||||
// that this new function expression indicates it captures 'this' so that the
|
||||
// es2015 transformer will properly substitute 'this' with '_this'.
|
||||
if (node.transformFlags & TransformFlags.ContainsLexicalThis) {
|
||||
setEmitFlags(func, EmitFlags.CapturesThis);
|
||||
}
|
||||
}
|
||||
|
||||
return createCall(createPropertyAccess(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
//// [dynamicImportWithNestedThis_es2015.ts]
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
private _path = './other';
|
||||
|
||||
dynamic() {
|
||||
return import(this._path);
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
c.dynamic();
|
||||
|
||||
//// [dynamicImportWithNestedThis_es2015.js]
|
||||
(function (factory) {
|
||||
if (typeof module === "object" && typeof module.exports === "object") {
|
||||
var v = factory(require, exports);
|
||||
if (v !== undefined) module.exports = v;
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
define(["require", "exports"], factory);
|
||||
}
|
||||
})(function (require, exports) {
|
||||
"use strict";
|
||||
var __syncRequire = typeof module === "object" && typeof module.exports === "object";
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
constructor() {
|
||||
this._path = './other';
|
||||
}
|
||||
dynamic() {
|
||||
return __syncRequire ? Promise.resolve().then(() => require(this._path)) : new Promise((resolve_1, reject_1) => { require([this._path], resolve_1, reject_1); });
|
||||
}
|
||||
}
|
||||
const c = new C();
|
||||
c.dynamic();
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
=== tests/cases/compiler/dynamicImportWithNestedThis_es2015.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
>C : Symbol(C, Decl(dynamicImportWithNestedThis_es2015.ts, 0, 0))
|
||||
|
||||
private _path = './other';
|
||||
>_path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es2015.ts, 1, 9))
|
||||
|
||||
dynamic() {
|
||||
>dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es2015.ts, 2, 27))
|
||||
|
||||
return import(this._path);
|
||||
>this._path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es2015.ts, 1, 9))
|
||||
>this : Symbol(C, Decl(dynamicImportWithNestedThis_es2015.ts, 0, 0))
|
||||
>_path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es2015.ts, 1, 9))
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
>c : Symbol(c, Decl(dynamicImportWithNestedThis_es2015.ts, 9, 5))
|
||||
>C : Symbol(C, Decl(dynamicImportWithNestedThis_es2015.ts, 0, 0))
|
||||
|
||||
c.dynamic();
|
||||
>c.dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es2015.ts, 2, 27))
|
||||
>c : Symbol(c, Decl(dynamicImportWithNestedThis_es2015.ts, 9, 5))
|
||||
>dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es2015.ts, 2, 27))
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
=== tests/cases/compiler/dynamicImportWithNestedThis_es2015.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
>C : C
|
||||
|
||||
private _path = './other';
|
||||
>_path : string
|
||||
>'./other' : "./other"
|
||||
|
||||
dynamic() {
|
||||
>dynamic : () => Promise<any>
|
||||
|
||||
return import(this._path);
|
||||
>import(this._path) : Promise<any>
|
||||
>this._path : string
|
||||
>this : this
|
||||
>_path : string
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
>c : C
|
||||
>new C() : C
|
||||
>C : typeof C
|
||||
|
||||
c.dynamic();
|
||||
>c.dynamic() : Promise<any>
|
||||
>c.dynamic : () => Promise<any>
|
||||
>c : C
|
||||
>dynamic : () => Promise<any>
|
||||
|
39
tests/baselines/reference/dynamicImportWithNestedThis_es5.js
Normal file
39
tests/baselines/reference/dynamicImportWithNestedThis_es5.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
//// [dynamicImportWithNestedThis_es5.ts]
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
private _path = './other';
|
||||
|
||||
dynamic() {
|
||||
return import(this._path);
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
c.dynamic();
|
||||
|
||||
//// [dynamicImportWithNestedThis_es5.js]
|
||||
(function (factory) {
|
||||
if (typeof module === "object" && typeof module.exports === "object") {
|
||||
var v = factory(require, exports);
|
||||
if (v !== undefined) module.exports = v;
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
define(["require", "exports"], factory);
|
||||
}
|
||||
})(function (require, exports) {
|
||||
"use strict";
|
||||
var __syncRequire = typeof module === "object" && typeof module.exports === "object";
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
var C = /** @class */ (function () {
|
||||
function C() {
|
||||
this._path = './other';
|
||||
}
|
||||
C.prototype.dynamic = function () {
|
||||
var _this = this;
|
||||
return __syncRequire ? Promise.resolve().then(function () { return require(_this._path); }) : new Promise(function (resolve_1, reject_1) { require([_this._path], resolve_1, reject_1); });
|
||||
};
|
||||
return C;
|
||||
}());
|
||||
var c = new C();
|
||||
c.dynamic();
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
=== tests/cases/compiler/dynamicImportWithNestedThis_es5.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
>C : Symbol(C, Decl(dynamicImportWithNestedThis_es5.ts, 0, 0))
|
||||
|
||||
private _path = './other';
|
||||
>_path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es5.ts, 1, 9))
|
||||
|
||||
dynamic() {
|
||||
>dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es5.ts, 2, 27))
|
||||
|
||||
return import(this._path);
|
||||
>this._path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es5.ts, 1, 9))
|
||||
>this : Symbol(C, Decl(dynamicImportWithNestedThis_es5.ts, 0, 0))
|
||||
>_path : Symbol(C._path, Decl(dynamicImportWithNestedThis_es5.ts, 1, 9))
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
>c : Symbol(c, Decl(dynamicImportWithNestedThis_es5.ts, 9, 5))
|
||||
>C : Symbol(C, Decl(dynamicImportWithNestedThis_es5.ts, 0, 0))
|
||||
|
||||
c.dynamic();
|
||||
>c.dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es5.ts, 2, 27))
|
||||
>c : Symbol(c, Decl(dynamicImportWithNestedThis_es5.ts, 9, 5))
|
||||
>dynamic : Symbol(C.dynamic, Decl(dynamicImportWithNestedThis_es5.ts, 2, 27))
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
=== tests/cases/compiler/dynamicImportWithNestedThis_es5.ts ===
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
>C : C
|
||||
|
||||
private _path = './other';
|
||||
>_path : string
|
||||
>'./other' : "./other"
|
||||
|
||||
dynamic() {
|
||||
>dynamic : () => Promise<any>
|
||||
|
||||
return import(this._path);
|
||||
>import(this._path) : Promise<any>
|
||||
>this._path : string
|
||||
>this : this
|
||||
>_path : string
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
>c : C
|
||||
>new C() : C
|
||||
>C : typeof C
|
||||
|
||||
c.dynamic();
|
||||
>c.dynamic() : Promise<any>
|
||||
>c.dynamic : () => Promise<any>
|
||||
>c : C
|
||||
>dynamic : () => Promise<any>
|
||||
|
14
tests/cases/compiler/dynamicImportWithNestedThis_es2015.ts
Normal file
14
tests/cases/compiler/dynamicImportWithNestedThis_es2015.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// @lib: es2015
|
||||
// @target: es2015
|
||||
// @module: umd
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
private _path = './other';
|
||||
|
||||
dynamic() {
|
||||
return import(this._path);
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
c.dynamic();
|
14
tests/cases/compiler/dynamicImportWithNestedThis_es5.ts
Normal file
14
tests/cases/compiler/dynamicImportWithNestedThis_es5.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
// @lib: es2015
|
||||
// @target: es5
|
||||
// @module: umd
|
||||
// https://github.com/Microsoft/TypeScript/issues/17564
|
||||
class C {
|
||||
private _path = './other';
|
||||
|
||||
dynamic() {
|
||||
return import(this._path);
|
||||
}
|
||||
}
|
||||
|
||||
const c = new C();
|
||||
c.dynamic();
|
Loading…
Reference in a new issue