Skip to content

[WIP] feat(html/nursery): add noDupeElseIfBlocks, noDupeStyleProperties, noDupeUseDirectives#10504

Closed
Mokto wants to merge 24 commits into
biomejs:mainfrom
Mokto:feat/svelte-interpolated-attribute-string
Closed

[WIP] feat(html/nursery): add noDupeElseIfBlocks, noDupeStyleProperties, noDupeUseDirectives#10504
Mokto wants to merge 24 commits into
biomejs:mainfrom
Mokto:feat/svelte-interpolated-attribute-string

Conversation

@Mokto

@Mokto Mokto commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports three rules from eslint-plugin-svelte to Biome, all targeting Svelte templates (.svelte files):

All three rules are in the nursery group, tagged with RuleDomain::Svelte, and marked recommended: true.

Test plan

  • Snapshot tests for invalid and valid cases for all three rules (cargo test -p biome_html_analyze)
  • All 162 tests pass

This PR was written primarily by Claude Code.

@changeset-bot

changeset-bot Bot commented May 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fac0db7

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

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@github-actions github-actions Bot added A-Linter Area: linter A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools L-HTML Language: HTML and super languages labels May 29, 2026
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

This PR extends Biome's HTML/Svelte parser to parse {expression} interpolations inside quoted attribute values as structured AST nodes rather than opaque strings. Changes span the grammar (new SvelteTemplateAttributeValue node and element types), lexer (new contexts for chunk and attribute-value tokenisation), parser (re-lexing and routing), formatter (new rules and dispatch), linting (three rules updated to handle the new node), and tests (valid, edge and error cases).

