diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 288e022a4..84110abd0 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -163,6 +163,13 @@ mod react { pub mod require_render_return; } +mod react_perf { + pub mod no_jsx_as_prop; + pub mod no_new_array_as_prop; + pub mod no_new_function_as_props; + pub mod no_new_object_as_prop; +} + mod unicorn { pub mod catch_error_name; pub mod empty_brace_spaces; @@ -508,6 +515,10 @@ oxc_macros::declare_all_lint_rules! { react::no_is_mounted, react::no_unknown_property, react::require_render_return, + react_perf::no_jsx_as_prop, + react_perf::no_new_array_as_prop, + react_perf::no_new_function_as_props, + react_perf::no_new_object_as_prop, import::default, import::no_named_as_default_member, import::named, diff --git a/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs new file mode 100644 index 000000000..2fa131d5a --- /dev/null +++ b/crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs @@ -0,0 +1,95 @@ +use oxc_ast::{ + ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, utils::get_prop_value, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error( + "eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX." +)] +#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))] +struct NoJsxAsPropDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoJsxAsProp; + +declare_oxc_lint!( + /// ### What it does + /// + /// Prevent JSX that are local to the current method from being used as values of JSX props + /// + /// ### Example + /// ```javascript + /// // Bad + /// } /> + /// } /> + /// } /> + /// + /// // Good + /// + /// ``` + NoJsxAsProp, + restriction +); + +impl Rule for NoJsxAsProp { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_elem) = node.kind() { + check_jsx_element(jsx_elem, ctx); + } + } +} + +fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { + for item in &jsx_elem.opening_element.attributes { + match get_prop_value(item) { + None => return, + Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { + expression: JSXExpression::Expression(expr), + .. + })) => { + if let Some(span) = check_expression(expr) { + ctx.diagnostic(NoJsxAsPropDiagnostic(span)); + } + } + _ => {} + }; + } +} + +fn check_expression(expr: &Expression) -> Option { + match expr.without_parenthesized() { + Expression::JSXElement(expr) => Some(expr.span), + Expression::LogicalExpression(expr) => { + check_expression(&expr.left).or_else(|| check_expression(&expr.right)) + } + Expression::ConditionalExpression(expr) => { + check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate)) + } + _ => None, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![r""]; + + let fail = vec![ + r"} />", + r"} />", + r"} />", + r")} />", + ]; + + Tester::new(NoJsxAsProp::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs new file mode 100644 index 000000000..bc45773d8 --- /dev/null +++ b/crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs @@ -0,0 +1,117 @@ +use oxc_ast::{ + ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{get_prop_value, is_constructor_matching_name}, + AstNode, +}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope.")] +#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))] +struct NoNewArrayAsPropDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoNewArrayAsProp; + +declare_oxc_lint!( + /// ### What it does + /// + /// Prevent Arrays that are local to the current method from being used as values of JSX props + /// + /// ### Example + /// ```javascript + /// // Bad + /// + + /// + /// + /// + /// + /// + /// // Good + /// + /// ``` + NoNewArrayAsProp, + restriction +); + +impl Rule for NoNewArrayAsProp { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_elem) = node.kind() { + check_jsx_element(jsx_elem, ctx); + } + } +} + +fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { + for item in &jsx_elem.opening_element.attributes { + match get_prop_value(item) { + None => return, + Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { + expression: JSXExpression::Expression(expr), + .. + })) => { + if let Some(span) = check_expression(expr) { + ctx.diagnostic(NoNewArrayAsPropDiagnostic(span)); + } + } + _ => {} + }; + } +} + +fn check_expression(expr: &Expression) -> Option { + match expr.without_parenthesized() { + Expression::ArrayExpression(expr) => Some(expr.span), + Expression::CallExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Array") { + Some(expr.span) + } else { + None + } + } + Expression::NewExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Array") { + Some(expr.span) + } else { + None + } + } + Expression::LogicalExpression(expr) => { + check_expression(&expr.left).or_else(|| check_expression(&expr.right)) + } + Expression::ConditionalExpression(expr) => { + check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate)) + } + _ => None, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![r""]; + + let fail = vec![ + r"", + r"", + r"", + r"", + r"", + r"", + ]; + + Tester::new(NoNewArrayAsProp::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs b/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs new file mode 100644 index 000000000..bf04a3198 --- /dev/null +++ b/crates/oxc_linter/src/rules/react_perf/no_new_function_as_props.rs @@ -0,0 +1,143 @@ +use oxc_ast::{ + ast::{ + Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer, + MemberExpression, + }, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{get_prop_value, is_constructor_matching_name}, + AstNode, +}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope.")] +#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))] +struct NoNewFunctionAsPropsDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoNewFunctionAsProps; + +declare_oxc_lint!( + /// ### What it does + /// + /// Prevent Functions that are local to the current method from being used as values of JSX props + /// ### Example + /// ```javascript + /// // Bad + /// + /// + /// + /// // Good + /// + /// ``` + NoNewFunctionAsProps, + restriction +); + +impl Rule for NoNewFunctionAsProps { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_elem) = node.kind() { + check_jsx_element(jsx_elem, ctx); + } + } +} + +fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { + for item in &jsx_elem.opening_element.attributes { + match get_prop_value(item) { + None => return, + Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { + expression: JSXExpression::Expression(expr), + .. + })) => { + if let Some(span) = check_expression(expr) { + ctx.diagnostic(NoNewFunctionAsPropsDiagnostic(span)); + } + } + _ => {} + }; + } +} + +fn check_expression(expr: &Expression) -> Option { + match expr.without_parenthesized() { + Expression::ArrowExpression(expr) => Some(expr.span), + Expression::FunctionExpression(expr) => Some(expr.span), + Expression::CallExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Function") { + return Some(expr.span); + } + + let Expression::MemberExpression(member_expr) = &expr.callee else { + return None; + }; + + let property_name = MemberExpression::static_property_name(member_expr); + + if property_name == Some("bind") { + Some(expr.span) + } else { + None + } + } + Expression::NewExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Function") { + Some(expr.span) + } else { + None + } + } + Expression::LogicalExpression(expr) => { + check_expression(&expr.left).or_else(|| check_expression(&expr.right)) + } + Expression::ConditionalExpression(expr) => { + check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate)) + } + _ => None, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + r"", + r"", + r"", + r"", + r"var a;", + r"var a;a = 1;", + r"var a;", + r"function foo ({prop1 = function(){}, prop2}) { + return + }", + r"function foo ({prop1, prop2 = function(){}}) { + return + }", + ]; + + let fail = vec![ + r"", + r" true} />", + r"", + r"", + r"", + r"", + r"", + r"", + r"", + ]; + + Tester::new(NoNewFunctionAsProps::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs b/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs new file mode 100644 index 000000000..58eb0a332 --- /dev/null +++ b/crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs @@ -0,0 +1,117 @@ +use oxc_ast::{ + ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{get_prop_value, is_constructor_matching_name}, + AstNode, +}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope.")] +#[diagnostic(severity(warning), help(r"simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array)."))] +struct NoNewObjectAsPropDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoNewObjectAsProp; + +declare_oxc_lint!( + /// ### What it does + /// + /// Prevent Objects that are local to the current method from being used as values of JSX props + /// + /// ```javascript + /// // Bad + /// + /// + /// + /// + /// + //
+ /// + /// // Good + /// + /// ``` + NoNewObjectAsProp, + restriction +); + +impl Rule for NoNewObjectAsProp { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_elem) = node.kind() { + check_jsx_element(jsx_elem, ctx); + } + } +} + +fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) { + for item in &jsx_elem.opening_element.attributes { + match get_prop_value(item) { + None => return, + Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer { + expression: JSXExpression::Expression(expr), + .. + })) => { + if let Some(span) = check_expression(expr) { + ctx.diagnostic(NoNewObjectAsPropDiagnostic(span)); + } + } + _ => {} + }; + } +} + +fn check_expression(expr: &Expression) -> Option { + match expr.without_parenthesized() { + Expression::ObjectExpression(expr) => Some(expr.span), + Expression::CallExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Object") { + Some(expr.span) + } else { + None + } + } + Expression::NewExpression(expr) => { + if is_constructor_matching_name(&expr.callee, "Object") { + Some(expr.span) + } else { + None + } + } + Expression::LogicalExpression(expr) => { + check_expression(&expr.left).or_else(|| check_expression(&expr.right)) + } + Expression::ConditionalExpression(expr) => { + check_expression(&expr.consequent).or_else(|| check_expression(&expr.alternate)) + } + _ => None, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![r""]; + + let fail = vec![ + r"", + r"", + r"", + r"
", + r"", + r"", + r"", + ]; + + Tester::new(NoNewObjectAsProp::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap b/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap new file mode 100644 index 000000000..07912913b --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap @@ -0,0 +1,33 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_jsx_as_prop +--- + ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX. + ╭─[no_jsx_as_prop.tsx:1:1] + 1 │ } /> + · ─────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX. + ╭─[no_jsx_as_prop.tsx:1:1] + 1 │ } /> + · ─────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX. + ╭─[no_jsx_as_prop.tsx:1:1] + 1 │ } /> + · ─────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-jsx-as-prop): JSX attribute values should not contain other JSX. + ╭─[no_jsx_as_prop.tsx:1:1] + 1 │ )} /> + · ─────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + diff --git a/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap b/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap new file mode 100644 index 000000000..3d2f6c3f8 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap @@ -0,0 +1,47 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_new_array_as_prop +--- + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ─────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ─────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-array-as-prop): JSX attribute values should not contain Arrays created in the same scope. + ╭─[no_new_array_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + diff --git a/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap b/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap new file mode 100644 index 000000000..184183d30 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_new_function_as_props.snap @@ -0,0 +1,68 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_new_function_as_props +--- + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ─────────────────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ true} /> + · ────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ───────────────────────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ──────────────────────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ───────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ───────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ──────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-function-as-props): JSX attribute values should not contain functions created in the same scope. + ╭─[no_new_function_as_props.tsx:1:1] + 1 │ + · ──────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + diff --git a/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap b/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap new file mode 100644 index 000000000..3e76bf107 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap @@ -0,0 +1,54 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_new_object_as_prop +--- + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ──────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ──────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │
+ · ───────────────── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + ⚠ eslint-plugin-react-perf(no-new-object-as-prop): JSX attribute values should not contain objects created in the same scope. + ╭─[no_new_object_as_prop.tsx:1:1] + 1 │ + · ── + ╰──── + help: simplify props or memoize props in the parent component (https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array). + + diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs index b432d7dbd..117b892b1 100644 --- a/crates/oxc_linter/src/utils/mod.rs +++ b/crates/oxc_linter/src/utils/mod.rs @@ -1,6 +1,7 @@ mod jest; mod node; mod react; +mod react_perf; mod unicorn; -pub use self::{jest::*, node::*, react::*, unicorn::*}; +pub use self::{jest::*, node::*, react::*, react_perf::*, unicorn::*}; diff --git a/crates/oxc_linter/src/utils/react_perf.rs b/crates/oxc_linter/src/utils/react_perf.rs new file mode 100644 index 000000000..4f13ee46f --- /dev/null +++ b/crates/oxc_linter/src/utils/react_perf.rs @@ -0,0 +1,8 @@ +use oxc_ast::ast::Expression; + +pub fn is_constructor_matching_name(callee: &Expression<'_>, name: &str) -> bool { + let Expression::Identifier(ident) = callee else { + return false; + }; + ident.name == name +}