From 278fd4b3df06bc7b7ca93e4c1485cc529260a6e7 Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Sun, 29 Mar 2026 06:25:35 +0530 Subject: [PATCH 1/2] fix(lint): skip useJsxKeyInIterable in Astro templates --- .changeset/calm-pencils-share.md | 5 ++ .../correctness/use_jsx_key_in_iterable.rs | 67 ++++++++++++++++++- .../valid_issue_9572.astro | 2 + .../valid_issue_9572.astro.snap | 10 +++ 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 .changeset/calm-pencils-share.md create mode 100644 crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro create mode 100644 crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro.snap diff --git a/.changeset/calm-pencils-share.md b/.changeset/calm-pencils-share.md new file mode 100644 index 000000000000..975094d0b582 --- /dev/null +++ b/.changeset/calm-pencils-share.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#9572](https://github.com/biomejs/biome/issues/9572): `useJsxKeyInIterable` no longer reports missing keys inside Astro template expressions. 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 6e9f834d6225..3bf8b8afe6bd 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 @@ -8,8 +8,9 @@ use biome_js_semantic::SemanticModel; use biome_js_syntax::{ AnyJsExpression, AnyJsFunctionBody, AnyJsMemberExpression, AnyJsObjectMember, AnyJsStatement, AnyJsSwitchClause, AnyJsxAttribute, AnyJsxChild, JsArrayElementList, JsArrayExpression, - JsCallArgumentList, JsCallArguments, JsCallExpression, JsFunctionBody, JsNewExpression, - JsObjectExpression, JsStatementList, JsxAttributeList, JsxExpressionChild, JsxTagExpression, + JsCallArgumentList, JsCallArguments, JsCallExpression, JsFileSource, JsFunctionBody, + JsNewExpression, JsObjectExpression, JsStatementList, JsxAttributeList, JsxExpressionChild, + JsxTagExpression, }; use biome_rowan::{AstNode, AstNodeList, AstSeparatedList, TextRange, declare_node_union}; use biome_rule_options::use_jsx_key_in_iterable::UseJsxKeyInIterableOptions; @@ -84,6 +85,11 @@ impl Rule for UseJsxKeyInIterable { type Options = UseJsxKeyInIterableOptions; fn run(ctx: &RuleContext) -> Self::Signals { + let file_source = ctx.source_type::(); + if file_source.is_template_expression() && file_source.as_embedding_kind().is_astro() { + return Vec::new().into_boxed_slice(); + } + let node = ctx.query(); let model = ctx.model(); let options = ctx.options(); @@ -534,3 +540,60 @@ fn unwrap_parenthesis(expr: AnyJsExpression) -> Option { } Some(inner_expr) } + +#[cfg(test)] +mod tests { + use crate::JsAnalyzerServices; + use biome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleFilter}; + use biome_js_parser::{JsParserOptions, parse}; + use biome_js_semantic::{SemanticModelOptions, semantic_model}; + use biome_js_syntax::{EmbeddingKind, JsFileSource}; + use std::slice; + + fn run_rule(source: &str, source_type: JsFileSource) -> usize { + let parsed = parse(source, source_type, JsParserOptions::default()); + let rule_filter = RuleFilter::Rule("correctness", "useJsxKeyInIterable"); + let options = AnalyzerOptions::default(); + let semantic_model = semantic_model(&parsed.tree(), SemanticModelOptions::default()); + let services = JsAnalyzerServices::from(( + Default::default(), + Default::default(), + source_type, + Some(semantic_model), + )); + let mut diagnostics = 0; + + crate::analyze( + &parsed.tree(), + AnalysisFilter { + enabled_rules: Some(slice::from_ref(&rule_filter)), + ..AnalysisFilter::default() + }, + &options, + &[], + services, + |signal| { + if signal.diagnostic().is_some() { + diagnostics += 1; + } + + ControlFlow::::Continue(()) + }, + ); + + diagnostics + } + + #[test] + fn astro_template_expressions_do_not_require_keys() { + let source_type = + JsFileSource::tsx().with_embedding_kind(EmbeddingKind::Astro { frontmatter: false }); + + assert_eq!(run_rule("[1, 2].map((item) =>
{item}
)", source_type), 0); + } + + #[test] + fn jsx_iterables_still_require_keys() { + assert_eq!(run_rule("[1, 2].map((item) =>
{item}
)", JsFileSource::tsx()), 1); + } +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro new file mode 100644 index 000000000000..b9ea2cbe88fc --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro @@ -0,0 +1,2 @@ + +{[1, 2].map((item) =>
{item}
)} diff --git a/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro.snap b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro.snap new file mode 100644 index 000000000000..a3dc03001b15 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/useJsxKeyInIterable/valid_issue_9572.astro.snap @@ -0,0 +1,10 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid_issue_9572.astro +--- +# Input +```astro + +{[1, 2].map((item) =>
{item}
)} + +``` From 520bd2ca7b2d675d3346e3833661944ae770a24e Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Sun, 29 Mar 2026 09:06:40 +0530 Subject: [PATCH 2/2] test(lint): cover Astro arrays in jsx-key rule --- .../src/lint/correctness/use_jsx_key_in_iterable.rs | 8 ++++++++ 1 file changed, 8 insertions(+) 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 3bf8b8afe6bd..06119888a330 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 @@ -592,6 +592,14 @@ mod tests { assert_eq!(run_rule("[1, 2].map((item) =>
{item}
)", source_type), 0); } + #[test] + fn astro_template_expression_arrays_do_not_require_keys() { + let source_type = + JsFileSource::tsx().with_embedding_kind(EmbeddingKind::Astro { frontmatter: false }); + + assert_eq!(run_rule("[
]", source_type), 0); + } + #[test] fn jsx_iterables_still_require_keys() { assert_eq!(run_rule("[1, 2].map((item) =>
{item}
)", JsFileSource::tsx()), 1);