From b8b0efc770138c277ec5f9d24b4780dbb180d84f Mon Sep 17 00:00:00 2001 From: Redas Jefisovas <114405740+holoflash@users.noreply.github.com> Date: Wed, 15 Oct 2025 21:44:16 +0200 Subject: [PATCH 1/2] fix: detect missing keys in arrays of pushed elements --- lib/rules/jsx-key.js | 41 ++++++++++++++++++++++++++++++++++++++ tests/lib/rules/jsx-key.js | 14 +++++++++++++ 2 files changed, 55 insertions(+) diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 825d21f4bb..fff7eb295a 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -186,6 +186,41 @@ module.exports = { )`.replace(/\s/g, ''); let isWithinChildrenToArray = false; + /** + * Checks if the given node is a push call expression that has JSX Elements or JSX Fragments as arguments, + * and the JSX is missing a key prop + * @param {ASTNode} node + */ + function checkArrayPushArguments(node) { + if (isWithinChildrenToArray) { + return; + } + + node.arguments.forEach((arg) => { + if ( + arg.type === 'JSXElement' + && !hasProp(arg.openingElement.attributes, 'key') + ) { + report(context, messages.missingArrayKey, 'missingArrayKey', { + node: arg, + }); + } else if (checkFragmentShorthand && arg.type === 'JSXFragment') { + report( + context, + messages.missingArrayKeyUsePrag, + 'missingArrayKeyUsePrag', + { + node: arg, + data: { + reactPrag: reactPragma, + fragPrag: fragmentPragma, + }, + } + ); + } + }); + } + const seen = new WeakSet(); return { @@ -197,6 +232,12 @@ module.exports = { isWithinChildrenToArray = false; }, + 'CallExpression[callee.type="MemberExpression"][callee.property.name="push"]'( + node + ) { + checkArrayPushArguments(node); + }, + 'ArrayExpression, JSXElement > JSXElement'(node) { if (isWithinChildrenToArray) { return; diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 2c5ba7a5c1..a15c6910a8 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -424,5 +424,19 @@ ruleTester.run('jsx-key', rule, { options: [{ checkKeyMustBeforeSpread: true }], errors: [{ messageId: 'keyBeforeSpread' }], }, + { + code: ` + const TestCase = () => { + const keyLessItems = []; + + for (let i = 0; i < 4; i++) { + keyLessItems.push(