From e27ef8131f955fedddc3f3111a7073ea7a6f5e17 Mon Sep 17 00:00:00 2001 From: Ciaran Harvey Date: Thu, 30 May 2024 17:10:57 +0100 Subject: [PATCH 1/8] [Fix] `prop-types`: null-check rootNode before calling getScope --- CHANGELOG.md | 5 +++++ lib/util/propTypes.js | 3 ++- tests/lib/rules/prop-types.js | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0d5cd3185..e4bccfaa25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +### Fixed +* [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) + +[#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 + ## [7.34.2] - 2024.05.24 ### Fixed diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index 6f879e2347..22063afb8e 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -372,7 +372,8 @@ module.exports = function propTypesInstructions(context, components, utils) { */ function resolveValueForIdentifierNode(node, rootNode, callback) { if ( - node + rootNode + && node && node.type === 'Identifier' ) { const scope = getScope(context, rootNode); diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index df762d74ef..97206b7d44 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -3361,6 +3361,20 @@ ruleTester.run('prop-types', rule, { `, features: ['ts', 'no-babel'], }, + { + code: ` + import React from "react"; + + const returnTypeProp = (someProp: any) => ({ someProp }); + + const SomeComponent: React.FunctionComponent< + ReturnType + > = ({ someProp }) => { + return
{someProp}
; + }; + `, + features: ['ts', 'no-babel'], + }, { code: ` export const EuiSuperSelectControl: ( @@ -7840,6 +7854,26 @@ ruleTester.run('prop-types', rule, { ], features: ['ts', 'no-babel'], }, + { + code: ` + import React from "react"; + + const returnTypeProp = (someProp: any) => ({ someProp }); + + const SomeComponent: React.FunctionComponent< + ReturnType + > = ({ someIncorrectProp }) => { + return
{someProp}
; + }; + `, + errors: [ + { + messageId: 'missingPropType', + data: { name: 'someIncorrectProp' }, + }, + ], + features: ['ts', 'no-babel'], + }, { code: ` import React from 'react'; From a79beb364f297571af9d368e1d5c35f56f84a8a7 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 31 May 2024 16:46:05 -0700 Subject: [PATCH 2/8] [Fix] `boolean-prop-naming`: avoid a crash with a spread prop Fixes #3733 --- CHANGELOG.md | 2 ++ lib/rules/boolean-prop-naming.js | 2 +- tests/lib/rules/boolean-prop-naming.js | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4bccfaa25..bef2238f72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed * [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) +* [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb) [#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 +[#3733]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3733 ## [7.34.2] - 2024.05.24 diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index f64da93242..e6ea400025 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -399,7 +399,7 @@ module.exports = { } if (propType) { - [].concat(propType).forEach((prop) => { + [].concat(propType).filter(Boolean).forEach((prop) => { validatePropNaming( component.node, prop.properties || prop.members || prop.body diff --git a/tests/lib/rules/boolean-prop-naming.js b/tests/lib/rules/boolean-prop-naming.js index 2dec7ed70c..10ff9097f2 100644 --- a/tests/lib/rules/boolean-prop-naming.js +++ b/tests/lib/rules/boolean-prop-naming.js @@ -415,7 +415,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^is[A-Z]([A-Za-z0-9]?)+' }], features: ['ts'], - errors: [], }, { code: ` @@ -426,7 +425,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^is[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], }, { code: ` @@ -439,7 +437,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '(is|has)[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], }, { code: ` @@ -451,7 +448,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^is[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], }, { code: ` @@ -465,7 +461,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], }, { code: ` @@ -479,7 +474,6 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], }, { code: ` @@ -495,7 +489,21 @@ ruleTester.run('boolean-prop-naming', rule, { `, options: [{ rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+' }], features: ['types'], - errors: [], + }, + { + code: ` + export const DataRow = (props: { label: string; value: string; } & React.HTMLAttributes) => { + const { label, value, ...otherProps } = props; + return ( +
+ {label} + {value} +
+ ); + }; + `, + options: [{ rule: '(^(is|has|should|without)[A-Z]([A-Za-z0-9]?)+|disabled|required|checked|defaultChecked)' }], + features: ['types'], }, ]), From a944aa519246d1f4c7a494ebd40e5b2c23601b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Ne=CC=81meth?= Date: Tue, 14 May 2024 10:54:14 +0200 Subject: [PATCH 3/8] [Fix] `jsx-boolean-value`: `assumeUndefinedIsFalse` with `never` must not allow explicit `true` value --- CHANGELOG.md | 2 ++ lib/rules/jsx-boolean-value.js | 1 - tests/lib/rules/jsx-boolean-value.js | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bef2238f72..3b9d9ed001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed * [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) * [`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) [#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 +[#3757]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3757 [#3733]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3733 ## [7.34.2] - 2024.05.24 diff --git a/lib/rules/jsx-boolean-value.js b/lib/rules/jsx-boolean-value.js index 1cff171147..4572e7ff8d 100644 --- a/lib/rules/jsx-boolean-value.js +++ b/lib/rules/jsx-boolean-value.js @@ -132,7 +132,6 @@ module.exports = { } if ( isNever(configuration, exceptions, propName) - && !configObject.assumeUndefinedIsFalse && value && value.type === 'JSXExpressionContainer' && value.expression.value === true diff --git a/tests/lib/rules/jsx-boolean-value.js b/tests/lib/rules/jsx-boolean-value.js index e8cbbb3e7f..f4216f2ce1 100644 --- a/tests/lib/rules/jsx-boolean-value.js +++ b/tests/lib/rules/jsx-boolean-value.js @@ -52,6 +52,10 @@ ruleTester.run('jsx-boolean-value', rule, { code: ';', options: ['never', { assumeUndefinedIsFalse: true }], }, + { + code: ';', + options: ['never', { assumeUndefinedIsFalse: false }], + }, { code: ';', options: ['never', { assumeUndefinedIsFalse: true, always: ['foo'] }], @@ -145,6 +149,21 @@ ruleTester.run('jsx-boolean-value', rule, { }, ], }, + { + code: ';', + output: ';', + options: ['never', { assumeUndefinedIsFalse: true }], + errors: [ + { + messageId: 'omitBoolean', + data: { propName: 'foo' }, + }, + { + messageId: 'omitPropAndBoolean', + data: { propName: 'bak' }, + }, + ], + }, { code: ';', output: ';', From 393bfa2fc071bfd08cef2327790e2ccc95507d72 Mon Sep 17 00:00:00 2001 From: Julien Rousseau Date: Wed, 12 Jun 2024 20:49:45 -0400 Subject: [PATCH 4/8] [Fix] `no-object-type-as-default-prop`: enable rule for components with many parameters --- CHANGELOG.md | 2 ++ lib/rules/no-object-type-as-default-prop.js | 2 +- .../rules/no-object-type-as-default-prop.js | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9d9ed001..cd3540427d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) * [`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) +[#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 [#3733]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3733 diff --git a/lib/rules/no-object-type-as-default-prop.js b/lib/rules/no-object-type-as-default-prop.js index 7be98cb4b3..42012413e1 100644 --- a/lib/rules/no-object-type-as-default-prop.js +++ b/lib/rules/no-object-type-as-default-prop.js @@ -30,7 +30,7 @@ const messages = { function hasUsedObjectDestructuringSyntax(params) { return ( params != null - && params.length === 1 + && params.length >= 1 && params[0].type === 'ObjectPattern' ); } diff --git a/tests/lib/rules/no-object-type-as-default-prop.js b/tests/lib/rules/no-object-type-as-default-prop.js index e2660fb938..b515ef6a49 100644 --- a/tests/lib/rules/no-object-type-as-default-prop.js +++ b/tests/lib/rules/no-object-type-as-default-prop.js @@ -143,6 +143,11 @@ ruleTester.run('no-object-type-as-default-prop', rule, { return null; }; `, + ` + const Foo = ({bar = 1}, context) => { + return null; + }; + `, ` export default function NotAComponent({foo = {}}) {} ` @@ -183,6 +188,24 @@ ruleTester.run('no-object-type-as-default-prop', rule, { } `, errors: expectedViolations, + }, + { + code: ` + const Foo = ({ + a = {}, + b = ['one', 'two'], + c = /regex/i, + d = () => {}, + e = function() {}, + f = class {}, + g = new Thing(), + h = , + i = Symbol('foo') + }, context) => { + return null; + } + `, + errors: expectedViolations, } )), }); From 6dc7803acecac6e79d71d9619665403fe3073d79 Mon Sep 17 00:00:00 2001 From: akulsr0 Date: Tue, 18 Jun 2024 18:56:03 +0530 Subject: [PATCH 5/8] [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' }], + }, ]), }); From 7d16666058e2b06193f08342c8b1f20a5468cf20 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:44:51 -0700 Subject: [PATCH 6/8] [Dev Deps] update `@babel/core`, `@babel/eslint-parser`, `@babel/plugin-syntax-decorators`, `@babel/plugin-syntax-do-expressions`, `@babel/plugin-syntax-function-bind`, `@babel/preset-react`, `eslint-doc-generator`, `eslint-remote-tester-repositories` --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4f5155c832..e004c4fb91 100644 --- a/package.json +++ b/package.json @@ -45,12 +45,12 @@ "string.prototype.matchall": "^4.0.11" }, "devDependencies": { - "@babel/core": "^7.24.6", - "@babel/eslint-parser": "^7.24.6", - "@babel/plugin-syntax-decorators": "^7.24.6", - "@babel/plugin-syntax-do-expressions": "^7.24.6", - "@babel/plugin-syntax-function-bind": "^7.24.6", - "@babel/preset-react": "^7.24.6", + "@babel/core": "^7.24.7", + "@babel/eslint-parser": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7", + "@babel/plugin-syntax-do-expressions": "^7.24.7", + "@babel/plugin-syntax-function-bind": "^7.24.7", + "@babel/preset-react": "^7.24.7", "@types/eslint": "=7.2.10", "@types/estree": "0.0.52", "@types/node": "^4.9.5", @@ -59,11 +59,11 @@ "babel-eslint": "^8 || ^9 || ^10.1.0", "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint-config-airbnb-base": "^15.0.0", - "eslint-doc-generator": "^1.7.0", + "eslint-doc-generator": "^1.7.1", "eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5", "eslint-plugin-import": "^2.29.1", "eslint-remote-tester": "^3.0.1", - "eslint-remote-tester-repositories": "^1.0.1", + "eslint-remote-tester-repositories": "^2.0.0", "eslint-scope": "^3.7.3", "espree": "^3.5.4", "glob": "=10.3.7", From eb56061c95d3e5e234d2f33def724c17aedc752e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:45:35 -0700 Subject: [PATCH 7/8] [Deps] update `array.prototype.tosorted` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e004c4fb91..c6bec01eff 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", From cef8123ff92460ed2369d670e8c7b9539e5994bd Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:48:05 -0700 Subject: [PATCH 8/8] Update CHANGELOG and bump version --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cf73592b..42e7d214f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +## [7.34.3] - 2024.06.18 + ### Fixed * [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) * [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb) @@ -12,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`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) +[7.34.3]: https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.2...v7.34.3 [#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 diff --git a/package.json b/package.json index c6bec01eff..ba818e4eec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-react", - "version": "7.34.2", + "version": "7.34.3", "author": "Yannick Croissant ", "description": "React specific linting rules for ESLint", "main": "index.js",