Skip to content

fix: lowercase component member expressions in Astro/Svelte#9302

Merged
dyc3 merged 15 commits intobiomejs:mainfrom
sepagian:fix/lowercase-member-expressions
Mar 4, 2026
Merged

fix: lowercase component member expressions in Astro/Svelte#9302
dyc3 merged 15 commits intobiomejs:mainfrom
sepagian:fix/lowercase-member-expressions

Conversation

@sepagian
Copy link
Copy Markdown
Contributor

@sepagian sepagian commented Mar 2, 2026

Summary

Fixes #9300 "Formatter breaks dots notation in Svelte component names"

Fixed Svelte/Astro lowercase component member expressions being formatted with extra space.

Input

<form.Field>
</form.Field>

Output

<form.Field>
</form.Field>
// was incorrectly formatted as <form .Field>

Test Plan

Test Plan

  • Added test cases for Svelte: lowercase-member.svelte
  • Added test cases for Astro: lowercase-member.astro

This PR was implemented with guidance from Claude Code AI assistant.
The solution was reviewed and validated by me as the contributor.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 2, 2026

🦋 Changeset detected

Latest commit: e8911b7

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

@github-actions github-actions bot added A-Parser Area: parser A-Formatter Area: formatter L-HTML Language: HTML and super languages labels Mar 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds lexer and parser support to recognise and emit dot (PRD) tokens for lowercase component member expressions inside tag names for Svelte and Astro. Introduces an HtmlReLexContext variant InsideTagSvelte, routes re-lexing for inside-tag Svelte/Astro contexts to emit dot tokens, updates tag-name parsing to detect components and member chains (converting lowercase bases to component kinds when required), and adjusts lexing context for element and closing-tag parsing. Adds parser/formatter tests and a CLI regression test asserting constructs like <form.Field> and <Data.Client> are preserved. No public API changes.

