Skip to content

Commit 0771bc1

Browse files
committed
[Fix] jsx-key: avoid a crash with optional chaining
1 parent c57ccd1 commit 0771bc1

File tree

4 files changed

+24
-7
lines changed

4 files changed

+24
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
55

66
## Unreleased
77

8+
### Fixed
9+
* [`jsx-key`]: avoid a crash with optional chaining ([#3371][], @ljharb)
10+
811
## [7.31.1] - 2022.08.26
912

1013
### Fixed
1114
* [`jsx-key`]: fix detecting missing key in `Array.from`'s mapping function ([#3369][] @sjarva)
1215
* [`jsx-no-leaked-render`]: coerce strategy now allows a ternary ([#3370][], @sjarva)
1316

1417
[7.31.1]: https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.31.0...v7.31.1
18+
[#3371]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3371
1519
[#3370]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3370
1620
[#3369]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3369
1721

lib/rules/jsx-key.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ module.exports = {
226226
CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],\
227227
OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
228228
OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]'(node) {
229-
const fn = node.arguments[0];
230-
if (!astUtil.isFunctionLikeExpression(fn)) {
229+
const fn = node.arguments.length > 0 && node.arguments[0];
230+
if (!fn || !astUtil.isFunctionLikeExpression(fn)) {
231231
return;
232232
}
233233

@@ -239,7 +239,6 @@ module.exports = {
239239
// Array.from
240240
'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]'(node) {
241241
const fn = node.arguments.length > 1 && node.arguments[1];
242-
243242
if (!astUtil.isFunctionLikeExpression(fn)) {
244243
return;
245244
}

tests/helpers/parsers.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function minEcmaVersion(features, parserOptions) {
1515
const minEcmaVersionForFeatures = {
1616
'class fields': 2022,
1717
'optional chaining': 2020,
18+
'nullish coalescing': 2020,
1819
};
1920
const result = Math.max.apply(
2021
Math,
@@ -135,7 +136,10 @@ const parsers = {
135136
|| (features.has('fragment') && semver.satisfies(version, '< 5'));
136137

137138
const skipBabel = features.has('no-babel');
138-
const skipOldBabel = skipBabel || features.has('no-babel-old') || semver.satisfies(version, '>= 8');
139+
const skipOldBabel = skipBabel
140+
|| features.has('no-babel-old')
141+
|| features.has('optional chaining')
142+
|| semver.satisfies(version, '>= 8');
139143
const skipNewBabel = skipBabel
140144
|| features.has('no-babel-new')
141145
|| !semver.satisfies(version, '^7.5.0') // require('@babel/eslint-parser/package.json').peerDependencies.eslint

tests/lib/rules/jsx-key.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ ruleTester.run('jsx-key', rule, {
9999
100100
import './ResourceVideo.sass';
101101
import VimeoVideoPlayInModal from '../vimeoVideoPlayInModal/VimeoVideoPlayInModal';
102-
102+
103103
type Props = {
104104
videoUrl: string;
105105
videoTitle: string;
@@ -115,7 +115,7 @@ ruleTester.run('jsx-key', rule, {
115115
</div>
116116
);
117117
};
118-
118+
119119
export default ResourceVideo;
120120
`,
121121
features: ['types'],
@@ -125,7 +125,7 @@ ruleTester.run('jsx-key', rule, {
125125
// testrule.jsx
126126
const trackLink = () => {};
127127
const getAnalyticsUiElement = () => {};
128-
128+
129129
const onTextButtonClick = (e, item) => trackLink([, getAnalyticsUiElement(item), item.name], e);
130130
`,
131131
},
@@ -153,6 +153,16 @@ ruleTester.run('jsx-key', rule, {
153153
`,
154154
features: ['optional chaining'],
155155
},
156+
{
157+
code: `
158+
const baz = foo?.bar?.()?.[1] ?? 'qux';
159+
160+
qux()?.map()
161+
162+
const directiveRanges = comments?.map(tryParseTSDirective)
163+
`,
164+
features: ['optional chaining', 'nullish coalescing'],
165+
},
156166
]),
157167
invalid: parsers.all([
158168
{

0 commit comments

Comments
 (0)