Skip to content

Commit

Permalink
[Fix] jsx-no-leaked-render: autofix nested "&&" logical expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
hduprat authored and ljharb committed Aug 10, 2022
1 parent b7f388b commit cac3838
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [`forbid-prop-types`]: Ignore objects that are not of type React.PropTypes ([#3326][] @TildaDares)
* [`display-name`], component detection: fix false positive for HOF returning only nulls and literals ([#3305][] @golopot)
* [`jsx-no-target-blank`]: False negative when rel attribute is assigned using ConditionalExpression ([#3332][] @V2dha)
* [`jsx-no-leaked-render`]: autofix nested "&&" logical expressions ([#3353][] @hduprat)

### Changed
* [Refactor] [`jsx-indent-props`]: improved readability of the checkNodesIndent function ([#3315][] @caroline223)
Expand All @@ -33,6 +34,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [readme] remove dead codeclimate badge, add actions badge (@ljharb)
* [readme] Remove dead david-dm badge ([#3262][] @ddzz)

[#3353]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3353
[#3350]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3350
[#3349]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3349
[#3347]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3347
Expand Down
27 changes: 19 additions & 8 deletions lib/rules/jsx-no-leaked-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,30 @@ function getIsCoerceValidNestedLogicalExpression(node) {
return COERCE_VALID_LEFT_SIDE_EXPRESSIONS.some((validExpression) => validExpression === node.type);
}

function extractExpressionBetweenLogicalAnds(node) {
if (node.type !== 'LogicalExpression') return [node];
if (node.operator !== '&&') return [node];
return [].concat(
extractExpressionBetweenLogicalAnds(node.left),
extractExpressionBetweenLogicalAnds(node.right)
);
}

function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) {
const sourceCode = context.getSourceCode();
const rightSideText = sourceCode.getText(rightNode);

if (fixStrategy === COERCE_STRATEGY) {
let leftSideText = sourceCode.getText(leftNode);
if (isParenthesized(context, leftNode)) {
leftSideText = `(${leftSideText})`;
}

const shouldPrefixDoubleNegation = leftNode.type !== 'UnaryExpression';

return fixer.replaceText(reportedNode, `${shouldPrefixDoubleNegation ? '!!' : ''}${leftSideText} && ${rightSideText}`);
const expressions = extractExpressionBetweenLogicalAnds(leftNode);
const newText = expressions.map((node) => {
let nodeText = sourceCode.getText(node);
if (isParenthesized(context, node)) {
nodeText = `(${nodeText})`;
}
return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`;
}).join(' && ');

return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`);
}

if (fixStrategy === TERNARY_STRATEGY) {
Expand Down
44 changes: 43 additions & 1 deletion tests/lib/rules/jsx-no-leaked-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -725,9 +725,51 @@ ruleTester.run('jsx-no-leaked-render', rule, {
}],
output: `
const Component = ({ count, somethingElse, title }) => {
return <div>{!!count && somethingElse && title}</div>
return <div>{!!count && !!somethingElse && title}</div>
}
`,
},
{
code: `
const Component = ({ items, somethingElse, title }) => {
return <div>{items.length > 0 && somethingElse && title}</div>
}
`,
options: [{ validStrategies: ['coerce'] }],
errors: [{
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
line: 3,
column: 24,
}],
output: `
const Component = ({ items, somethingElse, title }) => {
return <div>{items.length > 0 && !!somethingElse && title}</div>
}
`,
},
{
code: `
const MyComponent = () => {
const items = []
const breakpoint = { phones: true }
return <div>{items.length > 0 && breakpoint.phones && <span />}</div>
}
`,
options: [{ validStrategies: ['coerce', 'ternary'] }],
output: `
const MyComponent = () => {
const items = []
const breakpoint = { phones: true }
return <div>{items.length > 0 && !!breakpoint.phones && <span />}</div>
}
`,
errors: [{
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
line: 6,
column: 24,
}],
},
]),
});

0 comments on commit cac3838

Please sign in to comment.