TSX: Fix incorrectly removing parentheses around jsx elements being accessed in tsx file (#6640)

* Modify to wrap jsx element used with member-expression in tsx

* Add tests

* Update CHANGELOG.unreleased.md

* Modify to support no-inline jsx element

* Add pr number

* Fix from lint-docs

* Modify to rename isTsx => isTSXFile

* Modify to no considering file ext

* Support JSXFragment and add tests

* Update CHANGELOG.unreleased.md
master
Sosuke Suzuki 2019-10-16 00:11:48 +09:00 committed by GitHub
parent c949f5ba20
commit 3a998df02e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 209 additions and 8 deletions

View File

@ -898,6 +898,20 @@ function doSmth() {
}
```
#### TypeScript: sometimes removing parentheses around JSX made the code unparseable ([#6640] by [@sosukesuzuki])
<!-- prettier-ignore -->
```tsx
// Input
(<a />).toString();
// Prettier (stable)
<a />.toString():
// Prettier (master)
(<a />).toString();
```
[#5910]: https://github.com/prettier/prettier/pull/5910
[#6033]: https://github.com/prettier/prettier/pull/6033
[#6186]: https://github.com/prettier/prettier/pull/6186
@ -927,6 +941,7 @@ function doSmth() {
[#6604]: https://github.com/prettier/prettier/pull/6604
[#6496]: https://github.com/prettier/prettier/pull/6496
[#6605]: https://github.com/prettier/prettier/pull/6605
[#6640]: https://github.com/prettier/prettier/pull/6640
[@brainkim]: https://github.com/brainkim
[@duailibe]: https://github.com/duailibe
[@gavinjoyce]: https://github.com/gavinjoyce

View File

@ -6,8 +6,8 @@ const util = require("../common/util");
const comments = require("./comments");
const {
getLeftSidePathName,
hasNakedLeftSide,
hasFlowShorthandAnnotationComment
hasFlowShorthandAnnotationComment,
hasNakedLeftSide
} = require("./utils");
function hasClosureCompilerTypeCastComment(text, path) {
@ -722,6 +722,29 @@ function needsParens(path, options) {
return false;
}
return true;
case "JSXFragment":
case "JSXElement":
return (
parent.type !== "ArrayExpression" &&
parent.type !== "ArrowFunctionExpression" &&
parent.type !== "AssignmentExpression" &&
parent.type !== "AssignmentPattern" &&
parent.type !== "BinaryExpression" &&
parent.type !== "CallExpression" &&
parent.type !== "ConditionalExpression" &&
parent.type !== "ExpressionStatement" &&
parent.type !== "JsExpressionRoot" &&
parent.type !== "JSXAttribute" &&
parent.type !== "JSXElement" &&
parent.type !== "JSXExpressionContainer" &&
parent.type !== "JSXFragment" &&
parent.type !== "LogicalExpression" &&
parent.type !== "ObjectProperty" &&
parent.type !== "Property" &&
parent.type !== "ReturnStatement" &&
parent.type !== "TypeCastExpression" &&
parent.type !== "VariableDeclarator"
);
}
return false;

View File

@ -84,6 +84,7 @@ const {
isTemplateOnItsOwnLine,
isTestCall,
isTheOnlyJSXElementInMarkdown,
isTSXFile,
isTypeAnnotationAFunction,
matchJsxWhitespaceRegex,
needsHardlineAfterDanglingComment,
@ -2152,7 +2153,7 @@ function printPathNoParens(path, options, print, args) {
() => printJSXElement(path, options, print),
options
);
return maybeWrapJSXElementInParens(path, elem);
return maybeWrapJSXElementInParens(path, elem, options);
}
case "JSXOpeningElement": {
const n = path.getValue();
@ -3005,8 +3006,7 @@ function printPathNoParens(path, options, print, args) {
if (
parent.params &&
parent.params.length === 1 &&
options.filepath &&
/\.tsx$/i.test(options.filepath) &&
isTSXFile(options) &&
!n.constraint &&
grandParent.type === "ArrowFunctionExpression"
) {
@ -5385,7 +5385,7 @@ function printJSXElement(path, options, print) {
]);
}
function maybeWrapJSXElementInParens(path, elem) {
function maybeWrapJSXElementInParens(path, elem, options) {
const parent = path.getParentNode();
if (!parent) {
return elem;
@ -5413,12 +5413,14 @@ function maybeWrapJSXElementInParens(path, elem) {
"JSXExpressionContainer"
]);
const needsParens = pathNeedsParens(path, options);
return group(
concat([
ifBreak("("),
needsParens ? "" : ifBreak("("),
indent(concat([softline, elem])),
softline,
ifBreak(")")
needsParens ? "" : ifBreak(")")
]),
{ shouldBreak }
);

View File

@ -888,6 +888,10 @@ function identity(x) {
return x;
}
function isTSXFile(options) {
return options.filepath && /\.tsx$/i.test(options.filepath);
}
module.exports = {
classChildNeedsASIProtection,
classPropMayCauseASIProblems,
@ -934,6 +938,7 @@ module.exports = {
isTemplateOnItsOwnLine,
isTestCall,
isTheOnlyJSXElementInMarkdown,
isTSXFile,
isTypeAnnotationAFunction,
matchJsxWhitespaceRegex,
needsHardlineAfterDanglingComment,

View File

@ -55,6 +55,30 @@ foo = (
</ // close fragment
>;
[<></>, <></>];
const fun1 = () => <></>;
x = <></>
function fun2(param = <></>) {}
1 + <></>;
1 || <></>;
fun2(<></>);
test ? <></> : x;
<></>;
<a>
<></>
</a>;
const obj = {
foo: <></>
};
const fragmentVar = <></>;
function fun3() {
return <></>;
}
(<></>).toString();
(<></>).props;
(<></>)["computed"];
(<></>)["computed"]();
=====================================output=====================================
<></>;
@ -105,5 +129,29 @@ foo = (
// close fragment
>;
[<></>, <></>];
const fun1 = () => <></>;
x = <></>;
function fun2(param = <></>) {}
1 + <></>;
1 || <></>;
fun2(<></>);
test ? <></> : x;
<></>;
<a>
<></>
</a>;
const obj = {
foo: <></>
};
const fragmentVar = <></>;
function fun3() {
return <></>;
}
(<></>).toString();
(<></>).props;
(<></>)["computed"];
(<></>)["computed"]();
================================================================================
`;

View File

@ -46,3 +46,27 @@ foo = (
<Component />
</ // close fragment
>;
[<></>, <></>];
const fun1 = () => <></>;
x = <></>
function fun2(param = <></>) {}
1 + <></>;
1 || <></>;
fun2(<></>);
test ? <></> : x;
<></>;
<a>
<></>
</a>;
const obj = {
foo: <></>
};
const fragmentVar = <></>;
function fun3() {
return <></>;
}
(<></>).toString();
(<></>).props;
(<></>)["computed"];
(<></>)["computed"]();

View File

@ -38,6 +38,66 @@ printWidth: 80
================================================================================
`;
exports[`member-expression.tsx 1`] = `
====================================options=====================================
parsers: ["typescript"]
printWidth: 80
| printWidth
=====================================input======================================
(<a />).method();
(<a />).property;
(<a />)["computed"];
(<a />)["computed"]();
(
<div>
<a>foo</a>
</div>
).method();
(
<div>
<a>foo</a>
</div>
).property;
(
<div>
<a>foo</a>
</div>
)["computed"];
(
<div>
<a>foo</a>
</div>
)["computed"]();
=====================================output=====================================
(<a />).method();
(<a />).property;
(<a />)["computed"];
(<a />)["computed"]();
(
<div>
<a>foo</a>
</div>
).method();
(
<div>
<a>foo</a>
</div>
).property;
(
<div>
<a>foo</a>
</div>
)["computed"];
(
<div>
<a>foo</a>
</div>
)["computed"]();
================================================================================
`;
exports[`not-react.ts 1`] = `
====================================options=====================================
parsers: ["typescript"]

View File

@ -0,0 +1,24 @@
(<a />).method();
(<a />).property;
(<a />)["computed"];
(<a />)["computed"]();
(
<div>
<a>foo</a>
</div>
).method();
(
<div>
<a>foo</a>
</div>
).property;
(
<div>
<a>foo</a>
</div>
)["computed"];
(
<div>
<a>foo</a>
</div>
)["computed"]();