Skip to content

Conversation

@ematipico
Copy link
Member

Summary

Closes #8020

This one took a while! I had to get creating with the parsing of keyword when parsing expressions.

The PR was partially implemented with AI, this is where I asked help:

  • Implementation of restricted syntax when parsing an expression
  • Implementation of the parsing logic of the new syntax
  • The AI also helped with fixing some regression introduced by the different parsing of identifiers
  • generation of new test cases for parser and formatter

Where I implemented the PR:

  • fixed parsing logic, and improved the error recovery created by AI
  • wrote and improved the new syntax
  • formatter

In this PR I also changed how identifiers are parsed. Up until now, we were using different tokens for svelte and vue, which is not needed. Everything is now an IDENT.

Test Plan

CI should stay green

Docs

Not needed, since we're still under experimental

@changeset-bot
Copy link

changeset-bot bot commented Dec 17, 2025

🦋 Changeset detected

Latest commit: b22f665

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 requested review from a team December 17, 2025 17:30
@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 Dec 17, 2025
@github-actions
Copy link
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 52645 52645 0
Passed 51432 51432 0
Failed 1171 1171 0
Panics 42 42 0
Coverage 97.70% 97.70% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 6339 6339 0
Passed 2110 2110 0
Failed 4229 4229 0
Panics 0 0 0
Coverage 33.29% 33.29% 0.00%

ts/babel

Test result main count This PR count Difference
Total 626 626 0
Passed 561 561 0
Failed 65 65 0
Panics 0 0 0
Coverage 89.62% 89.62% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18845 18845 0
Passed 14089 14089 0
Failed 4755 4755 0
Panics 1 1 0
Coverage 74.76% 74.76% 0.00%

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