Possibly related PRs

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: fixing lowercase component member expressions (e.g., <form.Field>) in Svelte/Astro files.
Description check ✅ Passed The description clearly relates to the changeset, explaining the issue fixed (#9300), providing before/after examples, and documenting the test plan.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from #9300: fixes the formatter to preserve dot-notation in lowercase component member expressions, adds regression tests for both Svelte and Astro, and updates lexer/parser logic accordingly.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the reported issue: lexer/parser updates, test fixtures, regression tests, and a changeset entry. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (1)
crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte (1)

1-4: Add one guard case for dotted attributes to prevent collateral regressions.

Given dot lexing changed in tag contexts, please add a non-component case like <div data.foo="x" /> to lock attribute behaviour too.

Based on learnings: “Add tests for all code changes ... bug fixes need tests that reproduce the bug and validate the fix.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte`
around lines 1 - 4, The spec currently tests dotted names in tag and component
positions (e.g., <form.Field>, <Data.Client>, <foo.Bar.Baz />) but lacks a
non-component dotted attribute case; add a guard case like <div data.foo="x" />
(matching the file's Svelte test format) to ensure attribute dot-lexing in tag
contexts remains correct and prevents regressions when changing dot handling in
tags.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_html_parser/src/lexer/mod.rs`:
- Line 138: The lexer currently emits a standalone dot token (PRD =>
self.consume_byte(T![.]) and the same logic duplicated around the other match at
the later spot), which splits dotted attribute names like data.foo into separate
tokens and breaks parse_attribute; restrict dot tokenisation to component-name
contexts only by guarding those consume_byte(T![.]) calls with the lexer
state/check used for component names (e.g., only call self.consume_byte(T![.])
when in the ComponentName/component-name parsing context or when a helper like
is_component_name_context() / self.state == TokenizerState::ComponentName is
true); otherwise let the byte fall through to the identifier/attribute parsing
path so dots remain part of attribute names and parse_attribute can handle them.

In `@crates/biome_html_parser/src/syntax/mod.rs`:
- Around line 195-203: The lookahead for member expressions currently uses
p.nth_at(1, T![.]) while lexing is in HtmlLexContext::InsideTag so '.' isn’t
recognized; update the has_member_expression check to perform the lookahead
using the same component name lexing context used for parsing (i.e., use
component_name_context(p) when peeking for T![.] rather than the default
context). Concretely, replace the p.nth_at(1, T![.]) usage with the
context-aware lookahead variant (or a small helper) that supplies
component_name_context(p) so that is_possible_component, the
has_member_expression check, and the subsequent component parsing (m,
p.bump_with_context(HTML_LITERAL, component_name_context(p))) all see '.' as a
token and member chains like <form.Field> are detected. Ensure the new
helper/variant uses the exact same component_name_context(p) symbol so lexing
behavior is consistent.

---

Nitpick comments:
In `@crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte`:
- Around line 1-4: The spec currently tests dotted names in tag and component
positions (e.g., <form.Field>, <Data.Client>, <foo.Bar.Baz />) but lacks a
non-component dotted attribute case; add a guard case like <div data.foo="x" />
(matching the file's Svelte test format) to ensure attribute dot-lexing in tag
contexts remains correct and prevents regressions when changing dot handling in
tags.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1003229 and e104637.

⛔ Files ignored due to path filters (2)
  • crates/biome_html_formatter/tests/specs/html/astro/lowercase-member.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • .changeset/wet-dingos-spend.md
  • crates/biome_html_formatter/tests/specs/html/astro/lowercase-member.astro
  • crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/mod.rs

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 2, 2026

Merging this PR will degrade performance by 8.35%

❌ 1 regressed benchmark
✅ 63 untouched benchmarks
⏩ 152 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
synthetic/vue-expressions.vue[cached] 395.5 µs 431.5 µs -8.35%

Comparing sepagian:fix/lowercase-member-expressions (e8911b7) with main (3bc07ab)

Open in CodSpeed

Footnotes

  1. 152 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown
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.

Parser tests are missing (only formatter tests present)

@github-actions github-actions bot added the A-CLI Area: CLI label Mar 2, 2026
Copy link
Copy Markdown
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.

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

151-157: Keep context precedence consistent between helper and re-lex path.

Line 151 prioritises Svelte over Astro, but Line 255 does the opposite. It’s safer to derive re-lex choice from the same precedence to avoid drift.

♻️ Suggested tidy-up
-    if Astro.is_supported(p) {
-        p.re_lex(HtmlReLexContext::InsideTagAstro);
-    } else if Svelte.is_supported(p) {
-        p.re_lex(HtmlReLexContext::InsideTagSvelte);
-    }
+    match inside_tag_context(p) {
+        HtmlLexContext::InsideTagAstro => p.re_lex(HtmlReLexContext::InsideTagAstro),
+        HtmlLexContext::InsideTagSvelte => p.re_lex(HtmlReLexContext::InsideTagSvelte),
+        _ => {}
+    }

Also applies to: 255-259

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_parser/src/syntax/mod.rs` around lines 151 - 157, The
helper function inside_tag_context currently checks Vue, then Svelte, then
Astro, but the re-lex path uses the opposite Svelte/Astro precedence; make the
precedence consistent by aligning the check order between inside_tag_context and
the re-lex logic (so both choose Svelte before Astro or vice versa). Update the
conditional order in inside_tag_context or the re-lex branch (referencing
inside_tag_context and the re-lex function/branch around the other check) so
both use the same Vue → Svelte → Astro (or Vue → Astro → Svelte) ordering to
prevent divergent context selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_html_parser/src/syntax/mod.rs`:
- Around line 151-157: The helper function inside_tag_context currently checks
Vue, then Svelte, then Astro, but the re-lex path uses the opposite Svelte/Astro
precedence; make the precedence consistent by aligning the check order between
inside_tag_context and the re-lex logic (so both choose Svelte before Astro or
vice versa). Update the conditional order in inside_tag_context or the re-lex
branch (referencing inside_tag_context and the re-lex function/branch around the
other check) so both use the same Vue → Svelte → Astro (or Vue → Astro → Svelte)
ordering to prevent divergent context selection.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e104637 and 2aa2381.

⛔ Files ignored due to path filters (4)
  • crates/biome_html_formatter/tests/specs/html/astro/lowercase-member.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/astro/lowercase_component_member.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/lowercase_component_member.svelte.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (9)
  • .changeset/wet-dingos-spend.md
  • crates/biome_cli/tests/cases/regression_tests.rs
  • crates/biome_html_formatter/tests/specs/html/astro/lowercase-member.astro
  • crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte
  • crates/biome_html_parser/src/lexer/mod.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_parser/src/token_source.rs
  • crates/biome_html_parser/tests/html_specs/ok/astro/lowercase_component_member.astro
  • crates/biome_html_parser/tests/html_specs/ok/svelte/lowercase_component_member.svelte
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_html_parser/tests/html_specs/ok/svelte/lowercase_component_member.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_html_formatter/tests/specs/html/svelte/lowercase-member.svelte

Copy link
Copy Markdown
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.

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

151-160: Keep lex-context precedence consistent in one place.

inside_tag_context() prefers Svelte before Astro, but the post-tag re-lex in parse_element() does Astro before Svelte. If both flags are ever enabled together, that can cause mid-tag tokenisation drift. Worth consolidating this precedence to a single source of truth.

♻️ Small consistency refactor
-    if Astro.is_supported(p) {
-        p.re_lex(HtmlReLexContext::InsideTagAstro);
-    } else if Svelte.is_supported(p) {
-        p.re_lex(HtmlReLexContext::InsideTagSvelte);
-    }
+    if Svelte.is_supported(p) {
+        p.re_lex(HtmlReLexContext::InsideTagSvelte);
+    } else if Astro.is_supported(p) {
+        p.re_lex(HtmlReLexContext::InsideTagAstro);
+    }

Also applies to: 255-258

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_parser/src/syntax/mod.rs` around lines 151 - 160, The
lex-context precedence is inconsistent: inside_tag_context() checks Vue ->
Svelte -> Astro, while parse_element() re-lex post-tag using Astro before
Svelte, which can cause tokenisation drift when multiple flags are enabled;
unify the precedence by making parse_element() use inside_tag_context() (or vice
versa) so both use the same order (e.g., Vue, Svelte, Astro) and update the
other occurrence noted around lines 255-258 to call the same helper
(inside_tag_context) or the same precedence source so there is a single source
of truth for lex-context selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_html_parser/src/syntax/mod.rs`:
- Around line 151-160: The lex-context precedence is inconsistent:
inside_tag_context() checks Vue -> Svelte -> Astro, while parse_element() re-lex
post-tag using Astro before Svelte, which can cause tokenisation drift when
multiple flags are enabled; unify the precedence by making parse_element() use
inside_tag_context() (or vice versa) so both use the same order (e.g., Vue,
Svelte, Astro) and update the other occurrence noted around lines 255-258 to
call the same helper (inside_tag_context) or the same precedence source so there
is a single source of truth for lex-context selection.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2aa2381 and 9ae7ddb.

📒 Files selected for processing (1)
  • crates/biome_html_parser/src/syntax/mod.rs

@sepagian sepagian requested a review from dyc3 March 2, 2026 23:08
@sepagian sepagian requested a review from dyc3 March 3, 2026 14:07
@sepagian sepagian requested a review from dyc3 March 4, 2026 14:59
@dyc3 dyc3 merged commit 86fbc70 into biomejs:main Mar 4, 2026
16 of 17 checks passed
@github-actions github-actions bot mentioned this pull request Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📝 Formatter breaks dot notation in Svelte component names

2 participants