diff --git a/CHANGELOG.md b/CHANGELOG.md index 291ed244a0..ed11d157ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`jsx-no-target-blank`]: allow ternaries with literals ([#3464][] @akulsr0) * [`sort-prop-types`]: restore autofixing ([#2574][] @ROSSROSALES) * [`no-unknown-property`]: add `inert` attribute ([#3484][] @ljharb) +* [`jsx-key`]: detect keys in logical expression and conditional expression ([#3490][] @metreniuk) ### Changed * [Perf] component detection: improve performance by avoiding traversing parents unnecessarily ([#3459][] @golopot) * [Docs] `forbid-component-props`: inclusive language w/ allowlist ([#3473][] @AndersDJohnson) * [Docs] automate doc generation with `eslint-doc-generator` ([#3469][] @bmish) +[#3490]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3490 [#3484]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3484 [#3473]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3473 [#3469]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3469 diff --git a/docs/rules/jsx-key.md b/docs/rules/jsx-key.md index 1076d4c145..0415b6df54 100644 --- a/docs/rules/jsx-key.md +++ b/docs/rules/jsx-key.md @@ -20,11 +20,11 @@ data.map(x => {x}); ``` ```jsx - +Array.from([1, 2, 3], (x) => {x}); ``` ```jsx -Array.from([1, 2, 3], (x) => {x}); + ``` In the last example the key is being spread, which is currently possible, but discouraged in favor of the statically provided key. @@ -40,11 +40,11 @@ data.map((x) => {x}); ``` ```jsx - +Array.from([1, 2, 3], (x) => {x}); ``` ```jsx -Array.from([1, 2, 3], (x) => {x}); + ``` ## Rule Options diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index b1e0a62a4f..263ed8243c 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -149,10 +149,20 @@ module.exports = { */ function checkArrowFunctionWithJSX(node) { const isArrFn = node && node.type === 'ArrowFunctionExpression'; - - if (isArrFn && (node.body.type === 'JSXElement' || node.body.type === 'JSXFragment')) { + const shouldCheckNode = (n) => n && (n.type === 'JSXElement' || n.type === 'JSXFragment'); + if (isArrFn && shouldCheckNode(node.body)) { checkIteratorElement(node.body); } + if (node.body.type === 'ConditionalExpression') { + if (shouldCheckNode(node.body.consequent)) { + checkIteratorElement(node.body.consequent); + } + if (shouldCheckNode(node.body.alternate)) { + checkIteratorElement(node.body.alternate); + } + } else if (node.body.type === 'LogicalExpression' && shouldCheckNode(node.body.right)) { + checkIteratorElement(node.body.right); + } } const childrenToArraySelector = `:matches( diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 15b89c19b7..393affcaac 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -42,6 +42,8 @@ ruleTester.run('jsx-key', rule, { { code: '[, ];' }, { code: '[1, 2, 3].map(function(x) { return });' }, { code: '[1, 2, 3].map(x => );' }, + { code: '[1, 2 ,3].map(x => x && );' }, + { code: '[1, 2 ,3].map(x => x ? : );' }, { code: '[1, 2, 3].map(x => { return });' }, { code: 'Array.from([1, 2, 3], function(x) { return });' }, { code: 'Array.from([1, 2, 3], (x => ));' }, @@ -188,10 +190,10 @@ ruleTester.run('jsx-key', rule, { code: ` import Act from 'react'; import { Children as ReactChildren } from 'react'; - + const { Children } = Act; const { toArray } = Children; - + Act.Children.toArray([1, 2 ,3].map(x => )); Act.Children.toArray(Array.from([1, 2 ,3], x => )); Children.toArray([1, 2 ,3].map(x => )); @@ -225,6 +227,18 @@ ruleTester.run('jsx-key', rule, { code: '[1, 2 ,3].map(x => );', errors: [{ messageId: 'missingIterKey' }], }, + { + code: '[1, 2 ,3].map(x => x && );', + errors: [{ messageId: 'missingIterKey' }], + }, + { + code: '[1, 2 ,3].map(x => x ? : );', + errors: [{ messageId: 'missingIterKey' }], + }, + { + code: '[1, 2 ,3].map(x => x ? : );', + errors: [{ messageId: 'missingIterKey' }], + }, { code: '[1, 2 ,3].map(x => { return });', errors: [{ messageId: 'missingIterKey' }],