From 50c270e9f024bca2e4fa5af6f54606d1bac28e86 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sat, 11 May 2024 02:25:52 -0400 Subject: [PATCH] fix(lint/useJsxKeyInIterable): flag jsx in variable declarations (#2803) --- .../correctness/use_jsx_key_in_iterable.rs | 63 +++++++++++-------- .../useJsxKeyInIterable/invalid.jsx | 12 ++++ .../useJsxKeyInIterable/invalid.jsx.snap | 50 +++++++++++++++ .../correctness/useJsxKeyInIterable/valid.jsx | 15 +++++ .../useJsxKeyInIterable/valid.jsx.snap | 15 +++++ 5 files changed, 130 insertions(+), 25 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs b/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs index 9497a1242438..90291e526292 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_jsx_key_in_iterable.rs @@ -6,8 +6,8 @@ use biome_console::markup; use biome_js_semantic::SemanticModel; use biome_js_syntax::{ AnyJsExpression, AnyJsFunctionBody, AnyJsMemberExpression, AnyJsObjectMember, AnyJsxAttribute, - AnyJsxChild, JsArrayExpression, JsCallExpression, JsObjectExpression, JsxAttributeList, - JsxExpressionChild, JsxTagExpression, + AnyJsxChild, JsArrayExpression, JsCallExpression, JsFunctionBody, JsObjectExpression, + JsxAttributeList, JsxExpressionChild, JsxTagExpression, }; use biome_rowan::{declare_node_union, AstNode, AstNodeList, AstSeparatedList, TextRange}; @@ -160,18 +160,7 @@ fn handle_iterators(node: &JsCallExpression, model: &SemanticModel) -> Option { let body = callback.body().ok()?; - let res = body - .statements() - .into_iter() - .filter_map(|statement| { - let statement = statement.as_js_return_statement()?; - let returned_value = statement.argument()?; - handle_potential_react_component(returned_value, model, is_inside_jsx) - }) - .flatten() - .collect::>(); - - Some(res) + Some(handle_function_body(&body, model, is_inside_jsx)) } AnyJsExpression::JsArrowFunctionExpression(callback) => { let body = callback.body().ok()?; @@ -180,17 +169,7 @@ fn handle_iterators(node: &JsCallExpression, model: &SemanticModel) -> Option { - let res = body - .statements() - .into_iter() - .filter_map(|statement| { - let statement = statement.as_js_return_statement()?; - let returned_value = statement.argument()?; - handle_potential_react_component(returned_value, model, is_inside_jsx) - }) - .flatten() - .collect::>(); - Some(res) + Some(handle_function_body(&body, model, is_inside_jsx)) } } } @@ -198,6 +177,40 @@ fn handle_iterators(node: &JsCallExpression, model: &SemanticModel) -> Option Vec { + node.statements() + .iter() + .filter_map(|statement| { + if let Some(statement) = statement.as_js_variable_statement() { + let declaration = statement.declaration().ok()?; + Some( + declaration + .declarators() + .iter() + .filter_map(|declarator| { + let decl = declarator.ok()?; + let init = decl.initializer()?.expression().ok()?; + handle_potential_react_component(init, model, is_inside_jsx) + }) + .flatten() + .collect(), + ) + } else if let Some(statement) = statement.as_js_return_statement() { + let returned_value = statement.argument()?; + handle_potential_react_component(returned_value, model, is_inside_jsx) + } else { + None + } + }) + .flatten() + .collect() +} + fn handle_potential_react_component( node: AnyJsExpression, model: &SemanticModel, diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx index fd8da9bdf5eb..2b58242283e6 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx @@ -51,3 +51,15 @@ React.Children.map(c => React.cloneElement(c)); [].map((item) => { return <>{item.condition ?
:
foo
}; }); + +[].map((item) => { + const x = 5; + const div =
{x}
; + return div; +}); + +[].map(function(item) { + const x = 5; + const div =
{x}
; + return div; +}); diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx.snap index 6a8ffe57ecc4..a10373b73d62 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/invalid.jsx.snap @@ -58,6 +58,18 @@ React.Children.map(c => React.cloneElement(c)); return <>{item.condition ?
:
foo
}; }); +[].map((item) => { + const x = 5; + const div =
{x}
; + return div; +}); + +[].map(function(item) { + const x = 5; + const div =
{x}
; + return div; +}); + ``` # Diagnostics @@ -756,3 +768,41 @@ invalid.jsx:52:39 lint/correctness/useJsxKeyInIterable ━━━━━━━━ ``` + +``` +invalid.jsx:57:14 lint/correctness/useJsxKeyInIterable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Missing key property for this element in iterable. + + 55 │ [].map((item) => { + 56 │ const x = 5; + > 57 │ const div =
{x}
; + │ ^^^^^ + 58 │ return div; + 59 │ }); + + i The order of the items may change, and having a key can help React identify which item was moved. + + i Check the React documentation. + + +``` + +``` +invalid.jsx:63:14 lint/correctness/useJsxKeyInIterable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Missing key property for this element in iterable. + + 61 │ [].map(function(item) { + 62 │ const x = 5; + > 63 │ const div =
{x}
; + │ ^^^^^ + 64 │ return div; + 65 │ }); + + i The order of the items may change, and having a key can help React identify which item was moved. + + i Check the React documentation. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx index 11231cda6c96..8d7a218714e0 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx @@ -68,6 +68,10 @@ React.Children.map(c => React.cloneElement(c, {key: c})); return <>
foo
; }); +[].map((item) => { + return item.condition ?
:
foo
; +}); + [].map((item) => { return <>{item.condition ?
:
foo
}; }); @@ -76,3 +80,14 @@ React.Children.map(c => React.cloneElement(c, {key: c})); const div =
; return <>{item.condition ? div :
{div}
}; }); + +[].map(function(item) { + const x = 5; + return
{x}
; +}); + +[].map(function(item) { + const x = 5; + const div =
{x}
; + return div; +}); diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx.snap b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx.snap index 5378030cf2c4..7d3ab368c072 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid.jsx.snap @@ -74,6 +74,10 @@ React.Children.map(c => React.cloneElement(c, {key: c})); return <>
foo
; }); +[].map((item) => { + return item.condition ?
:
foo
; +}); + [].map((item) => { return <>{item.condition ?
:
foo
}; }); @@ -83,4 +87,15 @@ React.Children.map(c => React.cloneElement(c, {key: c})); return <>{item.condition ? div :
{div}
}; }); +[].map(function(item) { + const x = 5; + return
{x}
; +}); + +[].map(function(item) { + const x = 5; + const div =
{x}
; + return div; +}); + ```