Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9c40d27

Browse filesBrowse files
authored
Downlevel destructuring in module transformer if destructured variable has multiple names (microsoft#23832)
* Downlevel destructuring in module transformer if destructured variable has multiple names * Alter indentation
1 parent b5b7fc4 commit 9c40d27
Copy full SHA for 9c40d27

5 files changed

+321-19Lines changed: 321 additions & 19 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/transformers/module/module.ts‎

Copy file name to clipboardExpand all lines: src/compiler/transformers/module/module.ts
+90-19Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ namespace ts {
451451
*/
452452
function addExportEqualsIfNeeded(statements: Statement[], emitAsReturn: boolean) {
453453
if (currentModuleInfo.exportEquals) {
454-
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, importCallExpressionVisitor);
454+
const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, moduleExpressionElementVisitor);
455455
if (expressionResult) {
456456
if (emitAsReturn) {
457457
const statement = createReturn(expressionResult);
@@ -517,27 +517,82 @@ namespace ts {
517517
return visitEndOfDeclarationMarker(<EndOfDeclarationMarker>node);
518518

519519
default:
520-
return visitEachChild(node, importCallExpressionVisitor, context);
520+
return visitEachChild(node, moduleExpressionElementVisitor, context);
521521
}
522522
}
523523

524-
function importCallExpressionVisitor(node: Expression): VisitResult<Expression> {
525-
// This visitor does not need to descend into the tree if there is no dynamic import,
524+
function moduleExpressionElementVisitor(node: Expression): VisitResult<Expression> {
525+
// This visitor does not need to descend into the tree if there is no dynamic import or destructuring assignment,
526526
// as export/import statements are only transformed at the top level of a file.
527-
if (!(node.transformFlags & TransformFlags.ContainsDynamicImport)) {
527+
if (!(node.transformFlags & TransformFlags.ContainsDynamicImport) && !(node.transformFlags & TransformFlags.ContainsDestructuringAssignment)) {
528528
return node;
529529
}
530530

531531
if (isImportCall(node)) {
532532
return visitImportCallExpression(node);
533533
}
534+
else if (node.transformFlags & TransformFlags.DestructuringAssignment && isBinaryExpression(node)) {
535+
return visitDestructuringAssignment(node as DestructuringAssignment);
536+
}
534537
else {
535-
return visitEachChild(node, importCallExpressionVisitor, context);
538+
return visitEachChild(node, moduleExpressionElementVisitor, context);
536539
}
537540
}
538541

542+
function destructuringNeedsFlattening(node: Expression): boolean {
543+
if (isObjectLiteralExpression(node)) {
544+
for (const elem of node.properties) {
545+
switch (elem.kind) {
546+
case SyntaxKind.PropertyAssignment:
547+
if (destructuringNeedsFlattening(elem.initializer)) {
548+
return true;
549+
}
550+
break;
551+
case SyntaxKind.ShorthandPropertyAssignment:
552+
if (destructuringNeedsFlattening(elem.name)) {
553+
return true;
554+
}
555+
break;
556+
case SyntaxKind.SpreadAssignment:
557+
if (destructuringNeedsFlattening(elem.expression)) {
558+
return true;
559+
}
560+
break;
561+
case SyntaxKind.MethodDeclaration:
562+
case SyntaxKind.GetAccessor:
563+
case SyntaxKind.SetAccessor:
564+
return false;
565+
default: Debug.assertNever(elem, "Unhandled object member kind");
566+
}
567+
}
568+
}
569+
else if (isArrayLiteralExpression(node)) {
570+
for (const elem of node.elements) {
571+
if (isSpreadElement(elem)) {
572+
if (destructuringNeedsFlattening(elem.expression)) {
573+
return true;
574+
}
575+
}
576+
else if (destructuringNeedsFlattening(elem)) {
577+
return true;
578+
}
579+
}
580+
}
581+
else if (isIdentifier(node)) {
582+
return length(getExports(node)) > (isExportName(node) ? 1 : 0);
583+
}
584+
return false;
585+
}
586+
587+
function visitDestructuringAssignment(node: DestructuringAssignment): Expression {
588+
if (destructuringNeedsFlattening(node.left)) {
589+
return flattenDestructuringAssignment(node, moduleExpressionElementVisitor, context, FlattenLevel.All, /*needsValue*/ false, createAllExportExpressions);
590+
}
591+
return visitEachChild(node, moduleExpressionElementVisitor, context);
592+
}
593+
539594
function visitImportCallExpression(node: ImportCall): Expression {
540-
const argument = visitNode(firstOrUndefined(node.arguments), importCallExpressionVisitor);
595+
const argument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor);
541596
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
542597
switch (compilerOptions.module) {
543598
case ModuleKind.AMD:
@@ -959,10 +1014,10 @@ namespace ts {
9591014
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
9601015
// Defer exports until we encounter an EndOfDeclarationMarker node
9611016
const id = getOriginalNodeId(node);
962-
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
1017+
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
9631018
}
9641019
else {
965-
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
1020+
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true);
9661021
}
9671022

9681023
return singleOrMany(statements);
@@ -985,9 +1040,9 @@ namespace ts {
9851040
node.asteriskToken,
9861041
getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
9871042
/*typeParameters*/ undefined,
988-
visitNodes(node.parameters, importCallExpressionVisitor),
1043+
visitNodes(node.parameters, moduleExpressionElementVisitor),
9891044
/*type*/ undefined,
990-
visitEachChild(node.body, importCallExpressionVisitor, context)
1045+
visitEachChild(node.body, moduleExpressionElementVisitor, context)
9911046
),
9921047
/*location*/ node
9931048
),
@@ -996,7 +1051,7 @@ namespace ts {
9961051
);
9971052
}
9981053
else {
999-
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
1054+
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
10001055
}
10011056

10021057
if (hasAssociatedEndOfDeclarationMarker(node)) {
@@ -1027,8 +1082,8 @@ namespace ts {
10271082
visitNodes(node.modifiers, modifierVisitor, isModifier),
10281083
getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true),
10291084
/*typeParameters*/ undefined,
1030-
visitNodes(node.heritageClauses, importCallExpressionVisitor),
1031-
visitNodes(node.members, importCallExpressionVisitor)
1085+
visitNodes(node.heritageClauses, moduleExpressionElementVisitor),
1086+
visitNodes(node.members, moduleExpressionElementVisitor)
10321087
),
10331088
node
10341089
),
@@ -1037,7 +1092,7 @@ namespace ts {
10371092
);
10381093
}
10391094
else {
1040-
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
1095+
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
10411096
}
10421097

10431098
if (hasAssociatedEndOfDeclarationMarker(node)) {
@@ -1089,7 +1144,7 @@ namespace ts {
10891144
}
10901145
}
10911146
else {
1092-
statements = append(statements, visitEachChild(node, importCallExpressionVisitor, context));
1147+
statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context));
10931148
}
10941149

