Skip to content

Commit d608181

Browse files
hdupratljharb
authored andcommitted
[Fix] jsx-no-leaked-render: autofix nested "&&" logical expressions
1 parent b7f388b commit d608181

File tree

2 files changed

+59
-9
lines changed

2 files changed

+59
-9
lines changed

lib/rules/jsx-no-leaked-render.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,27 @@ function getIsCoerceValidNestedLogicalExpression(node) {
4040
return COERCE_VALID_LEFT_SIDE_EXPRESSIONS.some((validExpression) => validExpression === node.type);
4141
}
4242

43+
function extractExpressionBetweenLogicalAnds(node) {
44+
if (node.type !== 'LogicalExpression') return [node];
45+
if (node.operator !== '&&') return [node];
46+
return [].concat(extractExpressionBetweenLogicalAnds(node.left), extractExpressionBetweenLogicalAnds(node.right));
47+
}
48+
4349
function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) {
4450
const sourceCode = context.getSourceCode();
4551
const rightSideText = sourceCode.getText(rightNode);
4652

4753
if (fixStrategy === COERCE_STRATEGY) {
48-
let leftSideText = sourceCode.getText(leftNode);
49-
if (isParenthesized(context, leftNode)) {
50-
leftSideText = `(${leftSideText})`;
51-
}
52-
53-
const shouldPrefixDoubleNegation = leftNode.type !== 'UnaryExpression';
54-
55-
return fixer.replaceText(reportedNode, `${shouldPrefixDoubleNegation ? '!!' : ''}${leftSideText} && ${rightSideText}`);
54+
const expressions = extractExpressionBetweenLogicalAnds(leftNode);
55+
const newText = expressions.map((node) => {
56+
let nodeText = sourceCode.getText(node);
57+
if (isParenthesized(context, node)) {
58+
nodeText = `(${nodeText})`;
59+
}
60+
return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`;
61+
}).join(' && ');
62+
63+
return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`);
5664
}
5765

5866
if (fixStrategy === TERNARY_STRATEGY) {

tests/lib/rules/jsx-no-leaked-render.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,9 +725,51 @@ ruleTester.run('jsx-no-leaked-render', rule, {
725725
}],
726726
output: `
727727
const Component = ({ count, somethingElse, title }) => {
728-
return <div>{!!count && somethingElse && title}</div>
728+
return <div>{!!count && !!somethingElse && title}</div>
729729
}
730730
`,
731731
},
732+
{
733+
code: `
734+
const Component = ({ items, somethingElse, title }) => {
735+
return <div>{items.length > 0 && somethingElse && title}</div>
736+
}
737+
`,
738+
options: [{ validStrategies: ['coerce'] }],
739+
errors: [{
740+
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
741+
line: 3,
742+
column: 24,
743+
}],
744+
output: `
745+
const Component = ({ items, somethingElse, title }) => {
746+
return <div>{items.length > 0 && !!somethingElse && title}</div>
747+
}
748+
`,
749+
},
750+
{
751+
code: `
752+
const MyComponent = () => {
753+
const items = []
754+
const breakpoint = { phones: true }
755+
756+
return <div>{items.length > 0 && breakpoint.phones && <span />}</div>
757+
}
758+
`,
759+
options: [{ validStrategies: ['coerce', 'ternary'] }],
760+
output: `
761+
const MyComponent = () => {
762+
const items = []
763+
const breakpoint = { phones: true }
764+
765+
return <div>{items.length > 0 && !!breakpoint.phones && <span />}</div>
766+
}
767+
`,
768+
errors: [{
769+
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
770+
line: 6,
771+
column: 24,
772+
}],
773+
},
732774
]),
733775
});

0 commit comments

Comments
 (0)