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 [#5910]: https://github.com/prettier/prettier/pull/5910
[#6033]: https://github.com/prettier/prettier/pull/6033 [#6033]: https://github.com/prettier/prettier/pull/6033
[#6186]: https://github.com/prettier/prettier/pull/6186 [#6186]: https://github.com/prettier/prettier/pull/6186
@ -927,6 +941,7 @@ function doSmth() {
[#6604]: https://github.com/prettier/prettier/pull/6604 [#6604]: https://github.com/prettier/prettier/pull/6604
[#6496]: https://github.com/prettier/prettier/pull/6496 [#6496]: https://github.com/prettier/prettier/pull/6496
[#6605]: https://github.com/prettier/prettier/pull/6605 [#6605]: https://github.com/prettier/prettier/pull/6605
[#6640]: https://github.com/prettier/prettier/pull/6640
[@brainkim]: https://github.com/brainkim [@brainkim]: https://github.com/brainkim
[@duailibe]: https://github.com/duailibe [@duailibe]: https://github.com/duailibe
[@gavinjoyce]: https://github.com/gavinjoyce [@gavinjoyce]: https://github.com/gavinjoyce

View File

@ -6,8 +6,8 @@ const util = require("../common/util");
const comments = require("./comments"); const comments = require("./comments");
const { const {
getLeftSidePathName, getLeftSidePathName,
hasNakedLeftSide, hasFlowShorthandAnnotationComment,
hasFlowShorthandAnnotationComment hasNakedLeftSide
} = require("./utils"); } = require("./utils");
function hasClosureCompilerTypeCastComment(text, path) { function hasClosureCompilerTypeCastComment(text, path) {
@ -722,6 +722,29 @@ function needsParens(path, options) {
return false; return false;
} }
return true; 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; return false;

View File

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

View File

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

View File

@ -55,6 +55,30 @@ foo = (
</ // close fragment </ // 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===================================== =====================================output=====================================
<></>; <></>;
@ -105,5 +129,29 @@ foo = (
// close fragment // 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 /> <Component />
</ // close fragment </ // 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`] = ` exports[`not-react.ts 1`] = `
====================================options===================================== ====================================options=====================================
parsers: ["typescript"] 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"]();