From 3fb79e16b7f1b057544a0a2d91961e5a9cf7bee7 Mon Sep 17 00:00:00 2001 From: pnodet Date: Wed, 15 Nov 2023 12:40:16 +0700 Subject: [PATCH] [New] `prefer-read-only-props`, `prop-types`, component detection: allow components to be async functions --- CHANGELOG.md | 2 ++ lib/util/Components.js | 9 ++--- tests/lib/rules/prefer-read-only-props.js | 42 +++++++++++++++++++++++ tests/lib/rules/prop-types.js | 15 ++++---- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb2d0e888..f67436c6c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`no-invalid-html-attribute`]: add support for `apple-touch-startup-image` `rel` attributes in `link` tags ([#3638][] @thomashockaday) * [`no-unknown-property`]: add requireDataLowercase option ([#3645][] @HermanBilous) * [`no-unknown-property`]: add `displaystyle` on `` ([#3652][] @lounsbrough) +* [`prefer-read-only-props`], [`prop-types`], component detection: allow components to be async functions ([#3654][] @pnodet) ### Fixed * [`jsx-no-leaked-render`]: preserve RHS parens for multiline jsx elements while fixing ([#3623][] @akulsr0) @@ -22,6 +23,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [Refactor] [`function-component-definition`]: exit early if no type params ([#3634][] @HenryBrown0) * [Refactor] [`jsx-props-no-multi-spaces`]: extract type parameters to var ([#3634][] @HenryBrown0) +[#3654]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3654 [#3652]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3652 [#3645]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3645 [#3638]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3638 diff --git a/lib/util/Components.js b/lib/util/Components.js index 5e9e831845..99920b8a18 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -855,7 +855,7 @@ function componentRule(rule, context) { }, FunctionExpression(node) { - if (node.async) { + if (node.async && node.generator) { components.add(node, 0); return; } @@ -868,7 +868,7 @@ function componentRule(rule, context) { }, FunctionDeclaration(node) { - if (node.async) { + if (node.async && node.generator) { components.add(node, 0); return; } @@ -881,11 +881,6 @@ function componentRule(rule, context) { }, ArrowFunctionExpression(node) { - if (node.async) { - components.add(node, 0); - return; - } - const component = utils.getStatelessComponent(node); if (!component) { return; diff --git a/tests/lib/rules/prefer-read-only-props.js b/tests/lib/rules/prefer-read-only-props.js index 7c3ab55587..071f3568a5 100644 --- a/tests/lib/rules/prefer-read-only-props.js +++ b/tests/lib/rules/prefer-read-only-props.js @@ -218,6 +218,19 @@ ruleTester.run('prefer-read-only-props', rule, { `, features: ['ts', 'no-babel-old'], }, + { + code: ` + import React from "react"; + type Props = { + readonly name: string[]; + } + const MyComponent: React.FC = async ({ name }) => { + return
{name}
; + }; + export default MyComponent; + `, + features: ['ts', 'no-babel-old'], + }, { code: ` import React from "react"; @@ -500,6 +513,35 @@ ruleTester.run('prefer-read-only-props', rule, { }, ], }, + { + code: ` + import React from "react"; + type Props = { + name: string[]; + } + const MyComponent: React.FC = async ({ name }) => { + return
{name}
; + }; + export default MyComponent; + `, + output: ` + import React from "react"; + type Props = { + readonly name: string[]; + } + const MyComponent: React.FC = async ({ name }) => { + return
{name}
; + }; + export default MyComponent; + `, + features: ['ts', 'no-babel-old'], + errors: [ + { + messageId: 'readOnlyProp', + data: { name: 'name' }, + }, + ], + }, { code: ` type Props = { diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index e17d63661c..049e387da6 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -1571,25 +1571,28 @@ ruleTester.run('prop-types', rule, { options: [{ skipUndeclared: false }], }, { - // Async functions can't be components. + // Async generator functions can't be components. code: ` - var Hello = async function(props) { + var Hello = async function* (props) { + yield null; return
Hello {props.name}
; } `, }, { - // Async functions can't be components. + // Async generator functions can't be components. code: ` - async function Hello(props) { + async function* Hello(props) { + yield null; return
Hello {props.name}
; } `, }, { - // Async functions can't be components. + // Async generator functions can't be components. code: ` - var Hello = async (props) => { + var Hello = async function* (props) { + yield null; return
Hello {props.name}
; } `,