Skip to content
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

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

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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)
Expand All @@ -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
Expand Down
9 changes: 2 additions & 7 deletions lib/util/Components.js
Expand Up @@ -855,7 +855,7 @@ function componentRule(rule, context) {
},

FunctionExpression(node) {
if (node.async) {
if (node.async && node.generator) {
components.add(node, 0);
return;
}
Expand All @@ -868,7 +868,7 @@ function componentRule(rule, context) {
},

FunctionDeclaration(node) {
if (node.async) {
if (node.async && node.generator) {
components.add(node, 0);
return;
}
Expand All @@ -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;
Expand Down
42 changes: 42 additions & 0 deletions tests/lib/rules/prefer-read-only-props.js
Expand Up @@ -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";
Expand Down Expand Up @@ -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 = {
Expand Down
15 changes: 9 additions & 6 deletions tests/lib/rules/prop-types.js
Expand Up @@ -1571,25 +1571,28 @@ ruleTester.run('prop-types', rule, {
options: [{ skipUndeclared: false }],
},
{
// Async functions can't be components.
ljharb marked this conversation as resolved.
Show resolved Hide resolved
// 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>;
}
`,
Expand Down