Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(html): improved svelte parsing#8555

Merged
ematipico merged 1 commit intomainfrom
fix/svelte-await-diagnostic
Dec 22, 2025
Merged

fix(html): improved svelte parsing#8555
ematipico merged 1 commit intomainfrom
fix/svelte-await-diagnostic

Conversation

@ematipico
Copy link
Member

Summary

Closes #8517
Closes #8516

Test Plan

Added new tests

Docs

@changeset-bot
Copy link

changeset-bot bot commented Dec 22, 2025

⚠️ No Changeset found

Latest commit: f6e43e0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@github-actions github-actions bot added A-Parser Area: parser L-HTML Language: HTML and super languages labels Dec 22, 2025
@@ -0,0 +1,2 @@
{#each somethi{}}
Copy link
Member Author

Choose a reason for hiding this comment

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

Here, technically, we should have an error in the second {, however catching that causes some regressions, so I decided to leave it here for now

@ematipico ematipico requested a review from dyc3 December 22, 2025 13:20
@ematipico ematipico force-pushed the fix/svelte-await-diagnostic branch from 1fd6775 to 636d390 Compare December 22, 2025 13:23
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Updates the HTML lexer and Svelte parsing paths: the restricted single-text expression lexer now treats top-level '<' as a stop, tracks nested bracket-like tokens with a bracket stack (incrementing on opening tokens and decrementing on closing), centralises stop checks via RestrictedExpressionStopAt::matches_punct and matches_keyword, and rewinds instead of consuming stopping keywords. Svelte parser changes remove a runtime error in parse_svelte_block_item, defer then/catch ordering validation for await-blocks to a post-parse consistency check, and add/adjust tests for malformed curly usage and complex expressions.

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 directly describes the main change—improved Svelte parsing in the HTML parser—which aligns with the primary objective of fixing Svelte parsing issues.
Description check ✅ Passed The description references the closed issues (#8517 and #8516) and mentions new tests, directly relating to the changeset's purpose of fixing Svelte parsing.
Linked Issues check ✅ Passed The code changes successfully address both linked issues: restricted expression parsing now handles {:catch} without {:then} [#8517], and bracket tracking supports index and key syntax in {#each} blocks [#8516].
Out of Scope Changes check ✅ Passed All changes are within scope: lexer refactoring, Svelte block parsing logic, helper methods for punctuation/keyword matching, and test additions directly support the stated Svelte parsing improvements.
✨ 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 fix/svelte-await-diagnostic

📜 Recent 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 636d390 and f6e43e0.

⛔ Files ignored due to path filters (6)
  • crates/biome_html_parser/tests/html_specs/error/svelte/await_catch_before_then.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/await_invalid_catch_only_with_clause.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/each_unclosed.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (7)
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte
  • crates/biome_html_parser/tests/quick_test.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/lexer/mod.rs
🧠 Learnings (27)
📓 Common learnings
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.
Learnt from: ematipico
Repo: biomejs/biome PR: 7852
File: crates/biome_css_parser/src/syntax/property/mod.rs:161-168
Timestamp: 2025-10-25T07:22:18.540Z
Learning: In the Biome CSS parser, lexer token emission should not be gated behind parser options like `is_tailwind_directives_enabled()`. The lexer must emit correct tokens regardless of parser options to enable accurate diagnostics and error messages when the syntax is used incorrectly.
Learnt from: ematipico
Repo: biomejs/biome PR: 8509
File: crates/biome_html_parser/src/syntax/svelte.rs:465-467
Timestamp: 2025-12-19T11:30:58.874Z
Learning: In the Biome HTML parser, `parse_single_text_expression_content` does not advance the parser if the current token is empty. This is intentional to support cases where an expression is optional. When calling this function, callers must check if `p.cur_text().is_empty()` after the call and manually advance the parser with `p.bump_remap(HTML_LITERAL)` if needed.
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `full_options` code block property for complete biome.json configuration snippets in documentation

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Prefix line with `#` in documentation code examples sparingly; prefer concise complete snippets

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-04T13:29:49.287Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `options` code block property for rule-specific configuration snippets in documentation

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/spec_tests.rs : Use the `tests_macros::gen_tests!` macro in `spec_tests.rs` to generate test functions for each specification file matching the pattern `tests/specs/<language>/**/*.<ext>`

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ParseSeparatedList` and `ParseNodeList` for parsing lists with error recovery to avoid infinite loops

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Lines prefixed with `#` in rule documentation code examples will be hidden from output

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/language.rs : Implement `TestFormatLanguage` trait in `tests/language.rs` for the formatter's test language

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Each rule option must have its own h3 header with description, default value, options block, and code example

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Avoid string allocations by comparing against `&str` or using `TokenText`

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-12-21T21:15:03.782Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-21T21:15:03.782Z
Learning: Applies to **/*.rs : Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation code blocks should be ordered as language, expect_diagnostic, options/full_options/use_options, ignore, file

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation code blocks must have a language defined for syntax highlighting

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Assist rules should detect refactoring opportunities and emit code action signals

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should check syntax according to language specification and emit error diagnostics

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Each invalid code example in rule documentation must emit exactly one diagnostic

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement a token source struct that wraps the lexer and implements `TokenSourceWithBufferedLexer` and `LexerWithCheckpoint` for lookahead and re-lexing capabilities

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-09T12:47:46.298Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
🧬 Code graph analysis (1)
crates/biome_html_parser/src/lexer/mod.rs (2)
crates/biome_html_parser/src/token_source.rs (1)
  • current (217-219)
crates/biome_parser/src/lexer.rs (1)
  • prev_byte (215-221)
⏰ 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). (8)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • 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: Check Dependencies
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
🔇 Additional comments (10)
crates/biome_html_parser/src/syntax/svelte.rs (3)

154-155: LGTM!

Region markers added for code organisation. No functional change.


280-289: LGTM!

Returning Absent without emitting an error is the correct pattern here. The caller at parse_each_opening_block (lines 312-318) already provides appropriate error messaging when needed.


517-528: LGTM!

The deferred consistency check elegantly validates that {:catch} doesn't appear before {:then}. Using TextRange comparison to determine source order is a sound approach.

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

351-352: LGTM!

Local closures for classifying bracket-like tokens. Clean approach that keeps the logic readable.


356-370: LGTM!

The bracket stack tracking correctly handles nested expressions. Breaking on top-level < ensures the lexer stops before HTML tags, which addresses the parsing issues mentioned in the linked issues.


385-386: LGTM!

The leading space requirement (prev_byte == Some(b' ')) correctly ensures keywords like as aren't matched within identifiers. This handles Svelte's {#each items as item} syntax correctly.


371-377: LGTM!

Centralising punctuation stop checks via kind.matches_punct(current) improves maintainability.

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

7-9: LGTM!

Good choice of test input – exercises the destructuring pattern in {#each} blocks, directly relevant to issue #8516.

crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte (2)

5-7: LGTM!

This test case covers the full {#each expression as item, index (key)} syntax with a method call containing a comma, exercising the bracket stack tracking in the lexer.


1-3: Confirm the purpose of this test case.

Line 1 uses {#each cells.filter(...), item} without the required as keyword—this isn't valid Svelte syntax. Is this intentionally testing error recovery or a malformed edge case, or should it be corrected to {#each cells.filter(...) as _, item} (or similar)?


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

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: 0

🧹 Nitpick comments (1)
crates/biome_html_parser/src/lexer/mod.rs (1)

342-343: Doc comment may be misleading.

The comment states that a top-level { will emit an error token, but the implementation at lines 358-361 adds { to brackets_stack without emitting an error. As noted in the test file comments, this was intentional to avoid regressions. Consider updating the doc comment to reflect the actual behaviour.

Suggested doc comment update
-    ///
-    /// Finding a `{` at the top level will emit an error token.
+    ///
+    /// Note: A `{` at the top level is tracked as a nested bracket rather than
+    /// emitting an error, to avoid regressions in certain edge cases.
📜 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 1fd6775 and 636d390.

⛔ Files ignored due to path filters (6)
  • crates/biome_html_parser/tests/html_specs/error/svelte/await_catch_before_then.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/await_invalid_catch_only_with_clause.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/each_unclosed.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (7)
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte
  • crates/biome_html_parser/tests/quick_test.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_catch_no_then.svelte
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/tests/quick_test.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
🧠 Learnings (24)
📓 Common learnings
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement a token source struct that wraps the lexer and implements `TokenSourceWithBufferedLexer` and `LexerWithCheckpoint` for lookahead and re-lexing capabilities

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ParseSeparatedList` and `ParseNodeList` for parsing lists with error recovery to avoid infinite loops

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Avoid string allocations by comparing against `&str` or using `TokenText`

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Assist rules should detect refactoring opportunities and emit code action signals

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-09T12:47:46.298Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-12-04T13:29:49.287Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Applied to files:

  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte
  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*invalid* : Create test files prefixed with `invalid` for code that should trigger the rule

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*valid* : Create test files prefixed with `valid` for code that should not trigger the rule

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*.jsonc : Use `.jsonc` files to contain arrays of code snippet strings for snapshot tests

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation code blocks should be ordered as language, expect_diagnostic, options/full_options/use_options, ignore, file

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Prefix line with `#` in documentation code examples sparingly; prefer concise complete snippets

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should check syntax according to language specification and emit error diagnostics

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation code blocks must have a language defined for syntax highlighting

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Lines prefixed with `#` in rule documentation code examples will be hidden from output

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to 'fix' the code; if a token/node is known to be mandatory but is missing, return `None` instead

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules should perform static analysis of source code to detect invalid or error-prone patterns and emit diagnostics with proposed fixes

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case

Applied to files:

  • crates/biome_html_parser/src/syntax/svelte.rs
🧬 Code graph analysis (1)
crates/biome_html_parser/src/lexer/mod.rs (2)
crates/biome_html_parser/src/token_source.rs (1)
  • current (217-219)
crates/biome_parser/src/lexer.rs (1)
  • prev_byte (215-221)
⏰ 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). (8)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Documentation
  • GitHub Check: End-to-end tests
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
🔇 Additional comments (6)
crates/biome_html_parser/tests/html_specs/error/svelte/invalid_curly_in_expression.svelte (1)

1-2: Test case looks appropriate for exercising error handling.

This test covers the edge case of invalid curly braces within a Svelte expression. The known limitation regarding the second { not triggering an error (as noted in the previous review) is acceptable as a pragmatic trade-off to avoid regressions.

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

1-7: Good test coverage for complex #each expressions.

These test cases directly address issue #8516. The first block tests comma-separated index syntax with a filter expression, and the second tests the full as item, i (key) pattern. Both are valid Svelte constructs that the parser should now handle correctly.

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

517-529: Deferred ordering check correctly addresses issue #8517.

This cross-block consistency check allows {#await} blocks to have a {:catch} without a {:then} clause, only emitting an error when both exist and catch precedes then. The range comparison for ordering is sound.


281-289: LGTM – error handling delegated to the caller.

Returning Absent without emitting an error here is appropriate; parse_each_opening_block handles the missing binding case at lines 312-318.

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

379-397: Keyword stop handling with space requirement is sound.

Requiring prev_byte == Some(b' ') before accepting a keyword stop prevents false positives on identifiers containing keyword substrings (e.g., "asdf" won't stop on "as"). The rewind approach correctly leaves the keyword unconsumed for the parser.


344-410: Overall refactor of consume_restricted_single_text_expression looks good.

The centralised bracket tracking with is_opening_paren/is_closing_paren closures and the use of kind.matches_punct()/kind.matches_keyword() simplifies the logic. The early break on top-level < and the position-based return logic are correct.

@ematipico ematipico force-pushed the fix/svelte-await-diagnostic branch from 636d390 to 1225468 Compare December 22, 2025 13:43
@ematipico ematipico force-pushed the fix/svelte-await-diagnostic branch from 1225468 to f6e43e0 Compare December 22, 2025 13:44
@ematipico ematipico merged commit 619ac39 into main Dec 22, 2025
12 checks passed
@ematipico ematipico deleted the fix/svelte-await-diagnostic branch December 22, 2025 14:34
@dyc3
Copy link
Contributor

dyc3 commented Dec 22, 2025

FYI, tested it on my work repo and it parses fine :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser L-HTML Language: HTML and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 HTML parser does not allow Svelte {#await} with a {:catch} and no {:then} 🐛 HTML parser fails on Svelte {#each} that has both index and key

2 participants