Downlevel destructuring in module transformer if destructured variable has multiple names (#23832)

* Downlevel destructuring in module transformer if destructured variable has multiple names

* Alter indentation
This commit is contained in:
Wesley Wigham 2018-05-02 16:20:47 -07:00 committed by GitHub
parent b5b7fc4f1d
commit 9c40d276ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 321 additions and 19 deletions

View file

@ -451,7 +451,7 @@ namespace ts {
*/
function addExportEqualsIfNeeded(statements: Statement[], emitAsReturn: boolean) {
if (currentModuleInfo.exportEquals) {
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, importCallExpressionVisitor);
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, moduleExpressionElementVisitor);
if (expressionResult) {
if (emitAsReturn) {
const statement = createReturn(expressionResult);
@ -517,27 +517,82 @@ namespace ts {
return visitEndOfDeclarationMarker(<EndOfDeclarationMarker>node);
default:
return visitEachChild(node, importCallExpressionVisitor, context);
return visitEachChild(node, moduleExpressionElementVisitor, context);
}
}
function importCallExpressionVisitor(node: Expression): VisitResult<Expression> {
// This visitor does not need to descend into the tree if there is no dynamic import,
function moduleExpressionElementVisitor(node: Expression): VisitResult<Expression> {
// This visitor does not need to descend into the tree if there is no dynamic import or destructuring assignment,
// as export/import statements are only transformed at the top level of a file.
if (!(node.transformFlags & TransformFlags.ContainsDynamicImport)) {
if (!(node.transformFlags & TransformFlags.ContainsDynamicImport) && !(node.transformFlags & TransformFlags.ContainsDestructuringAssignment)) {
return node;
}
if (isImportCall(node)) {
return visitImportCallExpression(node);
}
else if (node.transformFlags & TransformFlags.DestructuringAssignment && isBinaryExpression(node)) {
return visitDestructuringAssignment(node as DestructuringAssignment);
}
else {
return visitEachChild(node, importCallExpressionVisitor, context);
return visitEachChild(node, moduleExpressionElementVisitor, context);
}
}
function destructuringNeedsFlattening(node: Expression): boolean {
if (isObjectLiteralExpression(node)) {
for (const elem of node.properties) {
switch (elem.kind) {
case SyntaxKind.PropertyAssignment:
if (destructuringNeedsFlattening(elem.initializer)) {
return true;
}
break;
case SyntaxKind.ShorthandPropertyAssignment:
if (destructuringNeedsFlattening(elem.name)) {
return true;
}
break;
case SyntaxKind.SpreadAssignment:
if (destructuringNeedsFlattening(elem.expression)) {
return true;
}
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return false;
default: Debug.assertNever(elem, "Unhandled object member kind");
}
}
}
else if (isArrayLiteralExpression(node)) {
for (const elem of node.elements) {
if (isSpreadElement(elem)) {
if (destructuringNeedsFlattening(elem.expression)) {
return true;
}
}
else if (destructuringNeedsFlattening(elem)) {
return true;
}
}
}
else if (isIdentifier(node)) {
return length(getExports(node)) > (isExportName(node) ? 1 : 0);
}
return false;
}
function visitDestructuringAssignment(node: DestructuringAssignment): Expression {
if (destructuringNeedsFlattening(node.left)) {
return flattenDestructuringAssignment(node, moduleExpressionElementVisitor, context, FlattenLevel.All, /*needsValue*/ false, createAllExportExpressions);
}
return visitEachChild(node, moduleExpressionElementVisitor, context);
}
function visitImportCallExpression(node: ImportCall): Expression {
const argument = visitNode(firstOrUndefined(node.arguments), importCallExpressionVisitor);
const argument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor);
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
switch (compilerOptions.module) {
case ModuleKind.AMD:
@ -959,10 +1014,10 @@ namespace ts {
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
// Defer exports until we encounter an EndOfDeclarationMarker node
const id = getOriginalNodeId(node);
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
}
else {
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
}
return singleOrMany(statements);
@ -985,9 +1040,9 @@ namespace ts {
node.asteriskToken,
getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
/*typeParameters*/ undefined,
visitNodes(node.parameters, importCallExpressionVisitor),
visitNodes(node.parameters, moduleExpressionElementVisitor),
/*type*/ undefined,
visitEachChild(node.body, importCallExpressionVisitor, context)
visitEachChild(node.body, moduleExpressionElementVisitor, context)
),
/*location*/ node
),
@ -996,7 +1051,7 @@ namespace ts {
);
}
else {
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
}
if (hasAssociatedEndOfDeclarationMarker(node)) {
@ -1027,8 +1082,8 @@ namespace ts {
visitNodes(node.modifiers, modifierVisitor, isModifier),
getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, importCallExpressionVisitor),
visitNodes(node.members, importCallExpressionVisitor)
visitNodes(node.heritageClauses, moduleExpressionElementVisitor),
visitNodes(node.members, moduleExpressionElementVisitor)
),
node
),
@ -1037,7 +1092,7 @@ namespace ts {
);
}
else {
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
}
if (hasAssociatedEndOfDeclarationMarker(node)) {
@ -1089,7 +1144,7 @@ namespace ts {
}
}
else {
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
}
if (hasAssociatedEndOfDeclarationMarker(node)) {
@ -1104,6 +1159,22 @@ namespace ts {
return singleOrMany(statements);
}
function createAllExportExpressions(name: Identifier, value: Expression, location?: TextRange) {
const exportedNames = getExports(name);
if (exportedNames) {
// For each additional export of the declaration, apply an export assignment.
let expression: Expression = isExportName(name) ? value : createAssignment(name, value);
for (const exportName of exportedNames) {
// Mark the node to prevent triggering substitution.
setEmitFlags(expression, EmitFlags.NoSubstitution);
expression = createExportExpression(exportName, expression, /*location*/ location);
}
return expression;
}
return createAssignment(name, value);
}
/**
* Transforms an exported variable with an initializer into an expression.
*
@ -1112,12 +1183,12 @@ namespace ts {
function transformInitializedVariable(node: VariableDeclaration): Expression {
if (isBindingPattern(node.name)) {
return flattenDestructuringAssignment(
visitNode(node, importCallExpressionVisitor),
visitNode(node, moduleExpressionElementVisitor),
/*visitor*/ undefined,
context,
FlattenLevel.All,
/*needsValue*/ false,
createExportExpression
createAllExportExpressions
);
}
else {
@ -1129,7 +1200,7 @@ namespace ts {
),
/*location*/ node.name
),
visitNode(node.initializer, importCallExpressionVisitor)
visitNode(node.initializer, moduleExpressionElementVisitor)
);
}
}

