Skip to content

Commit dcfa20b

Browse files
Anees Iqballjharb
Anees Iqbal
authored andcommitted
[New] Add jsx-embed-condition rule
Fixes #1979
1 parent 77e1b35 commit dcfa20b

File tree

5 files changed

+151
-0
lines changed

5 files changed

+151
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ Enable the rules that you would like to use.
162162
* [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md): Disallow unnecessary JSX expressions when literals alone are sufficient or enfore JSX expressions on literals in JSX children or attributes (fixable)
163163
* [react/jsx-curly-newline](docs/rules/jsx-curly-newline.md): Enforce consistent line breaks inside jsx curly (fixable)
164164
* [react/jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes (fixable)
165+
* [react/jsx-embed-condition](docs/rules/jsx-embed-condition.md): Disallows use of `&&` inside JSX Embeds to avoid conditional numbers from being rendered
165166
* [react/jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Disallow or enforce spaces around equal signs in JSX attributes (fixable)
166167
* [react/jsx-filename-extension](docs/rules/jsx-filename-extension.md): Restrict file extensions that may contain JSX
167168
* [react/jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md): Ensure proper position of the first property in JSX (fixable)

docs/rules/jsx-embed-condition.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Disallow && condition inside JSX Embed. (react/jsx-embed-condition)
2+
3+
This rule disallows use of `&&` inside JSX Embeds to avoid conditional numbers from being rendered.
4+
5+
## Why?
6+
7+
The Official React docs warns against using `&&` in inline JSX embed expressions. The reason behind this is explained well in the [Official React docs](https://reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator).
8+
Imagine having a var `x` with a possible value of `1` or `0`. If you write `{x && <div />}`, it'll render `<div />` when `x` is `1` but instead of rendering
9+
nothing when `x` is `0`, it'll render `0` literal. This can lead to hard to figure out bugs, especially in React Native.
10+
11+
## Rule Details
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```jsx
16+
<div>
17+
{x && <MyProfile />}
18+
</div>
19+
<div>
20+
{x || y && <strong>Hello</strong>}
21+
</div>
22+
```
23+
24+
Examples of **correct** code for this rule:
25+
26+
```jsx
27+
<div>
28+
{x ? <MyProfile /> : null}
29+
</div>
30+
// --
31+
<div>
32+
{x || y ? <strong>Hello</strong> : null}
33+
</div>
34+
```

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const allRules = {
2222
'jsx-closing-tag-location': require('./lib/rules/jsx-closing-tag-location'),
2323
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
2424
'jsx-curly-newline': require('./lib/rules/jsx-curly-newline'),
25+
'jsx-embed-condition': require('./lib/rules/jsx-embed-condition'),
2526
'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'),
2627
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
2728
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),

lib/rules/jsx-embed-condition.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @fileoverview Prevents usage of && condition in JSX Embeds.
3+
* @author Anees Iqbal <[email protected]>
4+
*/
5+
6+
'use strict';
7+
8+
const docsUrl = require('../util/docsUrl');
9+
10+
// -----------------------------------------------------------------------------
11+
// Rule Definition
12+
// -----------------------------------------------------------------------------
13+
14+
module.exports = {
15+
meta: {
16+
docs: {
17+
description: 'Prevents usage of && condition in JSX embed',
18+
category: 'Best Practices',
19+
recommended: false,
20+
url: docsUrl('jsx-embed-condition')
21+
},
22+
23+
schema: []
24+
},
25+
26+
create(context) {
27+
return {
28+
JSXExpressionContainer(node) {
29+
if (
30+
node.parent == null
31+
|| node.parent.type !== 'JSXElement'
32+
|| node.expression == null
33+
|| node.expression.type !== 'LogicalExpression'
34+
|| node.expression.operator === '??'
35+
) {
36+
return;
37+
}
38+
context.report({
39+
node,
40+
message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'
41+
});
42+
}
43+
};
44+
}
45+
};
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* @fileoverview Prevents usage of && condition in JSX Embeds.
3+
* @author Anees Iqbal <[email protected]>
4+
*/
5+
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Requirements
10+
// ------------------------------------------------------------------------------
11+
12+
const RuleTester = require('eslint').RuleTester;
13+
const rule = require('../../../lib/rules/jsx-embed-condition');
14+
const parsers = require('../../helpers/parsers');
15+
16+
const parserOptions = {
17+
ecmaVersion: 2018,
18+
sourceType: 'module',
19+
ecmaFeatures: {
20+
jsx: true
21+
}
22+
};
23+
24+
// ------------------------------------------------------------------------------
25+
// Tests
26+
// ------------------------------------------------------------------------------
27+
28+
const ruleTester = new RuleTester({parserOptions});
29+
ruleTester.run('jsx-embed-condition', rule, {
30+
valid: [].concat({
31+
code: '<App>Test</App>'
32+
}, {
33+
code: '<App test>Another</App>'
34+
}, {
35+
code: '<App foo={e => bar(e)}>Hello World</App>'
36+
}, {
37+
code: '<App>{x ? <div></div> : null}</App>'
38+
}, {
39+
code: '<App>{x ? <div>Hello</div> : null}</App>'
40+
}, {
41+
code: '<App>{x ? <div>{y ? <y /> : <z />}</div> : null}</App>'
42+
}, {
43+
code: '<App x={x && y}>{x ? <div>{y ? <y /> : <z />}</div> : null}</App>'
44+
}, parsers.TS({
45+
code: '<App test>{x ?? <div />}</App>',
46+
parserOptions: {
47+
ecmaVersion: 2020
48+
}
49+
}, {
50+
code: '<App test>{x ?? <div />}</App>',
51+
parser: parsers.TYPESCRIPT_ESLINT
52+
}, {
53+
code: '<App test>{x ?? <div />}</App>',
54+
parser: parsers['@TYPEDCRIPT_ESLINT']
55+
})),
56+
57+
invalid: [{
58+
code: '<div>{x && <div />}</div>',
59+
output: '<div>{x && <div />}</div>',
60+
errors: [
61+
{message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'}
62+
]
63+
}, {
64+
code: '<div>{x ? <div>{y && <div />}</div> : null}</div>',
65+
output: '<div>{x ? <div>{y && <div />}</div> : null}</div>',
66+
errors: [
67+
{message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'}
68+
]
69+
}]
70+
});

0 commit comments

Comments
 (0)