Adds full Svelte {#each} support: lexer/token updates (IDENT, AS_KW, EACH_KW, restricted single-expression contexts), parser rules for each opening/closing blocks, keyed/indexed items and {:else}, generated grammar additions, formatter implementations and wiring for new Svelte AST nodes, formatter behaviour tweaks (spacing and trimmed text handling), and extensive new tests and error-specs. No unrelated public API removals; several internal formatter modules and token kinds were extended.

Possibly related PRs

Suggested reviewers

  • dyc3

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR includes one additional scope change: unifying Svelte and Vue identifier tokens into a single IDENT token. While this refactoring improves consistency, it extends beyond the stated objective of implementing #each support. Clarify whether the identifier token unification was necessary for #each support or if it should be split into a separate PR for cleaner history.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(html): support for svelte #each' clearly and concisely describes the main feature addition — Svelte each-block support in the HTML formatter.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation (issue #8020), implementation details, AI assistance disclosure, and test strategy.
Linked Issues check ✅ Passed The PR fully addresses issue #8020 by implementing Svelte {#each} block support with parsing and formatting logic, including else clauses and closing blocks.
✨ 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/svelte-each-block

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

🧹 Nitpick comments (2)
crates/biome_html_formatter/src/html/auxiliary/text_expression.rs (1)

14-24: Consider using trim() instead of trim_start().trim_end().

The current chained approach allocates an intermediate string. Using trim() directly is more efficient and achieves the same result.

Apply this diff:

-        let trimmed_text = token_text.trim_start().trim_end();
+        let trimmed_text = token_text.trim();
crates/biome_html_parser/src/syntax/svelte.rs (1)

304-307: Minor: Redundant bitwise OR assignment.

has_errors |= true is equivalent to has_errors = true. The bitwise OR is unnecessary here since true | true == true.

     if result.is_none() {
-        has_errors |= true;
+        has_errors = true;
         p.bump_any();
     }

// ^^^^^^^^^^^^^^^^^
VueDirective =
name: 'vue_ident'
name: 'ident'
Copy link
Contributor

Choose a reason for hiding this comment

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

any particular reason for this change? I was planning to intentionally differentiate between an ident and a vue ident at the lexer level because all vue directives start with v-. Then, the parser logic for knowing the start of a vue directive is pretty easy. (maybe vue ident isn't the ideal name for this case)

Copy link
Member Author

@ematipico ematipico Dec 18, 2025

Choose a reason for hiding this comment

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

Because striking the difference doesn't really make a difference once the CST is constructed. The only difference it makes is during parsing, which we already do. After we understood that - for example - as or v-each are identifiers, we just need to emit a token.

Unless there's some future plan you have in mind, maintaining SVELTE_IDENT/VUE_IDENT might just cause confusion, and a waste of characters. Plus, JavaScript <> TypeScript are treated the same for example, where IDENT is used regardless of the dialect.

Copy link
Member Author

Choose a reason for hiding this comment

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

The only difference from other languages is that this method returns Option, because an identifier is such only in one dialect, while in another it's not.

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)
xtask/codegen/html.ungram (1)

397-397: Existing concern about identifier consolidation.

The change from vue_ident to ident has been previously flagged by dyc3, who noted that Vue directives start with v- and having a distinct token simplifies parser logic. This consolidation may increase parser complexity.

🧹 Nitpick comments (3)
xtask/codegen/html.ungram (1)

374-374: Misleading comment.

The comment states "Keep it different just for svelte", but SvelteName now uses the generic ident terminal rather than a Svelte-specific token. Either update the comment to reflect the consolidation, or reconsider whether the comment's intent is still relevant.

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

527-567: LGTM — consider documenting the keyword set.

The function correctly identifies language keywords and resets lexer position when no match is found. Per coding guidelines, adding a brief rustdoc comment listing the recognised keywords would improve discoverability.

🔎 Optional documentation improvement
+    /// Attempts to consume an HTML-ish language identifier keyword.
+    ///
+    /// Recognised keywords: `debug`, `attach`, `const`, `render`, `html`, `key`, `if`, `else`, `each`, `as`.
+    ///
+    /// If the current position matches a known keyword, returns `Some(HtmlSyntaxKind)`.
+    /// Otherwise, restores the lexer position and returns `None`.
     fn consume_language_identifier(&mut self, first: u8) -> Option<HtmlSyntaxKind> {
crates/biome_html_parser/src/token_source.rs (1)

85-93: Documentation for StopAtOpeningParenOrComma is incomplete.

The doc comment states "Stops at (" but the variant name suggests it also stops at ,. Consider updating the documentation for clarity.

🔎 Suggested fix
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub(crate) enum RestrictedExpressionKind {
     /// Stops at 'as' keyword or ',' (for Svelte #each blocks)
     StopAtAsOrComma,
-    /// Stops at `(`
+    /// Stops at `(` or `,`
     StopAtOpeningParenOrComma,
     /// Stops at `)`
     StopAtClosingParen,
 }
📜 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 621ebe6 and a18255a.

⛔ Files ignored due to path filters (11)
  • 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/interpolation/interpolation.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/svelte/each_with_whitespace.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/each_complex_combinations.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/each_with_index_and_key.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/each_with_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/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 (13)
  • .changeset/green-clubs-search.md (1 hunks)
  • crates/biome_html_formatter/src/html/auxiliary/double_text_expression.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs (1 hunks)
  • crates/biome_html_formatter/src/svelte/auxiliary/each_key.rs (1 hunks)
  • crates/biome_html_formatter/tests/specs/html/interpolation/interpolation.html (1 hunks)
  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json (1 hunks)
  • crates/biome_html_parser/src/lexer/mod.rs (11 hunks)
  • crates/biome_html_parser/src/syntax/svelte.rs (6 hunks)
  • crates/biome_html_parser/src/token_source.rs (5 hunks)
  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte (1 hunks)
  • crates/biome_html_parser/tests/quick_test.rs (1 hunks)
  • xtask/codegen/html.ungram (3 hunks)
  • xtask/codegen/src/html_kinds_src.rs (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • crates/biome_html_formatter/src/html/auxiliary/double_text_expression.rs
  • crates/biome_html_formatter/tests/specs/html/interpolation/interpolation.html
  • .changeset/green-clubs-search.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_html_formatter/src/svelte/auxiliary/each_key.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates

Files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
🧠 Learnings (45)
📚 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/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 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/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use language tags in documentation code blocks (js, ts, tsx, json, css) and order properties consistently as: language, then `expect_diagnostic`, then options modifiers, then `ignore`, then `file=path`

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.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/**/*.ungram : Nodes for enclosing syntax errors must have the `Bogus` word, e.g., `HtmlBogusAttribute`, and must be part of a variant

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/src/lexer/mod.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.{js,ts,tsx,jsx,json,css} : Test rules using snapshot tests via the `insta` library with test cases in `tests/specs/<group>/<rule_name>/` directories prefixed by `invalid` or `valid`

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
📚 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/**/language_kind.rs : Add a new variant to `LanguageKind` enum in `language_kind.rs` file and implement all methods for the new language variant

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.jsonc : Use `.jsonc` files in test specs with code snippets as array of strings to test rules in script environment (no import/export syntax)

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/svelte/each_missing_closing_paren.svelte
  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 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 : Import the `FormatNode` trait and implement it for your Node when creating formatters in biome_js_formatter

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.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_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • xtask/codegen/src/html_kinds_src.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.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/src/lib.rs : Expose a public `format_node` function that accepts formatting options and a root syntax node, returning a `FormatResult<Formatted<Context>>` with appropriate documentation

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.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: Implement the `FormatNodeRule<N>` trait with `fmt_fields` as the only required method; default implementations of `fmt`, `is_suppressed`, `fmt_leading_comments`, `fmt_dangling_comments`, and `fmt_trailing_comments` are provided

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.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_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.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/src/context.rs : Define `<Language>FormatContext` struct in a `context.rs` file containing `comments` and `source_map` fields, implementing `FormatContext` and `CstFormatContext` traits

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • 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 : 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_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
📚 Learning: 2025-12-12T10:11:05.564Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-12T10:11:05.564Z
Learning: Applies to crates/**/*.rs : Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • crates/biome_html_parser/tests/quick_test.rs
  • crates/biome_html_parser/src/token_source.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Wrap optional rule option fields in `Option<_>` to properly track set vs unset options during configuration merging

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 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 : For tokens that are not mandatory, use helper functions instead of hardcoding

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
  • 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: The formatter foundation relies on using the generic `Format` trait and `FormatNode` for nodes, with creation of an intermediate IR via a series of helpers

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Rule options struct fields should use `#[serde(rename_all = "camelCase")]`, `#[serde(deny_unknown_fields)]`, and `#[serde(default)]` attributes for proper JSON serialization

Applied to files:

  • crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.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:

  • xtask/codegen/src/html_kinds_src.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.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:

  • xtask/codegen/src/html_kinds_src.rs
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Use quick testing via `tests/quick_test.rs` by removing the `#[ignore]` macro and modifying the `SOURCE` variable to rapidly validate rule behavior during development

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/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-11-24T18:06:12.048Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:12.048Z
Learning: Applies to crates/biome_service/src/workspace/watcher.tests.rs : Implement watcher tests for workspace methods in watcher.tests.rs and end-to-end tests in LSP tests

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Store type data in linear vectors instead of using recursive data structures with `Arc` for improved data locality and performance

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/Cargo.toml : Include development dependencies in `Cargo.toml` for formatter tests: `biome_formatter_test`, `biome_<language>_factory`, `biome_<language>_parser`, `biome_parser`, `biome_service`, `countme`, `iai`, `quickcheck`, `quickcheck_macros`, and `tests_macros`

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` instead of `Arc` for types that reference other types to avoid stale cache issues when modules are replaced

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Use `Box<[T]>` instead of `Vec<T>` for rule options array fields to save memory (boxed slices and boxed str use 2 words instead of three words)

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Avoid using `unwrap()` or `expect()` on `Result` and `Option` types; instead use helper functions like `map`, `filter`, `and_then` to maintain code clarity and avoid panics

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Document rules with a one-line brief description in the first paragraph of the doc comment, followed by detailed paragraphs, `## Examples` section with `### Invalid` and `### Valid` subsections, and optional `## Options` section

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useShorthand` prefix for rules that report syntax rewritable using equivalent compact syntax (e.g., `useShorthandAssign`)

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noEmpty` prefix for rules that report empty code (e.g., `noEmptyBlockStatements`)

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Implement custom `Queryable` types and `Visitor` traits for rules requiring deep AST inspection to avoid redundant traversal passes

Applied to files:

  • crates/biome_html_parser/tests/quick_test.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : For rules ported from other ecosystems like ESLint or Clippy, add a `sources` field with `RuleSource` metadata using `.same()` for identical behavior or `.inspired()` for different behavior

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/**/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
  • crates/biome_html_parser/src/token_source.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
  • crates/biome_html_parser/src/token_source.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/token_source.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

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/**/language_kind.rs : Add a new language prefix to the `LANGUAGE_PREFIXES` constant in `language_kind.rs` file

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 : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.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/specs/**/options.json : Create an `options.json` file (formatted as `biome.json`) in test specification folders to apply non-default formatting options to all test files in that folder

Applied to files:

  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 Learning: 2025-08-05T14:43:29.581Z
Learnt from: dyc3
Repo: biomejs/biome PR: 7081
File: packages/@biomejs/biome/configuration_schema.json:7765-7781
Timestamp: 2025-08-05T14:43:29.581Z
Learning: The file `packages/biomejs/biome/configuration_schema.json` is auto-generated and should not be manually edited or reviewed for schema issues; any changes should be made at the code generation source.

Applied to files:

  • crates/biome_html_formatter/tests/specs/html/interpolation/options.json
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noRestricted` prefix for rules that report user-banned entities (e.g., `noRestrictedGlobals`)

Applied to files:

  • crates/biome_html_parser/src/token_source.rs
🧬 Code graph analysis (2)
crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs (8)
crates/biome_html_formatter/src/svelte/auxiliary/each_block.rs (1)
  • fmt_fields (7-22)
crates/biome_html_formatter/src/svelte/auxiliary/each_keyed_item.rs (1)
  • fmt_fields (7-15)
crates/biome_html_formatter/src/svelte/auxiliary/each_index.rs (1)
  • fmt_fields (7-11)
crates/biome_html_formatter/src/svelte/auxiliary/each_opening_block.rs (1)
  • fmt_fields (7-27)
crates/biome_html_formatter/src/svelte/auxiliary/else_if_clause.rs (1)
  • fmt_fields (9-39)
crates/biome_html_formatter/src/svelte/auxiliary/if_opening_block.rs (1)
  • fmt_fields (9-36)
crates/biome_formatter/src/builders.rs (1)
  • space (606-608)
crates/biome_html_formatter/src/generated.rs (16)
  • format (28-33)
  • format (66-71)
  • format (104-109)
  • format (142-144)
  • format (174-179)
  • format (212-217)
  • format (250-255)
  • format (288-293)
  • format (326-331)
  • format (364-370)
  • format (404-409)
  • format (442-447)
  • format (480-485)
  • format (512-517)
  • format (550-555)
  • format (588-594)
crates/biome_html_parser/src/lexer/mod.rs (1)
crates/biome_html_parser/src/token_source.rs (1)
  • current (195-197)
⏰ 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: Check Dependencies
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: autofix
  • GitHub Check: Documentation
  • GitHub Check: Test Node.js API
  • GitHub Check: Parser conformance
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Lint project (depot-windows-2022)
🔇 Additional comments (18)
crates/biome_html_formatter/tests/specs/html/interpolation/options.json (1)

1-11: LGTM!

The test configuration correctly enables interpolation parsing and formatting for the new Svelte #each block feature. The schema reference and settings are appropriate.

xtask/codegen/html.ungram (2)

202-203: LGTM!

The addition of SvelteEachBlock to the union follows the existing pattern for Svelte block types.


316-371: LGTM!

The grammar comprehensively covers Svelte #each block syntax, including keyed iteration, optional index and key bindings, and else clauses. The structure follows the established pattern from SvelteIfBlock.

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

29-30: LGTM!

Parentheses tokens are necessary for the Svelte #each key syntax: (key).


33-34: LGTM!

Adding as and each keywords is necessary for Svelte #each block support.


37-37: LGTM!

The consolidation of identifier tokens (HTML_IDENT, SVELTE_IDENT, VUE_IDENTIDENT) aligns with the grammar changes and simplifies the lexer.


80-86: LGTM!

The new Svelte each-related nodes match the grammar definitions and follow the established naming conventions.

crates/biome_html_formatter/src/svelte/auxiliary/each_as_keyed_item.rs (2)

1-5: LGTM!

The imports and struct definition follow the standard formatter boilerplate pattern.


6-27: LGTM!

The formatter implementation correctly handles the as name [, index] [(key)] syntax. The spacing and conditional formatting of optional index and key fields follow established patterns from similar formatters.

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

7-14: LGTM!

The test case covers a comprehensive Svelte {#each} block with item binding, index, and keyed iteration. Note that the test uses with_vue() options—if this is intentional for cross-compatibility testing, all good; otherwise, consider adding a with_svelte() option if one becomes available.

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

1-2: LGTM!

A well-crafted error case fixture that exercises parser error recovery for unclosed key expressions in Svelte {#each} blocks.

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

485-499: LGTM!

Clean implementation for consuming Svelte identifiers.


469-483: LGTM!

Good addition of parenthesis handling for keyed iteration and consistent identifier consumption pattern.


95-97: LGTM!

The fallback to consume_unexpected_character is appropriate for the inside-tag context where unknown identifiers should be flagged.


1086-1088: LGTM!

Proper integration of the restricted expression context into the main lexer dispatch.


1213-1220: LGTM!

Solid refactoring—generalising identifier checks makes the lexer more maintainable as template language support expands.

crates/biome_html_parser/src/token_source.rs (2)

47-48: LGTM!

Clean addition of the restricted expression context with a convenient constructor.

Also applies to: 70-73


118-123: LGTM!

The HtmlText variant name is more descriptive than the previous naming, and the doc comment helpfully points to the implementing function.

@ematipico ematipico requested a review from dyc3 December 18, 2025 11:37
@ematipico
Copy link
Member Author

I improved the parsing since last time. The AI botched some parts of the syntax, so now I improved a18255a (#8493)

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 (4)
crates/biome_html_parser/src/syntax/svelte.rs (2)

221-243: Consider adding a brief rustdoc comment for parse_each_key.

The function parses the (key) part of the #each block but lacks inline documentation. As per the coding guidelines for Rust crates, updating rustdoc for new features is recommended.

🔎 Suggested addition
+/// Parses the key expression `(key)` inside a Svelte `#each` block.
+///
+/// Example: `{#each items as item (item.id)}`
 fn parse_each_key(p: &mut HtmlParser) -> ParsedSyntax {

279-292: Error emitted when neither as nor , is present—verify this is the desired UX.

The error message assumes the user intended one of these two syntaxes, but if the expression itself is malformed, this might be confusing. The recovery is reasonable, but users might benefit from a more contextual message when the expression parsing already failed upstream.

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

377-398: Keyword lookahead within restricted expressions is clever but has a subtlety.

When consume_language_identifier returns None (not a keyword), the position is already reset internally (line 563), so advance_byte_or_char on line 396 is correct. However, the comment on line 395 says "position was reset by consume_language_identifier" which is accurate but might be clearer if it noted this is the non-keyword path.

🔎 Slightly clearer comment
                     } else {
-                        // Not a keyword, advance one byte (position was reset by consume_language_identifier)
+                        // Not a keyword; consume_language_identifier already reset position, so advance manually
                         self.advance_byte_or_char(current);
                     }

527-567: Generic consume_language_identifier is a good refactor.

Unifying keyword detection into a single function with a fixed-size buffer and rewind-on-no-match is clean. The keyword list covers the Svelte needs (as, each, etc.).

One thought: the buffer size of 14 bytes is sufficient for current keywords (longest is "attach" at 6), but if longer keywords are added in future, this could silently fail to match. A compile-time assertion or comment noting the max keyword length would be defensive.

🔎 Optional defensive comment
     fn consume_language_identifier(&mut self, first: u8) -> Option<HtmlSyntaxKind> {
         self.assert_current_char_boundary();
         let starting_position = self.position;
+        // Buffer sized for longest keyword; update if adding keywords longer than 14 chars
         const BUFFER_SIZE: usize = 14;
📜 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 a18255a and b22f665.

📒 Files selected for processing (3)
  • crates/biome_html_parser/src/lexer/mod.rs (11 hunks)
  • crates/biome_html_parser/src/syntax/svelte.rs (6 hunks)
  • crates/biome_html_parser/src/token_source.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates

Files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
🧠 Learnings (23)
📚 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
  • crates/biome_html_parser/src/token_source.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/**/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
  • crates/biome_html_parser/src/token_source.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/**/language_kind.rs : Add a new variant to `LanguageKind` enum in `language_kind.rs` file and implement all methods for the new language variant

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use language tags in documentation code blocks (js, ts, tsx, json, css) and order properties consistently as: language, then `expect_diagnostic`, then options modifiers, then `ignore`, then `file=path`

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • 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 : 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
  • crates/biome_html_parser/src/token_source.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/**/language_kind.rs : Add a new language prefix to the `LANGUAGE_PREFIXES` constant in `language_kind.rs` file

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
  • crates/biome_html_parser/src/token_source.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 `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/token_source.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/token_source.rs
  • crates/biome_html_parser/src/syntax/svelte.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

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
  • 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/lexer/mod.rs
📚 Learning: 2025-10-25T07:22:18.540Z
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.

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 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/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/**/*.ungram : Nodes for enclosing syntax errors must have the `Bogus` word, e.g., `HtmlBogusAttribute`, and must be part of a variant

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 : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.rs
  • 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 : For tokens that are not mandatory, use helper functions instead of hardcoding

Applied to files:

  • crates/biome_html_parser/src/lexer/mod.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/src/lexer/mod.rs
  • crates/biome_html_parser/src/token_source.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/src/context.rs : Define `<Language>FormatContext` struct in a `context.rs` file containing `comments` and `source_map` fields, implementing `FormatContext` and `CstFormatContext` traits

Applied to files:

  • crates/biome_html_parser/src/token_source.rs
📚 Learning: 2025-12-12T10:11:05.564Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-12T10:11:05.564Z
Learning: Applies to crates/**/*.rs : Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates

Applied to files:

  • crates/biome_html_parser/src/token_source.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/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 take a mutable reference to the parser as their only parameter and return a `ParsedSyntax`

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 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/syntax/svelte.rs
🧬 Code graph analysis (1)
crates/biome_html_parser/src/syntax/svelte.rs (3)
crates/biome_html_parser/src/token_source.rs (1)
  • restricted_expression (71-73)
crates/biome_html_parser/src/syntax/parse_error.rs (1)
  • expected_svelte_closing_block (104-106)
crates/biome_html_parser/src/syntax/mod.rs (1)
  • parse_single_text_expression_content (639-648)
⏰ 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). (3)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Parser conformance
🔇 Additional comments (16)
crates/biome_html_parser/src/syntax/svelte.rs (7)

154-182: LGTM! Clean structure for the each block parser.

The parse_each_block function follows the established pattern from parse_if_block and parse_key_block. The bogus node emission when has_errors is set is a sensible approach for error recovery.


184-219: Well-structured keyed item parsing with appropriate context switching.

The use of RestrictedExpressionStopAt::OpeningParenOrComma and re-lexing via HtmlReLexContext::Svelte correctly handles the complex tokenisation needs for as item, index (key) syntax.


294-335: Solid implementation with good error tracking.

The has_errors flag propagation and the recovery via p.bump_any() on line 319 are sensible. The pattern of returning a tuple (ParsedSyntax, bool) is a pragmatic approach for signalling downstream bogus node emission.


584-591: Updated is_at_list_end logic is cleaner with direct lookahead.

The condition p.at(T!["{:"]) && p.nth_at(1, T![else]) && !p.nth_at(2, T![if]) correctly identifies a standalone {:else} block without consuming tokens. This is a nice improvement over the previous checkpoint-based approach.


609-626: New helpers are concise and well-placed.

Both is_at_svelte_keyword and at_else_opening_block are appropriately scoped and follow existing patterns. The keyword set in is_at_svelte_keyword aligns with the Svelte specification.


245-258: The wrapper node is required by the grammar. SvelteEachKeyedItem is defined in xtask/codegen/html.ungram as a distinct rule wrapping SvelteEachIndex, so the function's implementation is correct.

Likely an incorrect or invalid review comment.


517-522: Formatter already handles IDENT tokens correctly within SvelteNameFields.

The formatter in crates/biome_html_formatter/src/svelte/auxiliary/name.rs extracts ident_token from SvelteNameFields and delegates to its .format() method without checking token kind. No downstream formatter changes are needed—this implementation already works with the IDENT token.

crates/biome_html_parser/src/token_source.rs (4)

19-60: Well-structured context enum extension.

The addition of RestrictedSingleExpression(RestrictedExpressionStopAt) follows the established pattern of the TextExpression(TextExpressionKind) variant. The derive adjustments (removing PartialOrd, Ord) are sensible since ordering isn't semantically meaningful for lex contexts.


70-74: Consistent constructor pattern.

restricted_expression mirrors single_expression and double_expression nicely.


85-93: Clear enum for stop conditions.

The RestrictedExpressionStopAt variants are self-documenting. The doc comments succinctly explain each stop condition.


118-123: HtmlText re-lex context is aptly named.

The docstring clarifies the re-lex behaviour. This is a clean addition.

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

339-410: Solid implementation of restricted expression lexing with proper nesting.

The bracket stack tracking and keyword-aware stopping logic is well thought out. The fix to return ERROR_TOKEN instead of EOF when nothing is consumed (lines 405-409) addresses the previous review concern correctly.

One minor observation: the checkpoint_pos save/restore pattern for rewinding when hitting a stop keyword is effective but could benefit from a brief inline comment explaining the rewind rationale.


469-499: Svelte context lexing updated cleanly.

The addition of ( and ) tokens (lines 474-475) and the fallback to consume_svelte_identifier (line 480) integrate well with the new each-block parsing. The consume_svelte_identifier function correctly produces IDENT tokens for non-keyword identifiers.


1086-1088: Integration point for restricted expressions in next_token.

The match arm correctly delegates to consume_restricted_single_text_expression. This is the expected integration pattern.


1169-1169: Re-lex context for HtmlText correctly wired.

Maps to consume_html_text as documented in the token source. Consistent with the design.


1213-1220: Function renames from is_at_svelte_* to generic is_at_*_identifier are sensible.

These are now used for broader language identifier detection, not just Svelte. The rename reflects the generalisation.

Comment on lines +315 to +320
// In case there's nothing parsed, it's possible we have whitespaces or noice.
// We consume any possible token, so we can recover and resume normal parsing.
if result.is_none() {
has_errors |= true;
p.bump_any();
}
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 | 🟡 Minor

Typo in comment: "noice" should be "noise".

Minor textual fix.

🔎 Fix
-    // In case there's nothing parsed, it's possible we have whitespaces or noice.
+    // In case there's nothing parsed, it's possible we have whitespaces or noise.
🤖 Prompt for AI Agents
In crates/biome_html_parser/src/syntax/svelte.rs around lines 315 to 320, the
comment contains a typo: change "noice" to "noise" so the comment reads "we have
whitespaces or noise." Make only this textual correction in the comment; do not
alter code logic.

Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

Nice work! I like the improvements to the lexer too

@ematipico ematipico merged commit 5fc24f4 into main Dec 18, 2025
14 checks passed
@ematipico ematipico deleted the feat/svelte-each-block branch December 18, 2025 13:07
@panaman67
Copy link

Thank you @ematipico !! :)

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 {#each} {:else} {/each}

4 participants