View file

@ -0,0 +1,53 @@
//// [destructuringAssignmentWithExportedName.ts]
export let exportedFoo: any;
let nonexportedFoo: any;
// sanity checks
exportedFoo = null;
nonexportedFoo = null;
if (null as any) {
({ exportedFoo, nonexportedFoo } = null as any);
}
else if (null as any) {
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
}
else if (null as any) {
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
}
else if (null as any) {
([exportedFoo, nonexportedFoo] = null as any);
}
else {
([[exportedFoo, nonexportedFoo]] = null as any);
}
export { nonexportedFoo };
export { exportedFoo as foo, nonexportedFoo as nfoo };
//// [destructuringAssignmentWithExportedName.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.foo = exports.exportedFoo;
let nonexportedFoo;
exports.nonexportedFoo = nonexportedFoo;
exports.nfoo = nonexportedFoo;
// sanity checks
exports.foo = exports.exportedFoo = null;
exports.nfoo = exports.nonexportedFoo = nonexportedFoo = null;
if (null) {
(_a = null, exports.foo = exports.exportedFoo = _a.exportedFoo, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _a.nonexportedFoo);
}
else if (null) {
(_b = null, exports.foo = exports.exportedFoo = _b.foo, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _b.bar);
}
else if (null) {
(_c = null.foo, exports.foo = exports.exportedFoo = _c.bar, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _c.baz);
}
else if (null) {
(_d = null, exports.foo = exports.exportedFoo = _d[0], exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _d[1]);
}
else {
(_e = null[0], exports.foo = exports.exportedFoo = _e[0], exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _e[1]);
}
var _a, _b, _c, _d, _e;

View file

