Skip to content

Commit 85f81f9

Browse files
authored
fix(parse/html/vue): parse dynamic slot directives that contain quotes (#8856)
1 parent ac3a71f commit 85f81f9

6 files changed

Lines changed: 463 additions & 1 deletion

File tree

.changeset/fair-ligers-smell.md

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 [#8710](https://github.com/biomejs/biome/issues/8710): Biome now parses Vue dynamic slot shorthand arguments that use template literals in `[]`.

crates/biome_html_parser/src/lexer/mod.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,42 @@ impl<'src> HtmlLexer<'src> {
155155
}
156156
}
157157

158+
/// Consume a token in the [HtmlLexContext::VueDirectiveArgument] context.
159+
fn consume_token_vue_directive_argument(&mut self) -> HtmlSyntaxKind {
160+
let start = self.text_position();
161+
let mut brackets_stack = 0;
162+
let mut quotes_seen = QuotesSeen::new();
163+
164+
while let Some(byte) = self.current_byte() {
165+
quotes_seen.check_byte(byte);
166+
let char = biome_unicode_table::lookup_byte(byte);
167+
use biome_unicode_table::Dispatch::*;
168+
169+
if quotes_seen.is_empty() {
170+
match char {
171+
BTO => {
172+
brackets_stack += 1;
173+
}
174+
BTC => {
175+
if brackets_stack == 0 {
176+
break;
177+
}
178+
brackets_stack -= 1;
179+
}
180+
_ => {}
181+
}
182+
}
183+
184+
self.advance_byte_or_char(byte);
185+
}
186+
187+
if self.text_position() != start {
188+
HTML_LITERAL
189+
} else {
190+
ERROR_TOKEN
191+
}
192+
}
193+
158194
/// Consume a token in the [HtmlLexContext::Regular] context.
159195
fn consume_token(&mut self, current: u8) -> HtmlSyntaxKind {
160196
match current {
@@ -1078,6 +1114,9 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> {
10781114
HtmlLexContext::Regular => self.consume_token(current),
10791115
HtmlLexContext::InsideTag => self.consume_token_inside_tag(current),
10801116
HtmlLexContext::InsideTagVue => self.consume_token_inside_tag_vue(current),
1117+
HtmlLexContext::VueDirectiveArgument => {
1118+
self.consume_token_vue_directive_argument()
1119+
}
10811120
HtmlLexContext::AttributeValue => self.consume_token_attribute_value(current),
10821121
HtmlLexContext::Doctype => self.consume_token_doctype(current),
10831122
HtmlLexContext::EmbeddedLanguage(lang) => {

crates/biome_html_parser/src/syntax/vue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ fn parse_vue_dynamic_argument(p: &mut HtmlParser) -> ParsedSyntax {
161161

162162
let m = p.start();
163163

164-
p.bump_with_context(T!['['], HtmlLexContext::InsideTagVue);
164+
p.bump_with_context(T!['['], HtmlLexContext::VueDirectiveArgument);
165165
p.expect_with_context(HTML_LITERAL, HtmlLexContext::InsideTagVue);
166166
p.expect_with_context(T![']'], HtmlLexContext::InsideTagVue);
167167

crates/biome_html_parser/src/token_source.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub(crate) enum HtmlLexContext {
2929
InsideTag,
3030
/// Like [InsideTag], but with Vue-specific tokens enabled.
3131
InsideTagVue,
32+
/// Lexes Vue directive arguments inside `[]`.
33+
VueDirectiveArgument,
3234
/// When the parser encounters a `=` token (the beginning of the attribute initializer clause), it switches to this context.
3335
///
3436
/// This is because attribute values can start and end with a `"` or `'` character, or be unquoted, and the lexer needs to know to start lexing a string literal.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template
2+
v-for="episode in episodes"
3+
#[`${episode.id}-cell`]="{ row }"
4+
:key="episode.id"
5+
>
6+
...
7+
</template>
8+
9+
<template
10+
v-for="episode in episodes"
11+
v-slot:[`${episode.id}-cell`]="{ row }"
12+
:key="episode.id"
13+
>
14+
...
15+
</template>
16+
17+
<template
18+
v-for="episode in episodes"
19+
#[episode.id]="{ row }"
20+
:key="episode.id"
21+
>
22+
...
23+
</template>

0 commit comments

Comments
 (0)