diff --git a/README.md b/README.md index 68461e0ca9..e8badcf445 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ Enable the rules that you would like to use. * [react/no-unescaped-entities](docs/rules/no-unescaped-entities.md): Detect unescaped HTML entities, which might represent malformed tags * [react/no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property (fixable) * [react/no-unsafe](docs/rules/no-unsafe.md): Prevent usage of unsafe lifecycle methods +* [react/no-unused-elements](docs/rules/no-unused-elements.md): Disallow unused React elements * [react/no-unused-prop-types](docs/rules/no-unused-prop-types.md): Prevent definitions of unused prop types * [react/no-unused-state](docs/rules/no-unused-state.md): Prevent definition of unused state fields * [react/no-will-update-set-state](docs/rules/no-will-update-set-state.md): Prevent usage of setState in componentWillUpdate @@ -179,7 +180,7 @@ Enable the rules that you would like to use. * [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Enforce no duplicate props * [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent using string literals in React component definition * [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md): Forbid `javascript:` URLs -* [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Forbid `target="_blank"` attribute without `rel="noreferrer"` +* [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Forbid `target="_blank"` attribute without `rel="noreferrer"` (fixable) * [react/jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX * [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md): Disallow unnecessary fragments (fixable) * [react/jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md): Limit to one expression per line in JSX (fixable) diff --git a/docs/rules/no-unused-elements.md b/docs/rules/no-unused-elements.md new file mode 100644 index 0000000000..ef24d5bd68 --- /dev/null +++ b/docs/rules/no-unused-elements.md @@ -0,0 +1,25 @@ +# Disallow unused React elements (react/no-unused-elements) + +An unused React element indicates a logic error. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```jsx +
; +``` + +```js +React.createElement('div'); +``` + +Examples of **correct** code for this rule: + +```js +return
; +``` + +```js +const partial =
; +``` diff --git a/index.js b/index.js index 8edb1177f1..6db1be60f6 100644 --- a/index.js +++ b/index.js @@ -76,6 +76,7 @@ const allRules = { 'no-unescaped-entities': require('./lib/rules/no-unescaped-entities'), 'no-unknown-property': require('./lib/rules/no-unknown-property'), 'no-unsafe': require('./lib/rules/no-unsafe'), + 'no-unused-elements': require('./lib/rules/no-unused-elements'), 'no-unused-prop-types': require('./lib/rules/no-unused-prop-types'), 'no-unused-state': require('./lib/rules/no-unused-state'), 'no-will-update-set-state': require('./lib/rules/no-will-update-set-state'), diff --git a/lib/rules/no-unused-elements.js b/lib/rules/no-unused-elements.js new file mode 100644 index 0000000000..db8a70e8e7 --- /dev/null +++ b/lib/rules/no-unused-elements.js @@ -0,0 +1,53 @@ +/** + * @fileoverview Disallow unused React elements + * @author Duncan Beevers + */ + +'use strict'; + +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const NO_UNUSED_ELEMENTS_MESSAGE = 'noUnusedElements'; + +module.exports = { + meta: { + docs: { + description: 'Disallow unused React elements', + category: 'Best Practices', + recommended: false, + url: docsUrl('no-unused-elements') + }, + messages: { + [NO_UNUSED_ELEMENTS_MESSAGE]: 'Unused React element' + }, + schema: [{ + type: 'object', + additionalProperties: false + }] + }, + + create: Components.detect((context, components, utils) => ({ + JSXElement(node) { + if (node.parent.type === 'ExpressionStatement') { + context.report({ + node, + messageId: NO_UNUSED_ELEMENTS_MESSAGE + }); + } + }, + CallExpression(node) { + if (utils.isCreateElement(node) && node.parent.type === 'ExpressionStatement') { + context.report({ + node, + messageId: NO_UNUSED_ELEMENTS_MESSAGE + }); + } + } + }) + ) +}; diff --git a/tests/lib/rules/no-unused-elements.js b/tests/lib/rules/no-unused-elements.js new file mode 100644 index 0000000000..6f6187535a --- /dev/null +++ b/tests/lib/rules/no-unused-elements.js @@ -0,0 +1,63 @@ +/** + * @fileoverview Disallow unused React elements + * @author Duncan Beevers + */ + +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const RuleTester = require('eslint').RuleTester; +const rule = require('../../../lib/rules/no-unused-elements'); + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + jsx: true + } + } +}); + +const tests = { + valid: [ + { + code: 'const partial =
' + }, + { + code: '() =>
' + }, + { + code: 'const partial = React.createElement(\'div\', {}, \'\')' + } + ], + invalid: [ + { + code: '
', + errors: [{ + message: 'Unused React element' + }] + }, + { + code: 'React.createElement(\'div\', {}, \'\')', + errors: [{ + message: 'Unused React element' + }] + }, + { + code: 'if (condition) {
}', + errors: [{ + message: 'Unused React element' + }] + } + ] +}; + +ruleTester.run('no-unused-elements', rule, tests);