diff --git a/CHANGELOG.md b/CHANGELOG.md index ca44284b72..300aabda17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`jsx-no-literals`]: properly error on children with noAttributeStrings: true ([#3317][] @TildaDares) * [`jsx-key`]: catch key errors inside conditional statements ([#3320][] @TildaDares) * [`display-name`]: Accept forwardRef and Memo nesting in newer React versions ([#3321][] @TildaDares) +* [`jsx-key`]: avoid a crash from optional chaining from [#3320][] ([#3327][] @ljharb) ### Changed * [Refactor] [`jsx-indent-props`]: improved readability of the checkNodesIndent function ([#3315][] @caroline223) * [Tests] [`jsx-indent`], [`jsx-one-expression-per-line`]: add passing test cases ([#3314][] @ROSSROSALES) * [Refactor] `boolean-prop-naming`, `jsx-indent`: avoid assigning to arguments ([#3316][] @caroline223) +[#3327]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3327 [#3321]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3321 [#3320]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3320 [#3317]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3317 diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 2b61cc20a1..e2aa757d3d 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -95,7 +95,7 @@ module.exports = { if (node.alternate) { getReturnStatements(node.alternate, returnStatements); } - } else { + } else if (node.body) { node.body.forEach((item) => { if (item.type === 'IfStatement') { getReturnStatements(item, returnStatements); diff --git a/tests/helpers/parsers.js b/tests/helpers/parsers.js index 12a4df78b0..560282f05b 100644 --- a/tests/helpers/parsers.js +++ b/tests/helpers/parsers.js @@ -130,11 +130,13 @@ const parsers = { const tsOld = !skipTS && !features.has('no-ts-old'); const tsNew = !skipTS && !features.has('no-ts-new'); + const minES = features.has('class fields') ? 2022 : (features.has('optional chaining') ? 2020 : 5); // eslint-disable-line no-nested-ternary + return [].concat( skipBase ? [] : addComment( - Object.assign({}, test, features.has('class fields') && { + Object.assign({}, test, minES > 5 && { parserOptions: Object.assign({}, test.parserOptions, { - ecmaVersion: Math.max((test.parserOptions && test.parserOptions.ecmaVersion) || 0, 2022), + ecmaVersion: Math.max((test.parserOptions && test.parserOptions.ecmaVersion) || 0, minES), }), }), 'default' diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 4dae8973f3..6c007bb8bc 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -124,6 +124,30 @@ ruleTester.run('jsx-key', rule, { const onTextButtonClick = (e, item) => trackLink([, getAnalyticsUiElement(item), item.name], e); `, }, + { + code: ` + function Component({ allRatings }) { + return ( + + {Object.entries(allRatings)?.map(([key, value], index) => { + const rate = value?.split(/(?=[%, /])/); + + if (!rate) return null; + + return ( +
  • + + {rate?.[0]} + {rate?.[1]} +
  • + ); + })} +
    + ); + } + `, + features: ['optional chaining'], + }, ]), invalid: parsers.all([ { diff --git a/tests/lib/rules/jsx-no-constructed-context-values.js b/tests/lib/rules/jsx-no-constructed-context-values.js index 7a59da820c..1ec0592382 100644 --- a/tests/lib/rules/jsx-no-constructed-context-values.js +++ b/tests/lib/rules/jsx-no-constructed-context-values.js @@ -63,9 +63,6 @@ ruleTester.run('react-no-constructed-context-values', rule, { } `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` diff --git a/tests/lib/rules/no-array-index-key.js b/tests/lib/rules/no-array-index-key.js index 1faa12b1eb..e765d001ab 100644 --- a/tests/lib/rules/no-array-index-key.js +++ b/tests/lib/rules/no-array-index-key.js @@ -136,9 +136,6 @@ ruleTester.run('no-array-index-key', rule, { { code: 'foo?.map(child => )', features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, } ), @@ -349,9 +346,6 @@ ruleTester.run('no-array-index-key', rule, { code: 'foo?.map((child, i) => )', errors: [{ messageId: 'noArrayIndex' }], features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index e70717427a..80f6405d32 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -807,9 +807,6 @@ ruleTester.run('no-unused-prop-types', rule, { }; `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` @@ -839,9 +836,6 @@ ruleTester.run('no-unused-prop-types', rule, { module.exports = HelloComponent(); `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` @@ -871,9 +865,6 @@ ruleTester.run('no-unused-prop-types', rule, { module.exports = HelloComponent(); `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` @@ -926,9 +917,6 @@ ruleTester.run('no-unused-prop-types', rule, { }; `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: ` diff --git a/tests/lib/rules/no-unused-state.js b/tests/lib/rules/no-unused-state.js index 380296ec4e..c37145a047 100644 --- a/tests/lib/rules/no-unused-state.js +++ b/tests/lib/rules/no-unused-state.js @@ -353,9 +353,6 @@ eslintTester.run('no-unused-state', rule, { } `, features: ['optional chaining'], - parserOptions: { - ecmaVersion: 2020, - }, }, { code: `