@ -0,0 +1,54 @@
=== tests/cases/compiler/destructuringAssignmentWithExportedName.ts ===
export let exportedFoo: any;
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
let nonexportedFoo: any;
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
// sanity checks
exportedFoo = null;
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
nonexportedFoo = null;
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
if (null as any) {
({ exportedFoo, nonexportedFoo } = null as any);
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 8, 6))
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 8, 19))
}
else if (null as any) {
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 11, 3))
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
>bar : Symbol(bar, Decl(destructuringAssignmentWithExportedName.ts, 11, 21))
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
}
else if (null as any) {
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 14, 3))
>bar : Symbol(bar, Decl(destructuringAssignmentWithExportedName.ts, 14, 10))
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
>baz : Symbol(baz, Decl(destructuringAssignmentWithExportedName.ts, 14, 28))
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
}
else if (null as any) {
([exportedFoo, nonexportedFoo] = null as any);
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
}
else {
([[exportedFoo, nonexportedFoo]] = null as any);
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
}
export { nonexportedFoo };
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 23, 8))
export { exportedFoo as foo, nonexportedFoo as nfoo };
>exportedFoo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 24, 8))
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 24, 8))
>nonexportedFoo : Symbol(nfoo, Decl(destructuringAssignmentWithExportedName.ts, 24, 28))
>nfoo : Symbol(nfoo, Decl(destructuringAssignmentWithExportedName.ts, 24, 28))

View file

@ -0,0 +1,97 @@
=== tests/cases/compiler/destructuringAssignmentWithExportedName.ts ===
export let exportedFoo: any;
>exportedFoo : any
let nonexportedFoo: any;
>nonexportedFoo : any
// sanity checks
exportedFoo = null;
>exportedFoo = null : null
>exportedFoo : any
>null : null
nonexportedFoo = null;
>nonexportedFoo = null : null
>nonexportedFoo : any
>null : null
if (null as any) {
>null as any : any
>null : null
({ exportedFoo, nonexportedFoo } = null as any);
>({ exportedFoo, nonexportedFoo } = null as any) : any
>{ exportedFoo, nonexportedFoo } = null as any : any
>{ exportedFoo, nonexportedFoo } : { exportedFoo: any; nonexportedFoo: any; }
>exportedFoo : any
>nonexportedFoo : any
>null as any : any
>null : null
}
else if (null as any) {
>null as any : any
>null : null
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
>({ foo: exportedFoo, bar: nonexportedFoo } = null as any) : any
>{ foo: exportedFoo, bar: nonexportedFoo } = null as any : any
>{ foo: exportedFoo, bar: nonexportedFoo } : { foo: any; bar: any; }
>foo : any
>exportedFoo : any
>bar : any
>nonexportedFoo : any
>null as any : any
>null : null
}
else if (null as any) {
>null as any : any
>null : null
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
>({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any) : any
>{ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any : any
>{ foo: { bar: exportedFoo, baz: nonexportedFoo } } : { foo: { bar: any; baz: any; }; }
>foo : { bar: any; baz: any; }
>{ bar: exportedFoo, baz: nonexportedFoo } : { bar: any; baz: any; }
>bar : any
>exportedFoo : any
>baz : any
>nonexportedFoo : any
>null as any : any
>null : null
}
else if (null as any) {
>null as any : any
>null : null
([exportedFoo, nonexportedFoo] = null as any);
>([exportedFoo, nonexportedFoo] = null as any) : any
>[exportedFoo, nonexportedFoo] = null as any : any
>[exportedFoo, nonexportedFoo] : [any, any]
>exportedFoo : any
>nonexportedFoo : any
>null as any : any
>null : null
}
else {
([[exportedFoo, nonexportedFoo]] = null as any);
>([[exportedFoo, nonexportedFoo]] = null as any) : any
>[[exportedFoo, nonexportedFoo]] = null as any : any
>[[exportedFoo, nonexportedFoo]] : [[any, any]]
>[exportedFoo, nonexportedFoo] : [any, any]
>exportedFoo : any
>nonexportedFoo : any
>null as any : any
>null : null
}
export { nonexportedFoo };
>nonexportedFoo : any
export { exportedFoo as foo, nonexportedFoo as nfoo };
>exportedFoo : any
>foo : any
>nonexportedFoo : any
>nfoo : any

View file

@ -0,0 +1,27 @@
// @target: es2015
// @module: commonjs
export let exportedFoo: any;
let nonexportedFoo: any;
// sanity checks
exportedFoo = null;
nonexportedFoo = null;
if (null as any) {
({ exportedFoo, nonexportedFoo } = null as any);
}
else if (null as any) {
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
}
else if (null as any) {
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
}
else if (null as any) {
([exportedFoo, nonexportedFoo] = null as any);
}
else {
([[exportedFoo, nonexportedFoo]] = null as any);
}
export { nonexportedFoo };
export { exportedFoo as foo, nonexportedFoo as nfoo };