Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/chatty-jeans-shine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@biomejs/biome": patch
---

Added parsing support for Svelte's new [comments-in-tags](https://github.com/sveltejs/svelte/pull/17671) feature.

The HTML parser will now accept JS style comments in tags in Svelte files.
```svelte
<button
// single-line comment
onclick={doTheThing}
>click me</button>

<div
/* block comment */
class="foo"
>text</div>
```
44 changes: 40 additions & 4 deletions crates/biome_html_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,16 @@ impl CommentStyle for HtmlCommentStyle {
.any(|(key, ..)| key == category!("format"))
}

fn get_comment_kind(_comment: &SyntaxTriviaPieceComments<HtmlLanguage>) -> CommentKind {
// HTML comments are block comments (<!-- ... -->), not line comments
// They don't extend to the end of the line like // comments do
CommentKind::Block
fn get_comment_kind(comment: &SyntaxTriviaPieceComments<HtmlLanguage>) -> CommentKind {
// Svelte/Vue files can have JS-style `//` line comments inside tags.
// These must be treated as line comments so the formatter uses `line_suffix`,
// which forces a newline after them — preventing `>` from being swallowed.
// HTML `<!-- -->` comments and `/* */` block comments are both block-style.
if comment.text().starts_with("//") {
CommentKind::Line
} else {
CommentKind::Block
}
}

/// This allows us to override which comments are associated with which nodes.
Expand Down Expand Up @@ -112,6 +118,36 @@ impl CommentStyle for HtmlCommentStyle {
}
}

// Attach comments between attributes to the following attribute as leading comments.
// This is required for suppression comments (e.g. `// biome-ignore format: reason`)
// to work on attributes:
//
// ```svelte
// <div
// // biome-ignore format: reason
// class="foo"
// >
// ```
//
// Without this rule, the comment would be a trailing comment of the preceding attribute
// (or a dangling comment of the opening element if it's the first attribute), and
// `is_suppressed(class_attr)` would return false.
//
// NOTE: Do NOT use `comment.kind().is_line()` here to detect `//` comments — the
// comment kind is determined after `place_comment()` runs in the pipeline, so
// `kind()` always returns `Block` at this point. Instead, inspect the text directly.
if matches!(
comment.enclosing_node().kind(),
HtmlSyntaxKind::HTML_OPENING_ELEMENT | HtmlSyntaxKind::HTML_SELF_CLOSING_ELEMENT
) && let Some(following_node) = comment.following_node()
{
// Only re-attach for attribute-level following nodes.
// The closing tag case is already handled by the check above.
if !HtmlClosingElement::can_cast(following_node.kind()) {
return CommentPlacement::leading(following_node.clone(), comment);
}
}

// Fix trailing comments that should actually be leading comments for the next node.
// ```html
// 123<!--biome-ignore format: prettier ignore-->456
Expand Down
16 changes: 7 additions & 9 deletions crates/biome_html_formatter/src/verbatim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use biome_formatter::format_element::tag::VerbatimKind;
use biome_formatter::formatter::Formatter;
use biome_formatter::prelude::{
Tag, empty_line, expand_parent, format_with, hard_line_break, line_suffix,
should_nestle_adjacent_doc_comments, soft_line_break_or_space, space, text,
should_nestle_adjacent_doc_comments, space, text,
};

use biome_formatter::{
Expand Down Expand Up @@ -274,17 +274,15 @@ fn format_leading_comments_impl(
}

CommentKind::Line => {
// TODO: review logic here
// `//` line comments always require a hard line break after them because
// everything after `//` to end-of-line is part of the comment. Using
// `soft_line_break_or_space` would collapse the comment with the next
// attribute onto a single line, turning the `>` into part of the comment.
match comment.lines_after() {
0 => {}
1 => {
if comment.lines_before() == 0 {
biome_formatter::write!(f, [soft_line_break_or_space()])?;
} else {
biome_formatter::write!(f, [hard_line_break()])?;
}
_ => {
biome_formatter::write!(f, [hard_line_break()])?;
}
_ => biome_formatter::write!(f, [empty_line()])?,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Component
/* block comment */
prop="value"
/>
<Component
// line comment
prop="value"
/>
<Component /* inline block comment */ prop="value" />
<Component // inline line comment
prop="value" />
<Component.Member
/* block comment */
prop="value"
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
info: svelte/comments-in-component-tags.svelte
---

# Input

```svelte
<Component
/* block comment */
prop="value"
/>
<Component
// line comment
prop="value"
/>
<Component /* inline block comment */ prop="value" />
<Component // inline line comment
prop="value" />
<Component.Member
/* block comment */
prop="value"
/>

```


=============================

# Outputs

## Output 1

-----
Indent style: Tab
Indent width: 2
Line ending: LF
Line width: 80
Attribute Position: Auto
Bracket same line: false
Whitespace sensitivity: css
Indent script and style: false
Self close void elements: never
Trailing newline: true
-----

```svelte
<Component
/* block comment */
prop="value"
/>
<Component
// line comment
prop="value"
/>
<Component /* inline block comment */ prop="value" />
<Component
// inline line comment
prop="value"
/>
<Component.Member
/* block comment */
prop="value"
/>

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!-- Block comments between attributes (already working) -->
<div /* block comment */ class="foo" /* block comment */ >text</div>
<div /* block comment */ class="foo" data-foo="info" /* block comment */ data-bar="info" >text</div>

<!-- Single-line comment between attributes — was producing invalid syntax before this fix -->
<div // comment
class="foo"
>text</div>

<!-- // comment as the first attribute (no preceding node) -->
<div
// first comment
class="foo"
>text</div>

<!-- // comment as the last attribute before > -->
<div
class="foo"
// last comment
>text</div>

<!-- Multiple consecutive // comments -->
<div
// first line comment
// second line comment
class="foo"
>text</div>

<!-- // comment before a Svelte directive attribute -->
<div
// comment before directive
on:click={handler}
>text</div>

<!-- biome-ignore suppression comment on an attribute -->
<div
// biome-ignore format: reason
class = "foo"
data-bar = "baz"
>text</div>

<!-- // comment on a self-closing Svelte component -->
<Component
// comment
prop="value"
/>

<!-- /* block comment */ on a self-closing Svelte component -->
<Component
/* block comment */
prop="value"
/>

<!-- // biome-ignore suppression comment on a component attribute -->
<Component
// biome-ignore format: reason
prop="value" data-bar="baz"
/>
Loading
Loading