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

Skip to content

Commit c80361d

Browse files
authored
fix(formatter): preserve component tag casing in Svelte/Astro/Vue files (#7869)
1 parent 7165d06 commit c80361d

16 files changed

+314
-14
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#7864](https://github.com/biomejs/biome/issues/7864): Biome now preserves component tag name casing in Svelte, Astro, and Vue files.

crates/biome_html_formatter/src/context.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ use biome_html_syntax::{HtmlFileSource, HtmlLanguage};
99

1010
use crate::comments::{FormatHtmlComment, HtmlCommentStyle, HtmlComments};
1111

12-
#[derive(Debug, Clone, Default)]
12+
#[derive(Debug, Clone)]
1313
pub struct HtmlFormatOptions {
14+
/// The file source.
15+
file_source: HtmlFileSource,
16+
1417
/// The indent style.
1518
indent_style: IndentStyle,
1619

@@ -46,13 +49,35 @@ pub struct HtmlFormatOptions {
4649
self_close_void_elements: SelfCloseVoidElements,
4750
}
4851

52+
impl Default for HtmlFormatOptions {
53+
fn default() -> Self {
54+
Self {
55+
file_source: HtmlFileSource::html(),
56+
indent_style: IndentStyle::default(),
57+
indent_width: IndentWidth::default(),
58+
line_ending: LineEnding::default(),
59+
line_width: LineWidth::default(),
60+
attribute_position: AttributePosition::default(),
61+
bracket_same_line: BracketSameLine::default(),
62+
whitespace_sensitivity: WhitespaceSensitivity::default(),
63+
indent_script_and_style: IndentScriptAndStyle::default(),
64+
self_close_void_elements: SelfCloseVoidElements::default(),
65+
}
66+
}
67+
}
68+
4969
impl HtmlFormatOptions {
50-
pub fn new(_file_source: HtmlFileSource) -> Self {
70+
pub fn new(file_source: HtmlFileSource) -> Self {
5171
Self {
72+
file_source,
5273
..Default::default()
5374
}
5475
}
5576

77+
pub fn file_source(&self) -> &HtmlFileSource {
78+
&self.file_source
79+
}
80+
5681
pub fn with_indent_style(mut self, indent_style: IndentStyle) -> Self {
5782
self.indent_style = indent_style;
5883
self

crates/biome_html_formatter/src/html/auxiliary/opening_element.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
html::lists::attribute_list::FormatHtmlAttributeListOptions,
33
prelude::*,
4-
utils::metadata::{is_canonical_html_tag, is_element_whitespace_sensitive},
4+
utils::metadata::{is_element_whitespace_sensitive, should_lowercase_html_tag},
55
};
66
use biome_formatter::{FormatRuleWithOptions, GroupId, write};
77
use biome_html_syntax::{HtmlOpeningElement, HtmlOpeningElementFields};
@@ -48,7 +48,7 @@ impl FormatNodeRule<HtmlOpeningElement> for FormatHtmlOpeningElement {
4848
let l_angle_token = l_angle_token?;
4949
let name = name?;
5050
let is_whitespace_sensitive = is_element_whitespace_sensitive(f, &name);
51-
let is_canonical_html_tag = is_canonical_html_tag(&name);
51+
let is_canonical_html_element = should_lowercase_html_tag(f, &name);
5252

5353
let bracket_same_line = f.options().bracket_same_line().value();
5454

@@ -66,7 +66,7 @@ impl FormatNodeRule<HtmlOpeningElement> for FormatHtmlOpeningElement {
6666
attributes
6767
.format()
6868
.with_options(FormatHtmlAttributeListOptions {
69-
is_canonical_html_element: is_canonical_html_tag,
69+
is_canonical_html_element,
7070
tag_name: Some(name.clone()),
7171
})
7272
.fmt(f)?;

crates/biome_html_formatter/src/html/auxiliary/self_closing_element.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
html::lists::attribute_list::FormatHtmlAttributeListOptions, prelude::*,
3-
utils::metadata::is_canonical_html_tag,
3+
utils::metadata::should_lowercase_html_tag,
44
};
55
use biome_formatter::write;
66
use biome_html_syntax::{HtmlSelfClosingElement, HtmlSelfClosingElementFields};
@@ -18,7 +18,7 @@ impl FormatNodeRule<HtmlSelfClosingElement> for FormatHtmlSelfClosingElement {
1818
let bracket_same_line = f.options().bracket_same_line().value();
1919
let self_close_void_elements = f.options().self_close_void_elements();
2020
let name = name?;
21-
let is_canonical_html_tag = is_canonical_html_tag(&name);
21+
let is_canonical_html_element = should_lowercase_html_tag(f, &name);
2222

2323
write!(f, [l_angle_token.format(), name.format()])?;
2424

@@ -29,7 +29,7 @@ impl FormatNodeRule<HtmlSelfClosingElement> for FormatHtmlSelfClosingElement {
2929
attributes
3030
.format()
3131
.with_options(FormatHtmlAttributeListOptions {
32-
is_canonical_html_element: is_canonical_html_tag,
32+
is_canonical_html_element,
3333
tag_name: Some(name.clone()),
3434
})
3535
.fmt(f)?;

crates/biome_html_formatter/src/html/auxiliary/tag_name.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
use crate::{
22
prelude::*,
3-
utils::{formatters::FormatTokenAsLowercase, metadata::is_canonical_html_tag},
3+
utils::{formatters::FormatTokenAsLowercase, metadata::should_lowercase_html_tag},
44
};
55
use biome_formatter::write;
66
use biome_html_syntax::{HtmlTagName, HtmlTagNameFields};
77
#[derive(Debug, Clone, Default)]
88
pub(crate) struct FormatHtmlTagName;
99
impl FormatNodeRule<HtmlTagName> for FormatHtmlTagName {
1010
fn fmt_fields(&self, node: &HtmlTagName, f: &mut HtmlFormatter) -> FormatResult<()> {
11-
// TODO: maybe move this check to a parent node so we aren't checking this twice per tag?
12-
let is_canonical_html_tag = is_canonical_html_tag(node);
1311
let HtmlTagNameFields { value_token } = node.as_fields();
1412

15-
if is_canonical_html_tag {
13+
if should_lowercase_html_tag(f, node) {
1614
write!(f, [FormatTokenAsLowercase::from(value_token?)])
1715
} else {
1816
write![f, [value_token.format()]]

crates/biome_html_formatter/src/utils/metadata.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,14 @@ pub(crate) fn is_canonical_html_tag(tag_name: &HtmlTagName) -> bool {
724724
is_canonical_html_tag_name(tag_name.text_trimmed())
725725
}
726726

727+
/// Whether a tag should be lowercased in the current formatting context.
728+
///
729+
/// Returns `true` only for canonical HTML tags in pure HTML files (.html).
730+
/// Component frameworks preserve tag name casing.
731+
pub(crate) fn should_lowercase_html_tag(f: &HtmlFormatter, tag_name: &HtmlTagName) -> bool {
732+
f.options().file_source().is_html() && is_canonical_html_tag(tag_name)
733+
}
734+
727735
/// Whether the given attribute name is a known HTML attribute for the given tag name.
728736
///
729737
/// See [`HTML_ATTRIBUTES_BY_TAG`], [`HTML_GLOBAL_ATTRIBUTES`].

crates/biome_html_formatter/tests/spec_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub fn run(spec_input_file: &str, _expected_file: &str, test_directory: &str, _f
5151

5252
let source_type: HtmlFileSource = test_file.input_file().as_path().try_into().unwrap();
5353

54-
let options = HtmlFormatOptions::new(HtmlFileSource::html());
54+
let options = HtmlFormatOptions::new(source_type);
5555
let language = language::HtmlTestFormatLanguage::new(source_type);
5656

5757
let snapshot = SpecSnapshot::new(

crates/biome_html_formatter/tests/spec_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ mod spec_test;
44
mod formatter {
55

66
mod html {
7-
tests_macros::gen_tests! {"tests/specs/html/**/*.{html,vue}", crate::spec_test::run, ""}
7+
tests_macros::gen_tests! {"tests/specs/html/**/*.{html,vue,svelte,astro}", crate::spec_test::run, ""}
88
}
99
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
import Button from './Button.astro';
3+
import TextInput from './TextInput.astro';
4+
---
5+
6+
<Button label="test button" />
7+
<TextInput placeholder="enter text" />
8+
<button>native button</button>
9+
<input type="text" />
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
source: crates/biome_formatter_test/src/snapshot_builder.rs
3+
info: component-frameworks/astro-component-casing.astro
4+
---
5+
# Input
6+
7+
```astro
8+
---
9+
import Button from './Button.astro';
10+
import TextInput from './TextInput.astro';
11+
---
12+
13+
<Button label="test button" />
14+
<TextInput placeholder="enter text" />
15+
<button>native button</button>
16+
<input type="text" />
17+
18+
```
19+
20+
21+
=============================
22+
23+
# Outputs
24+
25+
## Output 1
26+
27+
-----
28+
Indent style: Tab
29+
Indent width: 2
30+
Line ending: LF
31+
Line width: 80
32+
Attribute Position: Auto
33+
Bracket same line: false
34+
Whitespace sensitivity: css
35+
Indent script and style: false
36+
Self close void elements: never
37+
-----
38+
39+
```astro
40+
---
41+
import Button from './Button.astro';
42+
import TextInput from './TextInput.astro';
43+
---
44+
<Button label="test button"/>
45+
<TextInput placeholder="enter text"/>
46+
<button>native button</button>
47+
<input type="text">
48+
```
49+
50+
51+
52+
## Unimplemented nodes/tokens
53+
54+
"import Button from './Button.astro';\nimport TextInput from './TextInput.astro';\n-" => 4..85

0 commit comments

Comments
 (0)