mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
feat(linter): eslint-plugin-react-perf (#2086)
Relates #2041 Closes #2042 Closes #2043 Closes #2044 Closes #2045
This commit is contained in:
parent
4f59c4fdfc
commit
5ca07bcb09
11 changed files with 695 additions and 1 deletions
|
|
@ -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,
|
||||
|
|
|
|||
95
crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs
Normal file
95
crates/oxc_linter/src/rules/react_perf/no_jsx_as_prop.rs
Normal file
|
|
@ -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
|
||||
/// <Item jsx={<SubItem />} />
|
||||
/// <Item jsx={this.props.jsx || <SubItem />} />
|
||||
/// <Item jsx={this.props.jsx ? this.props.jsx : <SubItem />} />
|
||||
///
|
||||
/// // Good
|
||||
/// <Item callback={this.props.jsx} />
|
||||
/// ```
|
||||
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<Span> {
|
||||
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"<Item callback={this.props.jsx} />"];
|
||||
|
||||
let fail = vec![
|
||||
r"<Item jsx={<SubItem />} />",
|
||||
r"<Item jsx={this.props.jsx || <SubItem />} />",
|
||||
r"<Item jsx={this.props.jsx ? this.props.jsx : <SubItem />} />",
|
||||
r"<Item jsx={this.props.jsx || (this.props.component ? this.props.component : <SubItem />)} />",
|
||||
];
|
||||
|
||||
Tester::new(NoJsxAsProp::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
117
crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs
Normal file
117
crates/oxc_linter/src/rules/react_perf/no_new_array_as_prop.rs
Normal file
|
|
@ -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
|
||||
/// <Item list={[]} />
|
||||
|
||||
/// <Item list={new Array()} />
|
||||
/// <Item list={Array()} />
|
||||
/// <Item list={this.props.list || []} />
|
||||
/// <Item list={this.props.list ? this.props.list : []} />
|
||||
///
|
||||
/// // Good
|
||||
/// <Item list={this.props.list} />
|
||||
/// ```
|
||||
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<Span> {
|
||||
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"<Item list={this.props.list} />"];
|
||||
|
||||
let fail = vec![
|
||||
r"<Item list={[]} />",
|
||||
r"<Item list={new Array()} />",
|
||||
r"<Item list={Array()} />",
|
||||
r"<Item list={this.props.list || []} />",
|
||||
r"<Item list={this.props.list ? this.props.list : []} />",
|
||||
r"<Item list={this.props.list || (this.props.arr ? this.props.arr : [])} />",
|
||||
];
|
||||
|
||||
Tester::new(NoNewArrayAsProp::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
|
|
@ -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
|
||||
/// <Item callback={new Function(...)} />
|
||||
/// <Item callback={this.props.callback || function() {}} />
|
||||
///
|
||||
/// // Good
|
||||
/// <Item callback={this.props.callback} />
|
||||
/// ```
|
||||
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<Span> {
|
||||
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"<Item callback={this.props.callback} />",
|
||||
r"<Item promise={new Promise()} />",
|
||||
r"<Item onClick={bind(foo)} />",
|
||||
r"<Item prop={0} />",
|
||||
r"var a;<Item prop={a} />",
|
||||
r"var a;a = 1;<Item prop={a} />",
|
||||
r"var a;<Item prop={a} />",
|
||||
r"function foo ({prop1 = function(){}, prop2}) {
|
||||
return <Comp prop={prop2} />
|
||||
}",
|
||||
r"function foo ({prop1, prop2 = function(){}}) {
|
||||
return <Comp prop={prop1} />
|
||||
}",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r"<Item prop={function(){return true}} />",
|
||||
r"<Item prop={() => true} />",
|
||||
r"<Item prop={new Function('a', 'alert(a)')}/>",
|
||||
r"<Item prop={Function()}/>",
|
||||
r"<Item onClick={this.clickHandler.bind(this)} />",
|
||||
r"<Item callback={this.props.callback || function() {}} />",
|
||||
r"<Item callback={this.props.callback ? this.props.callback : function() {}} />",
|
||||
r"<Item prop={this.props.callback || this.props.callback ? this.props.callback : function(){}} />",
|
||||
r"<Item prop={this.props.callback || (this.props.cb ? this.props.cb : function(){})} />",
|
||||
];
|
||||
|
||||
Tester::new(NoNewFunctionAsProps::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
117
crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs
Normal file
117
crates/oxc_linter/src/rules/react_perf/no_new_object_as_prop.rs
Normal file
|
|
@ -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
|
||||
/// <Item config={{}} />
|
||||
/// <Item config={new Object()} />
|
||||
/// <Item config={Object()} />
|
||||
/// <Item config={this.props.config || {}} />
|
||||
/// <Item config={this.props.config ? this.props.config : {}} />
|
||||
// <div style={{display: 'none'}} />
|
||||
///
|
||||
/// // Good
|
||||
/// <Item config={staticConfig} />
|
||||
/// ```
|
||||
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<Span> {
|
||||
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"<Item config={staticConfig} />"];
|
||||
|
||||
let fail = vec![
|
||||
r"<Item config={{}} />",
|
||||
r"<Item config={new Object()} />",
|
||||
r"<Item config={Object()} />",
|
||||
r"<div style={{display: 'none'}} />",
|
||||
r"<Item config={this.props.config || {}} />",
|
||||
r"<Item config={this.props.config ? this.props.config : {}} />",
|
||||
r"<Item config={this.props.config || (this.props.default ? this.props.default : {})} />",
|
||||
];
|
||||
|
||||
Tester::new(NoNewObjectAsProp::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
33
crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap
Normal file
33
crates/oxc_linter/src/snapshots/no_jsx_as_prop.snap
Normal file
|
|
@ -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 │ <Item jsx={<SubItem />} />
|
||||
· ───────────
|
||||
╰────
|
||||
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 │ <Item jsx={this.props.jsx || <SubItem />} />
|
||||
· ───────────
|
||||
╰────
|
||||
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 │ <Item jsx={this.props.jsx ? this.props.jsx : <SubItem />} />
|
||||
· ───────────
|
||||
╰────
|
||||
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 │ <Item jsx={this.props.jsx || (this.props.component ? this.props.component : <SubItem />)} />
|
||||
· ───────────
|
||||
╰────
|
||||
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).
|
||||
|
||||
|
||||
47
crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap
Normal file
47
crates/oxc_linter/src/snapshots/no_new_array_as_prop.snap
Normal file
|
|
@ -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 │ <Item list={[]} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item list={new Array()} />
|
||||
· ───────────
|
||||
╰────
|
||||
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 │ <Item list={Array()} />
|
||||
· ───────
|
||||
╰────
|
||||
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 │ <Item list={this.props.list || []} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item list={this.props.list ? this.props.list : []} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item list={this.props.list || (this.props.arr ? this.props.arr : [])} />
|
||||
· ──
|
||||
╰────
|
||||
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).
|
||||
|
||||
|
||||
|
|
@ -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 │ <Item prop={function(){return 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 │ <Item prop={() => 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 │ <Item prop={new Function('a', 'alert(a)')}/>
|
||||
· ─────────────────────────────
|
||||
╰────
|
||||
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 │ <Item prop={Function()}/>
|
||||
· ──────────
|
||||
╰────
|
||||
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 │ <Item onClick={this.clickHandler.bind(this)} />
|
||||
· ────────────────────────────
|
||||
╰────
|
||||
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 │ <Item callback={this.props.callback || function() {}} />
|
||||
· ─────────────
|
||||
╰────
|
||||
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 │ <Item callback={this.props.callback ? this.props.callback : function() {}} />
|
||||
· ─────────────
|
||||
╰────
|
||||
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 │ <Item prop={this.props.callback || this.props.callback ? this.props.callback : function(){}} />
|
||||
· ────────────
|
||||
╰────
|
||||
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 │ <Item prop={this.props.callback || (this.props.cb ? this.props.cb : function(){})} />
|
||||
· ────────────
|
||||
╰────
|
||||
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).
|
||||
|
||||
|
||||
54
crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap
Normal file
54
crates/oxc_linter/src/snapshots/no_new_object_as_prop.snap
Normal file
|
|
@ -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 │ <Item config={{}} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item config={new Object()} />
|
||||
· ────────────
|
||||
╰────
|
||||
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 │ <Item config={Object()} />
|
||||
· ────────
|
||||
╰────
|
||||
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 │ <div style={{display: 'none'}} />
|
||||
· ─────────────────
|
||||
╰────
|
||||
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 │ <Item config={this.props.config || {}} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item config={this.props.config ? this.props.config : {}} />
|
||||
· ──
|
||||
╰────
|
||||
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 │ <Item config={this.props.config || (this.props.default ? this.props.default : {})} />
|
||||
· ──
|
||||
╰────
|
||||
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).
|
||||
|
||||
|
||||
|
|
@ -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::*};
|
||||
|
|
|
|||
8
crates/oxc_linter/src/utils/react_perf.rs
Normal file
8
crates/oxc_linter/src/utils/react_perf.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in a new issue