Parenthesize export assignments if needed (#19590)

* parenthesize export assignments if needed

* Add default-specific parenthesization to handle lookahead

* New parenthesization logic for export default

* Handle commalist and comma cases
This commit is contained in:
Wesley Wigham 2017-10-30 18:23:32 -07:00 committed by GitHub
parent a89c055a48
commit c2aa13dac5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 350 additions and 3 deletions

View file

@ -1982,7 +1982,7 @@ namespace ts {
node.decorators = asNodeArray(decorators);
node.modifiers = asNodeArray(modifiers);
node.isExportEquals = isExportEquals;
node.expression = expression;
node.expression = isExportEquals ? parenthesizeBinaryOperand(SyntaxKind.EqualsToken, expression, /*isLeftSideOfBinary*/ false, /*leftOperand*/ undefined) : parenthesizeDefaultExpression(expression);
return node;
}
@ -3885,6 +3885,27 @@ namespace ts {
: e;
}
/**
* [Per the spec](https://tc39.github.io/ecma262/#prod-ExportDeclaration), `export default` accepts _AssigmentExpression_ but
* has a lookahead restriction for `function`, `async function`, and `class`.
*
* Basically, that means we need to parenthesize in the following cases:
*
* - BinaryExpression of CommaToken
* - CommaList (synthetic list of multiple comma expressions)
* - FunctionExpression
* - ClassExpression
*/
export function parenthesizeDefaultExpression(e: Expression) {
const check = skipPartiallyEmittedExpressions(e);
return (check.kind === SyntaxKind.ClassExpression ||
check.kind === SyntaxKind.FunctionExpression ||
check.kind === SyntaxKind.CommaListExpression ||
isBinaryExpression(check) && check.operatorToken.kind === SyntaxKind.CommaToken)
? createParen(e)
: e;
}
/**
* Wraps an expression in parentheses if it is needed in order to use the expression
* as the expression of a NewExpression node.

View file

@ -0,0 +1,72 @@
//// [tests/cases/compiler/exportDefaultParenthesize.ts] ////
//// [commalist.ts]
export default {
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
};
//// [comma.ts]
export default {
['foo']: 42
};
//// [functionexpression.ts]
export default () => 42;
//// [commalist.js]
export default (_a = {},
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a['foo' + ''] = 42,
_a);
var _a;
//// [comma.js]
export default (_a = {},
_a['foo'] = 42,
_a);
var _a;
//// [functionexpression.js]
export default (function () { return 42; });

View file

@ -0,0 +1,38 @@
=== tests/cases/compiler/commalist.ts ===
export default {
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code. ['foo'+'']: 42,
No type information for this code.};
No type information for this code.
No type information for this code.=== tests/cases/compiler/comma.ts ===
export default {
['foo']: 42
>'foo' : Symbol(['foo'], Decl(comma.ts, 0, 16))
};
=== tests/cases/compiler/functionexpression.ts ===
export default () => 42;
No type information for this code.
No type information for this code.

View file

@ -0,0 +1,159 @@
=== tests/cases/compiler/commalist.ts ===
export default {
>{ ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42, ['foo'+'']: 42,} : { [x: string]: number; }
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
['foo'+'']: 42,
>'foo'+'' : string
>'foo' : "foo"
>'' : ""
>42 : 42
};
=== tests/cases/compiler/comma.ts ===
export default {
>{ ['foo']: 42} : { ['foo']: number; }
['foo']: 42
>'foo' : "foo"
>42 : 42
};
=== tests/cases/compiler/functionexpression.ts ===
export default () => 42;
>() => 42 : () => number
>42 : 42

View file

@ -0,0 +1,6 @@
//// [classexpr.ts]
export default (class Foo {} as any);
//// [classexpr.js]
export default (class Foo {
});

View file

@ -0,0 +1,4 @@
=== tests/cases/compiler/classexpr.ts ===
export default (class Foo {} as any);
>Foo : Symbol(Foo, Decl(classexpr.ts, 0, 16))

View file

@ -0,0 +1,7 @@
=== tests/cases/compiler/classexpr.ts ===
export default (class Foo {} as any);
>(class Foo {} as any) : any
>class Foo {} as any : any
>class Foo {} : typeof Foo
>Foo : typeof Foo

View file

@ -5,5 +5,5 @@ export = { ["hi"]: "there" };
define(["require", "exports"], function (require, exports) {
"use strict";
var _a;
return _a = {}, _a["hi"] = "there", _a;
return (_a = {}, _a["hi"] = "there", _a);
});

View file

@ -13,5 +13,5 @@ export = { ["hi"]: "there" };
})(function (require, exports) {
"use strict";
var _a;
return _a = {}, _a["hi"] = "there", _a;
return (_a = {}, _a["hi"] = "there", _a);
});

View file

@ -0,0 +1,36 @@
// @target: es5
// @module: esnext
// @filename: commalist.ts
export default {
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
['foo'+'']: 42,
};
// @filename: comma.ts
export default {
['foo']: 42
};
// @filename: functionexpression.ts
export default () => 42;

View file

@ -0,0 +1,4 @@
// @target: es6
// @module: esnext
// @filename: classexpr.ts
export default (class Foo {} as any);