Skip to content

Commit 4d19cd6

Browse files
committed
[Fix] jsx-key: avoid a crash from optional chaining from #3320
Fixes #3327
1 parent 5919660 commit 4d19cd6

8 files changed

+31
-27
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1212
* [`jsx-no-literals`]: properly error on children with noAttributeStrings: true ([#3317][] @TildaDares)
1313
* [`jsx-key`]: catch key errors inside conditional statements ([#3320][] @TildaDares)
1414
* [`display-name`]: Accept forwardRef and Memo nesting in newer React versions ([#3321][] @TildaDares)
15+
* [`jsx-key`]: avoid a crash from optional chaining from [#3320][] ([#3327][] @ljharb)
1516

1617
### Changed
1718
* [Refactor] [`jsx-indent-props`]: improved readability of the checkNodesIndent function ([#3315][] @caroline223)
1819
* [Tests] [`jsx-indent`], [`jsx-one-expression-per-line`]: add passing test cases ([#3314][] @ROSSROSALES)
1920
* [Refactor] `boolean-prop-naming`, `jsx-indent`: avoid assigning to arguments ([#3316][] @caroline223)
2021

22+
[#3327]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3327
2123
[#3321]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3321
2224
[#3320]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3320
2325
[#3317]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3317

lib/rules/jsx-key.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ module.exports = {
9595
if (node.alternate) {
9696
getReturnStatements(node.alternate, returnStatements);
9797
}
98-
} else {
98+
} else if (node.body) {
9999
node.body.forEach((item) => {
100100
if (item.type === 'IfStatement') {
101101
getReturnStatements(item, returnStatements);

tests/helpers/parsers.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,13 @@ const parsers = {
130130
const tsOld = !skipTS && !features.has('no-ts-old');
131131
const tsNew = !skipTS && !features.has('no-ts-new');
132132

133+
const minES = features.has('class fields') ? 2022 : (features.has('optional chaining') ? 2020 : 5); // eslint-disable-line no-nested-ternary
134+
133135
return [].concat(
134136
skipBase ? [] : addComment(
135-
Object.assign({}, test, features.has('class fields') && {
137+
Object.assign({}, test, minES > 5 && {
136138
parserOptions: Object.assign({}, test.parserOptions, {
137-
ecmaVersion: Math.max((test.parserOptions && test.parserOptions.ecmaVersion) || 0, 2022),
139+
ecmaVersion: Math.max((test.parserOptions && test.parserOptions.ecmaVersion) || 0, minES),
138140
}),
139141
}),
140142
'default'

tests/lib/rules/jsx-key.js

+24
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ ruleTester.run('jsx-key', rule, {
124124
const onTextButtonClick = (e, item) => trackLink([, getAnalyticsUiElement(item), item.name], e);
125125
`,
126126
},
127+
{
128+
code: `
129+
function Component({ allRatings }) {
130+
return (
131+
<RatingDetailsStyles>
132+
{Object.entries(allRatings)?.map(([key, value], index) => {
133+
const rate = value?.split(/(?=[%, /])/);
134+
135+
if (!rate) return null;
136+
137+
return (
138+
<li key={\`\${entertainment.tmdbId}\${index}\`}>
139+
<img src={\`/assets/rating/\${key}.png\`} />
140+
<span className="rating-details--rate">{rate?.[0]}</span>
141+
<span className="rating-details--rate-suffix">{rate?.[1]}</span>
142+
</li>
143+
);
144+
})}
145+
</RatingDetailsStyles>
146+
);
147+
}
148+
`,
149+
features: ['optional chaining'],
150+
},
127151
]),
128152
invalid: parsers.all([
129153
{

tests/lib/rules/jsx-no-constructed-context-values.js

-3
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ ruleTester.run('react-no-constructed-context-values', rule, {
6363
}
6464
`,
6565
features: ['optional chaining'],
66-
parserOptions: {
67-
ecmaVersion: 2020,
68-
},
6966
},
7067
{
7168
code: `

tests/lib/rules/no-array-index-key.js

-6
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,6 @@ ruleTester.run('no-array-index-key', rule, {
136136
{
137137
code: 'foo?.map(child => <Foo key={child.i} />)',
138138
features: ['optional chaining'],
139-
parserOptions: {
140-
ecmaVersion: 2020,
141-
},
142139
}
143140
),
144141

@@ -349,9 +346,6 @@ ruleTester.run('no-array-index-key', rule, {
349346
code: 'foo?.map((child, i) => <Foo key={i} />)',
350347
errors: [{ messageId: 'noArrayIndex' }],
351348
features: ['optional chaining'],
352-
parserOptions: {
353-
ecmaVersion: 2020,
354-
},
355349
},
356350
{
357351
code: `

tests/lib/rules/no-unused-prop-types.js

-12
Original file line numberDiff line numberDiff line change
@@ -807,9 +807,6 @@ ruleTester.run('no-unused-prop-types', rule, {
807807
};
808808
`,
809809
features: ['optional chaining'],
810-
parserOptions: {
811-
ecmaVersion: 2020,
812-
},
813810
},
814811
{
815812
code: `
@@ -839,9 +836,6 @@ ruleTester.run('no-unused-prop-types', rule, {
839836
module.exports = HelloComponent();
840837
`,
841838
features: ['optional chaining'],
842-
parserOptions: {
843-
ecmaVersion: 2020,
844-
},
845839
},
846840
{
847841
code: `
@@ -871,9 +865,6 @@ ruleTester.run('no-unused-prop-types', rule, {
871865
module.exports = HelloComponent();
872866
`,
873867
features: ['optional chaining'],
874-
parserOptions: {
875-
ecmaVersion: 2020,
876-
},
877868
},
878869
{
879870
code: `
@@ -926,9 +917,6 @@ ruleTester.run('no-unused-prop-types', rule, {
926917
};
927918
`,
928919
features: ['optional chaining'],
929-
parserOptions: {
930-
ecmaVersion: 2020,
931-
},
932920
},
933921
{
934922
code: `

tests/lib/rules/no-unused-state.js

-3
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,6 @@ eslintTester.run('no-unused-state', rule, {
353353
}
354354
`,
355355
features: ['optional chaining'],
356-
parserOptions: {
357-
ecmaVersion: 2020,
358-
},
359356
},
360357
{
361358
code: `

0 commit comments

Comments
 (0)