Possibly related PRs

  • biomejs/biome#10275: Modifies Svelte text-expression parsing and re-lexing context handling, overlapping with the parser's interpolation detection logic.
  • biomejs/biome#9988: Refactors the useButtonType lint that this PR also updates to handle template attribute values.

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title advertises three new nursery rules (noDupeElseIfBlocks, noDupeStyleProperties, noDupeUseDirectives) but the changeset exclusively implements Svelte interpolation parsing infrastructure with no rule implementations present. Either implement the three advertised rules or update the title to '[WIP] feat(html/parser): parse Svelte interpolations in quoted attribute values' to match the actual changeset.
Description check ⚠️ Warning The PR description describes three ported linter rules (noDupeElseIfBlocks, noDupeStyleProperties, noDupeUseDirectives) but the changeset contains exclusively HTML/Svelte parser changes with no rule implementations or tests for these rules. Update the description to reflect the actual changes: Svelte attribute interpolation parsing infrastructure, or complete the rule implementations to match the advertised scope.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/biome_html_formatter/src/shared.rs`:
- Line 21: FmtAnyAttributeInitializer currently formats SvelteInterpolatedString
with node.format().fmt(f) which ignores the parent compact flag; change the
FmtAnyAttributeInitializer branch for
AnyHtmlAttributeInitializer::SvelteInterpolatedString to call
node.format().with_options(self.compact).fmt(f) (or otherwise pass self.compact
into the formatter), and update the SvelteInterpolatedString formatter (and its
parts/chunks formatters) to accept and propagate the compact option (use
parts.format().with_options(compact) when writing parts) so the compact
formatting flag is threaded through end-to-end.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cac1a72-6711-4e86-af70-d1c121db4117

📥 Commits

Reviewing files that changed from the base of the PR and between 0c03ee3 and 45ce82b.

⛔ Files ignored due to path filters (10)
  • 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/svelte/attribute_interpolation.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/svelte/attribute_interpolation_unterminated.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation_edge_cases.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (22)
  • .changeset/svelte-interpolated-attribute-strings.md
  • crates/biome_html_analyze/src/lint/a11y/no_redundant_alt.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/shared.rs
  • crates/biome_html_formatter/src/svelte/any/interpolated_string_part.rs
  • crates/biome_html_formatter/src/svelte/any/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/interpolated_string.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/interpolated_string_chunk.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/svelte/lists/interpolated_string_part_list.rs
  • crates/biome_html_formatter/src/svelte/lists/mod.rs
  • crates/biome_html_formatter/tests/specs/html/svelte/attribute_interpolation.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/error/svelte/attribute_interpolation_unterminated.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation.svelte
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation_edge_cases.svelte
  • crates/biome_html_syntax/src/attribute_ext.rs
  • xtask/codegen/html.ungram
  • xtask/codegen/src/html_kinds_src.rs

Comment thread crates/biome_html_formatter/src/shared.rs Outdated
@codspeed-hq

codspeed-hq Bot commented May 29, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 7.61%

❌ 1 regressed benchmark
✅ 67 untouched benchmarks
⏩ 188 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
synthetic/wide-siblings.html[uncached] 1.7 ms 1.8 ms -7.61%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Mokto:feat/svelte-interpolated-attribute-string (fac0db7) with main (e39bb2c)

Open in CodSpeed

Footnotes

  1. 188 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.

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unfortunately the grammar is incorrect, as it might create issues. It should be reworked

Comment thread xtask/codegen/html.ungram Outdated
Comment on lines +231 to +237
// A quoted attribute value that mixes literal text with one or more Svelte
// `{expression}` interpolations, e.g. `style="top: {top}px"` or `class="card {cls}"`.
// The literal segments (including the surrounding quotes) are kept as chunks so
// the original text round-trips, and each interpolation reuses
// `HtmlAttributeSingleTextExpression`.
SvelteInterpolatedString =
parts: SvelteInterpolatedStringPartList

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please keep comments inline with the document and the other documents

It's simply the grammar that handles, with a second line that heights the piece of code.

Comment thread xtask/codegen/html.ungram Outdated
SvelteInterpolatedStringChunk
| HtmlAttributeSingleTextExpression

SvelteInterpolatedStringChunk = 'html_string_literal'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is incorrect. It's not a "string literal". Strings have a starting and closing quote. This here, doesn't have quotes, or it might have only one quote. For example

foo="{lorem} something {ipsum}"

The something part doesn't have a quote. I suggest checking the grammar used for JavaScript template literal, and take inspiration from them.

1: SVELTE_INTERPOLATED_STRING@11..22
0: SVELTE_INTERPOLATED_STRING_PART_LIST@11..22
0: SVELTE_INTERPOLATED_STRING_CHUNK@11..13
0: HTML_STRING_LITERAL@11..13 "\"a" [] []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's the issue I was talking about. A quote for a string literal, but it doesn't have the closing one.

Since string literals usually use the inner_string_text, it might result in a panic because it assumes it has both quotes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_html_analyze/src/lint/a11y/use_button_type.rs (1)

90-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

False positive: dynamic Svelte template values flagged as invalid.

When the attribute contains only interpolations (e.g., <button type="{buttonType}">), string_value() returns None, but we've already entered the is_static_string block. This causes us to return an error state instead of assuming valid (the intended behaviour per line 105-106).

🐛 Proposed fix
-        let is_static_string = value.as_html_string().is_some()
-            || value.as_svelte_template_attribute_value().is_some();
-        if is_static_string {
-            // Static string value - validate it
-            if let Some(string_value) = value.string_value()
-                && ALLOWED_BUTTON_TYPES.contains(&&*string_value)
-            {
-                return None;
-            }
-            // Invalid static value
-            return Some(UseButtonTypeState {
-                missing_prop: false,
-            });
-        }
-
-        // Dynamic expression - assume valid
-        None
+        // Try to extract a static string value. If we get one, validate it.
+        // If the value is dynamic (expression or template with interpolations), assume valid.
+        if let Some(string_value) = value.string_value() {
+            if ALLOWED_BUTTON_TYPES.contains(&&*string_value) {
+                return None;
+            }
+            // Invalid static value
+            return Some(UseButtonTypeState {
+                missing_prop: false,
+            });
+        }
+
+        // Dynamic expression or template with interpolations - assume valid
+        None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_html_analyze/src/lint/a11y/use_button_type.rs` around lines 90 -
103, The code flags dynamic-only Svelte template attribute values as invalid
because is_static_string is true when value.as_svelte_template_attribute_value()
is Some even if value.string_value() is None; update the logic so that dynamic
Svelte template values are treated as non-static (or explicitly return None for
them). Concretely, modify the is_static_string check or add a guard in the block
around value.string_value() so that if as_svelte_template_attribute_value() is
Some but value.string_value() is None you return None (treat as valid) instead
of returning UseButtonTypeState { missing_prop: false }; keep
ALLOWED_BUTTON_TYPES usage for actual static string validation.
♻️ Duplicate comments (1)
crates/biome_html_formatter/src/shared.rs (1)

21-21: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Thread compact through this new Svelte branch.

Line 21 drops self.compact, unlike the neighbouring attribute initialiser branches. That means compact formatting stops at SvelteTemplateAttributeValue, and the downstream formatter in crates/biome_html_formatter/src/svelte/value/template_attribute_value.rs:12-17 currently just writes elements.format() with no options to pick it back up. Compact mode has gone on holiday again.

#!/bin/bash
set -euo pipefail

echo "== FmtAnyAttributeInitializer =="
fd -p 'shared.rs' crates/biome_html_formatter/src -x sed -n '12,24p' {}

echo
echo "== Svelte template attribute formatter =="
fd -p 'template_attribute_value.rs' crates/biome_html_formatter/src/svelte/value -x sed -n '1,80p' {}

echo
echo "== Existing compact-aware formatter calls nearby =="
rg -n -C2 'with_options\(self\.compact\)|with_options\(compact\)' crates/biome_html_formatter/src

