Skip to content

Commit

Permalink
[New] jsx-no-leaked-render: add ignoreAttributes option
Browse files Browse the repository at this point in the history
When true, validation of JSX attribute values is skipped.
  • Loading branch information
aleclarson authored and ljharb committed Sep 26, 2022
1 parent 417e1ca commit 9ed3411
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/rules/jsx-no-leaked-render.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ const Component = ({ elements }) => {

The supported options are:

### `ignoreAttributes`

*TODO*

### `validStrategies`

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.
Expand Down
25 changes: 25 additions & 0 deletions lib/rules/jsx-no-leaked-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ function extractExpressionBetweenLogicalAnds(node) {
);
}

const stopTypes = {
__proto__: null,
JSXElement: true,
JSXFragment: true,
};

function isWithinAttribute(node) {
let parent = node.parent;
while (!stopTypes[parent.type]) {
if (parent.type === 'JSXAttribute') return true;
parent = parent.parent;
}
return false;
}

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

Expand Down Expand Up @@ -140,6 +155,10 @@ module.exports = {
uniqueItems: true,
default: DEFAULT_VALID_STRATEGIES,
},
ignoreAttributes: {
type: 'boolean',
default: false,
},
},
additionalProperties: false,
},
Expand All @@ -153,6 +172,9 @@ module.exports = {

return {
'JSXExpressionContainer > LogicalExpression[operator="&&"]'(node) {
if (config.ignoreAttributes && isWithinAttribute(node)) {
return;
}
const leftSide = node.left;

const isCoerceValidLeftSide = COERCE_VALID_LEFT_SIDE_EXPRESSIONS
Expand Down Expand Up @@ -189,6 +211,9 @@ module.exports = {
if (validStrategies.has(TERNARY_STRATEGY)) {
return;
}
if (config.ignoreAttributes && isWithinAttribute(node)) {
return;
}

const isValidTernaryAlternate = TERNARY_INVALID_ALTERNATE_VALUES.indexOf(node.alternate.value) === -1;
const isJSXElementAlternate = node.alternate.type === 'JSXElement';
Expand Down
57 changes: 56 additions & 1 deletion tests/lib/rules/jsx-no-leaked-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ ruleTester.run('jsx-no-leaked-render', rule, {
`,
options: [{ validStrategies: ['coerce'] }],
},

// See #3292
{
code: `
const Component = ({ enabled, checked }) => {
return <CheckBox checked={enabled && checked} />
}
`,
options: [{ ignoreAttributes: true }],
},
]) || [],

invalid: parsers.all([].concat(
Expand Down Expand Up @@ -885,6 +895,25 @@ ruleTester.run('jsx-no-leaked-render', rule, {
column: 24,
}],
},

// See #3292
{
code: `
const Component = ({ enabled, checked }) => {
return <CheckBox checked={enabled && checked} />
}
`,
output: `
const Component = ({ enabled, checked }) => {
return <CheckBox checked={enabled ? checked : null} />
}
`,
errors: [{
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
line: 3,
column: 37,
}],
},
{
code: `
const MyComponent = () => {
Expand Down Expand Up @@ -1010,6 +1039,32 @@ ruleTester.run('jsx-no-leaked-render', rule, {
line: 4,
column: 33,
}],
}
},
{
code: `
const Component = ({ enabled }) => {
return (
<Foo bar={
<Something>{enabled && <MuchWow />}</Something>
} />
)
}
`,
output: `
const Component = ({ enabled }) => {
return (
<Foo bar={
<Something>{enabled ? <MuchWow /> : null}</Something>
} />
)
}
`,
options: [{ ignoreAttributes: true }],
errors: [{
message: 'Potential leaked value that might cause unintentionally rendered values or rendering crashes',
line: 5,
column: 27,
}],
},
)),
});

0 comments on commit 9ed3411

Please sign in to comment.