Skip to content

Commit 7f1e060

Browse files
authored
fix(parse/html/vue): don't treat : as special token outside of vue directives (#9162)
1 parent a794cd9 commit 7f1e060

8 files changed

Lines changed: 686 additions & 62 deletions

File tree

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 [#9161](https://github.com/biomejs/biome/issues/9161): The Vue parser now correctly handles colon attributes like `xlink:href` and `xmlns:xlink` by parsing them as single attributes instead of splitting them into separate tokens.

crates/biome_html_parser/src/lexer/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,11 @@ impl<'src> HtmlLexer<'src> {
759759
}
760760
}
761761
IdentifierContext::Vue => {
762-
if is_attribute_name_byte_vue(byte) {
762+
if byte == b':' && is_vue_directive_prefix_bytes(&buffer[..len]) {
763+
break;
764+
}
765+
766+
if is_attribute_name_byte_vue(byte) || byte == b':' {
763767
if len < BUFFER_SIZE {
764768
buffer[len] = byte;
765769
len += 1;
@@ -1430,6 +1434,10 @@ fn is_astro_directive_keyword_bytes(bytes: &[u8]) -> bool {
14301434
)
14311435
}
14321436

1437+
fn is_vue_directive_prefix_bytes(bytes: &[u8]) -> bool {
1438+
bytes.starts_with(b"v-")
1439+
}
1440+
14331441
/// Identifiers can contain letters, numbers and `_`
14341442
fn is_at_continue_identifier(byte: u8) -> bool {
14351443
byte.is_ascii_alphanumeric() || byte == b'_'
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<template>
22
<AvatarPrimitive.Fallback
3-
bind:ref
43
class="something nice"
54
/>
65
</template>

crates/biome_html_parser/tests/html_specs/ok/vue/component_with_attributes.vue.snap

Lines changed: 32 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ expression: snapshot
88
```vue
99
<template>
1010
<AvatarPrimitive.Fallback
11-
bind:ref
1211
class="something nice"
1312
/>
1413
</template>
@@ -48,101 +47,74 @@ HtmlRoot {
4847
attributes: HtmlAttributeList [
4948
HtmlAttribute {
5049
name: HtmlAttributeName {
51-
value_token: HTML_LITERAL@38..47 "bind" [Newline("\n"), Whitespace(" ")] [],
52-
},
53-
initializer: missing (optional),
54-
},
55-
VueVBindShorthandDirective {
56-
arg: VueDirectiveArgument {
57-
colon_token: COLON@47..48 ":" [] [],
58-
arg: VueStaticArgument {
59-
name_token: HTML_LITERAL@48..51 "ref" [] [],
60-
},
61-
},
62-
modifiers: VueModifierList [],
63-
initializer: missing (optional),
64-
},
65-
HtmlAttribute {
66-
name: HtmlAttributeName {
67-
value_token: HTML_LITERAL@51..61 "class" [Newline("\n"), Whitespace(" ")] [],
50+
value_token: HTML_LITERAL@38..48 "class" [Newline("\n"), Whitespace(" ")] [],
6851
},
6952
initializer: HtmlAttributeInitializerClause {
70-
eq_token: EQ@61..62 "=" [] [],
53+
eq_token: EQ@48..49 "=" [] [],
7154
value: HtmlString {
72-
value_token: HTML_STRING_LITERAL@62..78 "\"something nice\"" [] [],
55+
value_token: HTML_STRING_LITERAL@49..65 "\"something nice\"" [] [],
7356
},
7457
},
7558
},
7659
],
77-
slash_token: SLASH@78..82 "/" [Newline("\n"), Whitespace(" ")] [],
78-
r_angle_token: R_ANGLE@82..83 ">" [] [],
60+
slash_token: SLASH@65..69 "/" [Newline("\n"), Whitespace(" ")] [],
61+
r_angle_token: R_ANGLE@69..70 ">" [] [],
7962
},
8063
],
8164
closing_element: HtmlClosingElement {
82-
l_angle_token: L_ANGLE@83..85 "<" [Newline("\n")] [],
83-
slash_token: SLASH@85..86 "/" [] [],
65+
l_angle_token: L_ANGLE@70..72 "<" [Newline("\n")] [],
66+
slash_token: SLASH@72..73 "/" [] [],
8467
name: HtmlTagName {
85-
value_token: HTML_LITERAL@86..94 "template" [] [],
68+
value_token: HTML_LITERAL@73..81 "template" [] [],
8669
},
87-
r_angle_token: R_ANGLE@94..95 ">" [] [],
70+
r_angle_token: R_ANGLE@81..82 ">" [] [],
8871
},
8972
},
9073
],
91-
eof_token: EOF@95..96 "" [Newline("\n")] [],
74+
eof_token: EOF@82..83 "" [Newline("\n")] [],
9275
}
9376
```
9477

9578
## CST
9679

9780
```
98-
0: HTML_ROOT@0..96
81+
0: HTML_ROOT@0..83
9982
0: (empty)
10083
1: (empty)
10184
2: (empty)
102-
3: HTML_ELEMENT_LIST@0..95
103-
0: HTML_ELEMENT@0..95
85+
3: HTML_ELEMENT_LIST@0..82
86+
0: HTML_ELEMENT@0..82
10487
10588
0: [email protected] "<" [] []
10689
10790
0: [email protected] "template" [] []
10891
10992
3: [email protected] ">" [] []
110-
1: HTML_ELEMENT_LIST@10..83
111-
0: HTML_SELF_CLOSING_ELEMENT@10..83
93+
1: HTML_ELEMENT_LIST@10..70
94+
0: HTML_SELF_CLOSING_ELEMENT@10..70
11295
0: [email protected] "<" [Newline("\n"), Whitespace(" ")] []
11396
11497
11598
0: [email protected] "AvatarPrimitive" [] []
11699
1: [email protected] "." [] []
117100
118101
0: [email protected] "Fallback" [] []
119-
120-
121-
122-
0: [email protected] "bind" [Newline("\n"), Whitespace(" ")] []
123-
1: (empty)
124-
125-
126-
0: [email protected] ":" [] []
127-
128-
0: [email protected] "ref" [] []
129-
130-
2: (empty)
131-
132-
133-
0: [email protected] "class" [Newline("\n"), Whitespace(" ")] []
134-
135-
0: [email protected] "=" [] []
136-
137-
0: [email protected] "\"something nice\"" [] []
138-
3: [email protected] "/" [Newline("\n"), Whitespace(" ")] []
139-
4: [email protected] ">" [] []
140-
141-
0: [email protected] "<" [Newline("\n")] []
142-
1: [email protected] "/" [] []
143-
144-
0: [email protected] "template" [] []
145-
3: [email protected] ">" [] []
146-
4: [email protected] "" [Newline("\n")] []
102+
103+
104+
105+
0: [email protected] "class" [Newline("\n"), Whitespace(" ")] []
106+
107+
0: [email protected] "=" [] []
108+
109+
0: [email protected] "\"something nice\"" [] []
110+
3: [email protected] "/" [Newline("\n"), Whitespace(" ")] []
111+
4: [email protected] ">" [] []
112+
113+
0: [email protected] "<" [Newline("\n")] []
114+
1: [email protected] "/" [] []
115+
116+
0: [email protected] "template" [] []
117+
3: [email protected] ">" [] []
118+
4: [email protected] "" [Newline("\n")] []
147119
148120
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<svg width="200" height="150"
2+
xmlns="http://www.w3.org/2000/svg"
3+
xmlns:xlink="http://www.w3.org/1999/xlink">
4+
5+
<defs>
6+
<circle id="myCircle" cx="0" cy="0" r="30" fill="orange" />
7+
</defs>
8+
9+
<!-- xlink:href should be parsed as a single attribute -->
10+
<use xlink:href="#myCircle" x="50" y="50" />
11+
<use xlink:href="#myCircle" x="150" y="100" fill="steelblue" />
12+
13+
</svg>

0 commit comments

Comments
 (0)