Expected result: the new SvelteTemplateAttributeValue arm is the odd one out in FmtAnyAttributeInitializer, and the template-attribute formatter shown above has no option plumbing to recover that flag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_html_formatter/src/shared.rs` at line 21, The
SvelteTemplateAttributeValue branch in FmtAnyAttributeInitializer drops
self.compact so compact mode isn't propagated; update the
AnyHtmlAttributeInitializer::SvelteTemplateAttributeValue arm to thread the
compact flag into the child's formatter the same way other arms do (i.e., call
the node's formatter with the parent's compact options rather than plain
node.format().fmt(f)), and ensure the Svelte template attribute formatter (the
template_attribute_value formatter that calls elements.format()) accepts and
uses those options (propagate self.compact into elements.format() via
with_options or the equivalent options plumbing so compact formatting is
preserved).
🧹 Nitpick comments (1)
crates/biome_html_syntax/src/attribute_ext.rs (1)

26-26: ⚡ Quick win

Consider using text() instead of text_trimmed() for template chunks.

Template chunks must preserve their exact literal content, including any whitespace. Whilst text_trimmed() likely works here (trivia-stripping rather than whitespace-stripping), using text() would be more explicit about preserving all content.

Example: style=" {x}" has a chunk " " (two spaces) that must not be trimmed.

📝 Suggested change
-                            text.push_str(chunk.html_template_chunk_token().ok()?.text_trimmed());
+                            text.push_str(chunk.html_template_chunk_token().ok()?.text());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_html_syntax/src/attribute_ext.rs` at line 26, The template chunk
handling currently uses chunk.html_template_chunk_token().ok()?.text_trimmed(),
which can remove significant literal whitespace; update the code to call text()
instead of text_trimmed() so that template chunks preserve exact content
(replace the text_trimmed() call on the result of html_template_chunk_token()
with text()). Ensure this change is applied where text.push_str(...) is invoked
for template chunks in attribute_ext.rs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/biome_html_analyze/src/lint/a11y/use_button_type.rs`:
- Around line 90-103: The code flags dynamic-only Svelte template attribute
values as invalid because is_static_string is true when
value.as_svelte_template_attribute_value() is Some even if value.string_value()
is None; update the logic so that dynamic Svelte template values are treated as
non-static (or explicitly return None for them). Concretely, modify the
is_static_string check or add a guard in the block around value.string_value()
so that if as_svelte_template_attribute_value() is Some but value.string_value()
is None you return None (treat as valid) instead of returning UseButtonTypeState
{ missing_prop: false }; keep ALLOWED_BUTTON_TYPES usage for actual static
string validation.

---

Duplicate comments:
In `@crates/biome_html_formatter/src/shared.rs`:
- Line 21: The SvelteTemplateAttributeValue branch in FmtAnyAttributeInitializer
drops self.compact so compact mode isn't propagated; update the
AnyHtmlAttributeInitializer::SvelteTemplateAttributeValue arm to thread the
compact flag into the child's formatter the same way other arms do (i.e., call
the node's formatter with the parent's compact options rather than plain
node.format().fmt(f)), and ensure the Svelte template attribute formatter (the
template_attribute_value formatter that calls elements.format()) accepts and
uses those options (propagate self.compact into elements.format() via
with_options or the equivalent options plumbing so compact formatting is
preserved).

---

Nitpick comments:
In `@crates/biome_html_syntax/src/attribute_ext.rs`:
- Line 26: The template chunk handling currently uses
chunk.html_template_chunk_token().ok()?.text_trimmed(), which can remove
significant literal whitespace; update the code to call text() instead of
text_trimmed() so that template chunks preserve exact content (replace the
text_trimmed() call on the result of html_template_chunk_token() with text()).
Ensure this change is applied where text.push_str(...) is invoked for template
chunks in attribute_ext.rs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5e47bccf-bc8a-4098-8ba9-699e80d8e896

📥 Commits

Reviewing files that changed from the base of the PR and between 03d4bd8 and 63153cb.

⛔ Files ignored due to path filters (25)
  • 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_parser/tests/html_specs/error/svelte/attribute_interpolation_unterminated.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attribute_interpolation_edge_cases.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_multiline.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/component.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/component_with_attributes.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/animate_in_each.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/bind_checked.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/bind_files.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/bind_group.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/style_basic.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/style_important_modifier.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/style_kebab_case.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/style_multiple.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/directives/style_multiple_with_important.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/js_comments_in_component_tags.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/js_comments_in_tag.svelte.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 **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/svg_with_colon_attribute.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (22)
  • crates/biome_html_analyze/src/lint/a11y/no_redundant_alt.rs
  • crates/biome_html_analyze/src/lint/a11y/use_button_type.rs
  • crates/biome_html_analyze/src/lint/nursery/no_script_url.rs
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/shared.rs
  • crates/biome_html_formatter/src/svelte/any/directive_initializer_clause.rs
  • crates/biome_html_formatter/src/svelte/any/mod.rs
  • crates/biome_html_formatter/src/svelte/any/template_element.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/template_chunk_element.rs
  • crates/biome_html_formatter/src/svelte/lists/mod.rs
  • crates/biome_html_formatter/src/svelte/lists/template_element_list.rs
  • crates/biome_html_formatter/src/svelte/value/mod.rs
  • crates/biome_html_formatter/src/svelte/value/template_attribute_value.rs
  • 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_syntax/src/attribute_ext.rs
  • xtask/codegen/html.ungram
  • xtask/codegen/src/html_kinds_src.rs
  • xtask/glue/src/lib.rs
✅ Files skipped from review due to trivial changes (6)
  • crates/biome_html_formatter/src/svelte/any/directive_initializer_clause.rs
  • crates/biome_html_formatter/src/svelte/lists/mod.rs
  • crates/biome_html_formatter/src/svelte/value/mod.rs
  • crates/biome_html_formatter/src/svelte/any/template_element.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/generated.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_html_formatter/src/svelte/any/mod.rs
  • crates/biome_html_formatter/src/svelte/auxiliary/mod.rs

@Mokto

Mokto commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

@ematipico i think it's ready again 👍

@Mokto Mokto requested a review from ematipico June 2, 2026 15:59

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The parser is now more powerful, but we should focus on the linter now, because that's where the users see the benefits.

This PR should add new test cases where now lint rules don't emit false positives, or catch more cases

Comment on lines +5 to +9
The HTML parser now parses Svelte `{expression}` interpolations inside quoted attribute values into structured nodes, instead of treating the whole value as an opaque string.

```svelte
<div style="top: {top}px" class="card {cls}"></div>
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should reword this changeset. Biome is already able to parse it. Instead, since now we improved the parsing, and we can detect the bindings inside the interpolation, we should focus on talking about the lint rules that benefit from this change.

