From 4bdeb5c97ec3334a479520189015ed97c1932e0f Mon Sep 17 00:00:00 2001 From: golopot Date: Thu, 16 May 2019 20:48:45 +0800 Subject: [PATCH] Start reporting fragments in html element --- README.md | 2 +- docs/rules/jsx-no-useless-fragment.md | 18 +++++++++-- lib/rules/jsx-no-useless-fragment.js | 26 +++++++++++++--- lib/types.d.ts | 2 ++ tests/lib/rules/jsx-no-useless-fragment.js | 35 +++++++++++++++++++++- 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 463cff4ceb..77decc5a46 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ Enable the rules that you would like to use. * [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings * [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='_blank'` * [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 fragments with less than 2 children +* [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md): Disallow unnescessary fragments * [react/jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md): Limit to one expression per line in JSX * [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md): Enforce curly braces or disallow unnecessary curly braces in JSX * [react/jsx-fragments](docs/rules/jsx-fragments.md): Enforce shorthand or standard form for React fragments diff --git a/docs/rules/jsx-no-useless-fragment.md b/docs/rules/jsx-no-useless-fragment.md index 8e2f9e3fed..a096d133d4 100644 --- a/docs/rules/jsx-no-useless-fragment.md +++ b/docs/rules/jsx-no-useless-fragment.md @@ -1,6 +1,6 @@ -# Disallow fragments with less than 2 children (react/jsx-no-useless-fragment) +# Disallow unnecessary fragments (react/jsx-no-useless-fragment) -A fragment is redundant if it only wraps a single child. +A fragment is redundant if it contains only one child, or if it is the child of a html element. ## Rule Details @@ -18,6 +18,13 @@ The following patterns are considered warnings: foo foo + +
+ <> +
+
+ +
``` The following patterns are **not** considered warnings: @@ -31,4 +38,11 @@ The following patterns are **not** considered warnings: <>foo {bar} <> {foo} + + + <> +
+
+ + ``` diff --git a/lib/rules/jsx-no-useless-fragment.js b/lib/rules/jsx-no-useless-fragment.js index 5a0380cd5c..9c9d5f9b33 100644 --- a/lib/rules/jsx-no-useless-fragment.js +++ b/lib/rules/jsx-no-useless-fragment.js @@ -62,7 +62,8 @@ module.exports = { url: docsUrl('jsx-no-useless-fragment') }, messages: { - NeedsMoreChidren: 'Fragments should contain more than one child.' + NeedsMoreChidren: 'Fragments should contain more than one child.', + ChildOfHtmlElement: 'Fragment in a html element is useless.' } }, @@ -79,22 +80,39 @@ module.exports = { ) < 2; } - function checkChildrenLength(node) { + /** + * @param {JSXElement|JSXFragment} node + * @returns {boolean} + */ + function isChildOfHtmlElement(node) { + return node.parent.type === 'JSXElement' + && node.parent.openingElement.name.type === 'JSXIdentifier' + && /^[a-z]+$/.test(node.parent.openingElement.name.name); + } + + function checkNode(node) { if (hasLessThanTwoChildren(node)) { context.report({ node, messageId: 'NeedsMoreChidren' }); } + + if (isChildOfHtmlElement(node)) { + context.report({ + node, + messageId: 'ChildOfHtmlElement' + }); + } } return { JSXElement(node) { if (isFragment(node, context)) { - checkChildrenLength(node); + checkNode(node); } }, - JSXFragment: checkChildrenLength + JSXFragment: checkNode }; } }; diff --git a/lib/types.d.ts b/lib/types.d.ts index d1ae21f948..91e5b4bd48 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -9,6 +9,8 @@ declare global { type Token = eslint.AST.Token; type Fixer = eslint.Rule.RuleFixer; type JSXAttribute = ASTNode; + type JSXElement = ASTNode; + type JSXFragment = ASTNode; type JSXSpreadAttribute = ASTNode; interface Context extends eslint.SourceCode { diff --git a/tests/lib/rules/jsx-no-useless-fragment.js b/tests/lib/rules/jsx-no-useless-fragment.js index eb53b73a15..4c7d887009 100644 --- a/tests/lib/rules/jsx-no-useless-fragment.js +++ b/tests/lib/rules/jsx-no-useless-fragment.js @@ -47,7 +47,15 @@ ruleTester.run('jsx-no-uselses-fragment', rule, { }, '', '', - '' + '', + { + code: '<>
', + parser: parsers.BABEL_ESLINT + }, + { + code: '
{"a"}{"b"}} />', + parser: parsers.BABEL_ESLINT + } ], invalid: [ { @@ -95,6 +103,31 @@ ruleTester.run('jsx-no-uselses-fragment', rule, { code: '<>', errors: [{messageId: 'NeedsMoreChidren'}], parser: parsers.BABEL_ESLINT + }, + { + code: '
<>foo
', + errors: [{messageId: 'NeedsMoreChidren'}, {messageId: 'ChildOfHtmlElement'}], + parser: parsers.BABEL_ESLINT + }, + { + code: '
<>{"a"}{"b"}
', + errors: [{messageId: 'ChildOfHtmlElement'}], + parser: parsers.BABEL_ESLINT + }, + { + code: ` +
+ + + <>{"a"}{"b"} +
`, + errors: [{messageId: 'ChildOfHtmlElement'}], + parser: parsers.BABEL_ESLINT + }, + + { + code: '
{"a"}{"b"}
', + errors: [{messageId: 'ChildOfHtmlElement'}] } ] });