fix: avoid downleveled dynamic import closing over specifier expression (#49663)

* fix: evaluate dynamic import specifier expressions synchronously

* refactor

* Update src/compiler/transformers/module/module.ts

Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>

* [Experiment]

Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>
This commit is contained in:
Joshua Chen
2022-10-18 16:46:51 -04:00
committed by GitHub
parent 11066b264f
commit 1f8959f5dc
14 changed files with 180 additions and 42 deletions

View File

@@ -721,7 +721,7 @@ namespace ts {
return createImportCallExpressionUMD(argument ?? factory.createVoidZero(), containsLexicalThis);
case ModuleKind.CommonJS:
default:
return createImportCallExpressionCommonJS(argument, containsLexicalThis);
return createImportCallExpressionCommonJS(argument);
}
}
@@ -745,7 +745,7 @@ namespace ts {
return factory.createConditionalExpression(
/*condition*/ factory.createIdentifier("__syncRequire"),
/*questionToken*/ undefined,
/*whenTrue*/ createImportCallExpressionCommonJS(arg, containsLexicalThis),
/*whenTrue*/ createImportCallExpressionCommonJS(arg),
/*colonToken*/ undefined,
/*whenFalse*/ createImportCallExpressionAMD(argClone, containsLexicalThis)
);
@@ -755,7 +755,7 @@ namespace ts {
return factory.createComma(factory.createAssignment(temp, arg), factory.createConditionalExpression(
/*condition*/ factory.createIdentifier("__syncRequire"),
/*questionToken*/ undefined,
/*whenTrue*/ createImportCallExpressionCommonJS(temp, containsLexicalThis),
/*whenTrue*/ createImportCallExpressionCommonJS(temp, /* isInlineable */ true),
/*colonToken*/ undefined,
/*whenFalse*/ createImportCallExpressionAMD(temp, containsLexicalThis)
));
@@ -820,14 +820,25 @@ namespace ts {
return promise;
}
function createImportCallExpressionCommonJS(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// import("./blah")
function createImportCallExpressionCommonJS(arg: Expression | undefined, isInlineable?: boolean): Expression {
// import(x)
// emit as
// Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/
// var _a;
// (_a = x, Promise.resolve().then(() => require(_a)) /*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
const promiseResolveCall = factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []);
let requireCall: Expression = factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, arg ? [arg] : []);
// If the arg is not inlineable, we have to evaluate it in the current scope with a temp var
const temp = arg && !isSimpleInlineableExpression(arg) && !isInlineable ? factory.createTempVariable(hoistVariableDeclaration) : undefined;
const promiseResolveCall = factory.createCallExpression(
factory.createPropertyAccessExpression(factory.createIdentifier("Promise"), "resolve"),
/*typeArguments*/ undefined,
/*argumentsArray*/ [],
);
let requireCall: Expression = factory.createCallExpression(
factory.createIdentifier("require"),
/*typeArguments*/ undefined,
temp ? [temp] : arg ? [arg] : [],
);
if (getESModuleInterop(compilerOptions)) {
requireCall = emitHelpers().createImportStarHelper(requireCall);
}
@@ -851,16 +862,11 @@ namespace ts {
/*parameters*/ [],
/*type*/ undefined,
factory.createBlock([factory.createReturnStatement(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 (containsLexicalThis) {
setEmitFlags(func, EmitFlags.CapturesThis);
}
}
return factory.createCallExpression(factory.createPropertyAccessExpression(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
const downleveledImport = factory.createCallExpression(factory.createPropertyAccessExpression(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
return temp === undefined ? downleveledImport : factory.createCommaListExpression([factory.createAssignment(temp, arg!), downleveledImport]);
}
function getHelperExpressionForExport(node: ExportDeclaration, innerExpr: Expression) {