Skip to content

Commit

Permalink
[New] Add jsx-embed-condition rule
Browse files Browse the repository at this point in the history
Fixes #1979
  • Loading branch information
Anees Iqbal authored and ljharb committed Dec 26, 2020
1 parent 77e1b35 commit ee232bb
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -162,6 +162,7 @@ Enable the rules that you would like to use.
* [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)
* [react/jsx-curly-newline](docs/rules/jsx-curly-newline.md): Enforce consistent line breaks inside jsx curly (fixable)
* [react/jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes (fixable)
* [react/jsx-embed-condition](docs/rules/jsx-embed-condition.md): Disallows use of `&&` inside JSX Embeds to avoid conditional numbers from being rendered
* [react/jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Disallow or enforce spaces around equal signs in JSX attributes (fixable)
* [react/jsx-filename-extension](docs/rules/jsx-filename-extension.md): Restrict file extensions that may contain JSX
* [react/jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md): Ensure proper position of the first property in JSX (fixable)
Expand Down
34 changes: 34 additions & 0 deletions docs/rules/jsx-embed-condition.md
@@ -0,0 +1,34 @@
# Disallow && condition inside JSX Embed. (react/jsx-embed-condition)

This rule disallows use of `&&` inside JSX Embeds to avoid conditional numbers from being rendered.

## Why?

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).
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
nothing when `x` is `0`, it'll render `0` literal. This can lead to hard to figure out bugs, especially in React Native.

## Rule Details

Examples of **incorrect** code for this rule:

```jsx
<div>
{x && <MyProfile />}
</div>
<div>
{x || y && <strong>Hello</strong>}
</div>
```

Examples of **correct** code for this rule:

```jsx
<div>
{x ? <MyProfile /> : null}
</div>
// --
<div>
{x || y ? <strong>Hello</strong> : null}
</div>
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -22,6 +22,7 @@ const allRules = {
'jsx-closing-tag-location': require('./lib/rules/jsx-closing-tag-location'),
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
'jsx-curly-newline': require('./lib/rules/jsx-curly-newline'),
'jsx-embed-condition': require('./lib/rules/jsx-embed-condition'),
'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'),
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
Expand Down
45 changes: 45 additions & 0 deletions lib/rules/jsx-embed-condition.js
@@ -0,0 +1,45 @@
/**
* @fileoverview Prevents usage of && condition in JSX Embeds.
* @author Anees Iqbal <i@steelbrain.me>
*/

'use strict';

const docsUrl = require('../util/docsUrl');

// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'Prevents usage of && condition in JSX embed',
category: 'Best Practices',
recommended: false,
url: docsUrl('jsx-embed-condition')
},

schema: []
},

create(context) {
return {
JSXExpressionContainer(node) {
if (
node.parent == null
|| node.parent.type !== 'JSXElement'
|| node.expression == null
|| node.expression.type !== 'LogicalExpression'
|| node.expression.operator === '??'
) {
return;
}
context.report({
node,
message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'
});
}
};
}
};
72 changes: 72 additions & 0 deletions tests/lib/rules/jsx-embed-condition.js
@@ -0,0 +1,72 @@
/**
* @fileoverview Prevents usage of && condition in JSX Embeds.
* @author Anees Iqbal <i@steelbrain.me>
*/

'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const RuleTester = require('eslint').RuleTester;
const version = require('eslint/package.json').version;
const semver = require('semver');
const rule = require('../../../lib/rules/jsx-embed-condition');
const parsers = require('../../helpers/parsers');

const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
};

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const ruleTester = new RuleTester({parserOptions});
ruleTester.run('jsx-embed-condition', rule, {
valid: [].concat({
code: '<App>Test</App>'
}, {
code: '<App test>Another</App>'
}, {
code: '<App foo={e => bar(e)}>Hello World</App>'
}, {
code: '<App>{x ? <div></div> : null}</App>'
}, {
code: '<App>{x ? <div>Hello</div> : null}</App>'
}, {
code: '<App>{x ? <div>{y ? <y /> : <z />}</div> : null}</App>'
}, {
code: '<App x={x && y}>{x ? <div>{y ? <y /> : <z />}</div> : null}</App>'
}, semver.satisfies(version, '<= 5') ? [] : [{
code: '<App test>{x ?? <div />}</App>',
parserOptions: {
ecmaVersion: 2020
}
}, {
code: '<App test>{x ?? <div />}</App>',
parser: parsers.TYPESCRIPT_ESLINT
}, {
code: '<App test>{x ?? <div />}</App>',
parser: parsers['@TYPEDCRIPT_ESLINT']
}]),

invalid: [{
code: '<div>{x && <div />}</div>',
output: '<div>{x && <div />}</div>',
errors: [
{message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'}
]
}, {
code: '<div>{x ? <div>{y && <div />}</div> : null}</div>',
output: '<div>{x ? <div>{y && <div />}</div> : null}</div>',
errors: [
{message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'}
]
}]
});

0 comments on commit ee232bb

Please sign in to comment.