10951150
if (hasAssociatedEndOfDeclarationMarker(node)) {
@@ -1104,6 +1159,22 @@ namespace ts {
11041159
return singleOrMany(statements);
11051160
}
11061161

1162+
function createAllExportExpressions(name: Identifier, value: Expression, location?: TextRange) {
1163+
const exportedNames = getExports(name);
1164+
if (exportedNames) {
1165+
// For each additional export of the declaration, apply an export assignment.
1166+
let expression: Expression = isExportName(name) ? value : createAssignment(name, value);
1167+
for (const exportName of exportedNames) {
1168+
// Mark the node to prevent triggering substitution.
1169+
setEmitFlags(expression, EmitFlags.NoSubstitution);
1170+
expression = createExportExpression(exportName, expression, /*location*/ location);
1171+
}
1172+
1173+
return expression;
1174+
}
1175+
return createAssignment(name, value);
1176+
}
1177+
11071178
/**
11081179
* Transforms an exported variable with an initializer into an expression.
11091180
*
@@ -1112,12 +1183,12 @@ namespace ts {
11121183
function transformInitializedVariable(node: VariableDeclaration): Expression {
11131184
if (isBindingPattern(node.name)) {
11141185
return flattenDestructuringAssignment(
1115-
visitNode(node, importCallExpressionVisitor),
1186+
visitNode(node, moduleExpressionElementVisitor),
11161187
/*visitor*/ undefined,
11171188
context,
11181189
FlattenLevel.All,
11191190
/*needsValue*/ false,
1120-
createExportExpression
1191+
createAllExportExpressions
11211192
);
11221193
}
11231194
else {
@@ -1129,7 +1200,7 @@ namespace ts {
11291200
),
11301201
/*location*/ node.name
11311202
),
1132-
visitNode(node.initializer, importCallExpressionVisitor)
1203+
visitNode(node.initializer, moduleExpressionElementVisitor)
11331204
);
11341205
}
11351206
}
Collapse file
+53Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//// [destructuringAssignmentWithExportedName.ts]
2+
export let exportedFoo: any;
3+
let nonexportedFoo: any;
4+
5+
// sanity checks
6+
exportedFoo = null;
7+
nonexportedFoo = null;
8+
9+
if (null as any) {
10+
({ exportedFoo, nonexportedFoo } = null as any);
11+
}
12+
else if (null as any) {
13+
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
14+
}
15+
else if (null as any) {
16+
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
17+
}
18+
else if (null as any) {
19+
([exportedFoo, nonexportedFoo] = null as any);
20+
}
21+
else {
22+
([[exportedFoo, nonexportedFoo]] = null as any);
23+
}
24+
25+
export { nonexportedFoo };
26+
export { exportedFoo as foo, nonexportedFoo as nfoo };
27+
28+
//// [destructuringAssignmentWithExportedName.js]
29+
"use strict";
30+
Object.defineProperty(exports, "__esModule", { value: true });
31+
exports.foo = exports.exportedFoo;
32+
let nonexportedFoo;
33+
exports.nonexportedFoo = nonexportedFoo;
34+
exports.nfoo = nonexportedFoo;
35+
// sanity checks
36+
exports.foo = exports.exportedFoo = null;
37+
exports.nfoo = exports.nonexportedFoo = nonexportedFoo = null;
38+
if (null) {
39+
(_a = null, exports.foo = exports.exportedFoo = _a.exportedFoo, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _a.nonexportedFoo);
40+
}
41+
else if (null) {
42+
(_b = null, exports.foo = exports.exportedFoo = _b.foo, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _b.bar);
43+
}
44+
else if (null) {
45+
(_c = null.foo, exports.foo = exports.exportedFoo = _c.bar, exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _c.baz);
46+
}
47+
else if (null) {
48+
(_d = null, exports.foo = exports.exportedFoo = _d[0], exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _d[1]);
49+
}
50+
else {
51+
(_e = null[0], exports.foo = exports.exportedFoo = _e[0], exports.nfoo = exports.nonexportedFoo = nonexportedFoo = _e[1]);
52+
}
53+
var _a, _b, _c, _d, _e;
Collapse file
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
=== tests/cases/compiler/destructuringAssignmentWithExportedName.ts ===
2+
export let exportedFoo: any;
3+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
4+
5+
let nonexportedFoo: any;
6+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
7+
8+
// sanity checks
9+
exportedFoo = null;
10+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
11+
12+
nonexportedFoo = null;
13+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
14+
15+
if (null as any) {
16+
({ exportedFoo, nonexportedFoo } = null as any);
17+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 8, 6))
18+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 8, 19))
19+
}
20+
else if (null as any) {
21+
({ foo: exportedFoo, bar: nonexportedFoo } = null as any);
22+
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 11, 3))
23+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
24+
>bar : Symbol(bar, Decl(destructuringAssignmentWithExportedName.ts, 11, 21))
25+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
26+
}
27+
else if (null as any) {
28+
({ foo: { bar: exportedFoo, baz: nonexportedFoo } } = null as any);
29+
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 14, 3))
30+
>bar : Symbol(bar, Decl(destructuringAssignmentWithExportedName.ts, 14, 10))
31+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
32+
>baz : Symbol(baz, Decl(destructuringAssignmentWithExportedName.ts, 14, 28))
33+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
34+
}
35+
else if (null as any) {
36+
([exportedFoo, nonexportedFoo] = null as any);
37+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
38+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
39+
}
40+
else {
41+
([[exportedFoo, nonexportedFoo]] = null as any);
42+
>exportedFoo : Symbol(exportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 0, 10))
43+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 1, 3))
44+
}
45+
46+
export { nonexportedFoo };
47+
>nonexportedFoo : Symbol(nonexportedFoo, Decl(destructuringAssignmentWithExportedName.ts, 23, 8))
48+
49+
export { exportedFoo as foo, nonexportedFoo as nfoo };
50+
>exportedFoo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 24, 8))
51+
>foo : Symbol(foo, Decl(destructuringAssignmentWithExportedName.ts, 24, 8))
52+
>nonexportedFoo : Symbol(nfoo, Decl(destructuringAssignmentWithExportedName.ts, 24, 28))
53+
>nfoo : Symbol(nfoo, Decl(destructuringAssignmentWithExportedName.ts, 24, 28))
54+

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.