From 6dc7803acecac6e79d71d9619665403fe3073d79 Mon Sep 17 00:00:00 2001 From: akulsr0 Date: Tue, 18 Jun 2024 18:56:03 +0530 Subject: [PATCH] [Fix] `jsx-key`: incorrect behavior for checkKeyMustBeforeSpread with map callbacks --- CHANGELOG.md | 2 ++ lib/rules/jsx-key.js | 42 ++++++++++++++++++++++---------------- tests/lib/rules/jsx-key.js | 15 ++++++++++++++ 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3540427d..46cf73592b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb) * [`jsx-boolean-value`]: `assumeUndefinedIsFalse` with `never` must not allow explicit `true` value ([#3757][] @6uliver) * [`no-object-type-as-default-prop`]: enable rule for components with many parameters ([#3768][] @JulienR1) +* [`jsx-key`]: incorrect behavior for checkKeyMustBeforeSpread with map callbacks ([#3769][] @akulsr0) +[#3769]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3769 [#3768]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3768 [#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 [#3757]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3757 diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index feee7ad5a0..825d21f4bb 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -73,11 +73,31 @@ module.exports = { const reactPragma = pragmaUtil.getFromContext(context); const fragmentPragma = pragmaUtil.getFragmentFromContext(context); + function isKeyAfterSpread(attributes) { + let hasFoundSpread = false; + return attributes.some((attribute) => { + if (attribute.type === 'JSXSpreadAttribute') { + hasFoundSpread = true; + return false; + } + if (attribute.type !== 'JSXAttribute') { + return false; + } + return hasFoundSpread && propName(attribute) === 'key'; + }); + } + function checkIteratorElement(node) { - if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) { - report(context, messages.missingIterKey, 'missingIterKey', { - node, - }); + if (node.type === 'JSXElement') { + if (!hasProp(node.openingElement.attributes, 'key')) { + report(context, messages.missingIterKey, 'missingIterKey', { node }); + } else { + const attrs = node.openingElement.attributes; + + if (checkKeyMustBeforeSpread && isKeyAfterSpread(attrs)) { + report(context, messages.keyBeforeSpread, 'keyBeforeSpread', { node }); + } + } } else if (checkFragmentShorthand && node.type === 'JSXFragment') { report(context, messages.missingIterKeyUsePrag, 'missingIterKeyUsePrag', { node, @@ -115,20 +135,6 @@ module.exports = { return returnStatements; } - function isKeyAfterSpread(attributes) { - let hasFoundSpread = false; - return attributes.some((attribute) => { - if (attribute.type === 'JSXSpreadAttribute') { - hasFoundSpread = true; - return false; - } - if (attribute.type !== 'JSXAttribute') { - return false; - } - return hasFoundSpread && propName(attribute) === 'key'; - }); - } - /** * Checks if the given node is a function expression or arrow function, * and checks if there is a missing key prop in return statement's arguments diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 17109e4215..748e9a2fd9 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -409,5 +409,20 @@ ruleTester.run('jsx-key', rule, { { messageId: 'missingIterKey' }, ], }, + { + code: ` + const TestCase = () => { + const list = [1, 2, 3, 4, 5]; + + return ( +
+ {list.map(x =>
)} +
+ ); + }; + `, + options: [{ checkKeyMustBeforeSpread: true }], + errors: [{ messageId: 'keyBeforeSpread' }], + }, ]), });