Skip to content

Conversation

@ematipico
Copy link
Member

Summary

Closes #7971

Test Plan

Added new tests

Docs

@changeset-bot
Copy link

changeset-bot bot commented Nov 5, 2025

🦋 Changeset detected

Latest commit: 5eaa2eb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ematipico ematipico changed the title wip feat(html/svelte): key blocks Nov 5, 2025
@github-actions github-actions bot added A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools L-HTML Language: HTML and super languages labels Nov 5, 2025
@ematipico ematipico force-pushed the feat/html-svelte-key branch from 3295620 to 6e6d57e Compare November 5, 2025 14:45
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 5, 2025

Walkthrough

Adds parser, lexer, grammar and formatter support for Svelte {#key ...} / {/key} blocks. Introduces new syntax kinds and AST nodes (SvelteKeyBlock, SvelteKeyOpeningBlock, SvelteKeyClosingBlock), lexer recognition of the key keyword, parsing of opening/children/closing blocks (including new SvelteElementList and parse_html_element integration), formatter rules for opening/block/closing nodes and wiring into generated format integrations, and tests covering valid and error cases plus a patch changelog entry.

Possibly related PRs

Suggested reviewers

  • dyc3

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(html/svelte): key blocks' clearly and concisely describes the main change—adding support for Svelte key blocks.
Description check ✅ Passed The description references the linked issue #7971, mentions added tests, and follows contribution guidelines appropriately.
Linked Issues check ✅ Passed The PR successfully implements Svelte {#key} {/key} blocks with full parsing and formatting support, including opening/closing blocks, element lists, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly aligned with implementing Svelte key blocks—parser enhancements, formatter integrations, grammar updates, and test files. No unrelated modifications detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/html-svelte-key

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 50975 50975 0
Passed 49762 49762 0
Failed 1171 1171 0
Panics 42 42 0
Coverage 97.62% 97.62% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 40 40 0
Passed 37 37 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.50% 92.50% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 6320 6320 0
Passed 2106 2106 0
Failed 4214 4214 0
Panics 0 0 0
Coverage 33.32% 33.32% 0.00%

ts/babel

Test result main count This PR count Difference
Total 835 835 0
Passed 742 742 0
Failed 93 93 0
Panics 0 0 0
Coverage 88.86% 88.86% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18813 18813 0
Passed 14065 14065 0
Failed 4747 4747 0
Panics 1 1 0
Coverage 74.76% 74.76% 0.00%

@ematipico ematipico requested review from a team and dyc3 November 5, 2025 14:50
@ematipico ematipico force-pushed the feat/html-svelte-key branch from 6e6d57e to 81ca589 Compare November 5, 2025 14:53
@ematipico ematipico force-pushed the feat/html-svelte-key branch from 81ca589 to 26baa70 Compare November 5, 2025 14:58
@dyc3
Copy link
Contributor

dyc3 commented Nov 5, 2025

Would be nice to have some error snapshots, but I don't want to block on that.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.changeset/light-toys-check.md (1)

5-11: Tweak the changelog wording.

A tiny preposition goes a long way—let’s add “for” so the sentence reads naturally.

-Added support Svelte syntax `{#key}`. Biome now is able to parse and format the Svelte syntax [`{#key}`](https://svelte.dev/docs/svelte/key):
+Added support for the Svelte syntax `{#key}`. Biome now is able to parse and format the Svelte syntax [`{#key}`](https://svelte.dev/docs/svelte/key):
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8b6753 and 26baa70.

⛔ Files ignored due to path filters (12)
  • crates/biome_html_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_formatter/tests/specs/html/component-frameworks/svelte-component-casing.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/svelte/debug.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/prettier/html/front-matter/empty2.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (18)
  • .changeset/light-toys-check.md (1 hunks)
  • crates/biome_html_formatter/src/generated.rs (1 hunks)
  • crates/biome_html_formatter/src/html/lists/element_list.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/any/block.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs (1 hunks)
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte (1 hunks)
  • crates/biome_html_parser/src/lexer/mod.rs (4 hunks)
  • crates/biome_html_parser/src/syntax/mod.rs (6 hunks)
  • crates/biome_html_parser/src/syntax/parse_error.rs (1 hunks)
  • crates/biome_html_parser/src/syntax/svelte.rs (2 hunks)
  • crates/biome_html_parser/src/token_source.rs (1 hunks)
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte (1 hunks)
  • crates/biome_html_parser/tests/spec_test.rs (1 hunks)
  • xtask/codegen/html.ungram (1 hunks)
  • xtask/codegen/src/html_kinds_src.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (37)
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Implement FormatLanguage for HtmlFormatLanguage with associated types: SyntaxLanguage=HtmlLanguage, Context=HtmlFormatContext, FormatRule=FormatHtmlSyntaxNode

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/cst.rs : Create FormatHtmlSyntaxNode in cst.rs implementing FormatRule<HtmlSyntaxNode> and AsFormat/IntoFormat for HtmlSyntaxNode using the provided plumbing

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/**/*.rs : After generation, remove usages of `format_verbatim_node` and implement real formatting with biome_formatter utilities

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import and use the `FormatNode` trait for AST nodes

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
📚 Learning: 2025-10-15T09:23:33.055Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:23:33.055Z
Learning: Applies to crates/biome_js_type_info/src/{type_info,local_inference,resolver,flattening}.rs : Avoid recursive type structures and cross-module Arcs; represent links between types using TypeReference and TypeData::Reference.

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/comments.rs : Expose a public HtmlComments type alias: `pub type HtmlComments = Comments<HtmlLanguage>;`

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., `node.l_paren_token().format()`) instead of emitting a static token

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/crates/biome_*_{syntax,factory}/** : Create per-language crates biome_<lang>_syntax and biome_<lang>_factory under crates/

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : Nodes for enclosing syntax errors must include the Bogus word (e.g., HtmlBogusAttribute)

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_parser/src/syntax/parse_error.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code; if a mandatory token/node is missing, return `None` instead

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/syntax/parse_error.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Lexer must implement the biome_parser::Lexer trait

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Define the HtmlFormatter type alias: `type HtmlFormatter<'buf> = Formatter<'buf, HtmlFormatContext>;`

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/language.rs : Create tests/language.rs defining `HtmlTestFormatLanguage` and implement the TestFormatLanguage trait

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: When parsing lists, implement error recovery (e.g., via ParseSeparatedList/ParseNodeList) to avoid infinite loops

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Implement the `Format` trait for your node type and use `JsFormatter` with `write!`/`format_args!` to define formatting

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/context.rs : Create HtmlFormatContext in context.rs with comments and source_map fields and implement FormatContext and CstFormatContext

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For non-mandatory tokens, use the provided helper constructors (e.g., `token`, `space_token`, `dynamic_token`)

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Expose a documented public function `format_node(options: HtmlFormatOptions, root: &HtmlSyntaxNode) -> FormatResult<Formatted<HtmlFormatContext>>` delegating to `biome_formatter::format_node`

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs
  • crates/biome_html_formatter/src/svelte/any/block.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/spec_tests.rs : In tests/spec_tests.rs, generate tests with `tests_macros::gen_tests! {"tests/specs/html/**/*.html", crate::spec_test::run, ""}`

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/specs/html/**/*.html : Place HTML test cases under tests/specs/html as .html files discovered by the test macro

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte
📚 Learning: 2025-10-15T09:25:05.698Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:25:05.698Z
Learning: Applies to crates/biome_service/../biome_lsp/src/server.tests.rs : Keep end-to-end LSP tests in ../biome_lsp/src/server.tests.rs

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/spec_test.rs : Implement a `run` function in tests/spec_test.rs that wires SpecSnapshot and includes!("language.rs") as shown

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/** : Create a tests directory containing a specs subfolder and the files spec_test.rs, spec_tests.rs, and language.rs

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte
  • crates/biome_html_formatter/tests/specs/html/svelte/key.svelte
📚 Learning: 2025-10-15T09:23:33.055Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:23:33.055Z
Learning: Applies to crates/biome_js_type_info/src/type_info.rs : Add new TypeScript type support by extending the TypeData enum rather than introducing parallel structures.

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-10-15T09:25:05.698Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:25:05.698Z
Learning: Applies to crates/biome_service/src/workspace/watcher.tests.rs : Place watcher tests related to workspace methods in src/workspace/watcher.tests.rs

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Add a new LanguageKind variant (e.g., Html) in language_kind.rs and implement/cover all methods

Applied to files:

  • crates/biome_html_parser/src/token_source.rs
  • xtask/codegen/src/html_kinds_src.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-03T12:28:56.788Z
Learnt from: ematipico
Repo: biomejs/biome PR: 7670
File: crates/biome_service/src/file_handlers/html.rs:744-748
Timestamp: 2025-10-03T12:28:56.788Z
Learning: In Biome's codebase, when creating tokens with new text content, use the factory pattern with functions like `ident(text)` from the respective `*_factory` crates (e.g., `biome_html_factory::make::ident`). There is no `.with_text()` method on tokens. The `ident()` function creates a new detached token with the IDENT kind, which is the standard approach for token construction.

Applied to files:

  • crates/biome_html_parser/src/token_source.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/crates/**/src/lexer/mod.rs : Create a lexer module at crates/<parser_crate>/src/lexer/mod.rs

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/src/*_kinds_src.rs : Add src/<lang>_kinds_src.rs under xtask/codegen that returns a static KindSrc

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • xtask/codegen/src/html_kinds_src.rs
📚 Learning: 2025-10-15T09:23:33.055Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:23:33.055Z
Learning: Applies to crates/biome_js_type_info/biome_module_graph/src/**/*.rs : Do not copy or clone data between module graph entries (including behind Arc). Each module must avoid holding duplicated data from another module to enable simple invalidation.

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Add the provided AsFormat, IntoFormat, and iterator plumbing code to lib.rs

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Use `dbg_write!` to debug and inspect the emitted IR during formatting

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Add a new language prefix (e.g., html_) to LANGUAGE_PREFIXES in language_kind.rs

Applied to files:

  • xtask/codegen/src/html_kinds_src.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/comments.rs : Define HtmlCommentStyle implementing CommentStyle in comments.rs

Applied to files:

  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs
  • crates/biome_html_formatter/src/html/lists/element_list.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Use p.eat for optional tokens, p.expect for required tokens; use .ok() for optional nodes and .or_add_diagnostic(...) for required nodes

Applied to files:

  • crates/biome_html_parser/src/syntax/parse_error.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : List node types must end with the postfix List (e.g., HtmlAttributeList)

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Parse rules should return ParsedSyntax; return Absent without consuming tokens when the node cannot be predicted

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
🧬 Code graph analysis (7)
crates/biome_html_parser/src/syntax/mod.rs (2)
crates/biome_html_parser/src/syntax/svelte.rs (4)
  • parse_svelte_at_block (92-106)
  • parse_svelte_hash_block (17-24)
  • parse_element (138-140)
  • parse_element (185-187)
crates/biome_html_parser/src/syntax/parse_error.rs (1)
  • disabled_interpolation (12-14)
crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs (3)
crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs (1)
  • fmt_fields (9-48)
crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs (1)
  • fmt_fields (8-26)
crates/biome_html_syntax/src/generated/nodes.rs (7)
  • sv_curly_slash_token (968-970)
  • key_token (971-973)
  • key_token (1017-1019)
  • r_curly_token (729-731)
  • r_curly_token (883-885)
  • r_curly_token (974-976)
  • r_curly_token (1023-1025)
crates/biome_html_formatter/src/generated.rs (1)
crates/biome_html_formatter/src/lib.rs (10)
  • format (51-51)
  • format (64-66)
  • format (81-86)
  • format (101-103)
  • format (297-299)
  • new (146-148)
  • into_format (112-112)
  • into_format (121-123)
  • into_format (135-137)
  • into_format (305-307)
crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs (4)
crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs (1)
  • fmt_fields (9-48)
crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs (1)
  • fmt_fields (8-23)
crates/biome_html_syntax/src/generated/nodes.rs (10)
  • sv_curly_hash_token (1014-1016)
  • key_token (971-973)
  • key_token (1017-1019)
  • expression (441-443)
  • expression (726-728)
  • expression (1020-1022)
  • r_curly_token (729-731)
  • r_curly_token (883-885)
  • r_curly_token (974-976)
  • r_curly_token (1023-1025)
crates/biome_formatter/src/builders.rs (1)
  • space (606-608)
crates/biome_html_formatter/src/svelte/auxiliary/key_block.rs (5)
crates/biome_html_syntax/src/generated/nodes.rs (5)
  • html (624-626)
  • opening_block (923-925)
  • children (486-488)
  • children (926-928)
  • closing_block (929-931)
crates/biome_html_formatter/src/html/lists/element_list.rs (2)
  • write (717-753)
  • write (863-877)
crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs (1)
  • fmt_fields (8-23)
crates/biome_html_formatter/src/svelte/auxiliary/key_opening_block.rs (1)
  • fmt_fields (8-26)
crates/biome_formatter/src/builders.rs (2)
  • if_group_breaks (1943-1952)
  • if_group_fits_on_line (2024-2033)
crates/biome_html_parser/src/syntax/parse_error.rs (1)
crates/biome_parser/src/diagnostic.rs (1)
  • expect_one_of (491-494)
crates/biome_html_parser/src/syntax/svelte.rs (2)
crates/biome_html_parser/src/syntax/parse_error.rs (1)
  • expected_child (35-37)
crates/biome_html_parser/src/syntax/mod.rs (9)
  • parse_html_element (213-244)
  • parse_element (114-187)
  • parse_element (254-256)
  • parse_element (286-288)
  • parse_element (562-592)
  • is_at_list_end (258-263)
  • is_at_list_end (290-292)
  • recover (265-275)
  • recover (294-304)
🪛 LanguageTool
.changeset/light-toys-check.md

[style] ~5-~5: As a shorter alternative for ‘able to’, consider using “can”.
Context: ...pport Svelte syntax {#key}. Biome now is able to parse and format the Svelte syntax [`{#...

(BE_ABLE_TO)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
🔇 Additional comments (12)
crates/biome_html_formatter/src/html/lists/element_list.rs (1)

26-26: Doc comment reformatting looks fine.

Minor style change; no functional impact.

crates/biome_html_parser/src/syntax/parse_error.rs (1)

36-36: Good addition for Svelte key block support.

Expanding the expected child types to include "closing block" improves diagnostic accuracy for Svelte contexts.

xtask/codegen/src/html_kinds_src.rs (1)

24-24: Codegen additions for Svelte key blocks look correct.

The new "key" keyword and three node types follow the established pattern for Svelte block support.

Also applies to: 61-63

crates/biome_html_parser/src/lexer/mod.rs (3)

6-6: KEY_KW additions look correct.

Proper integration of the new keyword for Svelte key block lexing.

Also applies to: 427-427


1021-1021: Minor formatting improvement.

Adding a newline before the QuotesSeen struct improves readability.


269-269: Based on my research, the change fixes nested curly bracket handling in the consume_single_text_expression method through a brackets_stack mechanism, resolving issues with parsing text expressions containing nested curly brackets in Astro and Svelte files.

The test output you provided shows extensive test coverage for single text expressions across Vue, Svelte, and Astro templates, with proper AST parsing structures visible in the snapshots. The visible test cases—including complex nested expressions like {{ call(fn({ inner })) }}—all parse correctly.

No issues found with the bracket handling change.

The conditional decrement you mentioned is part of a structural improvement, not a breaking change. The lexer modifications fix nested curly bracket handling through a brackets_stack mechanism, which improves upon the previous unconditional logic. Existing Svelte, Vue, and Astro expression parsing continues to work as expected.

crates/biome_html_formatter/src/svelte/any/block.rs (1)

13-13: Perfect pattern match.

The new match arm correctly integrates SvelteKeyBlock formatting.

crates/biome_html_formatter/tests/specs/html/svelte/key.svelte (1)

1-3: Basic test coverage in place.

The test covers the fundamental key block structure. Consider adding tests for edge cases (nested blocks, empty blocks, multiple children) in follow-up work.

crates/biome_html_parser/tests/spec_test.rs (1)

145-150: Quick test updated for key blocks.

The ignored development test now exercises the new key block parsing. Handy for local iteration.

crates/biome_html_parser/tests/html_specs/ok/svelte/key.svelte (1)

1-7: Nicely covers the keyword/identifier ambiguity.

Testing "key" as both a keyword (in {#key}) and as an identifier name (in {#key key}) is clever and thorough.

crates/biome_html_formatter/src/svelte/auxiliary/key_closing_block.rs (1)

15-21: Token order looks perfect.

The formatter now emits {/key} in the exact order Svelte expects, so nothing goes sideways when we rebuild the closing block.(svelte.dev)

xtask/codegen/html.ungram (1)

192-224: Grammar matches Svelte’s key-block shape.

Nice to see {#key expression} paired with {/key} exactly as the docs describe; the AST additions will line up cleanly with the formatter work above.(svelte.dev)

Comment on lines 26 to 40
pub(crate) fn parse_key_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}

let m = p.start();

parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK).ok();

SvelteElementList.parse_list(p);

parse_closing_block(p, T![key], SVELTE_KEY_CLOSING_BLOCK).ok();

Present(m.complete(p, SVELTE_KEY_BLOCK))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Please bail out when this isn’t {#key …}.

Right now parse_svelte_hash_block always falls through to parse_key_block. When we encounter {#if …} (or any other hash block), parse_opening_block rewinds and returns Absent, but parse_key_block still completes a SVELTE_KEY_BLOCK and the cursor never advances, so we recurse straight back into the same code path. That can spin forever and it also hijacks the existing {#if}/{#each} parsing. Please abandon the marker and return Absent as soon as the keyword-matching fails, then reinstate the old or_else chain so the other hash blocks still get their turn.

-    parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK).ok();
+    match parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK) {
+        Present(_) => {}
+        Absent => {
+            m.abandon(p);
+            return Absent;
+        }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub(crate) fn parse_key_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}
let m = p.start();
parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK).ok();
SvelteElementList.parse_list(p);
parse_closing_block(p, T![key], SVELTE_KEY_CLOSING_BLOCK).ok();
Present(m.complete(p, SVELTE_KEY_BLOCK))
}
pub(crate) fn parse_key_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}
let m = p.start();
match parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK) {
Present(_) => {}
Absent => {
m.abandon(p);
return Absent;
}
}
SvelteElementList.parse_list(p);
parse_closing_block(p, T![key], SVELTE_KEY_CLOSING_BLOCK).ok();
Present(m.complete(p, SVELTE_KEY_BLOCK))
}
🤖 Prompt for AI Agents
In crates/biome_html_parser/src/syntax/svelte.rs around lines 26–40,
parse_key_block currently always completes a SVELTE_KEY_BLOCK even when
parse_opening_block fails to match the "key" keyword; change the control flow so
that immediately after calling parse_opening_block you check its result and if
it indicates no match you call m.abandon(p) (to discard the marker) and return
Absent; only proceed to parse the element list and closing block when the
opening-block call succeeded. After this change, restore the previous or_else
chaining in parse_svelte_hash_block so other hash-block parsers ({#if}, {#each},
etc.) get a chance when parse_key_block returns Absent.

@ematipico
Copy link
Member Author

Would be nice to have some error snapshots, but I don't want to block on that.

Done! Good call. Check the snapshots

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
crates/biome_html_parser/src/syntax/svelte.rs (1)

33-46: Bail out when the opening isn’t {#key …}.

If parse_opening_block rewinds (e.g. we’re looking at {#if}), we still go on to build a SVELTE_KEY_BLOCK, so we recurse forever and hijack the other hash-block parsers. Cut the marker loose and return Absent immediately—same bug I waved at earlier.

-    let completed = parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK).ok();
+    let completed = match parse_opening_block(p, T![key], SVELTE_KEY_OPENING_BLOCK) {
+        Present(marker) => marker,
+        Absent => {
+            m.abandon(p);
+            return Absent;
+        }
+    };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26baa70 and 5eaa2eb.

⛔ Files ignored due to path filters (4)
  • crates/biome_html_parser/tests/html_specs/error/svelte/debug-trailing-comma.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/debug.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (6)
  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs (1 hunks)
  • crates/biome_html_parser/src/syntax/mod.rs (7 hunks)
  • crates/biome_html_parser/src/syntax/parse_error.rs (2 hunks)
  • crates/biome_html_parser/src/syntax/svelte.rs (2 hunks)
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte (1 hunks)
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_html_parser/src/syntax/parse_error.rs
🧰 Additional context used
🧠 Learnings (24)
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/**/*.rs : After generation, remove usages of `format_verbatim_node` and implement real formatting with biome_formatter utilities

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Implement FormatLanguage for HtmlFormatLanguage with associated types: SyntaxLanguage=HtmlLanguage, Context=HtmlFormatContext, FormatRule=FormatHtmlSyntaxNode

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/cst.rs : Create FormatHtmlSyntaxNode in cst.rs implementing FormatRule<HtmlSyntaxNode> and AsFormat/IntoFormat for HtmlSyntaxNode using the provided plumbing

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Expose a documented public function `format_node(options: HtmlFormatOptions, root: &HtmlSyntaxNode) -> FormatResult<Formatted<HtmlFormatContext>>` delegating to `biome_formatter::format_node`

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import and use the `FormatNode` trait for AST nodes

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Define the HtmlFormatter type alias: `type HtmlFormatter<'buf> = Formatter<'buf, HtmlFormatContext>;`

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., `node.l_paren_token().format()`) instead of emitting a static token

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/language.rs : Create tests/language.rs defining `HtmlTestFormatLanguage` and implement the TestFormatLanguage trait

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/comments.rs : Expose a public HtmlComments type alias: `pub type HtmlComments = Comments<HtmlLanguage>;`

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Implement the `Format` trait for your node type and use `JsFormatter` with `write!`/`format_args!` to define formatting

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/comments.rs : Define HtmlCommentStyle implementing CommentStyle in comments.rs

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/context.rs : Create HtmlFormatContext in context.rs with comments and source_map fields and implement FormatContext and CstFormatContext

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/text_expression.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/specs/html/**/*.html : Place HTML test cases under tests/specs/html as .html files discovered by the test macro

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/spec_tests.rs : In tests/spec_tests.rs, generate tests with `tests_macros::gen_tests! {"tests/specs/html/**/*.html", crate::spec_test::run, ""}`

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/** : Create a tests directory containing a specs subfolder and the files spec_test.rs, spec_tests.rs, and language.rs

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte
  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte
📚 Learning: 2025-10-15T09:25:05.698Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:25:05.698Z
Learning: Applies to crates/biome_service/../biome_lsp/src/server.tests.rs : Keep end-to-end LSP tests in ../biome_lsp/src/server.tests.rs

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: When parsing lists, implement error recovery (e.g., via ParseSeparatedList/ParseNodeList) to avoid infinite loops

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : List node types must end with the postfix List (e.g., HtmlAttributeList)

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code; if a mandatory token/node is missing, return `None` instead

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Parse rules should return ParsedSyntax; return Absent without consuming tokens when the node cannot be predicted

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/crates/biome_*_{syntax,factory}/** : Create per-language crates biome_<lang>_syntax and biome_<lang>_factory under crates/

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:23:33.055Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:23:33.055Z
Learning: Applies to crates/biome_js_type_info/src/{type_info,local_inference,resolver,flattening}.rs : Avoid recursive type structures and cross-module Arcs; represent links between types using TypeReference and TypeData::Reference.

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Lexer must implement the biome_parser::Lexer trait

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : Nodes for enclosing syntax errors must include the Bogus word (e.g., HtmlBogusAttribute)

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
🧬 Code graph analysis (3)
crates/biome_html_formatter/src/html/auxiliary/text_expression.rs (1)
crates/biome_html_formatter/src/verbatim.rs (1)
  • format_verbatim_skipped (40-46)
crates/biome_html_parser/src/syntax/svelte.rs (2)
crates/biome_html_parser/src/syntax/parse_error.rs (2)
  • expected_child (35-37)
  • expected_svelte_closing_block (96-98)
crates/biome_html_parser/src/syntax/mod.rs (9)
  • parse_html_element (213-244)
  • parse_element (114-187)
  • parse_element (254-256)
  • parse_element (286-288)
  • parse_element (560-593)
  • is_at_list_end (258-263)
  • is_at_list_end (290-292)
  • recover (265-275)
  • recover (294-304)
crates/biome_html_parser/src/syntax/mod.rs (2)
crates/biome_html_parser/src/syntax/svelte.rs (4)
  • parse_svelte_at_block (105-119)
  • parse_svelte_hash_block (17-24)
  • parse_element (151-153)
  • parse_element (198-200)
crates/biome_html_parser/src/token_source.rs (2)
  • double_expression (63-65)
  • single_expression (59-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Parser conformance
  • GitHub Check: Test Node.js API
  • GitHub Check: End-to-end tests
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Documentation
  • GitHub Check: autofix
🔇 Additional comments (5)
crates/biome_html_formatter/src/html/auxiliary/text_expression.rs (1)

7-7: Skipping the text expression is spot on.

Marking these nodes as skipped while still routing comments keeps the formatter honest. Nice and tidy.

crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_close.svelte (1)

1-2: Cheers for covering the missing closing block.

This snapshot nails the regression guard we needed.

crates/biome_html_parser/tests/html_specs/error/svelte/key_missing_expression.svelte (1)

1-3: Extra points for the empty-expression case.

Catching the blank {#key } path keeps the parser honest.

crates/biome_html_parser/src/syntax/mod.rs (2)

213-244: Unified element dispatcher looks good.

The new matcher keeps the element loop tidy and degrades lone braces to content as expected.


291-292: Nice touch adding } to the attribute break-set.

This saves us from chewing through Svelte props by accident.

Comment on lines +17 to +24
pub(crate) fn parse_svelte_hash_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}
// NOTE: use or_else chain here to parse
// other possible hash blocks
parse_key_block(p)
}
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Please restore the other {#…} handlers.

Handing back only parse_key_block means {#if}, {#each}, {#await}, etc. now fall straight through, so the cursor never advances and the parser gives up. Bring back the old or_else chain so the legacy blocks still get their turn.

-    parse_key_block(p)
+    parse_key_block(p)
+        .or_else(|| parse_if_block(p))
+        .or_else(|| parse_each_block(p))
+        .or_else(|| parse_await_block(p))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub(crate) fn parse_svelte_hash_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}
// NOTE: use or_else chain here to parse
// other possible hash blocks
parse_key_block(p)
}
pub(crate) fn parse_svelte_hash_block(p: &mut HtmlParser) -> ParsedSyntax {
if !p.at(T!["{#"]) {
return Absent;
}
// NOTE: use or_else chain here to parse
// other possible hash blocks
parse_key_block(p)
.or_else(|| parse_if_block(p))
.or_else(|| parse_each_block(p))
.or_else(|| parse_await_block(p))
}

Copy link
Member Author

@ematipico ematipico Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai We don't have those functions @coderabbitai, are you hallucinating?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for any other Svelte block parsing functions that might exist
rg -n "parse_(if|each|await|snippet|html)_block" --type=rust crates/biome_html_parser/

# Also check if there are any other {# block implementations
rg -n "T!\[\"\{#\"\]" --type=rust crates/biome_html_parser/

# Check what Svelte block types are defined in the syntax kinds
rg -n "SVELTE_.*_BLOCK" crates/biome_html_parser/src/syntax/svelte.rs

Length of output: 1306


@ematipico You're absolutely right—I apologize for the confusion! 🙏

I searched the codebase and confirmed that parse_if_block, parse_each_block, and parse_await_block don't exist. The current implementation is correct for this PR's scope. Right now, you only have:

  • parse_key_block (this PR) for {#key} blocks
  • parse_debug_block for {@debug} blocks

The comment "use or_else chain here to parse other possible hash blocks" is a forward-looking note. When/if you add support for other Svelte control-flow blocks like {#if}, {#each}, or {#await} in future PRs, that's when you'd extend the or_else chain. For now, having just parse_key_block(p) is perfectly appropriate.

Sorry for the noise on this one!


🧬 Code Graph Analysis Results

[crates/biome_html_parser/src/syntax/parse_error.rs:35-37]

pub(crate) fn expected_child(p: &HtmlParser, range: TextRange) -> ParseDiagnostic {
expect_one_of(&["element", "text", "closing block"], range).into_diagnostic(p)
}

[crates/biome_html_parser/src/syntax/parse_error.rs:96-98]

pub(crate) fn expected_svelte_closing_block(p: &HtmlParser, range: TextRange) -> ParseDiagnostic {
p.err_builder("Expected a closing block, instead found none.", range)
}

[crates/biome_html_parser/src/syntax/mod.rs:213-244]

pub(crate) fn parse_html_element(p: &mut HtmlParser) -> ParsedSyntax {
match p.cur() {
T!["<![CDATA["] => parse_cdata_section(p),
T![<] => parse_element(p),
T!["{{"] => HtmlSyntaxFeatures::DoubleTextExpressions.parse_exclusive_syntax(
p,
|p| parse_double_text_expression(p, HtmlLexContext::Regular),
|p, m| disabled_interpolation(p, m.range(p)),
),
T!["{@"] => parse_svelte_at_block(p),
T!["{#"] => parse_svelte_hash_block(p),
T!['{'] => parse_single_text_expression(p, HtmlLexContext::Regular).or_else(|| {
let m = p.start();
p.bump_remap(HTML_LITERAL);
Present(m.complete(p, HTML_CONTENT))
}),
T!["}}"] | T!['}'] => {
// The closing text expression should be handled by other functions.
// If we're here, we assume that text expressions are enabled and
// we remap to HTML_LITERAL
let m = p.start();
p.bump_remap(HTML_LITERAL);
Present(m.complete(p, HTML_CONTENT))
}
HTML_LITERAL => {
let m = p.start();
p.bump_with_context(HTML_LITERAL, HtmlLexContext::Regular);
Present(m.complete(p, HTML_CONTENT))
}
_ => Absent,
}
}


🧠 Learnings used
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/lib.rs : Implement FormatLanguage for HtmlFormatLanguage with associated types: SyntaxLanguage=HtmlLanguage, Context=HtmlFormatContext, FormatRule=FormatHtmlSyntaxNode

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/cst.rs : Create FormatHtmlSyntaxNode in cst.rs implementing FormatRule<HtmlSyntaxNode> and AsFormat/IntoFormat for HtmlSyntaxNode using the provided plumbing

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code; if a mandatory token/node is missing, return `None` instead

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : Nodes for enclosing syntax errors must include the Bogus word (e.g., HtmlBogusAttribute)

@ematipico ematipico merged commit 6e8a50e into main Nov 5, 2025
14 checks passed
@ematipico ematipico deleted the feat/html-svelte-key branch November 5, 2025 17:29
@github-actions github-actions bot mentioned this pull request Nov 5, 2025
ematipico added a commit to hamirmahal/biome that referenced this pull request Nov 19, 2025
l0ngvh pushed a commit to l0ngvh/biome that referenced this pull request Dec 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Formatter Area: formatter A-Parser Area: parser A-Tooling Area: internal tools L-HTML Language: HTML and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement svelte {#key} {/key}

3 participants