@Mokto Mokto requested a review from ematipico June 2, 2026 16:44
@Mokto Mokto changed the title feat(html_parser): parse Svelte interpolations in quoted attribute vals [WIP] feat(html/nursery): add noDupeElseIfBlocks, noDupeStyleProperties, noDupeUseDirectives Jun 3, 2026
@github-actions github-actions Bot added A-CLI Area: CLI A-Project Area: project A-Diagnostic Area: diagnostocis labels Jun 3, 2026
Mokto and others added 9 commits June 3, 2026 05:40
…lues

Quoted attribute values like `style="top: {top}px"` are now parsed into a
`SvelteInterpolatedString` node holding literal chunks and the existing
`HtmlAttributeSingleTextExpression` interpolations, instead of a single opaque
string token. Plain values without `{` are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Value lex context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ter spec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mokto and others added 15 commits June 3, 2026 05:40
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…etection

Replace the O(n) forward-scan in svelte_attribute_has_interpolation with a
single O(n) lex pass (same work as before), then an O(1) last-byte check to
detect interpolation. When interpolation is found, re_lex the opening quote
as a standalone token so the template parser gets its l_quote.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace lookup_byte table dispatch with a direct two-comparison byte scan.
Since `{`, `"` and `'` are ASCII (< 0x80) they cannot appear as continuation
bytes in UTF-8 multi-byte sequences, so scanning byte-by-byte is safe and
allows the compiler to auto-vectorize the loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard the cur_text() call and interpolation detection behind Svelte.is_supported()
to avoid overhead on every HTML_STRING_LITERAL in plain HTML/Vue/Astro files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…DupeUseDirectives

Port three eslint-plugin-svelte rules:
- noDupeElseIfBlocks: disallow duplicate conditions in {#if}/{:else if} chains
- noDupeStyleProperties: disallow duplicate style: directives on the same element
- noDupeUseDirectives: disallow duplicate use: directives on the same element

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Mokto Mokto force-pushed the feat/svelte-interpolated-attribute-string branch from cdf9621 to fac0db7 Compare June 3, 2026 03:41
@Mokto

Mokto commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by a clean PR from branch feat/svelte-dupe-rules — same changes without the unrelated Svelte parser work.

@Mokto Mokto closed this Jun 3, 2026
@ematipico

Copy link
Copy Markdown
Member

Where's the PR? Can you link it?

@Mokto

Mokto commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

#10549

@Mokto

Mokto commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Actually I completely messed up that PR, it was supposed to be the html parser. Sorry about that @ematipico. the code is available there: #10555.

Sorry for the double review :(

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

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Formatter Area: formatter A-Linter Area: linter A-Parser Area: parser A-Project Area: project 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.

2 participants