Skip to content

Commit 9c84be6

Browse files
committed
[New] jsx-no-leaked-render: add ignoreAttributes option
When true, validation of JSX attribute values is skipped.
1 parent b52e0ca commit 9c84be6

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

docs/rules/jsx-no-leaked-render.md

+4
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ const Component = ({ elements }) => {
151151

152152
The supported options are:
153153

154+
### `ignoreAttributes`
155+
156+
*TODO*
157+
154158
### `validStrategies`
155159

156160
An array containing `"coerce"`, `"ternary"`, or both (default: `["ternary", "coerce"]`) - Decide which strategies are considered valid to prevent leaked renders (at least 1 is required). The "coerce" option will transform the conditional of the JSX expression to a boolean. The "ternary" option transforms the binary expression into a ternary expression returning `null` for falsy values. The first option from the array will be the strategy used when autofixing, so the order of the values matters.

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

+23
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ function extractExpressionBetweenLogicalAnds(node) {
4949
);
5050
}
5151

52+
function isWithinAttribute(node) {
53+
const stopTypes = [
54+
'JSXElement',
55+
'JSXFragment',
56+
];
57+
let parent = node.parent;
58+
while (stopTypes.indexOf(parent.type) < 0) {
59+
if (parent.type === 'JSXAttribute') return true;
60+
parent = parent.parent;
61+
}
62+
return false;
63+
}
64+
5265
function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) {
5366
const sourceCode = context.getSourceCode();
5467
const rightSideText = sourceCode.getText(rightNode);
@@ -107,6 +120,10 @@ module.exports = {
107120
uniqueItems: true,
108121
default: DEFAULT_VALID_STRATEGIES,
109122
},
123+
ignoreAttributes: {
124+
type: 'boolean',
125+
default: false,
126+
},
110127
},
111128
additionalProperties: false,
112129
},
@@ -120,6 +137,9 @@ module.exports = {
120137

121138
return {
122139
'JSXExpressionContainer > LogicalExpression[operator="&&"]'(node) {
140+
if (config.ignoreAttributes && isWithinAttribute(node)) {
141+
return;
142+
}
123143
const leftSide = node.left;
124144

125145
const isCoerceValidLeftSide = COERCE_VALID_LEFT_SIDE_EXPRESSIONS
@@ -142,6 +162,9 @@ module.exports = {
142162
if (validStrategies.has(TERNARY_STRATEGY)) {
143163
return;
144164
}
165+
if (config.ignoreAttributes && isWithinAttribute(node)) {
166+
return;
167+
}
145168

146169
const isValidTernaryAlternate = TERNARY_INVALID_ALTERNATE_VALUES.indexOf(node.alternate.value) === -1;
147170
const isJSXElementAlternate = node.alternate.type === 'JSXElement';

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

+55
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,16 @@ ruleTester.run('jsx-no-leaked-render', rule, {
193193
`,
194194
options: [{ validStrategies: ['coerce'] }],
195195
},
196+
197+
// See #3292
198+
{
199+
code: `
200+
const Component = ({ enabled, checked }) => {
201+
return <CheckBox checked={enabled && checked} />
202+
}
203+
`,
204+
options: [{ ignoreAttributes: true }],
205+
},
196206
]),
197207

198208
invalid: parsers.all([
@@ -789,5 +799,50 @@ ruleTester.run('jsx-no-leaked-render', rule, {
789799
column: 24,
790800
}],
791801
},
802+
803+
// See #3292
804+
{
805+
code: `
806+
const Component = ({ enabled, checked }) => {
807+
return <CheckBox checked={enabled && checked} />
808+
}
809+
`,
810+
output: `
811+
const Component = ({ enabled, checked }) => {
812+
return <CheckBox checked={enabled ? checked : null} />
813+
}
814+
`,
815+
errors: [{
816+
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
817+
line: 3,
818+
column: 37,
819+
}],
820+
},
821+
{
822+
code: `
823+
const Component = ({ enabled }) => {
824+
return (
825+
<Foo bar={
826+
<Something>{enabled && <MuchWow />}</Something>
827+
} />
828+
)
829+
}
830+
`,
831+
output: `
832+
const Component = ({ enabled }) => {
833+
return (
834+
<Foo bar={
835+
<Something>{enabled ? <MuchWow /> : null}</Something>
836+
} />
837+
)
838+
}
839+
`,
840+
options: [{ ignoreAttributes: true }],
841+
errors: [{
842+
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
843+
line: 5,
844+
column: 27,
845+
}],
846+
},
792847
]),
793848
});

0 commit comments

Comments
 (0)