Skip to content

[New] prefer-read-only-props, prop-types, component detection: allow components to be async functions #3654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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 `<math>` ([#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
9 changes: 2 additions & 7 deletions lib/util/Components.js
Original file line number Diff line number Diff line change
@@ -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;
42 changes: 42 additions & 0 deletions tests/lib/rules/prefer-read-only-props.js
Original file line number Diff line number Diff line change
@@ -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<Props> = async ({ name }) => {
return <div>{name}</div>;
};
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<Props> = async ({ name }) => {
return <div>{name}</div>;
};
export default MyComponent;
`,
output: `
import React from "react";
type Props = {
readonly name: string[];
}
const MyComponent: React.FC<Props> = async ({ name }) => {
return <div>{name}</div>;
};
export default MyComponent;
`,
features: ['ts', 'no-babel-old'],
errors: [
{
messageId: 'readOnlyProp',
data: { name: 'name' },
},
],
},
{
code: `
type Props = {
15 changes: 9 additions & 6 deletions tests/lib/rules/prop-types.js
Original file line number Diff line number Diff line change
@@ -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 <div>Hello {props.name}</div>;
}
`,
},
{
// 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 <div>Hello {props.name}</div>;
}
`,
},
{
// 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 <div>Hello {props.name}</div>;
}
`,