From ee232bbaef4fb04740413d5048877559abf06222 Mon Sep 17 00:00:00 2001 From: Anees Iqbal Date: Sat, 26 Dec 2020 19:41:33 +0500 Subject: [PATCH] [New] Add `jsx-embed-condition` rule Fixes #1979 --- README.md | 1 + docs/rules/jsx-embed-condition.md | 34 ++++++++++++ index.js | 1 + lib/rules/jsx-embed-condition.js | 45 ++++++++++++++++ tests/lib/rules/jsx-embed-condition.js | 72 ++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 docs/rules/jsx-embed-condition.md create mode 100644 lib/rules/jsx-embed-condition.js create mode 100644 tests/lib/rules/jsx-embed-condition.js diff --git a/README.md b/README.md index 68461e0ca9..f9e8cf1fc3 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/docs/rules/jsx-embed-condition.md b/docs/rules/jsx-embed-condition.md new file mode 100644 index 0000000000..3ee904cd2b --- /dev/null +++ b/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 &&
}`, it'll render `
` 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 +
+ {x && } +
+
+ {x || y && Hello} +
+``` + +Examples of **correct** code for this rule: + +```jsx +
+ {x ? : null} +
+// -- +
+ {x || y ? Hello : null} +
+``` diff --git a/index.js b/index.js index 8edb1177f1..cbd292a67e 100644 --- a/index.js +++ b/index.js @@ -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'), diff --git a/lib/rules/jsx-embed-condition.js b/lib/rules/jsx-embed-condition.js new file mode 100644 index 0000000000..2d9d860957 --- /dev/null +++ b/lib/rules/jsx-embed-condition.js @@ -0,0 +1,45 @@ +/** + * @fileoverview Prevents usage of && condition in JSX Embeds. + * @author Anees Iqbal + */ + +'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' + }); + } + }; + } +}; diff --git a/tests/lib/rules/jsx-embed-condition.js b/tests/lib/rules/jsx-embed-condition.js new file mode 100644 index 0000000000..d3f213fe14 --- /dev/null +++ b/tests/lib/rules/jsx-embed-condition.js @@ -0,0 +1,72 @@ +/** + * @fileoverview Prevents usage of && condition in JSX Embeds. + * @author Anees Iqbal + */ + +'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: 'Test' + }, { + code: 'Another' + }, { + code: ' bar(e)}>Hello World' + }, { + code: '{x ?
: null}
' + }, { + code: '{x ?
Hello
: null}
' + }, { + code: '{x ?
{y ? : }
: null}
' + }, { + code: '{x ?
{y ? : }
: null}
' + }, semver.satisfies(version, '<= 5') ? [] : [{ + code: '{x ??
}', + parserOptions: { + ecmaVersion: 2020 + } + }, { + code: '{x ??
}', + parser: parsers.TYPESCRIPT_ESLINT + }, { + code: '{x ??
}', + parser: parsers['@TYPEDCRIPT_ESLINT'] + }]), + + invalid: [{ + code: '
{x &&
}
', + output: '
{x &&
}
', + errors: [ + {message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'} + ] + }, { + code: '
{x ?
{y &&
}
: null}
', + output: '
{x ?
{y &&
}
: null}
', + errors: [ + {message: 'Using && to condition JSX embeds is forbidden. Convert it to a ternary operation instead'} + ] + }] +});