diff --git a/.changeset/glimmer-template-support.md b/.changeset/glimmer-template-support.md
new file mode 100644
index 000000000000..d8a8d8214bfc
--- /dev/null
+++ b/.changeset/glimmer-template-support.md
@@ -0,0 +1,20 @@
+---
+"@biomejs/biome": minor
+---
+
+Added support for Glimmer template files (`.gjs` and `.gts`). Biome can now parse, format, and lint Glimmer Component files used in Ember.js applications.
+
+Glimmer templates are recognized using the `...` syntax and can appear in:
+- Variable assignments: `const Tpl = ...;`
+- Class bodies: `class C { ... }`
+- Expression contexts
+- Single unassigned templates (treated as default exports)
+
+**Phase 1 Implementation Notes:**
+- Template content is treated as **opaque tokens** - the content is preserved exactly as written without internal parsing or linting
+- The template syntax itself is validated (e.g., checking for unclosed tags)
+- Templates work with whitespace in the opening tag (e.g., ``, ``)
+- LSP language IDs "gjs" and "gts" are now recognized
+- Future phases will add internal template parsing and linting support
+
+The template content is preserved as-is during formatting, and the parser provides diagnostics for unclosed template tags.
diff --git a/crates/biome_grit_patterns/src/grit_js_parser.rs b/crates/biome_grit_patterns/src/grit_js_parser.rs
index dfe97eabf627..1689ee5efe98 100644
--- a/crates/biome_grit_patterns/src/grit_js_parser.rs
+++ b/crates/biome_grit_patterns/src/grit_js_parser.rs
@@ -2,7 +2,7 @@ use crate::{
grit_analysis_ext::GritAnalysisExt, grit_target_language::GritTargetParser,
grit_tree::GritTargetTree,
};
-use biome_js_parser::{JsParserOptions, parse};
+use biome_js_parser::{JsParserOptions, parse, parse_with_options};
use biome_js_syntax::{JsFileSource, JsLanguage};
use biome_parser::AnyParse;
use camino::Utf8Path;
@@ -34,7 +34,7 @@ impl GritTargetParser for GritJsParser {
_ => JsFileSource::ts(),
};
- parse(source, source_type, JsParserOptions::default()).into()
+ parse(source, source_type).into()
}
}
@@ -48,7 +48,7 @@ impl Parser for GritJsParser {
logs: &mut AnalysisLogs,
_old_tree: FileOrigin<'_, GritTargetTree>,
) -> Option {
- let parse_result = parse(
+ let parse_result = parse_with_options(
body,
JsFileSource::tsx(),
JsParserOptions::default().with_metavariables(),
@@ -75,7 +75,7 @@ impl Parser for GritJsParser {
|src: &str| src.len() as u32
};
- let parse_result = parse(
+ let parse_result = parse_with_options(
&context,
JsFileSource::tsx(),
JsParserOptions::default().with_metavariables(),
diff --git a/crates/biome_grit_patterns/src/grit_target_language/js_target_language/generated_mappings.rs b/crates/biome_grit_patterns/src/grit_target_language/js_target_language/generated_mappings.rs
index 916e8ec2808a..9622e07b65f6 100644
--- a/crates/biome_grit_patterns/src/grit_target_language/js_target_language/generated_mappings.rs
+++ b/crates/biome_grit_patterns/src/grit_target_language/js_target_language/generated_mappings.rs
@@ -298,6 +298,7 @@ pub fn kind_by_name(node_name: &str) -> Option {
"JsFunctionExpression" => lang::JsFunctionExpression::KIND_SET.iter().next(),
"JsGetterClassMember" => lang::JsGetterClassMember::KIND_SET.iter().next(),
"JsGetterObjectMember" => lang::JsGetterObjectMember::KIND_SET.iter().next(),
+ "JsGlimmerTemplate" => lang::JsGlimmerTemplate::KIND_SET.iter().next(),
"JsIdentifierAssignment" => lang::JsIdentifierAssignment::KIND_SET.iter().next(),
"JsIdentifierBinding" => lang::JsIdentifierBinding::KIND_SET.iter().next(),
"JsIdentifierExpression" => lang::JsIdentifierExpression::KIND_SET.iter().next(),
diff --git a/crates/biome_html_factory/src/generated/node_factory.rs b/crates/biome_html_factory/src/generated/node_factory.rs
index a0c6018a025a..e05dbe374ba4 100644
--- a/crates/biome_html_factory/src/generated/node_factory.rs
+++ b/crates/biome_html_factory/src/generated/node_factory.rs
@@ -40,6 +40,297 @@ pub fn astro_frontmatter_element(
],
))
}
+pub fn glimmer_block_helper(
+ opening: GlimmerBlockHelperOpening,
+ children: HtmlElementList,
+ closing: GlimmerBlockHelperClosing,
+) -> GlimmerBlockHelper {
+ GlimmerBlockHelper::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_HELPER,
+ [
+ Some(SyntaxElement::Node(opening.into_syntax())),
+ Some(SyntaxElement::Node(children.into_syntax())),
+ Some(SyntaxElement::Node(closing.into_syntax())),
+ ],
+ ))
+}
+pub fn glimmer_block_helper_closing(
+ l_curly2_token_token: SyntaxToken,
+ slash_token_token: SyntaxToken,
+ helper: GlimmerPath,
+ r_curly2_token_token: SyntaxToken,
+) -> GlimmerBlockHelperClosing {
+ GlimmerBlockHelperClosing::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_HELPER_CLOSING,
+ [
+ Some(SyntaxElement::Token(l_curly2_token_token)),
+ Some(SyntaxElement::Token(slash_token_token)),
+ Some(SyntaxElement::Node(helper.into_syntax())),
+ Some(SyntaxElement::Token(r_curly2_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_block_helper_opening(
+ l_curly2_token_token: SyntaxToken,
+ hash_token_token: SyntaxToken,
+ helper: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_curly2_token_token: SyntaxToken,
+) -> GlimmerBlockHelperOpeningBuilder {
+ GlimmerBlockHelperOpeningBuilder {
+ l_curly2_token_token,
+ hash_token_token,
+ helper,
+ arguments,
+ r_curly2_token_token,
+ block_params: None,
+ }
+}
+pub struct GlimmerBlockHelperOpeningBuilder {
+ l_curly2_token_token: SyntaxToken,
+ hash_token_token: SyntaxToken,
+ helper: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_curly2_token_token: SyntaxToken,
+ block_params: Option,
+}
+impl GlimmerBlockHelperOpeningBuilder {
+ pub fn with_block_params(mut self, block_params: GlimmerBlockParams) -> Self {
+ self.block_params = Some(block_params);
+ self
+ }
+ pub fn build(self) -> GlimmerBlockHelperOpening {
+ GlimmerBlockHelperOpening::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_HELPER_OPENING,
+ [
+ Some(SyntaxElement::Token(self.l_curly2_token_token)),
+ Some(SyntaxElement::Token(self.hash_token_token)),
+ Some(SyntaxElement::Node(self.helper.into_syntax())),
+ Some(SyntaxElement::Node(self.arguments.into_syntax())),
+ self.block_params
+ .map(|token| SyntaxElement::Node(token.into_syntax())),
+ Some(SyntaxElement::Token(self.r_curly2_token_token)),
+ ],
+ ))
+ }
+}
+pub fn glimmer_block_param(name_token_token: SyntaxToken) -> GlimmerBlockParam {
+ GlimmerBlockParam::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_PARAM,
+ [Some(SyntaxElement::Token(name_token_token))],
+ ))
+}
+pub fn glimmer_block_params(
+ as_token_token: SyntaxToken,
+ l_pipe_token_token: SyntaxToken,
+ params: GlimmerBlockParamList,
+ r_pipe_token_token: SyntaxToken,
+) -> GlimmerBlockParams {
+ GlimmerBlockParams::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_PARAMS,
+ [
+ Some(SyntaxElement::Token(as_token_token)),
+ Some(SyntaxElement::Token(l_pipe_token_token)),
+ Some(SyntaxElement::Node(params.into_syntax())),
+ Some(SyntaxElement::Token(r_pipe_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_element_modifier(
+ l_curly2_token_token: SyntaxToken,
+ path: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_curly2_token_token: SyntaxToken,
+) -> GlimmerElementModifier {
+ GlimmerElementModifier::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_ELEMENT_MODIFIER,
+ [
+ Some(SyntaxElement::Token(l_curly2_token_token)),
+ Some(SyntaxElement::Node(path.into_syntax())),
+ Some(SyntaxElement::Node(arguments.into_syntax())),
+ Some(SyntaxElement::Token(r_curly2_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_literal(value_token_token: SyntaxToken) -> GlimmerLiteral {
+ GlimmerLiteral::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_LITERAL,
+ [Some(SyntaxElement::Token(value_token_token))],
+ ))
+}
+pub fn glimmer_mustache_comment(comment_token_token: SyntaxToken) -> GlimmerMustacheComment {
+ GlimmerMustacheComment::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_MUSTACHE_COMMENT,
+ [Some(SyntaxElement::Token(comment_token_token))],
+ ))
+}
+pub fn glimmer_mustache_expression(
+ l_curly2_token_token: SyntaxToken,
+ path: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_curly2_token_token: SyntaxToken,
+) -> GlimmerMustacheExpression {
+ GlimmerMustacheExpression::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_MUSTACHE_EXPRESSION,
+ [
+ Some(SyntaxElement::Token(l_curly2_token_token)),
+ Some(SyntaxElement::Node(path.into_syntax())),
+ Some(SyntaxElement::Node(arguments.into_syntax())),
+ Some(SyntaxElement::Token(r_curly2_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_named_argument(
+ name_token_token: SyntaxToken,
+ eq_token_token: SyntaxToken,
+ value: AnyGlimmerArgumentValue,
+) -> GlimmerNamedArgument {
+ GlimmerNamedArgument::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_NAMED_ARGUMENT,
+ [
+ Some(SyntaxElement::Token(name_token_token)),
+ Some(SyntaxElement::Token(eq_token_token)),
+ Some(SyntaxElement::Node(value.into_syntax())),
+ ],
+ ))
+}
+pub fn glimmer_named_block(
+ opening: GlimmerNamedBlockOpening,
+ children: HtmlElementList,
+ closing: GlimmerNamedBlockClosing,
+) -> GlimmerNamedBlock {
+ GlimmerNamedBlock::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_NAMED_BLOCK,
+ [
+ Some(SyntaxElement::Node(opening.into_syntax())),
+ Some(SyntaxElement::Node(children.into_syntax())),
+ Some(SyntaxElement::Node(closing.into_syntax())),
+ ],
+ ))
+}
+pub fn glimmer_named_block_closing(
+ l_angle_token_token: SyntaxToken,
+ slash_token_token: SyntaxToken,
+ colon_token_token: SyntaxToken,
+ name_token_token: SyntaxToken,
+ r_angle_token_token: SyntaxToken,
+) -> GlimmerNamedBlockClosing {
+ GlimmerNamedBlockClosing::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_NAMED_BLOCK_CLOSING,
+ [
+ Some(SyntaxElement::Token(l_angle_token_token)),
+ Some(SyntaxElement::Token(slash_token_token)),
+ Some(SyntaxElement::Token(colon_token_token)),
+ Some(SyntaxElement::Token(name_token_token)),
+ Some(SyntaxElement::Token(r_angle_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_named_block_opening(
+ l_angle_token_token: SyntaxToken,
+ colon_token_token: SyntaxToken,
+ name_token_token: SyntaxToken,
+ attributes: HtmlAttributeList,
+ r_angle_token_token: SyntaxToken,
+) -> GlimmerNamedBlockOpening {
+ GlimmerNamedBlockOpening::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_NAMED_BLOCK_OPENING,
+ [
+ Some(SyntaxElement::Token(l_angle_token_token)),
+ Some(SyntaxElement::Token(colon_token_token)),
+ Some(SyntaxElement::Token(name_token_token)),
+ Some(SyntaxElement::Node(attributes.into_syntax())),
+ Some(SyntaxElement::Token(r_angle_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_path(segments: GlimmerPathSegmentList) -> GlimmerPathBuilder {
+ GlimmerPathBuilder {
+ segments,
+ at_token_token: None,
+ }
+}
+pub struct GlimmerPathBuilder {
+ segments: GlimmerPathSegmentList,
+ at_token_token: Option,
+}
+impl GlimmerPathBuilder {
+ pub fn with_at_token_token(mut self, at_token_token: SyntaxToken) -> Self {
+ self.at_token_token = Some(at_token_token);
+ self
+ }
+ pub fn build(self) -> GlimmerPath {
+ GlimmerPath::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_PATH,
+ [
+ self.at_token_token.map(|token| SyntaxElement::Token(token)),
+ Some(SyntaxElement::Node(self.segments.into_syntax())),
+ ],
+ ))
+ }
+}
+pub fn glimmer_path_segment(value_token_token: SyntaxToken) -> GlimmerPathSegment {
+ GlimmerPathSegment::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_PATH_SEGMENT,
+ [Some(SyntaxElement::Token(value_token_token))],
+ ))
+}
+pub fn glimmer_positional_argument(value: AnyGlimmerArgumentValue) -> GlimmerPositionalArgument {
+ GlimmerPositionalArgument::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_POSITIONAL_ARGUMENT,
+ [Some(SyntaxElement::Node(value.into_syntax()))],
+ ))
+}
+pub fn glimmer_splattribute(
+ dotdotdot_token_token: SyntaxToken,
+ attributes_token_token: SyntaxToken,
+) -> GlimmerSplattribute {
+ GlimmerSplattribute::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_SPLATTRIBUTE,
+ [
+ Some(SyntaxElement::Token(dotdotdot_token_token)),
+ Some(SyntaxElement::Token(attributes_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_string_literal(value_token_token: SyntaxToken) -> GlimmerStringLiteral {
+ GlimmerStringLiteral::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_STRING_LITERAL,
+ [Some(SyntaxElement::Token(value_token_token))],
+ ))
+}
+pub fn glimmer_subexpression(
+ l_paren_token_token: SyntaxToken,
+ path: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_paren_token_token: SyntaxToken,
+) -> GlimmerSubexpression {
+ GlimmerSubexpression::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_SUBEXPRESSION,
+ [
+ Some(SyntaxElement::Token(l_paren_token_token)),
+ Some(SyntaxElement::Node(path.into_syntax())),
+ Some(SyntaxElement::Node(arguments.into_syntax())),
+ Some(SyntaxElement::Token(r_paren_token_token)),
+ ],
+ ))
+}
+pub fn glimmer_triple_stash_expression(
+ l_curly3_token_token: SyntaxToken,
+ path: GlimmerPath,
+ arguments: GlimmerArgumentList,
+ r_curly3_token_token: SyntaxToken,
+) -> GlimmerTripleStashExpression {
+ GlimmerTripleStashExpression::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_TRIPLE_STASH_EXPRESSION,
+ [
+ Some(SyntaxElement::Token(l_curly3_token_token)),
+ Some(SyntaxElement::Node(path.into_syntax())),
+ Some(SyntaxElement::Node(arguments.into_syntax())),
+ Some(SyntaxElement::Token(r_curly3_token_token)),
+ ],
+ ))
+}
pub fn html_attribute(name: HtmlAttributeName) -> HtmlAttributeBuilder {
HtmlAttributeBuilder {
name,
@@ -369,6 +660,51 @@ pub fn svelte_name(svelte_ident_token: SyntaxToken) -> SvelteName {
[Some(SyntaxElement::Token(svelte_ident_token))],
))
}
+pub fn glimmer_argument_list(items: I) -> GlimmerArgumentList
+where
+ I: IntoIterator- ,
+ I::IntoIter: ExactSizeIterator,
+{
+ GlimmerArgumentList::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_ARGUMENT_LIST,
+ items
+ .into_iter()
+ .map(|item| Some(item.into_syntax().into())),
+ ))
+}
+pub fn glimmer_block_param_list(items: I) -> GlimmerBlockParamList
+where
+ I: IntoIterator
- ,
+ I::IntoIter: ExactSizeIterator,
+{
+ GlimmerBlockParamList::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BLOCK_PARAM_LIST,
+ items
+ .into_iter()
+ .map(|item| Some(item.into_syntax().into())),
+ ))
+}
+pub fn glimmer_path_segment_list(items: I, separators: S) -> GlimmerPathSegmentList
+where
+ I: IntoIterator
- ,
+ I::IntoIter: ExactSizeIterator,
+ S: IntoIterator
- ,
+ S::IntoIter: ExactSizeIterator,
+{
+ let mut items = items.into_iter();
+ let mut separators = separators.into_iter();
+ let length = items.len() + separators.len();
+ GlimmerPathSegmentList::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_PATH_SEGMENT_LIST,
+ (0..length).map(|index| {
+ if index % 2 == 0 {
+ Some(items.next()?.into_syntax().into())
+ } else {
+ Some(separators.next()?.into())
+ }
+ }),
+ ))
+}
pub fn html_attribute_list(items: I) -> HtmlAttributeList
where
I: IntoIterator
- ,
@@ -424,6 +760,16 @@ where
slots,
))
}
+pub fn glimmer_bogus_expression(slots: I) -> GlimmerBogusExpression
+where
+ I: IntoIterator
- >,
+ I::IntoIter: ExactSizeIterator,
+{
+ GlimmerBogusExpression::unwrap_cast(SyntaxNode::new_detached(
+ HtmlSyntaxKind::GLIMMER_BOGUS_EXPRESSION,
+ slots,
+ ))
+}
pub fn html_bogus(slots: I) -> HtmlBogus
where
I: IntoIterator
- >,
diff --git a/crates/biome_html_factory/src/generated/syntax_factory.rs b/crates/biome_html_factory/src/generated/syntax_factory.rs
index 6527c0d76455..5e6c2250c2d6 100644
--- a/crates/biome_html_factory/src/generated/syntax_factory.rs
+++ b/crates/biome_html_factory/src/generated/syntax_factory.rs
@@ -15,6 +15,7 @@ impl SyntaxFactory for HtmlSyntaxFactory {
) -> RawSyntaxNode {
match kind {
ASTRO_BOGUS_FRONTMATTER
+ | GLIMMER_BOGUS_EXPRESSION
| HTML_BOGUS
| HTML_BOGUS_ATTRIBUTE
| HTML_BOGUS_ELEMENT
@@ -72,6 +73,659 @@ impl SyntaxFactory for HtmlSyntaxFactory {
}
slots.into_node(ASTRO_FRONTMATTER_ELEMENT, children)
}
+ GLIMMER_BLOCK_HELPER => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && GlimmerBlockHelperOpening::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && HtmlElementList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerBlockHelperClosing::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_BLOCK_HELPER.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_BLOCK_HELPER, children)
+ }
+ GLIMMER_BLOCK_HELPER_CLOSING => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["{{"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [/]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["}}"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_BLOCK_HELPER_CLOSING.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_BLOCK_HELPER_CLOSING, children)
+ }
+ GLIMMER_BLOCK_HELPER_OPENING => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<6usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["{{"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [#]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerArgumentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerBlockParams::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["}}"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_BLOCK_HELPER_OPENING.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_BLOCK_HELPER_OPENING, children)
+ }
+ GLIMMER_BLOCK_PARAM => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_BLOCK_PARAM.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_BLOCK_PARAM, children)
+ }
+ GLIMMER_BLOCK_PARAMS => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [|]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerBlockParamList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [|]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_BLOCK_PARAMS.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_BLOCK_PARAMS, children)
+ }
+ GLIMMER_ELEMENT_MODIFIER => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["{{"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerArgumentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["}}"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_ELEMENT_MODIFIER.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_ELEMENT_MODIFIER, children)
+ }
+ GLIMMER_LITERAL => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == HTML_LITERAL
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_LITERAL.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_LITERAL, children)
+ }
+ GLIMMER_MUSTACHE_COMMENT => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == MUSTACHE_COMMENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_MUSTACHE_COMMENT.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_MUSTACHE_COMMENT, children)
+ }
+ GLIMMER_MUSTACHE_EXPRESSION => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["{{"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerArgumentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!["}}"]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_MUSTACHE_EXPRESSION.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_MUSTACHE_EXPRESSION, children)
+ }
+ GLIMMER_NAMED_ARGUMENT => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [=]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && AnyGlimmerArgumentValue::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_NAMED_ARGUMENT.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_NAMED_ARGUMENT, children)
+ }
+ GLIMMER_NAMED_BLOCK => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && GlimmerNamedBlockOpening::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && HtmlElementList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerNamedBlockClosing::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_NAMED_BLOCK.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_NAMED_BLOCK, children)
+ }
+ GLIMMER_NAMED_BLOCK_CLOSING => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<5usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [<]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [/]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [:]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [>]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_NAMED_BLOCK_CLOSING.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_NAMED_BLOCK_CLOSING, children)
+ }
+ GLIMMER_NAMED_BLOCK_OPENING => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<5usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [<]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [:]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && HtmlAttributeList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [>]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_NAMED_BLOCK_OPENING.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_NAMED_BLOCK_OPENING, children)
+ }
+ GLIMMER_PATH => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [@]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPathSegmentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_PATH.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_PATH, children)
+ }
+ GLIMMER_PATH_SEGMENT => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_PATH_SEGMENT.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_PATH_SEGMENT, children)
+ }
+ GLIMMER_POSITIONAL_ARGUMENT => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && AnyGlimmerArgumentValue::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_POSITIONAL_ARGUMENT.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_POSITIONAL_ARGUMENT, children)
+ }
+ GLIMMER_SPLATTRIBUTE => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T ! [...]
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == IDENT
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_SPLATTRIBUTE.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_SPLATTRIBUTE, children)
+ }
+ GLIMMER_STRING_LITERAL => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == HTML_STRING_LITERAL
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_STRING_LITERAL.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_STRING_LITERAL, children)
+ }
+ GLIMMER_SUBEXPRESSION => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == T!['(']
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerArgumentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == T![')']
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_SUBEXPRESSION.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_SUBEXPRESSION, children)
+ }
+ GLIMMER_TRIPLE_STASH_EXPRESSION => {
+ let mut elements = (&children).into_iter();
+ let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default();
+ let mut current_element = elements.next();
+ if let Some(element) = ¤t_element
+ && element.kind() == L_TRIPLE_CURLY
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerPath::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && GlimmerArgumentList::can_cast(element.kind())
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if let Some(element) = ¤t_element
+ && element.kind() == R_TRIPLE_CURLY
+ {
+ slots.mark_present();
+ current_element = elements.next();
+ }
+ slots.next_slot();
+ if current_element.is_some() {
+ return RawSyntaxNode::new(
+ GLIMMER_TRIPLE_STASH_EXPRESSION.to_bogus(),
+ children.into_iter().map(Some),
+ );
+ }
+ slots.into_node(GLIMMER_TRIPLE_STASH_EXPRESSION, children)
+ }
HTML_ATTRIBUTE => {
let mut elements = (&children).into_iter();
let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default();
@@ -671,6 +1325,19 @@ impl SyntaxFactory for HtmlSyntaxFactory {
}
slots.into_node(SVELTE_NAME, children)
}
+ GLIMMER_ARGUMENT_LIST => {
+ Self::make_node_list_syntax(kind, children, AnyGlimmerArgument::can_cast)
+ }
+ GLIMMER_BLOCK_PARAM_LIST => {
+ Self::make_node_list_syntax(kind, children, GlimmerBlockParam::can_cast)
+ }
+ GLIMMER_PATH_SEGMENT_LIST => Self::make_separated_list_syntax(
+ kind,
+ children,
+ GlimmerPathSegment::can_cast,
+ T ! [.],
+ false,
+ ),
HTML_ATTRIBUTE_LIST => {
Self::make_node_list_syntax(kind, children, AnyHtmlAttribute::can_cast)
}
diff --git a/crates/biome_html_formatter/src/generated.rs b/crates/biome_html_formatter/src/generated.rs
index 870a562cfa3b..1d11b38b6273 100644
--- a/crates/biome_html_formatter/src/generated.rs
+++ b/crates/biome_html_formatter/src/generated.rs
@@ -82,6 +82,758 @@ impl IntoFormat for biome_html_syntax::AstroFrontmatterElemen
)
}
}
+impl FormatRule
+ for crate::js::auxiliary::block_helper::FormatGlimmerBlockHelper
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBlockHelper,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockHelper {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockHelper,
+ crate::js::auxiliary::block_helper::FormatGlimmerBlockHelper,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper::FormatGlimmerBlockHelper::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockHelper {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockHelper,
+ crate::js::auxiliary::block_helper::FormatGlimmerBlockHelper,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper::FormatGlimmerBlockHelper::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::block_helper_closing::FormatGlimmerBlockHelperClosing
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBlockHelperClosing,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockHelperClosing {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockHelperClosing,
+ crate::js::auxiliary::block_helper_closing::FormatGlimmerBlockHelperClosing,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper_closing::FormatGlimmerBlockHelperClosing::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockHelperClosing {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockHelperClosing,
+ crate::js::auxiliary::block_helper_closing::FormatGlimmerBlockHelperClosing,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper_closing::FormatGlimmerBlockHelperClosing::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::block_helper_opening::FormatGlimmerBlockHelperOpening
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBlockHelperOpening,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockHelperOpening {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockHelperOpening,
+ crate::js::auxiliary::block_helper_opening::FormatGlimmerBlockHelperOpening,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper_opening::FormatGlimmerBlockHelperOpening::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockHelperOpening {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockHelperOpening,
+ crate::js::auxiliary::block_helper_opening::FormatGlimmerBlockHelperOpening,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::block_helper_opening::FormatGlimmerBlockHelperOpening::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::block_param::FormatGlimmerBlockParam
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBlockParam,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockParam {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockParam,
+ crate::js::auxiliary::block_param::FormatGlimmerBlockParam,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::block_param::FormatGlimmerBlockParam::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockParam {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockParam,
+ crate::js::auxiliary::block_param::FormatGlimmerBlockParam,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::block_param::FormatGlimmerBlockParam::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::block_params::FormatGlimmerBlockParams
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBlockParams,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockParams {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockParams,
+ crate::js::auxiliary::block_params::FormatGlimmerBlockParams,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::block_params::FormatGlimmerBlockParams::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockParams {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockParams,
+ crate::js::auxiliary::block_params::FormatGlimmerBlockParams,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::block_params::FormatGlimmerBlockParams::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::element_modifier::FormatGlimmerElementModifier
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerElementModifier,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerElementModifier {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerElementModifier,
+ crate::js::auxiliary::element_modifier::FormatGlimmerElementModifier,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::element_modifier::FormatGlimmerElementModifier::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerElementModifier {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerElementModifier,
+ crate::js::auxiliary::element_modifier::FormatGlimmerElementModifier,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::element_modifier::FormatGlimmerElementModifier::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::literal::FormatGlimmerLiteral
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerLiteral,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerLiteral {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerLiteral,
+ crate::js::auxiliary::literal::FormatGlimmerLiteral,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::literal::FormatGlimmerLiteral::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerLiteral {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerLiteral,
+ crate::js::auxiliary::literal::FormatGlimmerLiteral,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::literal::FormatGlimmerLiteral::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::mustache_comment::FormatGlimmerMustacheComment
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerMustacheComment,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerMustacheComment {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerMustacheComment,
+ crate::js::auxiliary::mustache_comment::FormatGlimmerMustacheComment,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::mustache_comment::FormatGlimmerMustacheComment::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerMustacheComment {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerMustacheComment,
+ crate::js::auxiliary::mustache_comment::FormatGlimmerMustacheComment,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::mustache_comment::FormatGlimmerMustacheComment::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::mustache_expression::FormatGlimmerMustacheExpression
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerMustacheExpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerMustacheExpression {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerMustacheExpression,
+ crate::js::auxiliary::mustache_expression::FormatGlimmerMustacheExpression,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::mustache_expression::FormatGlimmerMustacheExpression::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerMustacheExpression {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerMustacheExpression,
+ crate::js::auxiliary::mustache_expression::FormatGlimmerMustacheExpression,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::mustache_expression::FormatGlimmerMustacheExpression::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::named_argument::FormatGlimmerNamedArgument
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerNamedArgument,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerNamedArgument {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerNamedArgument,
+ crate::js::auxiliary::named_argument::FormatGlimmerNamedArgument,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::named_argument::FormatGlimmerNamedArgument::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerNamedArgument {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerNamedArgument,
+ crate::js::auxiliary::named_argument::FormatGlimmerNamedArgument,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::named_argument::FormatGlimmerNamedArgument::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::named_block::FormatGlimmerNamedBlock
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerNamedBlock,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerNamedBlock {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerNamedBlock,
+ crate::js::auxiliary::named_block::FormatGlimmerNamedBlock,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::named_block::FormatGlimmerNamedBlock::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerNamedBlock {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerNamedBlock,
+ crate::js::auxiliary::named_block::FormatGlimmerNamedBlock,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::named_block::FormatGlimmerNamedBlock::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::named_block_closing::FormatGlimmerNamedBlockClosing
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerNamedBlockClosing,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerNamedBlockClosing {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerNamedBlockClosing,
+ crate::js::auxiliary::named_block_closing::FormatGlimmerNamedBlockClosing,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::named_block_closing::FormatGlimmerNamedBlockClosing::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerNamedBlockClosing {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerNamedBlockClosing,
+ crate::js::auxiliary::named_block_closing::FormatGlimmerNamedBlockClosing,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::named_block_closing::FormatGlimmerNamedBlockClosing::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::named_block_opening::FormatGlimmerNamedBlockOpening
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerNamedBlockOpening,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerNamedBlockOpening {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerNamedBlockOpening,
+ crate::js::auxiliary::named_block_opening::FormatGlimmerNamedBlockOpening,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::named_block_opening::FormatGlimmerNamedBlockOpening::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerNamedBlockOpening {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerNamedBlockOpening,
+ crate::js::auxiliary::named_block_opening::FormatGlimmerNamedBlockOpening,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::named_block_opening::FormatGlimmerNamedBlockOpening::default(),
+ )
+ }
+}
+impl FormatRule for crate::js::auxiliary::path::FormatGlimmerPath {
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerPath,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerPath {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerPath,
+ crate::js::auxiliary::path::FormatGlimmerPath,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::path::FormatGlimmerPath::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerPath {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerPath,
+ crate::js::auxiliary::path::FormatGlimmerPath,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::path::FormatGlimmerPath::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::path_segment::FormatGlimmerPathSegment
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerPathSegment,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerPathSegment {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerPathSegment,
+ crate::js::auxiliary::path_segment::FormatGlimmerPathSegment,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::path_segment::FormatGlimmerPathSegment::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerPathSegment {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerPathSegment,
+ crate::js::auxiliary::path_segment::FormatGlimmerPathSegment,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::path_segment::FormatGlimmerPathSegment::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::positional_argument::FormatGlimmerPositionalArgument
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerPositionalArgument,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerPositionalArgument {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerPositionalArgument,
+ crate::js::auxiliary::positional_argument::FormatGlimmerPositionalArgument,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::positional_argument::FormatGlimmerPositionalArgument::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerPositionalArgument {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerPositionalArgument,
+ crate::js::auxiliary::positional_argument::FormatGlimmerPositionalArgument,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::positional_argument::FormatGlimmerPositionalArgument::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::splattribute::FormatGlimmerSplattribute
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerSplattribute,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerSplattribute {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerSplattribute,
+ crate::js::auxiliary::splattribute::FormatGlimmerSplattribute,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::splattribute::FormatGlimmerSplattribute::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerSplattribute {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerSplattribute,
+ crate::js::auxiliary::splattribute::FormatGlimmerSplattribute,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::splattribute::FormatGlimmerSplattribute::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::string_literal::FormatGlimmerStringLiteral
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerStringLiteral,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerStringLiteral {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerStringLiteral,
+ crate::js::auxiliary::string_literal::FormatGlimmerStringLiteral,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::string_literal::FormatGlimmerStringLiteral::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerStringLiteral {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerStringLiteral,
+ crate::js::auxiliary::string_literal::FormatGlimmerStringLiteral,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::string_literal::FormatGlimmerStringLiteral::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::subexpression::FormatGlimmerSubexpression
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerSubexpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerSubexpression {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerSubexpression,
+ crate::js::auxiliary::subexpression::FormatGlimmerSubexpression,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::auxiliary::subexpression::FormatGlimmerSubexpression::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerSubexpression {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerSubexpression,
+ crate::js::auxiliary::subexpression::FormatGlimmerSubexpression,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::auxiliary::subexpression::FormatGlimmerSubexpression::default(),
+ )
+ }
+}
+impl FormatRule
+ for crate::js::auxiliary::triple_stash_expression::FormatGlimmerTripleStashExpression
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerTripleStashExpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerTripleStashExpression {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerTripleStashExpression,
+ crate::js::auxiliary::triple_stash_expression::FormatGlimmerTripleStashExpression,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule :: new (self , crate :: js :: auxiliary :: triple_stash_expression :: FormatGlimmerTripleStashExpression :: default ())
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerTripleStashExpression {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerTripleStashExpression,
+ crate::js::auxiliary::triple_stash_expression::FormatGlimmerTripleStashExpression,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule :: new (self , crate :: js :: auxiliary :: triple_stash_expression :: FormatGlimmerTripleStashExpression :: default ())
+ }
+}
impl FormatRule
for crate::html::auxiliary::attribute::FormatHtmlAttribute
{
@@ -788,6 +1540,81 @@ impl IntoFormat for biome_html_syntax::SvelteName {
)
}
}
+impl AsFormat for biome_html_syntax::GlimmerArgumentList {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerArgumentList,
+ crate::js::lists::argument_list::FormatGlimmerArgumentList,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::lists::argument_list::FormatGlimmerArgumentList::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerArgumentList {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerArgumentList,
+ crate::js::lists::argument_list::FormatGlimmerArgumentList,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::lists::argument_list::FormatGlimmerArgumentList::default(),
+ )
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBlockParamList {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBlockParamList,
+ crate::js::lists::block_param_list::FormatGlimmerBlockParamList,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::lists::block_param_list::FormatGlimmerBlockParamList::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBlockParamList {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBlockParamList,
+ crate::js::lists::block_param_list::FormatGlimmerBlockParamList,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::lists::block_param_list::FormatGlimmerBlockParamList::default(),
+ )
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerPathSegmentList {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerPathSegmentList,
+ crate::js::lists::path_segment_list::FormatGlimmerPathSegmentList,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::lists::path_segment_list::FormatGlimmerPathSegmentList::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerPathSegmentList {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerPathSegmentList,
+ crate::js::lists::path_segment_list::FormatGlimmerPathSegmentList,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::lists::path_segment_list::FormatGlimmerPathSegmentList::default(),
+ )
+ }
+}
impl AsFormat for biome_html_syntax::HtmlAttributeList {
type Format<'a> = FormatRefWithRule<
'a,
@@ -901,6 +1728,44 @@ impl IntoFormat for biome_html_syntax::AstroBogusFrontmatter
)
}
}
+impl FormatRule
+ for crate::js::bogus::bogus_expression::FormatGlimmerBogusExpression
+{
+ type Context = HtmlFormatContext;
+ #[inline(always)]
+ fn fmt(
+ &self,
+ node: &biome_html_syntax::GlimmerBogusExpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ FormatBogusNodeRule::::fmt(self, node, f)
+ }
+}
+impl AsFormat for biome_html_syntax::GlimmerBogusExpression {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::GlimmerBogusExpression,
+ crate::js::bogus::bogus_expression::FormatGlimmerBogusExpression,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::bogus::bogus_expression::FormatGlimmerBogusExpression::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::GlimmerBogusExpression {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::GlimmerBogusExpression,
+ crate::js::bogus::bogus_expression::FormatGlimmerBogusExpression,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::bogus::bogus_expression::FormatGlimmerBogusExpression::default(),
+ )
+ }
+}
impl FormatRule for crate::html::bogus::bogus::FormatHtmlBogus {
type Context = HtmlFormatContext;
#[inline(always)]
@@ -1104,6 +1969,81 @@ impl IntoFormat for biome_html_syntax::AnyAstroFrontmatterEle
)
}
}
+impl AsFormat for biome_html_syntax::AnyGlimmerArgument {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::AnyGlimmerArgument,
+ crate::js::any::argument::FormatAnyGlimmerArgument,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::any::argument::FormatAnyGlimmerArgument::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::AnyGlimmerArgument {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::AnyGlimmerArgument,
+ crate::js::any::argument::FormatAnyGlimmerArgument,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::any::argument::FormatAnyGlimmerArgument::default(),
+ )
+ }
+}
+impl AsFormat for biome_html_syntax::AnyGlimmerArgumentValue {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::AnyGlimmerArgumentValue,
+ crate::js::any::argument_value::FormatAnyGlimmerArgumentValue,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::any::argument_value::FormatAnyGlimmerArgumentValue::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::AnyGlimmerArgumentValue {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::AnyGlimmerArgumentValue,
+ crate::js::any::argument_value::FormatAnyGlimmerArgumentValue,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::any::argument_value::FormatAnyGlimmerArgumentValue::default(),
+ )
+ }
+}
+impl AsFormat for biome_html_syntax::AnyGlimmerExpression {
+ type Format<'a> = FormatRefWithRule<
+ 'a,
+ biome_html_syntax::AnyGlimmerExpression,
+ crate::js::any::expression::FormatAnyGlimmerExpression,
+ >;
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(
+ self,
+ crate::js::any::expression::FormatAnyGlimmerExpression::default(),
+ )
+ }
+}
+impl IntoFormat for biome_html_syntax::AnyGlimmerExpression {
+ type Format = FormatOwnedWithRule<
+ biome_html_syntax::AnyGlimmerExpression,
+ crate::js::any::expression::FormatAnyGlimmerExpression,
+ >;
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(
+ self,
+ crate::js::any::expression::FormatAnyGlimmerExpression::default(),
+ )
+ }
+}
impl AsFormat for biome_html_syntax::AnyHtmlAttribute {
type Format<'a> = FormatRefWithRule<
'a,
diff --git a/crates/biome_html_formatter/src/glimmer.rs b/crates/biome_html_formatter/src/glimmer.rs
new file mode 100644
index 000000000000..7538c2b6922d
--- /dev/null
+++ b/crates/biome_html_formatter/src/glimmer.rs
@@ -0,0 +1,62 @@
+// Glimmer formatter stubs
+// All Glimmer nodes preserve their original formatting for now
+
+use crate::prelude::*;
+use biome_formatter::write;
+use biome_html_syntax::*;
+use biome_rowan::AstNode;
+
+// Helper macro to create formatter stubs that preserve original formatting
+macro_rules! impl_verbatim_formatter {
+ ($node:ty) => {
+ impl crate::AsFormat for $node {
+ type Format<'a> = FormatRefWithRule<'a, $node, FormatVerbatimNode>;
+
+ fn format(&self) -> Self::Format<'_> {
+ FormatRefWithRule::new(self, FormatVerbatimNode)
+ }
+ }
+
+ impl crate::IntoFormat for $node {
+ type Format = FormatOwnedWithRule<$node, FormatVerbatimNode>;
+
+ fn into_format(self) -> Self::Format {
+ FormatOwnedWithRule::new(self, FormatVerbatimNode)
+ }
+ }
+ };
+}
+
+// Verbatim formatter that preserves original text
+struct FormatVerbatimNode;
+
+impl> biome_formatter::FormatRule for FormatVerbatimNode {
+ type Context = HtmlFormatContext;
+
+ fn fmt(&self, node: &T, f: &mut HtmlFormatter) -> biome_formatter::FormatResult<()> {
+ write!(f, [biome_formatter::format_verbatim_node(node.syntax())])
+ }
+}
+
+// Apply verbatim formatting to all Glimmer nodes
+impl_verbatim_formatter!(GlimmerMustacheExpression);
+impl_verbatim_formatter!(GlimmerMustacheComment);
+impl_verbatim_formatter!(GlimmerTripleStashExpression);
+impl_verbatim_formatter!(GlimmerBlockHelper);
+impl_verbatim_formatter!(GlimmerBlockHelperOpening);
+impl_verbatim_formatter!(GlimmerBlockHelperClosing);
+impl_verbatim_formatter!(GlimmerBlockParams);
+impl_verbatim_formatter!(GlimmerBlockParam);
+impl_verbatim_formatter!(GlimmerPath);
+impl_verbatim_formatter!(GlimmerPathSegment);
+impl_verbatim_formatter!(GlimmerPositionalArgument);
+impl_verbatim_formatter!(GlimmerNamedArgument);
+impl_verbatim_formatter!(GlimmerSubexpression);
+impl_verbatim_formatter!(GlimmerStringLiteral);
+impl_verbatim_formatter!(GlimmerLiteral);
+impl_verbatim_formatter!(GlimmerSplattribute);
+impl_verbatim_formatter!(GlimmerElementModifier);
+impl_verbatim_formatter!(GlimmerNamedBlock);
+impl_verbatim_formatter!(GlimmerNamedBlockOpening);
+impl_verbatim_formatter!(GlimmerNamedBlockClosing);
+impl_verbatim_formatter!(GlimmerBogusExpression);
diff --git a/crates/biome_html_formatter/src/html/any/attribute.rs b/crates/biome_html_formatter/src/html/any/attribute.rs
index b1c6704f75bf..7b07e4484cc3 100644
--- a/crates/biome_html_formatter/src/html/any/attribute.rs
+++ b/crates/biome_html_formatter/src/html/any/attribute.rs
@@ -8,6 +8,8 @@ impl FormatRule for FormatAnyHtmlAttribute {
type Context = HtmlFormatContext;
fn fmt(&self, node: &AnyHtmlAttribute, f: &mut HtmlFormatter) -> FormatResult<()> {
match node {
+ AnyHtmlAttribute::GlimmerElementModifier(node) => node.format().fmt(f),
+ AnyHtmlAttribute::GlimmerSplattribute(node) => node.format().fmt(f),
AnyHtmlAttribute::HtmlAttribute(node) => node.format().fmt(f),
AnyHtmlAttribute::HtmlBogusAttribute(node) => node.format().fmt(f),
AnyHtmlAttribute::HtmlDoubleTextExpression(node) => node.format().fmt(f),
diff --git a/crates/biome_html_formatter/src/html/any/content.rs b/crates/biome_html_formatter/src/html/any/content.rs
index 677a094d1981..7b131670bd3e 100644
--- a/crates/biome_html_formatter/src/html/any/content.rs
+++ b/crates/biome_html_formatter/src/html/any/content.rs
@@ -8,6 +8,7 @@ impl FormatRule for FormatAnyHtmlContent {
type Context = HtmlFormatContext;
fn fmt(&self, node: &AnyHtmlContent, f: &mut HtmlFormatter) -> FormatResult<()> {
match node {
+ AnyHtmlContent::AnyGlimmerExpression(node) => node.format().fmt(f),
AnyHtmlContent::AnyHtmlTextExpression(node) => node.format().fmt(f),
AnyHtmlContent::HtmlContent(node) => node.format().fmt(f),
AnyHtmlContent::HtmlEmbeddedContent(node) => node.format().fmt(f),
diff --git a/crates/biome_html_formatter/src/html/any/element.rs b/crates/biome_html_formatter/src/html/any/element.rs
index c316fd0cd047..802ed7c8ac44 100644
--- a/crates/biome_html_formatter/src/html/any/element.rs
+++ b/crates/biome_html_formatter/src/html/any/element.rs
@@ -13,6 +13,8 @@ impl FormatRule for FormatAnyHtmlElement {
AnyHtmlElement::HtmlCdataSection(node) => node.format().fmt(f),
AnyHtmlElement::HtmlElement(node) => node.format().fmt(f),
AnyHtmlElement::HtmlSelfClosingElement(node) => node.format().fmt(f),
+ AnyHtmlElement::GlimmerMustacheComment(node) => node.format().fmt(f),
+ AnyHtmlElement::GlimmerTripleStashExpression(node) => node.format().fmt(f),
}
}
}
diff --git a/crates/biome_html_formatter/src/html/lists/attribute_list.rs b/crates/biome_html_formatter/src/html/lists/attribute_list.rs
index 26252d23f692..2b6510c395e6 100644
--- a/crates/biome_html_formatter/src/html/lists/attribute_list.rs
+++ b/crates/biome_html_formatter/src/html/lists/attribute_list.rs
@@ -69,6 +69,12 @@ impl FormatRule for FormatHtmlAttributeList {
AnyHtmlAttribute::HtmlBogusAttribute(attr) => {
attr.format().fmt(f)
}
+ AnyHtmlAttribute::GlimmerElementModifier(attr) => {
+ attr.format().fmt(f)
+ }
+ AnyHtmlAttribute::GlimmerSplattribute(attr) => {
+ attr.format().fmt(f)
+ }
})
}))
.finish()?;
diff --git a/crates/biome_html_formatter/src/js/any/argument.rs b/crates/biome_html_formatter/src/js/any/argument.rs
new file mode 100644
index 000000000000..11c4871965ac
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/any/argument.rs
@@ -0,0 +1,15 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+use crate::prelude::*;
+use biome_html_syntax::AnyGlimmerArgument;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatAnyGlimmerArgument;
+impl FormatRule for FormatAnyGlimmerArgument {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &AnyGlimmerArgument, f: &mut HtmlFormatter) -> FormatResult<()> {
+ match node {
+ AnyGlimmerArgument::GlimmerNamedArgument(node) => node.format().fmt(f),
+ AnyGlimmerArgument::GlimmerPositionalArgument(node) => node.format().fmt(f),
+ }
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/any/argument_value.rs b/crates/biome_html_formatter/src/js/any/argument_value.rs
new file mode 100644
index 000000000000..f066ae37812f
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/any/argument_value.rs
@@ -0,0 +1,17 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+use crate::prelude::*;
+use biome_html_syntax::AnyGlimmerArgumentValue;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatAnyGlimmerArgumentValue;
+impl FormatRule for FormatAnyGlimmerArgumentValue {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &AnyGlimmerArgumentValue, f: &mut HtmlFormatter) -> FormatResult<()> {
+ match node {
+ AnyGlimmerArgumentValue::GlimmerLiteral(node) => node.format().fmt(f),
+ AnyGlimmerArgumentValue::GlimmerPath(node) => node.format().fmt(f),
+ AnyGlimmerArgumentValue::GlimmerStringLiteral(node) => node.format().fmt(f),
+ AnyGlimmerArgumentValue::GlimmerSubexpression(node) => node.format().fmt(f),
+ }
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/any/expression.rs b/crates/biome_html_formatter/src/js/any/expression.rs
new file mode 100644
index 000000000000..1c516c01ff96
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/any/expression.rs
@@ -0,0 +1,19 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+use crate::prelude::*;
+use biome_html_syntax::AnyGlimmerExpression;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatAnyGlimmerExpression;
+impl FormatRule for FormatAnyGlimmerExpression {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &AnyGlimmerExpression, f: &mut HtmlFormatter) -> FormatResult<()> {
+ match node {
+ AnyGlimmerExpression::GlimmerBlockHelper(node) => node.format().fmt(f),
+ AnyGlimmerExpression::GlimmerBogusExpression(node) => node.format().fmt(f),
+ AnyGlimmerExpression::GlimmerMustacheComment(node) => node.format().fmt(f),
+ AnyGlimmerExpression::GlimmerMustacheExpression(node) => node.format().fmt(f),
+ AnyGlimmerExpression::GlimmerNamedBlock(node) => node.format().fmt(f),
+ AnyGlimmerExpression::GlimmerTripleStashExpression(node) => node.format().fmt(f),
+ }
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/any/mod.rs b/crates/biome_html_formatter/src/js/any/mod.rs
new file mode 100644
index 000000000000..83d7908a063b
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/any/mod.rs
@@ -0,0 +1,5 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+pub(crate) mod argument;
+pub(crate) mod argument_value;
+pub(crate) mod expression;
diff --git a/crates/biome_html_formatter/src/js/auxiliary/block_helper.rs b/crates/biome_html_formatter/src/js/auxiliary/block_helper.rs
new file mode 100644
index 000000000000..b9564a8cacf7
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/block_helper.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockHelper;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockHelper;
+impl FormatNodeRule for FormatGlimmerBlockHelper {
+ fn fmt_fields(&self, node: &GlimmerBlockHelper, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/block_helper_closing.rs b/crates/biome_html_formatter/src/js/auxiliary/block_helper_closing.rs
new file mode 100644
index 000000000000..dcc241710f07
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/block_helper_closing.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockHelperClosing;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockHelperClosing;
+impl FormatNodeRule for FormatGlimmerBlockHelperClosing {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerBlockHelperClosing,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/block_helper_opening.rs b/crates/biome_html_formatter/src/js/auxiliary/block_helper_opening.rs
new file mode 100644
index 000000000000..7696ff416dc6
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/block_helper_opening.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockHelperOpening;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockHelperOpening;
+impl FormatNodeRule for FormatGlimmerBlockHelperOpening {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerBlockHelperOpening,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/block_param.rs b/crates/biome_html_formatter/src/js/auxiliary/block_param.rs
new file mode 100644
index 000000000000..52251a99c688
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/block_param.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockParam;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockParam;
+impl FormatNodeRule for FormatGlimmerBlockParam {
+ fn fmt_fields(&self, node: &GlimmerBlockParam, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/block_params.rs b/crates/biome_html_formatter/src/js/auxiliary/block_params.rs
new file mode 100644
index 000000000000..c67cee0081fc
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/block_params.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockParams;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockParams;
+impl FormatNodeRule for FormatGlimmerBlockParams {
+ fn fmt_fields(&self, node: &GlimmerBlockParams, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/element_modifier.rs b/crates/biome_html_formatter/src/js/auxiliary/element_modifier.rs
new file mode 100644
index 000000000000..49930c2cedaa
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/element_modifier.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerElementModifier;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerElementModifier;
+impl FormatNodeRule for FormatGlimmerElementModifier {
+ fn fmt_fields(&self, node: &GlimmerElementModifier, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/literal.rs b/crates/biome_html_formatter/src/js/auxiliary/literal.rs
new file mode 100644
index 000000000000..dfc29ed7a34e
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/literal.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerLiteral;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerLiteral;
+impl FormatNodeRule for FormatGlimmerLiteral {
+ fn fmt_fields(&self, node: &GlimmerLiteral, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/mod.rs b/crates/biome_html_formatter/src/js/auxiliary/mod.rs
new file mode 100644
index 000000000000..c7e23538f6ec
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/mod.rs
@@ -0,0 +1,22 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+pub(crate) mod block_helper;
+pub(crate) mod block_helper_closing;
+pub(crate) mod block_helper_opening;
+pub(crate) mod block_param;
+pub(crate) mod block_params;
+pub(crate) mod element_modifier;
+pub(crate) mod literal;
+pub(crate) mod mustache_comment;
+pub(crate) mod mustache_expression;
+pub(crate) mod named_argument;
+pub(crate) mod named_block;
+pub(crate) mod named_block_closing;
+pub(crate) mod named_block_opening;
+pub(crate) mod path;
+pub(crate) mod path_segment;
+pub(crate) mod positional_argument;
+pub(crate) mod splattribute;
+pub(crate) mod string_literal;
+pub(crate) mod subexpression;
+pub(crate) mod triple_stash_expression;
diff --git a/crates/biome_html_formatter/src/js/auxiliary/mustache_comment.rs b/crates/biome_html_formatter/src/js/auxiliary/mustache_comment.rs
new file mode 100644
index 000000000000..ee406261d0cb
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/mustache_comment.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerMustacheComment;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerMustacheComment;
+impl FormatNodeRule for FormatGlimmerMustacheComment {
+ fn fmt_fields(&self, node: &GlimmerMustacheComment, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/mustache_expression.rs b/crates/biome_html_formatter/src/js/auxiliary/mustache_expression.rs
new file mode 100644
index 000000000000..bbc4193df27b
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/mustache_expression.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerMustacheExpression;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerMustacheExpression;
+impl FormatNodeRule for FormatGlimmerMustacheExpression {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerMustacheExpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/named_argument.rs b/crates/biome_html_formatter/src/js/auxiliary/named_argument.rs
new file mode 100644
index 000000000000..c9ddaf309f6c
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/named_argument.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerNamedArgument;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerNamedArgument;
+impl FormatNodeRule for FormatGlimmerNamedArgument {
+ fn fmt_fields(&self, node: &GlimmerNamedArgument, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/named_block.rs b/crates/biome_html_formatter/src/js/auxiliary/named_block.rs
new file mode 100644
index 000000000000..b450b6f13371
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/named_block.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerNamedBlock;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerNamedBlock;
+impl FormatNodeRule for FormatGlimmerNamedBlock {
+ fn fmt_fields(&self, node: &GlimmerNamedBlock, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/named_block_closing.rs b/crates/biome_html_formatter/src/js/auxiliary/named_block_closing.rs
new file mode 100644
index 000000000000..c88ee8d97c46
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/named_block_closing.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerNamedBlockClosing;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerNamedBlockClosing;
+impl FormatNodeRule for FormatGlimmerNamedBlockClosing {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerNamedBlockClosing,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/named_block_opening.rs b/crates/biome_html_formatter/src/js/auxiliary/named_block_opening.rs
new file mode 100644
index 000000000000..59915c6a046b
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/named_block_opening.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerNamedBlockOpening;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerNamedBlockOpening;
+impl FormatNodeRule for FormatGlimmerNamedBlockOpening {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerNamedBlockOpening,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/path.rs b/crates/biome_html_formatter/src/js/auxiliary/path.rs
new file mode 100644
index 000000000000..10a227f80a1d
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/path.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerPath;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerPath;
+impl FormatNodeRule for FormatGlimmerPath {
+ fn fmt_fields(&self, node: &GlimmerPath, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/path_segment.rs b/crates/biome_html_formatter/src/js/auxiliary/path_segment.rs
new file mode 100644
index 000000000000..0fb9bfb0625f
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/path_segment.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerPathSegment;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerPathSegment;
+impl FormatNodeRule for FormatGlimmerPathSegment {
+ fn fmt_fields(&self, node: &GlimmerPathSegment, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/positional_argument.rs b/crates/biome_html_formatter/src/js/auxiliary/positional_argument.rs
new file mode 100644
index 000000000000..e1d43e3dfb6e
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/positional_argument.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerPositionalArgument;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerPositionalArgument;
+impl FormatNodeRule for FormatGlimmerPositionalArgument {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerPositionalArgument,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/splattribute.rs b/crates/biome_html_formatter/src/js/auxiliary/splattribute.rs
new file mode 100644
index 000000000000..c07794b7b9b9
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/splattribute.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerSplattribute;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerSplattribute;
+impl FormatNodeRule for FormatGlimmerSplattribute {
+ fn fmt_fields(&self, node: &GlimmerSplattribute, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/string_literal.rs b/crates/biome_html_formatter/src/js/auxiliary/string_literal.rs
new file mode 100644
index 000000000000..44b914f73c10
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/string_literal.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerStringLiteral;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerStringLiteral;
+impl FormatNodeRule for FormatGlimmerStringLiteral {
+ fn fmt_fields(&self, node: &GlimmerStringLiteral, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/subexpression.rs b/crates/biome_html_formatter/src/js/auxiliary/subexpression.rs
new file mode 100644
index 000000000000..32684d3b5ba8
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/subexpression.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerSubexpression;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerSubexpression;
+impl FormatNodeRule for FormatGlimmerSubexpression {
+ fn fmt_fields(&self, node: &GlimmerSubexpression, f: &mut HtmlFormatter) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/auxiliary/triple_stash_expression.rs b/crates/biome_html_formatter/src/js/auxiliary/triple_stash_expression.rs
new file mode 100644
index 000000000000..4e2ca8520aa0
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/auxiliary/triple_stash_expression.rs
@@ -0,0 +1,14 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerTripleStashExpression;
+use biome_rowan::AstNode;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerTripleStashExpression;
+impl FormatNodeRule for FormatGlimmerTripleStashExpression {
+ fn fmt_fields(
+ &self,
+ node: &GlimmerTripleStashExpression,
+ f: &mut HtmlFormatter,
+ ) -> FormatResult<()> {
+ format_html_verbatim_node(node.syntax()).fmt(f)
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/bogus/bogus_expression.rs b/crates/biome_html_formatter/src/js/bogus/bogus_expression.rs
new file mode 100644
index 000000000000..9647d39626df
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/bogus/bogus_expression.rs
@@ -0,0 +1,5 @@
+use crate::FormatBogusNodeRule;
+use biome_html_syntax::GlimmerBogusExpression;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBogusExpression;
+impl FormatBogusNodeRule for FormatGlimmerBogusExpression {}
diff --git a/crates/biome_html_formatter/src/js/bogus/mod.rs b/crates/biome_html_formatter/src/js/bogus/mod.rs
new file mode 100644
index 000000000000..e7c60f6a619b
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/bogus/mod.rs
@@ -0,0 +1,3 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+pub(crate) mod bogus_expression;
diff --git a/crates/biome_html_formatter/src/js/lists/argument_list.rs b/crates/biome_html_formatter/src/js/lists/argument_list.rs
new file mode 100644
index 000000000000..f2c2d3f20917
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/lists/argument_list.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerArgumentList;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerArgumentList;
+impl FormatRule for FormatGlimmerArgumentList {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &GlimmerArgumentList, f: &mut HtmlFormatter) -> FormatResult<()> {
+ f.join().entries(node.iter().formatted()).finish()
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/lists/block_param_list.rs b/crates/biome_html_formatter/src/js/lists/block_param_list.rs
new file mode 100644
index 000000000000..fade46b191d7
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/lists/block_param_list.rs
@@ -0,0 +1,10 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerBlockParamList;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerBlockParamList;
+impl FormatRule for FormatGlimmerBlockParamList {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &GlimmerBlockParamList, f: &mut HtmlFormatter) -> FormatResult<()> {
+ f.join().entries(node.iter().formatted()).finish()
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/lists/mod.rs b/crates/biome_html_formatter/src/js/lists/mod.rs
new file mode 100644
index 000000000000..1a17d555ead5
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/lists/mod.rs
@@ -0,0 +1,5 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+pub(crate) mod argument_list;
+pub(crate) mod block_param_list;
+pub(crate) mod path_segment_list;
diff --git a/crates/biome_html_formatter/src/js/lists/path_segment_list.rs b/crates/biome_html_formatter/src/js/lists/path_segment_list.rs
new file mode 100644
index 000000000000..0b82118173cc
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/lists/path_segment_list.rs
@@ -0,0 +1,18 @@
+use crate::prelude::*;
+use biome_html_syntax::GlimmerPathSegmentList;
+use biome_rowan::AstSeparatedList;
+#[derive(Debug, Clone, Default)]
+pub(crate) struct FormatGlimmerPathSegmentList;
+impl FormatRule for FormatGlimmerPathSegmentList {
+ type Context = HtmlFormatContext;
+ fn fmt(&self, node: &GlimmerPathSegmentList, f: &mut HtmlFormatter) -> FormatResult<()> {
+ // For separated lists, format each element and its separator
+ for element in node.elements() {
+ crate::prelude::write!(f, [element.node.format()])?;
+ if let Ok(Some(sep)) = element.trailing_separator() {
+ crate::prelude::write!(f, [sep.format()])?;
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/crates/biome_html_formatter/src/js/mod.rs b/crates/biome_html_formatter/src/js/mod.rs
new file mode 100644
index 000000000000..54da8fe0418f
--- /dev/null
+++ b/crates/biome_html_formatter/src/js/mod.rs
@@ -0,0 +1,6 @@
+//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
+
+pub(crate) mod any;
+pub(crate) mod auxiliary;
+pub(crate) mod bogus;
+pub(crate) mod lists;
diff --git a/crates/biome_html_formatter/src/lib.rs b/crates/biome_html_formatter/src/lib.rs
index 97fb5607a561..21db21455f9c 100644
--- a/crates/biome_html_formatter/src/lib.rs
+++ b/crates/biome_html_formatter/src/lib.rs
@@ -19,6 +19,7 @@ pub mod context;
mod cst;
mod generated;
mod html;
+mod js;
pub(crate) mod prelude;
pub(crate) mod separated;
mod svelte;
diff --git a/crates/biome_html_parser/src/lexer/mod.rs b/crates/biome_html_parser/src/lexer/mod.rs
index 20a8c890b69b..49b89ccb78b6 100644
--- a/crates/biome_html_parser/src/lexer/mod.rs
+++ b/crates/biome_html_parser/src/lexer/mod.rs
@@ -3,7 +3,8 @@ mod tests;
use crate::token_source::{HtmlEmbeddedLanguage, HtmlLexContext, TextExpressionKind};
use biome_html_syntax::HtmlSyntaxKind::{
COMMENT, DEBUG_KW, DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL,
- NEWLINE, SVELTE_IDENT, TOMBSTONE, UNICODE_BOM, WHITESPACE,
+ IDENT, L_TRIPLE_CURLY, MUSTACHE_COMMENT, NEWLINE, R_TRIPLE_CURLY, SVELTE_IDENT, TOMBSTONE,
+ UNICODE_BOM, WHITESPACE,
};
use biome_html_syntax::{HtmlSyntaxKind, T, TextLen, TextSize};
use biome_parser::diagnostic::ParseDiagnostic;
@@ -39,6 +40,7 @@ enum IdentifierContext {
None,
Doctype,
Svelte,
+ Glimmer,
}
impl IdentifierContext {
@@ -75,6 +77,8 @@ impl<'src> HtmlLexer<'src> {
b'/' => self.consume_byte(T![/]),
b'=' => self.consume_byte(T![=]),
b'!' => self.consume_byte(T![!]),
+ b'{' if self.at_opening_triple_text_expression() => self.consume_l_triple_text_expression(),
+ b'{' if self.at_mustache_comment() => self.consume_mustache_comment(),
b'{' if self.at_svelte_opening_block() => self.consume_svelte_opening_block(),
b'{' => {
if self.at_opening_double_text_expression() {
@@ -84,7 +88,9 @@ impl<'src> HtmlLexer<'src> {
}
}
b'}' => {
- if self.at_closing_double_text_expression() {
+ if self.at_closing_triple_text_expression() {
+ self.consume_r_triple_text_expression()
+ } else if self.at_closing_double_text_expression() {
self.consume_r_double_text_expression()
} else {
self.consume_byte(T!['}'])
@@ -114,6 +120,8 @@ impl<'src> HtmlLexer<'src> {
b'/' if self.current() == T![<] => self.consume_byte(T![/]),
b',' if self.current() == T![<] => self.consume_byte(T![,]),
b'-' if self.at_frontmatter_edge() => self.consume_frontmatter_edge(),
+ b'{' if self.at_opening_triple_text_expression() => self.consume_l_triple_text_expression(),
+ b'{' if self.at_mustache_comment() => self.consume_mustache_comment(),
b'{' if self.at_svelte_opening_block() => self.consume_svelte_opening_block(),
b'{' => {
if self.at_opening_double_text_expression() {
@@ -123,7 +131,9 @@ impl<'src> HtmlLexer<'src> {
}
}
b'}' => {
- if self.at_closing_double_text_expression() {
+ if self.at_closing_triple_text_expression() {
+ self.consume_r_triple_text_expression()
+ } else if self.at_closing_double_text_expression() {
self.consume_r_double_text_expression()
} else {
self.consume_byte(T!['}'])
@@ -241,7 +251,6 @@ impl<'src> HtmlLexer<'src> {
b'}' if self.at_closing_double_text_expression() => {
self.consume_r_double_text_expression()
}
- b'<' => self.consume_byte(T![<]),
_ => {
while let Some(current) = self.current_byte() {
match current {
@@ -256,6 +265,30 @@ impl<'src> HtmlLexer<'src> {
}
}
+ /// Consumes tokens within a Glimmer double mustache expression ('{{...}}').
+ /// Properly tokenizes Glimmer expression syntax (identifiers, dots, @, string literals).
+ fn consume_double_glimmer_expression(&mut self, current: u8) -> HtmlSyntaxKind {
+ match current {
+ b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(),
+ b'}' if self.at_closing_double_text_expression() => {
+ self.consume_r_double_text_expression()
+ }
+ b'.' => self.consume_byte(T![.]),
+ b'@' => self.consume_byte(T![@]),
+ b'#' => self.consume_byte(T![#]),
+ b'/' => self.consume_byte(T![/]),
+ b'|' => self.consume_byte(T![|]),
+ b'(' => self.consume_byte(T!['(']),
+ b')' => self.consume_byte(T![')']),
+ b'=' => self.consume_byte(T![=]),
+ b'\'' | b'"' => self.consume_string_literal(current),
+ _ if current.is_ascii_alphabetic() || current == b'_' => {
+ self.consume_identifier(current, IdentifierContext::Glimmer)
+ }
+ _ => self.consume_unexpected_character(),
+ }
+ }
+
// TODO: keep this function, and enhance svelte_expression until we don't need it anymore
/// Consumes tokens within a single text expression ('{...}') while tracking nested
/// brackets until the matching closing bracket is found.
@@ -289,6 +322,27 @@ impl<'src> HtmlLexer<'src> {
HTML_LITERAL
}
+ /// Consumes tokens within a triple text expression ('{{{...}}}') for Glimmer.
+ /// Unlike simple text expressions, this properly tokenizes identifiers, dots,
+ /// string literals, etc. for Glimmer expression syntax.
+ fn consume_triple_text_expression(&mut self, current: u8) -> HtmlSyntaxKind {
+ match current {
+ b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(),
+ b'}' if self.at_closing_triple_text_expression() => {
+ self.consume_r_triple_text_expression()
+ }
+ b'.' => self.consume_byte(T![.]),
+ b'@' => self.consume_byte(T![@]),
+ b'(' => self.consume_byte(T!['(']),
+ b')' => self.consume_byte(T![')']),
+ b'\'' | b'"' => self.consume_string_literal(current),
+ _ if current.is_ascii_alphabetic() || current == b'_' => {
+ self.consume_identifier(current, IdentifierContext::Glimmer)
+ }
+ _ => self.consume_unexpected_character(),
+ }
+ }
+
/// Consumes an HTML comment starting with '' is found.
/// Returns COMMENT token type.
fn consume_comment(&mut self) -> HtmlSyntaxKind {
@@ -421,16 +475,29 @@ impl<'src> HtmlLexer<'src> {
break;
}
}
+ IdentifierContext::Glimmer => {
+ // Glimmer identifiers: alphanumeric and underscore only
+ if byte.is_ascii_alphanumeric() || byte == b'_' {
+ if len < BUFFER_SIZE {
+ buffer[len] = byte;
+ len += 1;
+ }
+ self.advance(1)
+ } else {
+ break;
+ }
+ }
}
}
match &buffer[..len] {
- b"doctype" | b"DOCTYPE" if !context.is_svelte() => DOCTYPE_KW,
+ b"doctype" | b"DOCTYPE" if context.is_doctype() => DOCTYPE_KW,
b"html" | b"HTML" if context.is_doctype() => HTML_KW,
buffer if context.is_svelte() => match buffer {
b"debug" if self.current_kind == T!["{@"] => DEBUG_KW,
_ => SVELTE_IDENT,
},
+ _ if matches!(context, IdentifierContext::Glimmer) => IDENT,
_ => HTML_LITERAL,
}
}
@@ -580,6 +647,13 @@ impl<'src> HtmlLexer<'src> {
}
}
+ /// Consumes an opening triple text expression '{{{' token used for unescaped interpolation.
+ fn consume_l_triple_text_expression(&mut self) -> HtmlSyntaxKind {
+ debug_assert!(self.at_opening_triple_text_expression());
+ self.advance(3);
+ L_TRIPLE_CURLY
+ }
+
/// Consumes an opening double text expression '{{' token used for interpolation.
fn consume_l_double_text_expression(&mut self) -> HtmlSyntaxKind {
debug_assert!(self.at_opening_double_text_expression());
@@ -612,6 +686,13 @@ impl<'src> HtmlLexer<'src> {
T!["}}"]
}
+ /// Consumes a closing triple text expression '}}}' token used for unescaped interpolation.
+ fn consume_r_triple_text_expression(&mut self) -> HtmlSyntaxKind {
+ debug_assert!(self.at_closing_triple_text_expression());
+ self.advance(3);
+ R_TRIPLE_CURLY
+ }
+
/// Consumes a frontmatter fence '---' token that delimits Astro frontmatter blocks.
fn consume_frontmatter_edge(&mut self) -> HtmlSyntaxKind {
debug_assert!(self.at_frontmatter_edge());
@@ -619,6 +700,56 @@ impl<'src> HtmlLexer<'src> {
T![---]
}
+ /// Consumes a Glimmer mustache comment.
+ /// Handles both `{{! comment }}` and `{{!-- comment --}}` formats.
+ fn consume_mustache_comment(&mut self) -> HtmlSyntaxKind {
+ debug_assert!(self.at_mustache_comment());
+
+ let comment_start = self.text_position();
+
+ // Check if this is a multi-line comment {{!-- ... --}}
+ let is_multiline = self.byte_at(3) == Some(b'-') && self.byte_at(4) == Some(b'-');
+
+ // Advance past the opening: {{! or {{!--
+ if is_multiline {
+ self.advance(5); // {{!--
+ } else {
+ self.advance(3); // {{!
+ }
+
+ // Consume until we find the closing }} or --}}
+ while let Some(char) = self.current_byte() {
+ if is_multiline {
+ // Look for --}}
+ if self.current_byte() == Some(b'-')
+ && self.byte_at(1) == Some(b'-')
+ && self.byte_at(2) == Some(b'}')
+ && self.byte_at(3) == Some(b'}')
+ {
+ self.advance(4); // --}}
+ return MUSTACHE_COMMENT;
+ }
+ } else {
+ // Look for }}
+ if self.at_closing_double_text_expression() {
+ self.advance(2); // }}
+ return MUSTACHE_COMMENT;
+ }
+ }
+ self.advance_byte_or_char(char);
+ }
+
+ // If we reach EOF without finding closing, emit an error
+ let expected_closing = if is_multiline { "--}}" } else { "}}" };
+ let err = ParseDiagnostic::new(
+ format!("Unterminated mustache comment, expected closing '{}'", expected_closing),
+ comment_start..self.text_position(),
+ );
+ self.diagnostics.push(err);
+
+ MUSTACHE_COMMENT
+ }
+
#[inline(always)]
fn at_start_comment(&self) -> bool {
self.current_byte() == Some(b'<')
@@ -659,6 +790,20 @@ impl<'src> HtmlLexer<'src> {
&& self.byte_at(2) == Some(b'-')
}
+ #[inline(always)]
+ fn at_opening_triple_text_expression(&self) -> bool {
+ self.current_byte() == Some(b'{')
+ && self.byte_at(1) == Some(b'{')
+ && self.byte_at(2) == Some(b'{')
+ }
+
+ #[inline(always)]
+ fn at_mustache_comment(&self) -> bool {
+ self.current_byte() == Some(b'{')
+ && self.byte_at(1) == Some(b'{')
+ && self.byte_at(2) == Some(b'!')
+ }
+
#[inline(always)]
fn at_opening_double_text_expression(&self) -> bool {
self.current_byte() == Some(b'{') && self.byte_at(1) == Some(b'{')
@@ -678,6 +823,13 @@ impl<'src> HtmlLexer<'src> {
self.current_byte() == Some(b'}') && self.byte_at(1) == Some(b'}')
}
+ #[inline(always)]
+ fn at_closing_triple_text_expression(&self) -> bool {
+ self.current_byte() == Some(b'}')
+ && self.byte_at(1) == Some(b'}')
+ && self.byte_at(2) == Some(b'}')
+ }
+
/// Consumes the opening CDATA section marker ' HtmlSyntaxKind {
debug_assert!(self.at_start_cdata());
@@ -891,6 +1043,8 @@ impl<'src> Lexer<'src> for HtmlLexer<'src> {
HtmlLexContext::TextExpression(kind) => match kind {
TextExpressionKind::Double => self.consume_double_text_expression(current),
TextExpressionKind::Single => self.consume_single_text_expression(),
+ TextExpressionKind::Triple => self.consume_triple_text_expression(current),
+ TextExpressionKind::DoubleGlimmer => self.consume_double_glimmer_expression(current),
},
HtmlLexContext::CdataSection => self.consume_inside_cdata(current),
HtmlLexContext::AstroFencedCodeBlock => {
diff --git a/crates/biome_html_parser/src/parser.rs b/crates/biome_html_parser/src/parser.rs
index 66261d3154b6..f5d5a1655da5 100644
--- a/crates/biome_html_parser/src/parser.rs
+++ b/crates/biome_html_parser/src/parser.rs
@@ -113,6 +113,11 @@ impl HtmlParseOptions {
self
}
+ pub fn with_double_glimmer_expression(mut self) -> Self {
+ self.text_expression = Some(TextExpressionKind::DoubleGlimmer);
+ self
+ }
+
pub fn with_frontmatter(mut self) -> Self {
self.frontmatter = true;
self
@@ -154,6 +159,9 @@ impl From<&HtmlFileSource> for HtmlParseOptions {
HtmlVariant::Svelte => {
options = options.with_single_text_expression();
}
+ HtmlVariant::Glimmer => {
+ options = options.with_double_glimmer_expression();
+ }
}
options
diff --git a/crates/biome_html_parser/src/syntax/glimmer/mod.rs b/crates/biome_html_parser/src/syntax/glimmer/mod.rs
new file mode 100644
index 000000000000..480bfb71b96f
--- /dev/null
+++ b/crates/biome_html_parser/src/syntax/glimmer/mod.rs
@@ -0,0 +1,392 @@
+use crate::parser::HtmlParser;
+use crate::syntax::ParsedSyntax;
+use crate::syntax::ParsedSyntax::{Absent, Present};
+use crate::token_source::HtmlLexContext;
+use biome_html_syntax::HtmlSyntaxKind::*;
+use biome_html_syntax::T;
+use biome_parser::parse_lists::ParseNodeList;
+use biome_parser::Parser;
+
+/// Parses a Glimmer path like `this.foo`, `@arg`, or `helper`
+///
+/// GlimmerPath =
+/// segments: GlimmerPathSegmentList
+///
+/// GlimmerPathSegmentList = (GlimmerPathSegment ('.' GlimmerPathSegment)*)
+///
+/// GlimmerPathSegment =
+/// value_token: 'ident'
+pub(crate) fn parse_glimmer_path(p: &mut HtmlParser, context: HtmlLexContext) -> ParsedSyntax {
+ if !p.at(IDENT) && !p.at(AT) && !p.at(HTML_LITERAL) {
+ return Absent;
+ }
+
+ let m = p.start();
+
+ // Parse optional @ prefix (for argument references like @arg)
+ let has_at = p.at(AT);
+ if has_at {
+ p.bump_with_context(AT, context);
+ }
+
+ // Start the path segments list
+ let list_m = p.start();
+
+ // Parse first segment
+ if p.at(IDENT) || p.at(HTML_LITERAL) {
+ let segment_m = p.start();
+ p.bump_any_with_context(context); // IDENT or HTML_LITERAL
+ segment_m.complete(p, GLIMMER_PATH_SEGMENT);
+ } else if has_at {
+ // @ was present but no identifier follows
+ p.error(p.err_builder("Expected identifier after '@'", p.cur_range()));
+ }
+
+ // Parse remaining segments separated by dots
+ while p.at(DOT) {
+ // Bump DOT - it will be a separator in the list
+ p.bump_with_context(DOT, context);
+
+ if p.at(IDENT) || p.at(HTML_LITERAL) {
+ let segment_m = p.start();
+ p.bump_any_with_context(context);
+ segment_m.complete(p, GLIMMER_PATH_SEGMENT);
+ }
+ }
+
+ list_m.complete(p, GLIMMER_PATH_SEGMENT_LIST);
+
+ Present(m.complete(p, GLIMMER_PATH))
+}
+
+/// Parses a Glimmer argument list
+///
+/// GlimmerArgumentList = AnyGlimmerArgument*
+///
+/// Parses both:
+/// - GlimmerPositionalArgument (values like `"string"` or `path`)
+/// - GlimmerNamedArgument (name=value like `key=value`)
+pub(crate) fn parse_glimmer_argument_list(
+ p: &mut HtmlParser,
+ context: HtmlLexContext,
+) -> ParsedSyntax {
+ let m = p.start();
+
+ while !p.at(R_TRIPLE_CURLY) && !p.at(R_DOUBLE_CURLY) && !p.at(EOF) {
+ if p.at(HTML_STRING_LITERAL) {
+ // Positional argument with string literal
+ let arg_m = p.start();
+ let value_m = p.start();
+ p.bump_with_context(HTML_STRING_LITERAL, context);
+ value_m.complete(p, GLIMMER_STRING_LITERAL);
+ arg_m.complete(p, GLIMMER_POSITIONAL_ARGUMENT);
+ } else if p.at(IDENT) || p.at(HTML_LITERAL) {
+ // Check if this is the 'as' keyword followed by block params
+ // in patterns like "as |param|"
+ let checkpoint = p.checkpoint();
+ let is_as_keyword = p.cur_text() == "as";
+ p.bump_with_context(p.cur(), context);
+
+ if is_as_keyword && p.at(T![|]) {
+ // This is block params, not an argument - rewind and stop
+ p.rewind(checkpoint);
+ break;
+ }
+
+ // Not block params, check if it's a named argument or positional
+ // Check if next token is '='
+ if p.at(T![=]) {
+ // This is a named argument: rewind and parse properly
+ p.rewind(checkpoint);
+
+ let arg_m = p.start();
+ // Parse name_token (just bump the identifier)
+ p.bump_any_with_context(context);
+
+ // Parse eq_token '='
+ p.bump_with_context(T![=], context);
+
+ // Parse value (can be string literal or path)
+ if p.at(HTML_STRING_LITERAL) {
+ let value_m = p.start();
+ p.bump_with_context(HTML_STRING_LITERAL, context);
+ value_m.complete(p, GLIMMER_STRING_LITERAL);
+ } else {
+ let _ = parse_glimmer_path(p, context);
+ }
+
+ arg_m.complete(p, GLIMMER_NAMED_ARGUMENT);
+ } else {
+ // This is a positional argument: rewind and parse as path
+ p.rewind(checkpoint);
+
+ let arg_m = p.start();
+ let _ = parse_glimmer_path(p, context);
+ arg_m.complete(p, GLIMMER_POSITIONAL_ARGUMENT);
+ }
+ } else {
+ // Unknown token, stop parsing arguments
+ break;
+ }
+ }
+
+ Present(m.complete(p, GLIMMER_ARGUMENT_LIST))
+}
+
+/// Parse a Glimmer mustache expression (escaped output).
+/// Grammar: GlimmerMustacheExpression =
+/// l_curly2_token: '{{'
+/// path: GlimmerPath
+/// arguments: GlimmerArgumentList
+/// r_curly2_token: '}}'
+pub(crate) fn parse_glimmer_mustache_expression(p: &mut HtmlParser) -> ParsedSyntax {
+ use crate::token_source::TextExpressionKind;
+
+ if !p.at(T!["{{"]) {
+ return Absent;
+ }
+
+ // Only parse as Glimmer if DoubleGlimmer context is enabled (i.e., in .gjs/.gts files)
+ // For other files (Vue, etc.), return Absent to fall back to generic parsing
+ if p.options().text_expression != Some(TextExpressionKind::DoubleGlimmer) {
+ return Absent;
+ }
+
+ let m = p.start();
+
+ // Use DoubleGlimmer context for proper Glimmer tokenization
+ let context = HtmlLexContext::TextExpression(TextExpressionKind::DoubleGlimmer);
+
+ // Bump opening {{ with Glimmer context
+ p.bump_with_context(T!["{{"], context);
+
+ // Parse the path (e.g., this.foo, helper, @arg)
+ let _ = parse_glimmer_path(p, context);
+
+ // Parse the argument list (can be empty)
+ let _ = parse_glimmer_argument_list(p, context);
+
+ // Bump closing }} and switch back to regular context
+ if p.at(T!["}}"]) {
+ p.bump_with_context(T!["}}"], HtmlLexContext::Regular);
+ } else {
+ p.error(p.err_builder("Expected closing }}", p.cur_range()));
+ }
+
+ Present(m.complete(p, GLIMMER_MUSTACHE_EXPRESSION))
+}
+
+/// Parse a Glimmer block helper.
+/// Grammar: GlimmerBlockHelper =
+/// opening: GlimmerBlockHelperOpening
+/// children: HtmlElementList
+/// closing: GlimmerBlockHelperClosing
+pub(crate) fn parse_glimmer_block_helper(p: &mut HtmlParser) -> ParsedSyntax {
+ use crate::token_source::TextExpressionKind;
+
+ if !p.at(T!["{{"]) {
+ return Absent;
+ }
+
+ // Only parse as Glimmer if DoubleGlimmer context is enabled
+ if p.options().text_expression != Some(TextExpressionKind::DoubleGlimmer) {
+ return Absent;
+ }
+
+ // Check if this is a block helper by looking ahead for '#'
+ let checkpoint = p.checkpoint();
+ let context = HtmlLexContext::TextExpression(TextExpressionKind::DoubleGlimmer);
+
+ p.bump_with_context(T!["{{"], context);
+
+ if !p.at(T![#]) {
+ // Not a block helper, rewind
+ p.rewind(checkpoint);
+ return Absent;
+ }
+
+ // Rewind and start properly
+ p.rewind(checkpoint);
+
+ let m = p.start();
+
+ // Parse opening tag
+ let _ = parse_glimmer_block_helper_opening(p);
+
+ // Parse children (HTML elements/text between opening and closing)
+ // We need to manually control the parsing to stop at the closing tag
+ let list_m = p.start();
+ loop {
+ // Check if we're at the closing tag pattern {{/
+ if p.at(T!["{{"]) {
+ let checkpoint = p.checkpoint();
+ let ctx = HtmlLexContext::TextExpression(TextExpressionKind::DoubleGlimmer);
+ p.bump_with_context(T!["{{"], ctx);
+
+ if p.at(T![/]) {
+ // This is the closing tag, rewind and stop
+ p.rewind(checkpoint);
+ break;
+ } else {
+ // Not a closing tag, rewind and let ElementList handle it
+ p.rewind(checkpoint);
+ }
+ }
+
+ // Parse one element
+ let mut element_list = crate::syntax::ElementList::default();
+ if element_list.parse_element(p).is_absent() {
+ // Can't parse anything, stop
+ break;
+ }
+ }
+ list_m.complete(p, HTML_ELEMENT_LIST);
+
+ // Parse closing tag
+ if parse_glimmer_block_helper_closing(p).is_absent() {
+ p.error(p.err_builder("Expected closing tag for block helper", p.cur_range()));
+ }
+
+ Present(m.complete(p, GLIMMER_BLOCK_HELPER))
+}
+
+/// Parse the opening tag of a block helper.
+/// Grammar: GlimmerBlockHelperOpening =
+/// l_curly2_token: '{{'
+/// hash_token: '#'
+/// helper: GlimmerPath
+/// arguments: GlimmerArgumentList
+/// block_params: GlimmerBlockParams?
+/// r_curly2_token: '}}'
+fn parse_glimmer_block_helper_opening(p: &mut HtmlParser) -> ParsedSyntax {
+ use crate::token_source::TextExpressionKind;
+
+ if !p.at(T!["{{"]) {
+ return Absent;
+ }
+
+ let m = p.start();
+ let context = HtmlLexContext::TextExpression(TextExpressionKind::DoubleGlimmer);
+
+ // Bump opening {{
+ p.bump_with_context(T!["{{"], context);
+
+ // Bump # token
+ if p.at(T![#]) {
+ p.bump_with_context(T![#], context);
+ } else {
+ p.error(p.err_builder("Expected '#' for block helper", p.cur_range()));
+ }
+
+ // Parse helper path (e.g., 'if', 'each', 'let')
+ let _ = parse_glimmer_path(p, context);
+
+ // Parse arguments
+ let _ = parse_glimmer_argument_list(p, context);
+
+ // Parse optional block params (as |param1 param2|)
+ if p.at(IDENT) {
+ // Check if this is 'as' keyword
+ let checkpoint = p.checkpoint();
+ p.bump_with_context(IDENT, context);
+
+ if p.at(T![|]) {
+ // This looks like block params, rewind and parse properly
+ p.rewind(checkpoint);
+ let _ = parse_glimmer_block_params(p, context);
+ } else {
+ // Not block params, rewind
+ p.rewind(checkpoint);
+ }
+ }
+
+ // Bump closing }}
+ if p.at(T!["}}"]) {
+ p.bump_with_context(T!["}}"], HtmlLexContext::Regular);
+ } else {
+ p.error(p.err_builder("Expected closing }}", p.cur_range()));
+ }
+
+ Present(m.complete(p, GLIMMER_BLOCK_HELPER_OPENING))
+}
+
+/// Parse the closing tag of a block helper.
+/// Grammar: GlimmerBlockHelperClosing =
+/// l_curly2_token: '{{'
+/// slash_token: '/'
+/// helper: GlimmerPath
+/// r_curly2_token: '}}'
+fn parse_glimmer_block_helper_closing(p: &mut HtmlParser) -> ParsedSyntax {
+ use crate::token_source::TextExpressionKind;
+
+ if !p.at(T!["{{"]) {
+ return Absent;
+ }
+
+ let m = p.start();
+ let context = HtmlLexContext::TextExpression(TextExpressionKind::DoubleGlimmer);
+
+ // Bump opening {{
+ p.bump_with_context(T!["{{"], context);
+
+ // Bump / token
+ if p.at(T![/]) {
+ p.bump_with_context(T![/], context);
+ } else {
+ p.error(p.err_builder("Expected '/' for block helper closing", p.cur_range()));
+ }
+
+ // Parse helper path
+ let _ = parse_glimmer_path(p, context);
+
+ // Bump closing }}
+ if p.at(T!["}}"]) {
+ p.bump_with_context(T!["}}"], HtmlLexContext::Regular);
+ } else {
+ p.error(p.err_builder("Expected closing }}", p.cur_range()));
+ }
+
+ Present(m.complete(p, GLIMMER_BLOCK_HELPER_CLOSING))
+}
+
+/// Parse block parameters (as |param1 param2|).
+/// Grammar: GlimmerBlockParams =
+/// as_token: 'ident'
+/// l_pipe_token: '|'
+/// params: GlimmerBlockParamList
+/// r_pipe_token: '|'
+fn parse_glimmer_block_params(p: &mut HtmlParser, context: HtmlLexContext) -> ParsedSyntax {
+ if !p.at(IDENT) || p.cur_text() != "as" {
+ return Absent;
+ }
+
+ let m = p.start();
+
+ // Bump 'as' keyword
+ p.bump_with_context(IDENT, context);
+
+ // Bump opening |
+ if p.at(T![|]) {
+ p.bump_with_context(T![|], context);
+ } else {
+ p.error(p.err_builder("Expected '|' after 'as' keyword", p.cur_range()));
+ }
+
+ // Parse parameter list
+ let list_m = p.start();
+ while p.at(IDENT) {
+ let param_m = p.start();
+ p.bump_with_context(IDENT, context);
+ param_m.complete(p, GLIMMER_BLOCK_PARAM);
+ }
+ list_m.complete(p, GLIMMER_BLOCK_PARAM_LIST);
+
+ // Bump closing |
+ if p.at(T![|]) {
+ p.bump_with_context(T![|], context);
+ } else {
+ p.error(p.err_builder("Expected closing '|'", p.cur_range()));
+ }
+
+ Present(m.complete(p, GLIMMER_BLOCK_PARAMS))
+}
diff --git a/crates/biome_html_parser/src/syntax/mod.rs b/crates/biome_html_parser/src/syntax/mod.rs
index 47dcf987a28c..a17585bc5c17 100644
--- a/crates/biome_html_parser/src/syntax/mod.rs
+++ b/crates/biome_html_parser/src/syntax/mod.rs
@@ -1,9 +1,14 @@
mod astro;
+mod glimmer;
mod parse_error;
mod svelte;
use crate::parser::HtmlParser;
use crate::syntax::astro::parse_astro_fence;
+use crate::syntax::glimmer::{
+ parse_glimmer_argument_list, parse_glimmer_block_helper, parse_glimmer_mustache_expression,
+ parse_glimmer_path,
+};
use crate::syntax::parse_error::*;
use crate::syntax::svelte::parse_svelte_at_block;
use crate::token_source::{HtmlEmbeddedLanguage, HtmlLexContext, TextExpressionKind};
@@ -31,9 +36,10 @@ impl SyntaxFeature for HtmlSyntaxFeatures {
fn is_supported(&self, p: &HtmlParser) -> bool {
match self {
Self::Astro => p.options().frontmatter,
- Self::DoubleTextExpressions => {
- p.options().text_expression == Some(TextExpressionKind::Double)
- }
+ Self::DoubleTextExpressions => matches!(
+ p.options().text_expression,
+ Some(TextExpressionKind::Double | TextExpressionKind::DoubleGlimmer)
+ ),
Self::SingleTextExpressions => {
p.options().text_expression == Some(TextExpressionKind::Single)
}
@@ -211,7 +217,7 @@ fn parse_closing_tag(p: &mut HtmlParser) -> ParsedSyntax {
}
#[derive(Default)]
-struct ElementList;
+pub(crate) struct ElementList;
impl ParseNodeList for ElementList {
type Kind = HtmlSyntaxKind;
@@ -222,11 +228,24 @@ impl ParseNodeList for ElementList {
match p.cur() {
T![" parse_cdata_section(p),
T![<] => parse_element(p),
- T!["{{"] => HtmlSyntaxFeatures::DoubleTextExpressions.parse_exclusive_syntax(
- p,
- |p| parse_double_text_expression(p, HtmlLexContext::Regular),
- |p, m| disabled_interpolation(p, m.range(p)),
- ),
+ MUSTACHE_COMMENT => parse_mustache_comment(p),
+ L_TRIPLE_CURLY => parse_triple_stash_expression(p),
+ T!["{{"] => {
+ // Try parsing as Glimmer block helper first (checks for '#')
+ parse_glimmer_block_helper(p).or_else(|| {
+ // Try parsing as Glimmer mustache expression
+ // If we're in a Glimmer file (.gjs/.gts), tokens inside will be lexed
+ // with Glimmer context, so this will succeed
+ parse_glimmer_mustache_expression(p).or_else(|| {
+ // Fall back to generic double text expression for non-Glimmer files
+ HtmlSyntaxFeatures::DoubleTextExpressions.parse_exclusive_syntax(
+ p,
+ |p| parse_double_text_expression(p, HtmlLexContext::Regular),
+ |p, m| disabled_interpolation(p, m.range(p)),
+ )
+ })
+ })
+ }
T!["{@"] => parse_svelte_at_block(p),
T!['{'] => parse_single_text_expression(p, HtmlLexContext::Regular).or_else(|| {
let m = p.start();
@@ -586,8 +605,73 @@ impl TextExpression {
p.bump_remap_any_with_context(HtmlLexContext::TextExpression(self.kind))
}
}
+ TextExpressionKind::Triple | TextExpressionKind::DoubleGlimmer => {
+ // Triple-stash and DoubleGlimmer expressions are properly tokenized by the lexer,
+ // so we don't need to remap. Just bump any token.
+ p.bump_any_with_context(HtmlLexContext::TextExpression(self.kind));
+ }
}
Present(m.complete(p, HTML_TEXT_EXPRESSION))
}
}
+
+/// Parse a Glimmer mustache comment.
+/// Grammar: GlimmerMustacheComment = comment_token: 'mustache_comment'
+fn parse_mustache_comment(p: &mut HtmlParser) -> ParsedSyntax {
+ if !p.at(MUSTACHE_COMMENT) {
+ return Absent;
+ }
+
+ let m = p.start();
+ p.bump(MUSTACHE_COMMENT);
+ Present(m.complete(p, GLIMMER_MUSTACHE_COMMENT))
+}
+
+/// Parse a Glimmer triple-stash expression (unescaped HTML).
+/// Grammar: GlimmerTripleStashExpression =
+/// l_curly3_token: 'l_triple_curly'
+/// path: GlimmerPath
+/// arguments: GlimmerArgumentList
+/// r_curly3_token: 'r_triple_curly'
+fn parse_triple_stash_expression(p: &mut HtmlParser) -> ParsedSyntax {
+ if !p.at(L_TRIPLE_CURLY) {
+ return Absent;
+ }
+
+ let m = p.start();
+
+ let context = HtmlLexContext::TextExpression(TextExpressionKind::Triple);
+
+ // Bump with context to switch lexer to expression mode
+ p.bump_with_context(L_TRIPLE_CURLY, context);
+
+ // Parse the path (e.g., this.foo, helper, @arg)
+ let _ = parse_glimmer_path(p, context);
+
+ // Parse the argument list (can be empty)
+ let _ = parse_glimmer_argument_list(p, context);
+
+ // Bump closing }}} and switch back to regular context
+ if p.at(R_TRIPLE_CURLY) {
+ p.bump_with_context(R_TRIPLE_CURLY, HtmlLexContext::Regular);
+ } else {
+ // Missing closing }}}, emit error and try to recover
+ p.error(p.err_builder("Expected closing }}}", p.cur_range()));
+
+ // Try to resynchronize: consume tokens until we find }}} or reach a safe boundary
+ while !p.at(R_TRIPLE_CURLY) && !p.at(EOF) && !p.at(T![<]) {
+ p.bump_any();
+ }
+
+ // If we found }}}, consume it
+ if p.at(R_TRIPLE_CURLY) {
+ p.bump_with_context(R_TRIPLE_CURLY, HtmlLexContext::Regular);
+ } else {
+ // Reached EOF or safe boundary, switch context back to Regular
+ // (Note: We don't bump here since we're at a boundary token)
+ }
+ }
+
+ Present(m.complete(p, GLIMMER_TRIPLE_STASH_EXPRESSION))
+}
diff --git a/crates/biome_html_parser/src/token_source.rs b/crates/biome_html_parser/src/token_source.rs
index 49c8fb7d271b..085a629b707b 100644
--- a/crates/biome_html_parser/src/token_source.rs
+++ b/crates/biome_html_parser/src/token_source.rs
@@ -57,11 +57,15 @@ pub(crate) enum HtmlLexContext {
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) enum TextExpressionKind {
- // {{ expr }}
+ // {{ expr }} - Generic (Vue, etc.)
#[default]
Double,
// { expr }
Single,
+ // {{{ expr }}} - Glimmer triple-stash (unescaped HTML)
+ Triple,
+ // {{ expr }} - Glimmer mustache expression (like Triple but with double curlies)
+ DoubleGlimmer,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs b/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs
new file mode 100644
index 000000000000..4ebefb5f1b5a
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs
@@ -0,0 +1,4 @@
+
+ {{@}}
+ {{@ }}
+
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs.snap b/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs.snap
new file mode 100644
index 000000000000..c024b0aa615c
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/empty_path_after_at.gjs.snap
@@ -0,0 +1,131 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+
+ {{@}}
+ {{@ }}
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..9 "template" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@9..10 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: AT@15..16 "@" [] [],
+ segments: GlimmerPathSegmentList [],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@16..18 "}}" [] [],
+ },
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@18..23 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: AT@23..25 "@" [] [Whitespace(" ")],
+ segments: GlimmerPathSegmentList [],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@25..27 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@27..29 "<" [Newline("\n")] [],
+ slash_token: SLASH@29..30 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@30..38 "template" [] [],
+ },
+ r_angle_token: R_ANGLE@38..39 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@39..40 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..40
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..39
+ 0: HTML_ELEMENT@0..39
+ 0: HTML_OPENING_ELEMENT@0..10
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..9
+ 0: HTML_LITERAL@1..9 "template" [] []
+ 2: HTML_ATTRIBUTE_LIST@9..9
+ 3: R_ANGLE@9..10 ">" [] []
+ 1: HTML_ELEMENT_LIST@10..27
+ 0: GLIMMER_MUSTACHE_EXPRESSION@10..18
+ 0: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@15..16
+ 0: AT@15..16 "@" [] []
+ 1: GLIMMER_PATH_SEGMENT_LIST@16..16
+ 2: GLIMMER_ARGUMENT_LIST@16..16
+ 3: R_DOUBLE_CURLY@16..18 "}}" [] []
+ 1: GLIMMER_MUSTACHE_EXPRESSION@18..27
+ 0: L_DOUBLE_CURLY@18..23 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@23..25
+ 0: AT@23..25 "@" [] [Whitespace(" ")]
+ 1: GLIMMER_PATH_SEGMENT_LIST@25..25
+ 2: GLIMMER_ARGUMENT_LIST@25..25
+ 3: R_DOUBLE_CURLY@25..27 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@27..39
+ 0: L_ANGLE@27..29 "<" [Newline("\n")] []
+ 1: SLASH@29..30 "/" [] []
+ 2: HTML_TAG_NAME@30..38
+ 0: HTML_LITERAL@30..38 "template" [] []
+ 3: R_ANGLE@38..39 ">" [] []
+ 4: EOF@39..40 "" [Newline("\n")] []
+
+```
+
+## Diagnostics
+
+```
+empty_path_after_at.gjs:2:6 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected identifier after '@'
+
+ 1 │
+ > 2 │ {{@}}
+ │ ^^
+ 3 │ {{@ }}
+ 4 │
+
+empty_path_after_at.gjs:3:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected identifier after '@'
+
+ 1 │
+ 2 │ {{@}}
+ > 3 │ {{@ }}
+ │ ^^
+ 4 │
+ 5 │
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs
new file mode 100644
index 000000000000..13664911b087
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs
@@ -0,0 +1,4 @@
+
+ {{#if condition}}
+
Content
+
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs.snap b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs.snap
new file mode 100644
index 000000000000..84e176c9535d
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_block_closing.gjs.snap
@@ -0,0 +1,218 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+
+ {{#if condition}}
+ Content
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..9 "template" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@9..10 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] [],
+ hash_token_token: HASH@15..16 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@16..19 "if" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@19..28 "condition" [] [],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: missing (optional),
+ r_curly2_token_token: R_DOUBLE_CURLY@28..30 "}}" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@30..36 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@36..39 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@39..40 ">" [] [],
+ },
+ children: HtmlElementList [
+ HtmlContent {
+ value_token: HTML_LITERAL@40..47 "Content" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@47..48 "<" [] [],
+ slash_token: SLASH@48..49 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@49..52 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@52..53 ">" [] [],
+ },
+ },
+ HtmlSelfClosingElement {
+ l_angle_token: L_ANGLE@53..55 "<" [Newline("\n")] [],
+ name: missing (required),
+ attributes: HtmlAttributeList [],
+ slash_token: SLASH@55..56 "/" [] [],
+ r_angle_token: missing (required),
+ },
+ HtmlContent {
+ value_token: HTML_LITERAL@56..64 "template" [] [],
+ },
+ HtmlContent {
+ value_token: HTML_LITERAL@64..65 ">" [] [],
+ },
+ ],
+ closing: missing (required),
+ },
+ ],
+ closing_element: missing (required),
+ },
+ ],
+ eof_token: EOF@65..66 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..66
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..65
+ 0: HTML_ELEMENT@0..65
+ 0: HTML_OPENING_ELEMENT@0..10
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..9
+ 0: HTML_LITERAL@1..9 "template" [] []
+ 2: HTML_ATTRIBUTE_LIST@9..9
+ 3: R_ANGLE@9..10 ">" [] []
+ 1: HTML_ELEMENT_LIST@10..65
+ 0: GLIMMER_BLOCK_HELPER@10..65
+ 0: GLIMMER_BLOCK_HELPER_OPENING@10..30
+ 0: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: HASH@15..16 "#" [] []
+ 2: GLIMMER_PATH@16..19
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@16..19
+ 0: GLIMMER_PATH_SEGMENT@16..19
+ 0: IDENT@16..19 "if" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@19..28
+ 0: GLIMMER_POSITIONAL_ARGUMENT@19..28
+ 0: GLIMMER_PATH@19..28
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@19..28
+ 0: GLIMMER_PATH_SEGMENT@19..28
+ 0: IDENT@19..28 "condition" [] []
+ 4: (empty)
+ 5: R_DOUBLE_CURLY@28..30 "}}" [] []
+ 1: HTML_ELEMENT_LIST@30..65
+ 0: HTML_ELEMENT@30..53
+ 0: HTML_OPENING_ELEMENT@30..40
+ 0: L_ANGLE@30..36 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@36..39
+ 0: HTML_LITERAL@36..39 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@39..39
+ 3: R_ANGLE@39..40 ">" [] []
+ 1: HTML_ELEMENT_LIST@40..47
+ 0: HTML_CONTENT@40..47
+ 0: HTML_LITERAL@40..47 "Content" [] []
+ 2: HTML_CLOSING_ELEMENT@47..53
+ 0: L_ANGLE@47..48 "<" [] []
+ 1: SLASH@48..49 "/" [] []
+ 2: HTML_TAG_NAME@49..52
+ 0: HTML_LITERAL@49..52 "div" [] []
+ 3: R_ANGLE@52..53 ">" [] []
+ 1: HTML_SELF_CLOSING_ELEMENT@53..56
+ 0: L_ANGLE@53..55 "<" [Newline("\n")] []
+ 1: (empty)
+ 2: HTML_ATTRIBUTE_LIST@55..55
+ 3: SLASH@55..56 "/" [] []
+ 4: (empty)
+ 2: HTML_CONTENT@56..64
+ 0: HTML_LITERAL@56..64 "template" [] []
+ 3: HTML_CONTENT@64..65
+ 0: HTML_LITERAL@64..65 ">" [] []
+ 2: (empty)
+ 2: (empty)
+ 4: EOF@65..66 "" [Newline("\n")] []
+
+```
+
+## Diagnostics
+
+```
+missing_block_closing.gjs:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected an element name but instead found '/'.
+
+ 2 │ {{#if condition}}
+ 3 │ Content
+ > 4 │
+ │ ^
+ 5 │
+
+ i Expected an element name here.
+
+ 2 │ {{#if condition}}
+ 3 │ Content
+ > 4 │
+ │ ^
+ 5 │
+
+missing_block_closing.gjs:4:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × expected `>` but instead found `template`
+
+ 2 │ {{#if condition}}
+ 3 │
Content
+ > 4 │
+ │ ^^^^^^^^
+ 5 │
+
+ i Remove template
+
+missing_block_closing.gjs:5:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected closing tag for block helper
+
+ 3 │ Content
+ 4 │
+ > 5 │
+ │
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html
new file mode 100644
index 000000000000..b02f2b09e8c8
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html
@@ -0,0 +1,9 @@
+
+ {{{unsafe content
+ Next element
+
+
+
+ {{{unsafe content}}}
+ Valid
+
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html.snap b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html.snap
new file mode 100644
index 000000000000..95945c963d83
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/missing_triple_closing.html.snap
@@ -0,0 +1,289 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```html
+
+ {{{unsafe content
+ Next element
+
+
+
+ {{{unsafe content}}}
+ Valid
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlBogusElement {
+ items: [
+ HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..4 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@4..5 ">" [] [],
+ },
+ HtmlElementList [
+ HtmlBogusElement {
+ items: [
+ L_TRIPLE_CURLY@5..11 "{{{" [Newline("\n"), Whitespace(" ")] [],
+ GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@11..18 "unsafe" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@18..25 "content" [] [],
+ },
+ ],
+ },
+ },
+ ],
+ ERROR_TOKEN@25..29 "<" [Newline("\n"), Whitespace(" ")] [],
+ HTML_LITERAL@29..46 "span>Next element" [] [],
+ ],
+ },
+ ],
+ HtmlBogusElement {
+ items: [
+ L_ANGLE@46..47 "<" [] [],
+ SLASH@47..48 "/" [] [],
+ HtmlTagName {
+ value_token: HTML_LITERAL@48..52 "span" [] [],
+ },
+ R_ANGLE@52..53 ">" [] [],
+ ],
+ },
+ HtmlElementList [],
+ HtmlClosingElement {
+ l_angle_token: L_ANGLE@53..55 "<" [Newline("\n")] [],
+ slash_token: SLASH@55..56 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@56..59 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@59..60 ">" [] [],
+ },
+ ],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@60..63 "<" [Newline("\n"), Newline("\n")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@63..66 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@66..67 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerTripleStashExpression {
+ l_curly3_token_token: L_TRIPLE_CURLY@67..73 "{{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@73..80 "unsafe" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@80..87 "content" [] [],
+ },
+ ],
+ },
+ },
+ ],
+ r_curly3_token_token: R_TRIPLE_CURLY@87..90 "}}}" [] [],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@90..94 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@94..98 "span" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@98..99 ">" [] [],
+ },
+ children: HtmlElementList [
+ HtmlContent {
+ value_token: HTML_LITERAL@99..104 "Valid" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@104..105 "<" [] [],
+ slash_token: SLASH@105..106 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@106..110 "span" [] [],
+ },
+ r_angle_token: R_ANGLE@110..111 ">" [] [],
+ },
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@111..113 "<" [Newline("\n")] [],
+ slash_token: SLASH@113..114 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@114..117 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@117..118 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@118..119 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..119
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..118
+ 0: HTML_BOGUS_ELEMENT@0..60
+ 0: HTML_OPENING_ELEMENT@0..5
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..4
+ 0: HTML_LITERAL@1..4 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@4..4
+ 3: R_ANGLE@4..5 ">" [] []
+ 1: HTML_ELEMENT_LIST@5..46
+ 0: HTML_BOGUS_ELEMENT@5..46
+ 0: L_TRIPLE_CURLY@5..11 "{{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@11..18
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@11..18
+ 0: GLIMMER_PATH_SEGMENT@11..18
+ 0: IDENT@11..18 "unsafe" [] [Whitespace(" ")]
+ 2: GLIMMER_ARGUMENT_LIST@18..25
+ 0: GLIMMER_POSITIONAL_ARGUMENT@18..25
+ 0: GLIMMER_PATH@18..25
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@18..25
+ 0: GLIMMER_PATH_SEGMENT@18..25
+ 0: IDENT@18..25 "content" [] []
+ 3: ERROR_TOKEN@25..29 "<" [Newline("\n"), Whitespace(" ")] []
+ 4: HTML_LITERAL@29..46 "span>Next element" [] []
+ 2: HTML_BOGUS_ELEMENT@46..53
+ 0: L_ANGLE@46..47 "<" [] []
+ 1: SLASH@47..48 "/" [] []
+ 2: HTML_TAG_NAME@48..52
+ 0: HTML_LITERAL@48..52 "span" [] []
+ 3: R_ANGLE@52..53 ">" [] []
+ 3: HTML_ELEMENT_LIST@53..53
+ 4: HTML_CLOSING_ELEMENT@53..60
+ 0: L_ANGLE@53..55 "<" [Newline("\n")] []
+ 1: SLASH@55..56 "/" [] []
+ 2: HTML_TAG_NAME@56..59
+ 0: HTML_LITERAL@56..59 "div" [] []
+ 3: R_ANGLE@59..60 ">" [] []
+ 1: HTML_ELEMENT@60..118
+ 0: HTML_OPENING_ELEMENT@60..67
+ 0: L_ANGLE@60..63 "<" [Newline("\n"), Newline("\n")] []
+ 1: HTML_TAG_NAME@63..66
+ 0: HTML_LITERAL@63..66 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@66..66
+ 3: R_ANGLE@66..67 ">" [] []
+ 1: HTML_ELEMENT_LIST@67..111
+ 0: GLIMMER_TRIPLE_STASH_EXPRESSION@67..90
+ 0: L_TRIPLE_CURLY@67..73 "{{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@73..80
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@73..80
+ 0: GLIMMER_PATH_SEGMENT@73..80
+ 0: IDENT@73..80 "unsafe" [] [Whitespace(" ")]
+ 2: GLIMMER_ARGUMENT_LIST@80..87
+ 0: GLIMMER_POSITIONAL_ARGUMENT@80..87
+ 0: GLIMMER_PATH@80..87
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@80..87
+ 0: GLIMMER_PATH_SEGMENT@80..87
+ 0: IDENT@80..87 "content" [] []
+ 3: R_TRIPLE_CURLY@87..90 "}}}" [] []
+ 1: HTML_ELEMENT@90..111
+ 0: HTML_OPENING_ELEMENT@90..99
+ 0: L_ANGLE@90..94 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@94..98
+ 0: HTML_LITERAL@94..98 "span" [] []
+ 2: HTML_ATTRIBUTE_LIST@98..98
+ 3: R_ANGLE@98..99 ">" [] []
+ 1: HTML_ELEMENT_LIST@99..104
+ 0: HTML_CONTENT@99..104
+ 0: HTML_LITERAL@99..104 "Valid" [] []
+ 2: HTML_CLOSING_ELEMENT@104..111
+ 0: L_ANGLE@104..105 "<" [] []
+ 1: SLASH@105..106 "/" [] []
+ 2: HTML_TAG_NAME@106..110
+ 0: HTML_LITERAL@106..110 "span" [] []
+ 3: R_ANGLE@110..111 ">" [] []
+ 2: HTML_CLOSING_ELEMENT@111..118
+ 0: L_ANGLE@111..113 "<" [Newline("\n")] []
+ 1: SLASH@113..114 "/" [] []
+ 2: HTML_TAG_NAME@114..117
+ 0: HTML_LITERAL@114..117 "div" [] []
+ 3: R_ANGLE@117..118 ">" [] []
+ 4: EOF@118..119 "" [Newline("\n")] []
+
+```
+
+## Diagnostics
+
+```
+missing_triple_closing.html:3:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Unexpected character `<`
+
+ 1 │
+ 2 │ {{{unsafe content
+ > 3 │ Next element
+ │ ^
+ 4 │
+ 5 │
+
+missing_triple_closing.html:3:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected a matching closing tag but instead found ''.
+
+ 1 │
+ 2 │ {{{unsafe content
+ > 3 │ Next element
+ │ ^^^^^^^
+ 4 │
+ 5 │
+
+ i Expected a matching closing tag here.
+
+ 1 │
+ 2 │ {{{unsafe content
+ > 3 │ Next element
+ │ ^^^^^^^
+ 4 │
+ 5 │
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html b/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html
new file mode 100644
index 000000000000..1975786fc58e
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html
@@ -0,0 +1,9 @@
+
+ {{! This comment is not closed
+ Content
+
+
+
+ {{!-- This block comment is not closed
+ Content
+
diff --git a/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html.snap b/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html.snap
new file mode 100644
index 000000000000..f899155dd00d
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/error/glimmer/unterminated_comment.html.snap
@@ -0,0 +1,109 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```html
+
+ {{! This comment is not closed
+ Content
+
+
+
+ {{!-- This block comment is not closed
+ Content
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..4 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@4..5 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheComment {
+ comment_token_token: MUSTACHE_COMMENT@5..147 "{{! This comment is not closed\n Content\n\n\n\n {{!-- This block comment is not closed\n Content\n
\n" [Newline("\n"), Whitespace(" ")] [],
+ },
+ ],
+ closing_element: missing (required),
+ },
+ ],
+ eof_token: EOF@147..147 "" [] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..147
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..147
+ 0: HTML_ELEMENT@0..147
+ 0: HTML_OPENING_ELEMENT@0..5
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..4
+ 0: HTML_LITERAL@1..4 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@4..4
+ 3: R_ANGLE@4..5 ">" [] []
+ 1: HTML_ELEMENT_LIST@5..147
+ 0: GLIMMER_MUSTACHE_COMMENT@5..147
+ 0: MUSTACHE_COMMENT@5..147 "{{! This comment is not closed\n Content\n\n\n\n {{!-- This block comment is not closed\n Content\n
\n" [Newline("\n"), Whitespace(" ")] []
+ 2: (empty)
+ 4: EOF@147..147 "" [] []
+
+```
+
+## Diagnostics
+
+```
+unterminated_comment.html:2:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Unterminated mustache comment, expected closing '}}'
+
+ 1 │
+ > 2 │ {{! This comment is not closed
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ > 3 │ Content
+ > 4 │
+ > 5 │
+ > 6 │
+ > 7 │ {{!-- This block comment is not closed
+ > 8 │ Content
+ > 9 │
+ > 10 │
+ │
+
+unterminated_comment.html:10:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Expected a closing tag but instead found the end of the file.
+
+ 8 │ Content
+ 9 │
+ > 10 │
+ │
+
+ i Expected a closing tag here.
+
+ 8 │ Content
+ 9 │
+ > 10 │
+ │
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs
new file mode 100644
index 000000000000..5e09616573c5
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs
@@ -0,0 +1,11 @@
+{{#if showTitle}}
+ {{title}}
+{{/if}}
+
+{{#each items as |item index|}}
+ {{item.name}}
+{{/each}}
+
+{{#with user as |u|}}
+ {{u.name}}
+{{/with}}
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs.snap
new file mode 100644
index 000000000000..bdd38a8750c2
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_helper.gjs.snap
@@ -0,0 +1,476 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+{{#if showTitle}}
+ {{title}}
+{{/if}}
+
+{{#each items as |item index|}}
+ {{item.name}}
+{{/each}}
+
+{{#with user as |u|}}
+ {{u.name}}
+{{/with}}
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@0..2 "{{" [] [],
+ hash_token_token: HASH@2..3 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@3..6 "if" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@6..15 "showTitle" [] [],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: missing (optional),
+ r_curly2_token_token: R_DOUBLE_CURLY@15..17 "}}" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@17..21 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@21..23 "h1" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@23..24 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@24..26 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@26..31 "title" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@31..33 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@33..34 "<" [] [],
+ slash_token: SLASH@34..35 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@35..37 "h1" [] [],
+ },
+ r_angle_token: R_ANGLE@37..38 ">" [] [],
+ },
+ },
+ ],
+ closing: GlimmerBlockHelperClosing {
+ l_curly2_token_token: L_DOUBLE_CURLY@38..41 "{{" [Newline("\n")] [],
+ slash_token_token: SLASH@41..42 "/" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@42..44 "if" [] [],
+ },
+ ],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@44..46 "}}" [] [],
+ },
+ },
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@46..50 "{{" [Newline("\n"), Newline("\n")] [],
+ hash_token_token: HASH@50..51 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@51..56 "each" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@56..62 "items" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: GlimmerBlockParams {
+ as_token_token: IDENT@62..65 "as" [] [Whitespace(" ")],
+ l_pipe_token_token: PIPE@65..66 "|" [] [],
+ params: GlimmerBlockParamList [
+ GlimmerBlockParam {
+ name_token_token: IDENT@66..71 "item" [] [Whitespace(" ")],
+ },
+ GlimmerBlockParam {
+ name_token_token: IDENT@71..76 "index" [] [],
+ },
+ ],
+ r_pipe_token_token: PIPE@76..77 "|" [] [],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@77..79 "}}" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@79..83 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@83..85 "li" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@85..86 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@86..88 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@88..92 "item" [] [],
+ },
+ DOT@92..93 "." [] [],
+ GlimmerPathSegment {
+ value_token_token: IDENT@93..97 "name" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@97..99 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@99..100 "<" [] [],
+ slash_token: SLASH@100..101 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@101..103 "li" [] [],
+ },
+ r_angle_token: R_ANGLE@103..104 ">" [] [],
+ },
+ },
+ ],
+ closing: GlimmerBlockHelperClosing {
+ l_curly2_token_token: L_DOUBLE_CURLY@104..107 "{{" [Newline("\n")] [],
+ slash_token_token: SLASH@107..108 "/" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@108..112 "each" [] [],
+ },
+ ],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@112..114 "}}" [] [],
+ },
+ },
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@114..118 "{{" [Newline("\n"), Newline("\n")] [],
+ hash_token_token: HASH@118..119 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@119..124 "with" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@124..129 "user" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: GlimmerBlockParams {
+ as_token_token: IDENT@129..132 "as" [] [Whitespace(" ")],
+ l_pipe_token_token: PIPE@132..133 "|" [] [],
+ params: GlimmerBlockParamList [
+ GlimmerBlockParam {
+ name_token_token: IDENT@133..134 "u" [] [],
+ },
+ ],
+ r_pipe_token_token: PIPE@134..135 "|" [] [],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@135..137 "}}" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@137..141 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@141..142 "p" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@142..143 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@143..145 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@145..146 "u" [] [],
+ },
+ DOT@146..147 "." [] [],
+ GlimmerPathSegment {
+ value_token_token: IDENT@147..151 "name" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@151..153 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@153..154 "<" [] [],
+ slash_token: SLASH@154..155 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@155..156 "p" [] [],
+ },
+ r_angle_token: R_ANGLE@156..157 ">" [] [],
+ },
+ },
+ ],
+ closing: GlimmerBlockHelperClosing {
+ l_curly2_token_token: L_DOUBLE_CURLY@157..160 "{{" [Newline("\n")] [],
+ slash_token_token: SLASH@160..161 "/" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@161..165 "with" [] [],
+ },
+ ],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@165..167 "}}" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@167..168 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..168
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..167
+ 0: GLIMMER_BLOCK_HELPER@0..46
+ 0: GLIMMER_BLOCK_HELPER_OPENING@0..17
+ 0: L_DOUBLE_CURLY@0..2 "{{" [] []
+ 1: HASH@2..3 "#" [] []
+ 2: GLIMMER_PATH@3..6
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@3..6
+ 0: GLIMMER_PATH_SEGMENT@3..6
+ 0: IDENT@3..6 "if" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@6..15
+ 0: GLIMMER_POSITIONAL_ARGUMENT@6..15
+ 0: GLIMMER_PATH@6..15
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@6..15
+ 0: GLIMMER_PATH_SEGMENT@6..15
+ 0: IDENT@6..15 "showTitle" [] []
+ 4: (empty)
+ 5: R_DOUBLE_CURLY@15..17 "}}" [] []
+ 1: HTML_ELEMENT_LIST@17..38
+ 0: HTML_ELEMENT@17..38
+ 0: HTML_OPENING_ELEMENT@17..24
+ 0: L_ANGLE@17..21 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@21..23
+ 0: HTML_LITERAL@21..23 "h1" [] []
+ 2: HTML_ATTRIBUTE_LIST@23..23
+ 3: R_ANGLE@23..24 ">" [] []
+ 1: HTML_ELEMENT_LIST@24..33
+ 0: GLIMMER_MUSTACHE_EXPRESSION@24..33
+ 0: L_DOUBLE_CURLY@24..26 "{{" [] []
+ 1: GLIMMER_PATH@26..31
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@26..31
+ 0: GLIMMER_PATH_SEGMENT@26..31
+ 0: IDENT@26..31 "title" [] []
+ 2: GLIMMER_ARGUMENT_LIST@31..31
+ 3: R_DOUBLE_CURLY@31..33 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@33..38
+ 0: L_ANGLE@33..34 "<" [] []
+ 1: SLASH@34..35 "/" [] []
+ 2: HTML_TAG_NAME@35..37
+ 0: HTML_LITERAL@35..37 "h1" [] []
+ 3: R_ANGLE@37..38 ">" [] []
+ 2: GLIMMER_BLOCK_HELPER_CLOSING@38..46
+ 0: L_DOUBLE_CURLY@38..41 "{{" [Newline("\n")] []
+ 1: SLASH@41..42 "/" [] []
+ 2: GLIMMER_PATH@42..44
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@42..44
+ 0: GLIMMER_PATH_SEGMENT@42..44
+ 0: IDENT@42..44 "if" [] []
+ 3: R_DOUBLE_CURLY@44..46 "}}" [] []
+ 1: GLIMMER_BLOCK_HELPER@46..114
+ 0: GLIMMER_BLOCK_HELPER_OPENING@46..79
+ 0: L_DOUBLE_CURLY@46..50 "{{" [Newline("\n"), Newline("\n")] []
+ 1: HASH@50..51 "#" [] []
+ 2: GLIMMER_PATH@51..56
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@51..56
+ 0: GLIMMER_PATH_SEGMENT@51..56
+ 0: IDENT@51..56 "each" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@56..62
+ 0: GLIMMER_POSITIONAL_ARGUMENT@56..62
+ 0: GLIMMER_PATH@56..62
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@56..62
+ 0: GLIMMER_PATH_SEGMENT@56..62
+ 0: IDENT@56..62 "items" [] [Whitespace(" ")]
+ 4: GLIMMER_BLOCK_PARAMS@62..77
+ 0: IDENT@62..65 "as" [] [Whitespace(" ")]
+ 1: PIPE@65..66 "|" [] []
+ 2: GLIMMER_BLOCK_PARAM_LIST@66..76
+ 0: GLIMMER_BLOCK_PARAM@66..71
+ 0: IDENT@66..71 "item" [] [Whitespace(" ")]
+ 1: GLIMMER_BLOCK_PARAM@71..76
+ 0: IDENT@71..76 "index" [] []
+ 3: PIPE@76..77 "|" [] []
+ 5: R_DOUBLE_CURLY@77..79 "}}" [] []
+ 1: HTML_ELEMENT_LIST@79..104
+ 0: HTML_ELEMENT@79..104
+ 0: HTML_OPENING_ELEMENT@79..86
+ 0: L_ANGLE@79..83 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@83..85
+ 0: HTML_LITERAL@83..85 "li" [] []
+ 2: HTML_ATTRIBUTE_LIST@85..85
+ 3: R_ANGLE@85..86 ">" [] []
+ 1: HTML_ELEMENT_LIST@86..99
+ 0: GLIMMER_MUSTACHE_EXPRESSION@86..99
+ 0: L_DOUBLE_CURLY@86..88 "{{" [] []
+ 1: GLIMMER_PATH@88..97
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@88..97
+ 0: GLIMMER_PATH_SEGMENT@88..92
+ 0: IDENT@88..92 "item" [] []
+ 1: DOT@92..93 "." [] []
+ 2: GLIMMER_PATH_SEGMENT@93..97
+ 0: IDENT@93..97 "name" [] []
+ 2: GLIMMER_ARGUMENT_LIST@97..97
+ 3: R_DOUBLE_CURLY@97..99 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@99..104
+ 0: L_ANGLE@99..100 "<" [] []
+ 1: SLASH@100..101 "/" [] []
+ 2: HTML_TAG_NAME@101..103
+ 0: HTML_LITERAL@101..103 "li" [] []
+ 3: R_ANGLE@103..104 ">" [] []
+ 2: GLIMMER_BLOCK_HELPER_CLOSING@104..114
+ 0: L_DOUBLE_CURLY@104..107 "{{" [Newline("\n")] []
+ 1: SLASH@107..108 "/" [] []
+ 2: GLIMMER_PATH@108..112
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@108..112
+ 0: GLIMMER_PATH_SEGMENT@108..112
+ 0: IDENT@108..112 "each" [] []
+ 3: R_DOUBLE_CURLY@112..114 "}}" [] []
+ 2: GLIMMER_BLOCK_HELPER@114..167
+ 0: GLIMMER_BLOCK_HELPER_OPENING@114..137
+ 0: L_DOUBLE_CURLY@114..118 "{{" [Newline("\n"), Newline("\n")] []
+ 1: HASH@118..119 "#" [] []
+ 2: GLIMMER_PATH@119..124
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@119..124
+ 0: GLIMMER_PATH_SEGMENT@119..124
+ 0: IDENT@119..124 "with" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@124..129
+ 0: GLIMMER_POSITIONAL_ARGUMENT@124..129
+ 0: GLIMMER_PATH@124..129
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@124..129
+ 0: GLIMMER_PATH_SEGMENT@124..129
+ 0: IDENT@124..129 "user" [] [Whitespace(" ")]
+ 4: GLIMMER_BLOCK_PARAMS@129..135
+ 0: IDENT@129..132 "as" [] [Whitespace(" ")]
+ 1: PIPE@132..133 "|" [] []
+ 2: GLIMMER_BLOCK_PARAM_LIST@133..134
+ 0: GLIMMER_BLOCK_PARAM@133..134
+ 0: IDENT@133..134 "u" [] []
+ 3: PIPE@134..135 "|" [] []
+ 5: R_DOUBLE_CURLY@135..137 "}}" [] []
+ 1: HTML_ELEMENT_LIST@137..157
+ 0: HTML_ELEMENT@137..157
+ 0: HTML_OPENING_ELEMENT@137..143
+ 0: L_ANGLE@137..141 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@141..142
+ 0: HTML_LITERAL@141..142 "p" [] []
+ 2: HTML_ATTRIBUTE_LIST@142..142
+ 3: R_ANGLE@142..143 ">" [] []
+ 1: HTML_ELEMENT_LIST@143..153
+ 0: GLIMMER_MUSTACHE_EXPRESSION@143..153
+ 0: L_DOUBLE_CURLY@143..145 "{{" [] []
+ 1: GLIMMER_PATH@145..151
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@145..151
+ 0: GLIMMER_PATH_SEGMENT@145..146
+ 0: IDENT@145..146 "u" [] []
+ 1: DOT@146..147 "." [] []
+ 2: GLIMMER_PATH_SEGMENT@147..151
+ 0: IDENT@147..151 "name" [] []
+ 2: GLIMMER_ARGUMENT_LIST@151..151
+ 3: R_DOUBLE_CURLY@151..153 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@153..157
+ 0: L_ANGLE@153..154 "<" [] []
+ 1: SLASH@154..155 "/" [] []
+ 2: HTML_TAG_NAME@155..156
+ 0: HTML_LITERAL@155..156 "p" [] []
+ 3: R_ANGLE@156..157 ">" [] []
+ 2: GLIMMER_BLOCK_HELPER_CLOSING@157..167
+ 0: L_DOUBLE_CURLY@157..160 "{{" [Newline("\n")] []
+ 1: SLASH@160..161 "/" [] []
+ 2: GLIMMER_PATH@161..165
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@161..165
+ 0: GLIMMER_PATH_SEGMENT@161..165
+ 0: IDENT@161..165 "with" [] []
+ 3: R_DOUBLE_CURLY@165..167 "}}" [] []
+ 4: EOF@167..168 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs
new file mode 100644
index 000000000000..3c75517c5e21
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs
@@ -0,0 +1,9 @@
+
+ {{#each items as |item|}}
+ {{item}}
+ {{/each}}
+
+ {{#let value as |v|}}
+ {{v}}
+ {{/let}}
+
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs.snap
new file mode 100644
index 000000000000..a2a4bace641e
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/block_params_with_as.gjs.snap
@@ -0,0 +1,294 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+
+ {{#each items as |item|}}
+ {{item}}
+ {{/each}}
+
+ {{#let value as |v|}}
+ {{v}}
+ {{/let}}
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..9 "template" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@9..10 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] [],
+ hash_token_token: HASH@15..16 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@16..21 "each" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@21..27 "items" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: GlimmerBlockParams {
+ as_token_token: IDENT@27..30 "as" [] [Whitespace(" ")],
+ l_pipe_token_token: PIPE@30..31 "|" [] [],
+ params: GlimmerBlockParamList [
+ GlimmerBlockParam {
+ name_token_token: IDENT@31..35 "item" [] [],
+ },
+ ],
+ r_pipe_token_token: PIPE@35..36 "|" [] [],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@36..38 "}}" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@38..45 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@45..49 "item" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@49..51 "}}" [] [],
+ },
+ ],
+ closing: GlimmerBlockHelperClosing {
+ l_curly2_token_token: L_DOUBLE_CURLY@51..56 "{{" [Newline("\n"), Whitespace(" ")] [],
+ slash_token_token: SLASH@56..57 "/" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@57..61 "each" [] [],
+ },
+ ],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@61..63 "}}" [] [],
+ },
+ },
+ GlimmerBlockHelper {
+ opening: GlimmerBlockHelperOpening {
+ l_curly2_token_token: L_DOUBLE_CURLY@63..69 "{{" [Newline("\n"), Newline("\n"), Whitespace(" ")] [],
+ hash_token_token: HASH@69..70 "#" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@70..74 "let" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@74..80 "value" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ ],
+ block_params: GlimmerBlockParams {
+ as_token_token: IDENT@80..83 "as" [] [Whitespace(" ")],
+ l_pipe_token_token: PIPE@83..84 "|" [] [],
+ params: GlimmerBlockParamList [
+ GlimmerBlockParam {
+ name_token_token: IDENT@84..85 "v" [] [],
+ },
+ ],
+ r_pipe_token_token: PIPE@85..86 "|" [] [],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@86..88 "}}" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@88..95 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@95..96 "v" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@96..98 "}}" [] [],
+ },
+ ],
+ closing: GlimmerBlockHelperClosing {
+ l_curly2_token_token: L_DOUBLE_CURLY@98..103 "{{" [Newline("\n"), Whitespace(" ")] [],
+ slash_token_token: SLASH@103..104 "/" [] [],
+ helper: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@104..107 "let" [] [],
+ },
+ ],
+ },
+ r_curly2_token_token: R_DOUBLE_CURLY@107..109 "}}" [] [],
+ },
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@109..111 "<" [Newline("\n")] [],
+ slash_token: SLASH@111..112 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@112..120 "template" [] [],
+ },
+ r_angle_token: R_ANGLE@120..121 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@121..122 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..122
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..121
+ 0: HTML_ELEMENT@0..121
+ 0: HTML_OPENING_ELEMENT@0..10
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..9
+ 0: HTML_LITERAL@1..9 "template" [] []
+ 2: HTML_ATTRIBUTE_LIST@9..9
+ 3: R_ANGLE@9..10 ">" [] []
+ 1: HTML_ELEMENT_LIST@10..109
+ 0: GLIMMER_BLOCK_HELPER@10..63
+ 0: GLIMMER_BLOCK_HELPER_OPENING@10..38
+ 0: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: HASH@15..16 "#" [] []
+ 2: GLIMMER_PATH@16..21
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@16..21
+ 0: GLIMMER_PATH_SEGMENT@16..21
+ 0: IDENT@16..21 "each" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@21..27
+ 0: GLIMMER_POSITIONAL_ARGUMENT@21..27
+ 0: GLIMMER_PATH@21..27
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@21..27
+ 0: GLIMMER_PATH_SEGMENT@21..27
+ 0: IDENT@21..27 "items" [] [Whitespace(" ")]
+ 4: GLIMMER_BLOCK_PARAMS@27..36
+ 0: IDENT@27..30 "as" [] [Whitespace(" ")]
+ 1: PIPE@30..31 "|" [] []
+ 2: GLIMMER_BLOCK_PARAM_LIST@31..35
+ 0: GLIMMER_BLOCK_PARAM@31..35
+ 0: IDENT@31..35 "item" [] []
+ 3: PIPE@35..36 "|" [] []
+ 5: R_DOUBLE_CURLY@36..38 "}}" [] []
+ 1: HTML_ELEMENT_LIST@38..51
+ 0: GLIMMER_MUSTACHE_EXPRESSION@38..51
+ 0: L_DOUBLE_CURLY@38..45 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@45..49
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@45..49
+ 0: GLIMMER_PATH_SEGMENT@45..49
+ 0: IDENT@45..49 "item" [] []
+ 2: GLIMMER_ARGUMENT_LIST@49..49
+ 3: R_DOUBLE_CURLY@49..51 "}}" [] []
+ 2: GLIMMER_BLOCK_HELPER_CLOSING@51..63
+ 0: L_DOUBLE_CURLY@51..56 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: SLASH@56..57 "/" [] []
+ 2: GLIMMER_PATH@57..61
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@57..61
+ 0: GLIMMER_PATH_SEGMENT@57..61
+ 0: IDENT@57..61 "each" [] []
+ 3: R_DOUBLE_CURLY@61..63 "}}" [] []
+ 1: GLIMMER_BLOCK_HELPER@63..109
+ 0: GLIMMER_BLOCK_HELPER_OPENING@63..88
+ 0: L_DOUBLE_CURLY@63..69 "{{" [Newline("\n"), Newline("\n"), Whitespace(" ")] []
+ 1: HASH@69..70 "#" [] []
+ 2: GLIMMER_PATH@70..74
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@70..74
+ 0: GLIMMER_PATH_SEGMENT@70..74
+ 0: IDENT@70..74 "let" [] [Whitespace(" ")]
+ 3: GLIMMER_ARGUMENT_LIST@74..80
+ 0: GLIMMER_POSITIONAL_ARGUMENT@74..80
+ 0: GLIMMER_PATH@74..80
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@74..80
+ 0: GLIMMER_PATH_SEGMENT@74..80
+ 0: IDENT@74..80 "value" [] [Whitespace(" ")]
+ 4: GLIMMER_BLOCK_PARAMS@80..86
+ 0: IDENT@80..83 "as" [] [Whitespace(" ")]
+ 1: PIPE@83..84 "|" [] []
+ 2: GLIMMER_BLOCK_PARAM_LIST@84..85
+ 0: GLIMMER_BLOCK_PARAM@84..85
+ 0: IDENT@84..85 "v" [] []
+ 3: PIPE@85..86 "|" [] []
+ 5: R_DOUBLE_CURLY@86..88 "}}" [] []
+ 1: HTML_ELEMENT_LIST@88..98
+ 0: GLIMMER_MUSTACHE_EXPRESSION@88..98
+ 0: L_DOUBLE_CURLY@88..95 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@95..96
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@95..96
+ 0: GLIMMER_PATH_SEGMENT@95..96
+ 0: IDENT@95..96 "v" [] []
+ 2: GLIMMER_ARGUMENT_LIST@96..96
+ 3: R_DOUBLE_CURLY@96..98 "}}" [] []
+ 2: GLIMMER_BLOCK_HELPER_CLOSING@98..109
+ 0: L_DOUBLE_CURLY@98..103 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: SLASH@103..104 "/" [] []
+ 2: GLIMMER_PATH@104..107
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@104..107
+ 0: GLIMMER_PATH_SEGMENT@104..107
+ 0: IDENT@104..107 "let" [] []
+ 3: R_DOUBLE_CURLY@107..109 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@109..121
+ 0: L_ANGLE@109..111 "<" [Newline("\n")] []
+ 1: SLASH@111..112 "/" [] []
+ 2: HTML_TAG_NAME@112..120
+ 0: HTML_LITERAL@112..120 "template" [] []
+ 3: R_ANGLE@120..121 ">" [] []
+ 4: EOF@121..122 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs b/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs
new file mode 100644
index 000000000000..f2c718be8018
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs
@@ -0,0 +1,4 @@
+
+ {{doctype}}
+ {{DOCTYPE}}
+
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs.snap
new file mode 100644
index 000000000000..636cb7dc63fb
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/doctype_identifier.gjs.snap
@@ -0,0 +1,117 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+
+ {{doctype}}
+ {{DOCTYPE}}
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@0..1 "<" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@1..9 "template" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@9..10 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@15..22 "doctype" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@22..24 "}}" [] [],
+ },
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@24..29 "{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@29..36 "DOCTYPE" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@36..38 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@38..40 "<" [Newline("\n")] [],
+ slash_token: SLASH@40..41 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@41..49 "template" [] [],
+ },
+ r_angle_token: R_ANGLE@49..50 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@50..51 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..51
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..50
+ 0: HTML_ELEMENT@0..50
+ 0: HTML_OPENING_ELEMENT@0..10
+ 0: L_ANGLE@0..1 "<" [] []
+ 1: HTML_TAG_NAME@1..9
+ 0: HTML_LITERAL@1..9 "template" [] []
+ 2: HTML_ATTRIBUTE_LIST@9..9
+ 3: R_ANGLE@9..10 ">" [] []
+ 1: HTML_ELEMENT_LIST@10..38
+ 0: GLIMMER_MUSTACHE_EXPRESSION@10..24
+ 0: L_DOUBLE_CURLY@10..15 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@15..22
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@15..22
+ 0: GLIMMER_PATH_SEGMENT@15..22
+ 0: IDENT@15..22 "doctype" [] []
+ 2: GLIMMER_ARGUMENT_LIST@22..22
+ 3: R_DOUBLE_CURLY@22..24 "}}" [] []
+ 1: GLIMMER_MUSTACHE_EXPRESSION@24..38
+ 0: L_DOUBLE_CURLY@24..29 "{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@29..36
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@29..36
+ 0: GLIMMER_PATH_SEGMENT@29..36
+ 0: IDENT@29..36 "DOCTYPE" [] []
+ 2: GLIMMER_ARGUMENT_LIST@36..36
+ 3: R_DOUBLE_CURLY@36..38 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@38..50
+ 0: L_ANGLE@38..40 "<" [Newline("\n")] []
+ 1: SLASH@40..41 "/" [] []
+ 2: HTML_TAG_NAME@41..49
+ 0: HTML_LITERAL@41..49 "template" [] []
+ 3: R_ANGLE@49..50 ">" [] []
+ 4: EOF@50..51 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs
new file mode 100644
index 000000000000..77965fe122e1
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs
@@ -0,0 +1,6 @@
+{{this.title}}
+
+
{{@name}}
+
{{greeting "World"}}
+
{{helper arg1 arg2 key=value}}
+
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs.snap
new file mode 100644
index 000000000000..efe00b70b4dc
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache.gjs.snap
@@ -0,0 +1,334 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+{{this.title}}
+
+
{{@name}}
+
{{greeting "World"}}
+
{{helper arg1 arg2 key=value}}
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@0..2 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@2..6 "this" [] [],
+ },
+ DOT@6..7 "." [] [],
+ GlimmerPathSegment {
+ value_token_token: IDENT@7..12 "title" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@12..14 "}}" [] [],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@14..16 "<" [Newline("\n")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@16..19 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@19..20 ">" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@20..24 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@24..26 "h1" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@26..27 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@27..29 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: AT@29..30 "@" [] [],
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@30..34 "name" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly2_token_token: R_DOUBLE_CURLY@34..36 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@36..37 "<" [] [],
+ slash_token: SLASH@37..38 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@38..40 "h1" [] [],
+ },
+ r_angle_token: R_ANGLE@40..41 ">" [] [],
+ },
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@41..45 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@45..46 "p" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@46..47 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@47..49 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@49..58 "greeting" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerStringLiteral {
+ value_token_token: HTML_STRING_LITERAL@58..65 "\"World\"" [] [],
+ },
+ },
+ ],
+ r_curly2_token_token: R_DOUBLE_CURLY@65..67 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@67..68 "<" [] [],
+ slash_token: SLASH@68..69 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@69..70 "p" [] [],
+ },
+ r_angle_token: R_ANGLE@70..71 ">" [] [],
+ },
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@71..75 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@75..79 "span" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@79..80 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheExpression {
+ l_curly2_token_token: L_DOUBLE_CURLY@80..82 "{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@82..89 "helper" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@89..94 "arg1" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ GlimmerPositionalArgument {
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@94..99 "arg2" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ },
+ GlimmerNamedArgument {
+ name_token_token: IDENT@99..102 "key" [] [],
+ eq_token_token: EQ@102..103 "=" [] [],
+ value: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@103..108 "value" [] [],
+ },
+ ],
+ },
+ },
+ ],
+ r_curly2_token_token: R_DOUBLE_CURLY@108..110 "}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@110..111 "<" [] [],
+ slash_token: SLASH@111..112 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@112..116 "span" [] [],
+ },
+ r_angle_token: R_ANGLE@116..117 ">" [] [],
+ },
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@117..119 "<" [Newline("\n")] [],
+ slash_token: SLASH@119..120 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@120..123 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@123..124 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@124..125 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..125
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..124
+ 0: GLIMMER_MUSTACHE_EXPRESSION@0..14
+ 0: L_DOUBLE_CURLY@0..2 "{{" [] []
+ 1: GLIMMER_PATH@2..12
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@2..12
+ 0: GLIMMER_PATH_SEGMENT@2..6
+ 0: IDENT@2..6 "this" [] []
+ 1: DOT@6..7 "." [] []
+ 2: GLIMMER_PATH_SEGMENT@7..12
+ 0: IDENT@7..12 "title" [] []
+ 2: GLIMMER_ARGUMENT_LIST@12..12
+ 3: R_DOUBLE_CURLY@12..14 "}}" [] []
+ 1: HTML_ELEMENT@14..124
+ 0: HTML_OPENING_ELEMENT@14..20
+ 0: L_ANGLE@14..16 "<" [Newline("\n")] []
+ 1: HTML_TAG_NAME@16..19
+ 0: HTML_LITERAL@16..19 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@19..19
+ 3: R_ANGLE@19..20 ">" [] []
+ 1: HTML_ELEMENT_LIST@20..117
+ 0: HTML_ELEMENT@20..41
+ 0: HTML_OPENING_ELEMENT@20..27
+ 0: L_ANGLE@20..24 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@24..26
+ 0: HTML_LITERAL@24..26 "h1" [] []
+ 2: HTML_ATTRIBUTE_LIST@26..26
+ 3: R_ANGLE@26..27 ">" [] []
+ 1: HTML_ELEMENT_LIST@27..36
+ 0: GLIMMER_MUSTACHE_EXPRESSION@27..36
+ 0: L_DOUBLE_CURLY@27..29 "{{" [] []
+ 1: GLIMMER_PATH@29..34
+ 0: AT@29..30 "@" [] []
+ 1: GLIMMER_PATH_SEGMENT_LIST@30..34
+ 0: GLIMMER_PATH_SEGMENT@30..34
+ 0: IDENT@30..34 "name" [] []
+ 2: GLIMMER_ARGUMENT_LIST@34..34
+ 3: R_DOUBLE_CURLY@34..36 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@36..41
+ 0: L_ANGLE@36..37 "<" [] []
+ 1: SLASH@37..38 "/" [] []
+ 2: HTML_TAG_NAME@38..40
+ 0: HTML_LITERAL@38..40 "h1" [] []
+ 3: R_ANGLE@40..41 ">" [] []
+ 1: HTML_ELEMENT@41..71
+ 0: HTML_OPENING_ELEMENT@41..47
+ 0: L_ANGLE@41..45 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@45..46
+ 0: HTML_LITERAL@45..46 "p" [] []
+ 2: HTML_ATTRIBUTE_LIST@46..46
+ 3: R_ANGLE@46..47 ">" [] []
+ 1: HTML_ELEMENT_LIST@47..67
+ 0: GLIMMER_MUSTACHE_EXPRESSION@47..67
+ 0: L_DOUBLE_CURLY@47..49 "{{" [] []
+ 1: GLIMMER_PATH@49..58
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@49..58
+ 0: GLIMMER_PATH_SEGMENT@49..58
+ 0: IDENT@49..58 "greeting" [] [Whitespace(" ")]
+ 2: GLIMMER_ARGUMENT_LIST@58..65
+ 0: GLIMMER_POSITIONAL_ARGUMENT@58..65
+ 0: GLIMMER_STRING_LITERAL@58..65
+ 0: HTML_STRING_LITERAL@58..65 "\"World\"" [] []
+ 3: R_DOUBLE_CURLY@65..67 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@67..71
+ 0: L_ANGLE@67..68 "<" [] []
+ 1: SLASH@68..69 "/" [] []
+ 2: HTML_TAG_NAME@69..70
+ 0: HTML_LITERAL@69..70 "p" [] []
+ 3: R_ANGLE@70..71 ">" [] []
+ 2: HTML_ELEMENT@71..117
+ 0: HTML_OPENING_ELEMENT@71..80
+ 0: L_ANGLE@71..75 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@75..79
+ 0: HTML_LITERAL@75..79 "span" [] []
+ 2: HTML_ATTRIBUTE_LIST@79..79
+ 3: R_ANGLE@79..80 ">" [] []
+ 1: HTML_ELEMENT_LIST@80..110
+ 0: GLIMMER_MUSTACHE_EXPRESSION@80..110
+ 0: L_DOUBLE_CURLY@80..82 "{{" [] []
+ 1: GLIMMER_PATH@82..89
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@82..89
+ 0: GLIMMER_PATH_SEGMENT@82..89
+ 0: IDENT@82..89 "helper" [] [Whitespace(" ")]
+ 2: GLIMMER_ARGUMENT_LIST@89..108
+ 0: GLIMMER_POSITIONAL_ARGUMENT@89..94
+ 0: GLIMMER_PATH@89..94
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@89..94
+ 0: GLIMMER_PATH_SEGMENT@89..94
+ 0: IDENT@89..94 "arg1" [] [Whitespace(" ")]
+ 1: GLIMMER_POSITIONAL_ARGUMENT@94..99
+ 0: GLIMMER_PATH@94..99
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@94..99
+ 0: GLIMMER_PATH_SEGMENT@94..99
+ 0: IDENT@94..99 "arg2" [] [Whitespace(" ")]
+ 2: GLIMMER_NAMED_ARGUMENT@99..108
+ 0: IDENT@99..102 "key" [] []
+ 1: EQ@102..103 "=" [] []
+ 2: GLIMMER_PATH@103..108
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@103..108
+ 0: GLIMMER_PATH_SEGMENT@103..108
+ 0: IDENT@103..108 "value" [] []
+ 3: R_DOUBLE_CURLY@108..110 "}}" [] []
+ 2: HTML_CLOSING_ELEMENT@110..117
+ 0: L_ANGLE@110..111 "<" [] []
+ 1: SLASH@111..112 "/" [] []
+ 2: HTML_TAG_NAME@112..116
+ 0: HTML_LITERAL@112..116 "span" [] []
+ 3: R_ANGLE@116..117 ">" [] []
+ 2: HTML_CLOSING_ELEMENT@117..124
+ 0: L_ANGLE@117..119 "<" [Newline("\n")] []
+ 1: SLASH@119..120 "/" [] []
+ 2: HTML_TAG_NAME@120..123
+ 0: HTML_LITERAL@120..123 "div" [] []
+ 3: R_ANGLE@123..124 ">" [] []
+ 4: EOF@124..125 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html
new file mode 100644
index 000000000000..18fa86a0ab10
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html
@@ -0,0 +1,7 @@
+{{! This is a single-line comment }}
+
+ {{!-- This is a multi-line comment
+ that spans multiple lines
+ --}}
+
Hello World
+
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html.snap
new file mode 100644
index 000000000000..9c62af184843
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/mustache_comment.html.snap
@@ -0,0 +1,125 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```html
+{{! This is a single-line comment }}
+
+ {{!-- This is a multi-line comment
+ that spans multiple lines
+ --}}
+
Hello World
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ GlimmerMustacheComment {
+ comment_token_token: MUSTACHE_COMMENT@0..36 "{{! This is a single-line comment }}" [] [],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@36..38 "<" [Newline("\n")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@38..41 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@41..42 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerMustacheComment {
+ comment_token_token: MUSTACHE_COMMENT@42..116 "{{!-- This is a multi-line comment\n that spans multiple lines\n --}}" [Newline("\n"), Whitespace(" ")] [],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@116..120 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@120..121 "p" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@121..122 ">" [] [],
+ },
+ children: HtmlElementList [
+ HtmlContent {
+ value_token: HTML_LITERAL@122..133 "Hello World" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@133..134 "<" [] [],
+ slash_token: SLASH@134..135 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@135..136 "p" [] [],
+ },
+ r_angle_token: R_ANGLE@136..137 ">" [] [],
+ },
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@137..139 "<" [Newline("\n")] [],
+ slash_token: SLASH@139..140 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@140..143 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@143..144 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@144..145 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..145
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..144
+ 0: GLIMMER_MUSTACHE_COMMENT@0..36
+ 0: MUSTACHE_COMMENT@0..36 "{{! This is a single-line comment }}" [] []
+ 1: HTML_ELEMENT@36..144
+ 0: HTML_OPENING_ELEMENT@36..42
+ 0: L_ANGLE@36..38 "<" [Newline("\n")] []
+ 1: HTML_TAG_NAME@38..41
+ 0: HTML_LITERAL@38..41 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@41..41
+ 3: R_ANGLE@41..42 ">" [] []
+ 1: HTML_ELEMENT_LIST@42..137
+ 0: GLIMMER_MUSTACHE_COMMENT@42..116
+ 0: MUSTACHE_COMMENT@42..116 "{{!-- This is a multi-line comment\n that spans multiple lines\n --}}" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_ELEMENT@116..137
+ 0: HTML_OPENING_ELEMENT@116..122
+ 0: L_ANGLE@116..120 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@120..121
+ 0: HTML_LITERAL@120..121 "p" [] []
+ 2: HTML_ATTRIBUTE_LIST@121..121
+ 3: R_ANGLE@121..122 ">" [] []
+ 1: HTML_ELEMENT_LIST@122..133
+ 0: HTML_CONTENT@122..133
+ 0: HTML_LITERAL@122..133 "Hello World" [] []
+ 2: HTML_CLOSING_ELEMENT@133..137
+ 0: L_ANGLE@133..134 "<" [] []
+ 1: SLASH@134..135 "/" [] []
+ 2: HTML_TAG_NAME@135..136
+ 0: HTML_LITERAL@135..136 "p" [] []
+ 3: R_ANGLE@136..137 ">" [] []
+ 2: HTML_CLOSING_ELEMENT@137..144
+ 0: L_ANGLE@137..139 "<" [Newline("\n")] []
+ 1: SLASH@139..140 "/" [] []
+ 2: HTML_TAG_NAME@140..143
+ 0: HTML_LITERAL@140..143 "div" [] []
+ 3: R_ANGLE@143..144 ">" [] []
+ 4: EOF@144..145 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs b/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs
new file mode 100644
index 000000000000..372940d1ca15
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs
@@ -0,0 +1,5 @@
+{{{this.htmlContent}}}
+
+
{{{userProvidedHtml}}}
+ {{{helper "argument"}}}
+
diff --git a/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs.snap b/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs.snap
new file mode 100644
index 000000000000..a20400ba2795
--- /dev/null
+++ b/crates/biome_html_parser/tests/html_specs/ok/glimmer/triple_stash.gjs.snap
@@ -0,0 +1,190 @@
+---
+source: crates/biome_html_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```glimmer
+{{{this.htmlContent}}}
+
+
{{{userProvidedHtml}}}
+ {{{helper "argument"}}}
+
+
+```
+
+
+## AST
+
+```
+HtmlRoot {
+ bom_token: missing (optional),
+ frontmatter: missing (optional),
+ directive: missing (optional),
+ html: HtmlElementList [
+ GlimmerTripleStashExpression {
+ l_curly3_token_token: L_TRIPLE_CURLY@0..3 "{{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@3..7 "this" [] [],
+ },
+ DOT@7..8 "." [] [],
+ GlimmerPathSegment {
+ value_token_token: IDENT@8..19 "htmlContent" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly3_token_token: R_TRIPLE_CURLY@19..22 "}}}" [] [],
+ },
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@22..24 "<" [Newline("\n")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@24..27 "div" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@27..28 ">" [] [],
+ },
+ children: HtmlElementList [
+ HtmlElement {
+ opening_element: HtmlOpeningElement {
+ l_angle_token: L_ANGLE@28..32 "<" [Newline("\n"), Whitespace(" ")] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@32..33 "p" [] [],
+ },
+ attributes: HtmlAttributeList [],
+ r_angle_token: R_ANGLE@33..34 ">" [] [],
+ },
+ children: HtmlElementList [
+ GlimmerTripleStashExpression {
+ l_curly3_token_token: L_TRIPLE_CURLY@34..37 "{{{" [] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@37..53 "userProvidedHtml" [] [],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [],
+ r_curly3_token_token: R_TRIPLE_CURLY@53..56 "}}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@56..57 "<" [] [],
+ slash_token: SLASH@57..58 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@58..59 "p" [] [],
+ },
+ r_angle_token: R_ANGLE@59..60 ">" [] [],
+ },
+ },
+ GlimmerTripleStashExpression {
+ l_curly3_token_token: L_TRIPLE_CURLY@60..66 "{{{" [Newline("\n"), Whitespace(" ")] [],
+ path: GlimmerPath {
+ at_token_token: missing (optional),
+ segments: GlimmerPathSegmentList [
+ GlimmerPathSegment {
+ value_token_token: IDENT@66..73 "helper" [] [Whitespace(" ")],
+ },
+ ],
+ },
+ arguments: GlimmerArgumentList [
+ GlimmerPositionalArgument {
+ value: GlimmerStringLiteral {
+ value_token_token: HTML_STRING_LITERAL@73..83 "\"argument\"" [] [],
+ },
+ },
+ ],
+ r_curly3_token_token: R_TRIPLE_CURLY@83..86 "}}}" [] [],
+ },
+ ],
+ closing_element: HtmlClosingElement {
+ l_angle_token: L_ANGLE@86..88 "<" [Newline("\n")] [],
+ slash_token: SLASH@88..89 "/" [] [],
+ name: HtmlTagName {
+ value_token: HTML_LITERAL@89..92 "div" [] [],
+ },
+ r_angle_token: R_ANGLE@92..93 ">" [] [],
+ },
+ },
+ ],
+ eof_token: EOF@93..94 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: HTML_ROOT@0..94
+ 0: (empty)
+ 1: (empty)
+ 2: (empty)
+ 3: HTML_ELEMENT_LIST@0..93
+ 0: GLIMMER_TRIPLE_STASH_EXPRESSION@0..22
+ 0: L_TRIPLE_CURLY@0..3 "{{{" [] []
+ 1: GLIMMER_PATH@3..19
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@3..19
+ 0: GLIMMER_PATH_SEGMENT@3..7
+ 0: IDENT@3..7 "this" [] []
+ 1: DOT@7..8 "." [] []
+ 2: GLIMMER_PATH_SEGMENT@8..19
+ 0: IDENT@8..19 "htmlContent" [] []
+ 2: GLIMMER_ARGUMENT_LIST@19..19
+ 3: R_TRIPLE_CURLY@19..22 "}}}" [] []
+ 1: HTML_ELEMENT@22..93
+ 0: HTML_OPENING_ELEMENT@22..28
+ 0: L_ANGLE@22..24 "<" [Newline("\n")] []
+ 1: HTML_TAG_NAME@24..27
+ 0: HTML_LITERAL@24..27 "div" [] []
+ 2: HTML_ATTRIBUTE_LIST@27..27
+ 3: R_ANGLE@27..28 ">" [] []
+ 1: HTML_ELEMENT_LIST@28..86
+ 0: HTML_ELEMENT@28..60
+ 0: HTML_OPENING_ELEMENT@28..34
+ 0: L_ANGLE@28..32 "<" [Newline("\n"), Whitespace(" ")] []
+ 1: HTML_TAG_NAME@32..33
+ 0: HTML_LITERAL@32..33 "p" [] []
+ 2: HTML_ATTRIBUTE_LIST@33..33
+ 3: R_ANGLE@33..34 ">" [] []
+ 1: HTML_ELEMENT_LIST@34..56
+ 0: GLIMMER_TRIPLE_STASH_EXPRESSION@34..56
+ 0: L_TRIPLE_CURLY@34..37 "{{{" [] []
+ 1: GLIMMER_PATH@37..53
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@37..53
+ 0: GLIMMER_PATH_SEGMENT@37..53
+ 0: IDENT@37..53 "userProvidedHtml" [] []
+ 2: GLIMMER_ARGUMENT_LIST@53..53
+ 3: R_TRIPLE_CURLY@53..56 "}}}" [] []
+ 2: HTML_CLOSING_ELEMENT@56..60
+ 0: L_ANGLE@56..57 "<" [] []
+ 1: SLASH@57..58 "/" [] []
+ 2: HTML_TAG_NAME@58..59
+ 0: HTML_LITERAL@58..59 "p" [] []
+ 3: R_ANGLE@59..60 ">" [] []
+ 1: GLIMMER_TRIPLE_STASH_EXPRESSION@60..86
+ 0: L_TRIPLE_CURLY@60..66 "{{{" [Newline("\n"), Whitespace(" ")] []
+ 1: GLIMMER_PATH@66..73
+ 0: (empty)
+ 1: GLIMMER_PATH_SEGMENT_LIST@66..73
+ 0: GLIMMER_PATH_SEGMENT@66..73
+ 0: IDENT@66..73 "helper" [] [Whitespace(" ")]
+ 2: GLIMMER_ARGUMENT_LIST@73..83
+ 0: GLIMMER_POSITIONAL_ARGUMENT@73..83
+ 0: GLIMMER_STRING_LITERAL@73..83
+ 0: HTML_STRING_LITERAL@73..83 "\"argument\"" [] []
+ 3: R_TRIPLE_CURLY@83..86 "}}}" [] []
+ 2: HTML_CLOSING_ELEMENT@86..93
+ 0: L_ANGLE@86..88 "<" [Newline("\n")] []
+ 1: SLASH@88..89 "/" [] []
+ 2: HTML_TAG_NAME@89..92
+ 0: HTML_LITERAL@89..92 "div" [] []
+ 3: R_ANGLE@92..93 ">" [] []
+ 4: EOF@93..94 "" [Newline("\n")] []
+
+```
diff --git a/crates/biome_html_parser/tests/spec_test.rs b/crates/biome_html_parser/tests/spec_test.rs
index 8cd3853bae03..036a89960f53 100644
--- a/crates/biome_html_parser/tests/spec_test.rs
+++ b/crates/biome_html_parser/tests/spec_test.rs
@@ -44,6 +44,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_
HtmlVariant::Astro => "astro",
HtmlVariant::Vue => "vue",
HtmlVariant::Svelte => "svelte",
+ HtmlVariant::Glimmer => "glimmer",
};
writeln!(
snapshot,
@@ -142,10 +143,10 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_
#[ignore]
#[test]
pub fn quick_test() {
- let code = r#"{@debug something, something, something}
+ let code = r#"{{{this.htmlContent}}}
"#;
- let root = parse_html(code, (&HtmlFileSource::svelte()).into());
+ let root = parse_html(code, (&HtmlFileSource::glimmer()).into());
let syntax = root.syntax();
dbg!(&syntax, root.diagnostics(), root.has_errors());
if has_bogus_nodes_or_empty_slots(&syntax) {
diff --git a/crates/biome_html_parser/tests/spec_tests.rs b/crates/biome_html_parser/tests/spec_tests.rs
index 67a3919c310b..1951f8151371 100644
--- a/crates/biome_html_parser/tests/spec_tests.rs
+++ b/crates/biome_html_parser/tests/spec_tests.rs
@@ -6,11 +6,11 @@ use biome_html_syntax::ScriptType;
use biome_rowan::AstNode;
mod ok {
- tests_macros::gen_tests! {"tests/html_specs/ok/**/*.{html,astro,vue,svelte}", crate::spec_test::run, "ok"}
+ tests_macros::gen_tests! {"tests/html_specs/ok/**/*.{html,astro,vue,svelte,gjs,gts}", crate::spec_test::run, "ok"}
}
mod error {
- tests_macros::gen_tests! {"tests/html_specs/error/**/*.{html,astro,vue,svelte}", crate::spec_test::run, "error"}
+ tests_macros::gen_tests! {"tests/html_specs/error/**/*.{html,astro,vue,svelte,gjs,gts}", crate::spec_test::run, "error"}
}
#[test]
diff --git a/crates/biome_html_syntax/src/element_ext.rs b/crates/biome_html_syntax/src/element_ext.rs
index c3e08d5ef87e..378a0f0451bc 100644
--- a/crates/biome_html_syntax/src/element_ext.rs
+++ b/crates/biome_html_syntax/src/element_ext.rs
@@ -28,7 +28,9 @@ impl AnyHtmlElement {
Self::AnyHtmlContent(_)
| Self::HtmlBogusElement(_)
| Self::HtmlSelfClosingElement(_)
- | Self::HtmlCdataSection(_) => false,
+ | Self::HtmlCdataSection(_)
+ | Self::GlimmerMustacheComment(_)
+ | Self::GlimmerTripleStashExpression(_) => false,
Self::HtmlElement(element) => element.is_javascript_tag(),
}
}
@@ -38,7 +40,9 @@ impl AnyHtmlElement {
Self::AnyHtmlContent(_)
| Self::HtmlBogusElement(_)
| Self::HtmlSelfClosingElement(_)
- | Self::HtmlCdataSection(_) => false,
+ | Self::HtmlCdataSection(_)
+ | Self::GlimmerMustacheComment(_)
+ | Self::GlimmerTripleStashExpression(_) => false,
Self::HtmlElement(element) => element.is_style_tag(),
}
}
@@ -48,7 +52,11 @@ impl AnyHtmlElement {
Self::HtmlElement(element) => element.find_attribute_by_name(name_to_lookup),
Self::HtmlSelfClosingElement(element) => element.find_attribute_by_name(name_to_lookup),
// Other variants don't have attributes
- Self::AnyHtmlContent(_) | Self::HtmlBogusElement(_) | Self::HtmlCdataSection(_) => None,
+ Self::AnyHtmlContent(_)
+ | Self::HtmlBogusElement(_)
+ | Self::HtmlCdataSection(_)
+ | Self::GlimmerMustacheComment(_)
+ | Self::GlimmerTripleStashExpression(_) => None,
}
}
diff --git a/crates/biome_html_syntax/src/file_source.rs b/crates/biome_html_syntax/src/file_source.rs
index 2328c417f8d4..e5ea6027a085 100644
--- a/crates/biome_html_syntax/src/file_source.rs
+++ b/crates/biome_html_syntax/src/file_source.rs
@@ -31,6 +31,8 @@ pub enum HtmlVariant {
Vue,
/// Use this variant to parse a Svelte file
Svelte,
+ /// Use this variant to parse a Glimmer file (.gjs, .gts)
+ Glimmer,
}
impl Default for HtmlVariant {
@@ -99,6 +101,16 @@ impl HtmlFileSource {
}
}
+ pub fn glimmer() -> Self {
+ Self {
+ variant: HtmlVariant::Glimmer,
+ }
+ }
+
+ pub const fn is_glimmer(&self) -> bool {
+ matches!(self.variant, HtmlVariant::Glimmer)
+ }
+
/// Try to return the HTML file source corresponding to this file name from well-known files
pub fn try_from_well_known(path: &Utf8Path) -> Result {
let Some(extension) = path.extension() else {
@@ -116,6 +128,7 @@ impl HtmlFileSource {
"astro" => Ok(Self::astro()),
"vue" => Ok(Self::vue()),
"svelte" => Ok(Self::svelte()),
+ "gjs" | "gts" => Ok(Self::glimmer()),
_ => Err(FileSourceError::UnknownExtension),
}
}
@@ -135,6 +148,7 @@ impl HtmlFileSource {
"astro" => Ok(Self::astro()),
"vuejs" | "vue" => Ok(Self::vue()),
"svelte" => Ok(Self::svelte()),
+ "glimmer" | "glimmer-js" | "glimmer-ts" => Ok(Self::glimmer()),
_ => Err(FileSourceError::UnknownLanguageId),
}
}
@@ -156,3 +170,44 @@ impl TryFrom<&Utf8Path> for HtmlFileSource {
Self::try_from_extension(&extension.to_ascii_lowercase_cow())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_glimmer_variant() {
+ let glimmer_source = HtmlFileSource::glimmer();
+ assert!(glimmer_source.is_glimmer());
+ assert!(!glimmer_source.is_html());
+ assert!(!glimmer_source.is_vue());
+ assert!(!glimmer_source.is_svelte());
+ assert!(!glimmer_source.is_astro());
+ }
+
+ #[test]
+ fn test_glimmer_from_extension() {
+ let result = HtmlFileSource::try_from_extension("gjs");
+ assert!(result.is_ok());
+ assert!(result.unwrap().is_glimmer());
+
+ let result = HtmlFileSource::try_from_extension("gts");
+ assert!(result.is_ok());
+ assert!(result.unwrap().is_glimmer());
+ }
+
+ #[test]
+ fn test_glimmer_from_language_id() {
+ let result = HtmlFileSource::try_from_language_id("glimmer");
+ assert!(result.is_ok());
+ assert!(result.unwrap().is_glimmer());
+
+ let result = HtmlFileSource::try_from_language_id("glimmer-js");
+ assert!(result.is_ok());
+ assert!(result.unwrap().is_glimmer());
+
+ let result = HtmlFileSource::try_from_language_id("glimmer-ts");
+ assert!(result.is_ok());
+ assert!(result.unwrap().is_glimmer());
+ }
+}
diff --git a/crates/biome_html_syntax/src/generated/kind.rs b/crates/biome_html_syntax/src/generated/kind.rs
index abf41fbd3280..e1803eaeab78 100644
--- a/crates/biome_html_syntax/src/generated/kind.rs
+++ b/crates/biome_html_syntax/src/generated/kind.rs
@@ -30,6 +30,14 @@ pub enum HtmlSyntaxKind {
SV_CURLY_SLASH,
SV_CURLY_COLON,
COMMA,
+ HASH,
+ DOT,
+ PIPE,
+ AT,
+ DOTDOTDOT,
+ L_PAREN,
+ R_PAREN,
+ COLON,
NULL_KW,
TRUE_KW,
FALSE_KW,
@@ -44,6 +52,9 @@ pub enum HtmlSyntaxKind {
IDENT,
HTML_IDENT,
SVELTE_IDENT,
+ MUSTACHE_COMMENT,
+ L_TRIPLE_CURLY,
+ R_TRIPLE_CURLY,
HTML_ROOT,
HTML_DIRECTIVE,
HTML_SELF_CLOSING_TAG,
@@ -70,12 +81,36 @@ pub enum HtmlSyntaxKind {
SVELTE_DEBUG_BLOCK,
SVELTE_BINDING_LIST,
SVELTE_NAME,
+ GLIMMER_MUSTACHE_EXPRESSION,
+ GLIMMER_MUSTACHE_COMMENT,
+ GLIMMER_TRIPLE_STASH_EXPRESSION,
+ GLIMMER_BLOCK_HELPER,
+ GLIMMER_BLOCK_HELPER_OPENING,
+ GLIMMER_BLOCK_HELPER_CLOSING,
+ GLIMMER_BLOCK_PARAMS,
+ GLIMMER_BLOCK_PARAM_LIST,
+ GLIMMER_BLOCK_PARAM,
+ GLIMMER_PATH,
+ GLIMMER_PATH_SEGMENT_LIST,
+ GLIMMER_PATH_SEGMENT,
+ GLIMMER_ARGUMENT_LIST,
+ GLIMMER_POSITIONAL_ARGUMENT,
+ GLIMMER_NAMED_ARGUMENT,
+ GLIMMER_SUBEXPRESSION,
+ GLIMMER_STRING_LITERAL,
+ GLIMMER_LITERAL,
+ GLIMMER_SPLATTRIBUTE,
+ GLIMMER_ELEMENT_MODIFIER,
+ GLIMMER_NAMED_BLOCK,
+ GLIMMER_NAMED_BLOCK_OPENING,
+ GLIMMER_NAMED_BLOCK_CLOSING,
HTML_BOGUS,
HTML_BOGUS_ELEMENT,
HTML_BOGUS_ATTRIBUTE,
HTML_BOGUS_TEXT_EXPRESSION,
ASTRO_BOGUS_FRONTMATTER,
SVELTE_BOGUS_BLOCK,
+ GLIMMER_BOGUS_EXPRESSION,
#[doc(hidden)]
__LAST,
}
@@ -102,6 +137,14 @@ impl HtmlSyntaxKind {
| SV_CURLY_SLASH
| SV_CURLY_COLON
| COMMA
+ | HASH
+ | DOT
+ | PIPE
+ | AT
+ | DOTDOTDOT
+ | L_PAREN
+ | R_PAREN
+ | COLON
)
}
pub const fn is_literal(self) -> bool {
@@ -110,7 +153,12 @@ impl HtmlSyntaxKind {
pub const fn is_list(self) -> bool {
matches!(
self,
- HTML_ELEMENT_LIST | HTML_ATTRIBUTE_LIST | SVELTE_BINDING_LIST
+ HTML_ELEMENT_LIST
+ | HTML_ATTRIBUTE_LIST
+ | SVELTE_BINDING_LIST
+ | GLIMMER_BLOCK_PARAM_LIST
+ | GLIMMER_PATH_SEGMENT_LIST
+ | GLIMMER_ARGUMENT_LIST
)
}
pub fn from_keyword(ident: &str) -> Option {
@@ -145,6 +193,14 @@ impl HtmlSyntaxKind {
SV_CURLY_SLASH => "{/",
SV_CURLY_COLON => "{:",
COMMA => ",",
+ HASH => "#",
+ DOT => ".",
+ PIPE => "|",
+ AT => "@",
+ DOTDOTDOT => "...",
+ L_PAREN => "(",
+ R_PAREN => ")",
+ COLON => ":",
NULL_KW => "null",
TRUE_KW => "true",
FALSE_KW => "false",
@@ -160,4 +216,4 @@ impl HtmlSyntaxKind {
}
#[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"]
#[macro_export]
-macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [" { $ crate :: HtmlSyntaxKind :: CDATA_START } ; ["]]>"] => { $ crate :: HtmlSyntaxKind :: CDATA_END } ; [---] => { $ crate :: HtmlSyntaxKind :: FENCE } ; ['{'] => { $ crate :: HtmlSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: HtmlSyntaxKind :: R_CURLY } ; ["{{"] => { $ crate :: HtmlSyntaxKind :: L_DOUBLE_CURLY } ; ["}}"] => { $ crate :: HtmlSyntaxKind :: R_DOUBLE_CURLY } ; ["{@"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_AT } ; ["{#"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_HASH } ; ["{/"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_SLASH } ; ["{:"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_COLON } ; [,] => { $ crate :: HtmlSyntaxKind :: COMMA } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [debug] => { $ crate :: HtmlSyntaxKind :: DEBUG_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; }
+macro_rules ! T { [<] => { $ crate :: HtmlSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: HtmlSyntaxKind :: R_ANGLE } ; [/] => { $ crate :: HtmlSyntaxKind :: SLASH } ; [=] => { $ crate :: HtmlSyntaxKind :: EQ } ; [!] => { $ crate :: HtmlSyntaxKind :: BANG } ; [-] => { $ crate :: HtmlSyntaxKind :: MINUS } ; [" { $ crate :: HtmlSyntaxKind :: CDATA_START } ; ["]]>"] => { $ crate :: HtmlSyntaxKind :: CDATA_END } ; [---] => { $ crate :: HtmlSyntaxKind :: FENCE } ; ['{'] => { $ crate :: HtmlSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: HtmlSyntaxKind :: R_CURLY } ; ["{{"] => { $ crate :: HtmlSyntaxKind :: L_DOUBLE_CURLY } ; ["}}"] => { $ crate :: HtmlSyntaxKind :: R_DOUBLE_CURLY } ; ["{@"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_AT } ; ["{#"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_HASH } ; ["{/"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_SLASH } ; ["{:"] => { $ crate :: HtmlSyntaxKind :: SV_CURLY_COLON } ; [,] => { $ crate :: HtmlSyntaxKind :: COMMA } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; [.] => { $ crate :: HtmlSyntaxKind :: DOT } ; [|] => { $ crate :: HtmlSyntaxKind :: PIPE } ; [@] => { $ crate :: HtmlSyntaxKind :: AT } ; [...] => { $ crate :: HtmlSyntaxKind :: DOTDOTDOT } ; ['('] => { $ crate :: HtmlSyntaxKind :: L_PAREN } ; [')'] => { $ crate :: HtmlSyntaxKind :: R_PAREN } ; [:] => { $ crate :: HtmlSyntaxKind :: COLON } ; [null] => { $ crate :: HtmlSyntaxKind :: NULL_KW } ; [true] => { $ crate :: HtmlSyntaxKind :: TRUE_KW } ; [false] => { $ crate :: HtmlSyntaxKind :: FALSE_KW } ; [doctype] => { $ crate :: HtmlSyntaxKind :: DOCTYPE_KW } ; [html] => { $ crate :: HtmlSyntaxKind :: HTML_KW } ; [debug] => { $ crate :: HtmlSyntaxKind :: DEBUG_KW } ; [ident] => { $ crate :: HtmlSyntaxKind :: IDENT } ; [EOF] => { $ crate :: HtmlSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: HtmlSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: HtmlSyntaxKind :: HASH } ; }
diff --git a/crates/biome_html_syntax/src/generated/macros.rs b/crates/biome_html_syntax/src/generated/macros.rs
index 104c566738e0..0a039462d9ac 100644
--- a/crates/biome_html_syntax/src/generated/macros.rs
+++ b/crates/biome_html_syntax/src/generated/macros.rs
@@ -24,6 +24,91 @@ macro_rules! map_syntax_node {
let $pattern = unsafe { $crate::AstroFrontmatterElement::new_unchecked(node) };
$body
}
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_HELPER => {
+ let $pattern = unsafe { $crate::GlimmerBlockHelper::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_HELPER_CLOSING => {
+ let $pattern =
+ unsafe { $crate::GlimmerBlockHelperClosing::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_HELPER_OPENING => {
+ let $pattern =
+ unsafe { $crate::GlimmerBlockHelperOpening::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_PARAM => {
+ let $pattern = unsafe { $crate::GlimmerBlockParam::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_PARAMS => {
+ let $pattern = unsafe { $crate::GlimmerBlockParams::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_ELEMENT_MODIFIER => {
+ let $pattern = unsafe { $crate::GlimmerElementModifier::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_LITERAL => {
+ let $pattern = unsafe { $crate::GlimmerLiteral::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_MUSTACHE_COMMENT => {
+ let $pattern = unsafe { $crate::GlimmerMustacheComment::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_MUSTACHE_EXPRESSION => {
+ let $pattern =
+ unsafe { $crate::GlimmerMustacheExpression::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_NAMED_ARGUMENT => {
+ let $pattern = unsafe { $crate::GlimmerNamedArgument::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_NAMED_BLOCK => {
+ let $pattern = unsafe { $crate::GlimmerNamedBlock::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_NAMED_BLOCK_CLOSING => {
+ let $pattern = unsafe { $crate::GlimmerNamedBlockClosing::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_NAMED_BLOCK_OPENING => {
+ let $pattern = unsafe { $crate::GlimmerNamedBlockOpening::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_PATH => {
+ let $pattern = unsafe { $crate::GlimmerPath::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_PATH_SEGMENT => {
+ let $pattern = unsafe { $crate::GlimmerPathSegment::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_POSITIONAL_ARGUMENT => {
+ let $pattern =
+ unsafe { $crate::GlimmerPositionalArgument::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_SPLATTRIBUTE => {
+ let $pattern = unsafe { $crate::GlimmerSplattribute::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_STRING_LITERAL => {
+ let $pattern = unsafe { $crate::GlimmerStringLiteral::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_SUBEXPRESSION => {
+ let $pattern = unsafe { $crate::GlimmerSubexpression::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_TRIPLE_STASH_EXPRESSION => {
+ let $pattern =
+ unsafe { $crate::GlimmerTripleStashExpression::new_unchecked(node) };
+ $body
+ }
$crate::HtmlSyntaxKind::HTML_ATTRIBUTE => {
let $pattern = unsafe { $crate::HtmlAttribute::new_unchecked(node) };
$body
@@ -105,6 +190,10 @@ macro_rules! map_syntax_node {
let $pattern = unsafe { $crate::AstroBogusFrontmatter::new_unchecked(node) };
$body
}
+ $crate::HtmlSyntaxKind::GLIMMER_BOGUS_EXPRESSION => {
+ let $pattern = unsafe { $crate::GlimmerBogusExpression::new_unchecked(node) };
+ $body
+ }
$crate::HtmlSyntaxKind::HTML_BOGUS => {
let $pattern = unsafe { $crate::HtmlBogus::new_unchecked(node) };
$body
@@ -125,6 +214,18 @@ macro_rules! map_syntax_node {
let $pattern = unsafe { $crate::SvelteBogusBlock::new_unchecked(node) };
$body
}
+ $crate::HtmlSyntaxKind::GLIMMER_ARGUMENT_LIST => {
+ let $pattern = unsafe { $crate::GlimmerArgumentList::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_BLOCK_PARAM_LIST => {
+ let $pattern = unsafe { $crate::GlimmerBlockParamList::new_unchecked(node) };
+ $body
+ }
+ $crate::HtmlSyntaxKind::GLIMMER_PATH_SEGMENT_LIST => {
+ let $pattern = unsafe { $crate::GlimmerPathSegmentList::new_unchecked(node) };
+ $body
+ }
$crate::HtmlSyntaxKind::HTML_ATTRIBUTE_LIST => {
let $pattern = unsafe { $crate::HtmlAttributeList::new_unchecked(node) };
$body
diff --git a/crates/biome_html_syntax/src/generated/nodes.rs b/crates/biome_html_syntax/src/generated/nodes.rs
index 188d31eeada9..199a8fa33098 100644
--- a/crates/biome_html_syntax/src/generated/nodes.rs
+++ b/crates/biome_html_syntax/src/generated/nodes.rs
@@ -100,10 +100,10 @@ pub struct AstroFrontmatterElementFields {
pub r_fence_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlAttribute {
+pub struct GlimmerBlockHelper {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlAttribute {
+impl GlimmerBlockHelper {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -113,20 +113,24 @@ impl HtmlAttribute {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlAttributeFields {
- HtmlAttributeFields {
- name: self.name(),
- initializer: self.initializer(),
+ pub fn as_fields(&self) -> GlimmerBlockHelperFields {
+ GlimmerBlockHelperFields {
+ opening: self.opening(),
+ children: self.children(),
+ closing: self.closing(),
}
}
- pub fn name(&self) -> SyntaxResult {
+ pub fn opening(&self) -> SyntaxResult {
support::required_node(&self.syntax, 0usize)
}
- pub fn initializer(&self) -> Option {
- support::node(&self.syntax, 1usize)
+ pub fn children(&self) -> HtmlElementList {
+ support::list(&self.syntax, 1usize)
+ }
+ pub fn closing(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 2usize)
}
}
-impl Serialize for HtmlAttribute {
+impl Serialize for GlimmerBlockHelper {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -135,15 +139,16 @@ impl Serialize for HtmlAttribute {
}
}
#[derive(Serialize)]
-pub struct HtmlAttributeFields {
- pub name: SyntaxResult,
- pub initializer: Option,
+pub struct GlimmerBlockHelperFields {
+ pub opening: SyntaxResult,
+ pub children: HtmlElementList,
+ pub closing: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlAttributeInitializerClause {
+pub struct GlimmerBlockHelperClosing {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlAttributeInitializerClause {
+impl GlimmerBlockHelperClosing {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -153,20 +158,28 @@ impl HtmlAttributeInitializerClause {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlAttributeInitializerClauseFields {
- HtmlAttributeInitializerClauseFields {
- eq_token: self.eq_token(),
- value: self.value(),
+ pub fn as_fields(&self) -> GlimmerBlockHelperClosingFields {
+ GlimmerBlockHelperClosingFields {
+ l_curly2_token_token: self.l_curly2_token_token(),
+ slash_token_token: self.slash_token_token(),
+ helper: self.helper(),
+ r_curly2_token_token: self.r_curly2_token_token(),
}
}
- pub fn eq_token(&self) -> SyntaxResult {
+ pub fn l_curly2_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn value(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 1usize)
+ pub fn slash_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
+ }
+ pub fn helper(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 2usize)
+ }
+ pub fn r_curly2_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 3usize)
}
}
-impl Serialize for HtmlAttributeInitializerClause {
+impl Serialize for GlimmerBlockHelperClosing {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -175,15 +188,17 @@ impl Serialize for HtmlAttributeInitializerClause {
}
}
#[derive(Serialize)]
-pub struct HtmlAttributeInitializerClauseFields {
- pub eq_token: SyntaxResult,
- pub value: SyntaxResult,
+pub struct GlimmerBlockHelperClosingFields {
+ pub l_curly2_token_token: SyntaxResult,
+ pub slash_token_token: SyntaxResult,
+ pub helper: SyntaxResult,
+ pub r_curly2_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlAttributeName {
+pub struct GlimmerBlockHelperOpening {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlAttributeName {
+impl GlimmerBlockHelperOpening {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -193,16 +208,36 @@ impl HtmlAttributeName {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlAttributeNameFields {
- HtmlAttributeNameFields {
- value_token: self.value_token(),
+ pub fn as_fields(&self) -> GlimmerBlockHelperOpeningFields {
+ GlimmerBlockHelperOpeningFields {
+ l_curly2_token_token: self.l_curly2_token_token(),
+ hash_token_token: self.hash_token_token(),
+ helper: self.helper(),
+ arguments: self.arguments(),
+ block_params: self.block_params(),
+ r_curly2_token_token: self.r_curly2_token_token(),
}
}
- pub fn value_token(&self) -> SyntaxResult {
+ pub fn l_curly2_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
+ pub fn hash_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
+ }
+ pub fn helper(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 2usize)
+ }
+ pub fn arguments(&self) -> GlimmerArgumentList {
+ support::list(&self.syntax, 3usize)
+ }
+ pub fn block_params(&self) -> Option {
+ support::node(&self.syntax, 4usize)
+ }
+ pub fn r_curly2_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 5usize)
+ }
}
-impl Serialize for HtmlAttributeName {
+impl Serialize for GlimmerBlockHelperOpening {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -211,14 +246,19 @@ impl Serialize for HtmlAttributeName {
}
}
#[derive(Serialize)]
-pub struct HtmlAttributeNameFields {
- pub value_token: SyntaxResult,
+pub struct GlimmerBlockHelperOpeningFields {
+ pub l_curly2_token_token: SyntaxResult,
+ pub hash_token_token: SyntaxResult,
+ pub helper: SyntaxResult,
+ pub arguments: GlimmerArgumentList,
+ pub block_params: Option,
+ pub r_curly2_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlCdataSection {
+pub struct GlimmerBlockParam {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlCdataSection {
+impl GlimmerBlockParam {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -228,24 +268,16 @@ impl HtmlCdataSection {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlCdataSectionFields {
- HtmlCdataSectionFields {
- cdata_start_token: self.cdata_start_token(),
- content_token: self.content_token(),
- cdata_end_token: self.cdata_end_token(),
+ pub fn as_fields(&self) -> GlimmerBlockParamFields {
+ GlimmerBlockParamFields {
+ name_token_token: self.name_token_token(),
}
}
- pub fn cdata_start_token(&self) -> SyntaxResult {
+ pub fn name_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn content_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 1usize)
- }
- pub fn cdata_end_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 2usize)
- }
}
-impl Serialize for HtmlCdataSection {
+impl Serialize for GlimmerBlockParam {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -254,16 +286,14 @@ impl Serialize for HtmlCdataSection {
}
}
#[derive(Serialize)]
-pub struct HtmlCdataSectionFields {
- pub cdata_start_token: SyntaxResult,
- pub content_token: SyntaxResult,
- pub cdata_end_token: SyntaxResult,
+pub struct GlimmerBlockParamFields {
+ pub name_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlClosingElement {
+pub struct GlimmerBlockParams {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlClosingElement {
+impl GlimmerBlockParams {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -273,28 +303,28 @@ impl HtmlClosingElement {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlClosingElementFields {
- HtmlClosingElementFields {
- l_angle_token: self.l_angle_token(),
- slash_token: self.slash_token(),
- name: self.name(),
- r_angle_token: self.r_angle_token(),
+ pub fn as_fields(&self) -> GlimmerBlockParamsFields {
+ GlimmerBlockParamsFields {
+ as_token_token: self.as_token_token(),
+ l_pipe_token_token: self.l_pipe_token_token(),
+ params: self.params(),
+ r_pipe_token_token: self.r_pipe_token_token(),
}
}
- pub fn l_angle_token(&self) -> SyntaxResult {
+ pub fn as_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn slash_token(&self) -> SyntaxResult {
+ pub fn l_pipe_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 1usize)
}
- pub fn name(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 2usize)
+ pub fn params(&self) -> GlimmerBlockParamList {
+ support::list(&self.syntax, 2usize)
}
- pub fn r_angle_token(&self) -> SyntaxResult {
+ pub fn r_pipe_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 3usize)
}
}
-impl Serialize for HtmlClosingElement {
+impl Serialize for GlimmerBlockParams {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -303,17 +333,17 @@ impl Serialize for HtmlClosingElement {
}
}
#[derive(Serialize)]
-pub struct HtmlClosingElementFields {
- pub l_angle_token: SyntaxResult,
- pub slash_token: SyntaxResult,
- pub name: SyntaxResult,
- pub r_angle_token: SyntaxResult,
+pub struct GlimmerBlockParamsFields {
+ pub as_token_token: SyntaxResult,
+ pub l_pipe_token_token: SyntaxResult,
+ pub params: GlimmerBlockParamList,
+ pub r_pipe_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlContent {
+pub struct GlimmerElementModifier {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlContent {
+impl GlimmerElementModifier {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -323,16 +353,28 @@ impl HtmlContent {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlContentFields {
- HtmlContentFields {
- value_token: self.value_token(),
+ pub fn as_fields(&self) -> GlimmerElementModifierFields {
+ GlimmerElementModifierFields {
+ l_curly2_token_token: self.l_curly2_token_token(),
+ path: self.path(),
+ arguments: self.arguments(),
+ r_curly2_token_token: self.r_curly2_token_token(),
}
}
- pub fn value_token(&self) -> SyntaxResult {
+ pub fn l_curly2_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
+ pub fn path(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 1usize)
+ }
+ pub fn arguments(&self) -> GlimmerArgumentList {
+ support::list(&self.syntax, 2usize)
+ }
+ pub fn r_curly2_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 3usize)
+ }
}
-impl Serialize for HtmlContent {
+impl Serialize for GlimmerElementModifier {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -341,14 +383,17 @@ impl Serialize for HtmlContent {
}
}
#[derive(Serialize)]
-pub struct HtmlContentFields {
- pub value_token: SyntaxResult,
+pub struct GlimmerElementModifierFields {
+ pub l_curly2_token_token: SyntaxResult,
+ pub path: SyntaxResult,
+ pub arguments: GlimmerArgumentList,
+ pub r_curly2_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlDirective {
+pub struct GlimmerLiteral {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlDirective {
+impl GlimmerLiteral {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -358,44 +403,51 @@ impl HtmlDirective {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlDirectiveFields {
- HtmlDirectiveFields {
- l_angle_token: self.l_angle_token(),
- excl_token: self.excl_token(),
- doctype_token: self.doctype_token(),
- html_token: self.html_token(),
- quirk_token: self.quirk_token(),
- public_id_token: self.public_id_token(),
- system_id_token: self.system_id_token(),
- r_angle_token: self.r_angle_token(),
+ pub fn as_fields(&self) -> GlimmerLiteralFields {
+ GlimmerLiteralFields {
+ value_token_token: self.value_token_token(),
}
}
- pub fn l_angle_token(&self) -> SyntaxResult {
+ pub fn value_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn excl_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 1usize)
- }
- pub fn doctype_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 2usize)
- }
- pub fn html_token(&self) -> Option {
- support::token(&self.syntax, 3usize)
- }
- pub fn quirk_token(&self) -> Option {
- support::token(&self.syntax, 4usize)
+}
+impl Serialize for GlimmerLiteral {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ self.as_fields().serialize(serializer)
}
- pub fn public_id_token(&self) -> Option {
- support::token(&self.syntax, 5usize)
+}
+#[derive(Serialize)]
+pub struct GlimmerLiteralFields {
+ pub value_token_token: SyntaxResult,
+}
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct GlimmerMustacheComment {
+ pub(crate) syntax: SyntaxNode,
+}
+impl GlimmerMustacheComment {
+ #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
+ #[doc = r""]
+ #[doc = r" # Safety"]
+ #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
+ #[doc = r" or a match on [SyntaxNode::kind]"]
+ #[inline]
+ pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
+ Self { syntax }
}
- pub fn system_id_token(&self) -> Option {
- support::token(&self.syntax, 6usize)
+ pub fn as_fields(&self) -> GlimmerMustacheCommentFields {
+ GlimmerMustacheCommentFields {
+ comment_token_token: self.comment_token_token(),
+ }
}
- pub fn r_angle_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 7usize)
+ pub fn comment_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 0usize)
}
}
-impl Serialize for HtmlDirective {
+impl Serialize for GlimmerMustacheComment {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -404,21 +456,14 @@ impl Serialize for HtmlDirective {
}
}
#[derive(Serialize)]
-pub struct HtmlDirectiveFields {
- pub l_angle_token: SyntaxResult,
- pub excl_token: SyntaxResult,
- pub doctype_token: SyntaxResult,
- pub html_token: Option,
- pub quirk_token: Option,
- pub public_id_token: Option,
- pub system_id_token: Option,
- pub r_angle_token: SyntaxResult,
+pub struct GlimmerMustacheCommentFields {
+ pub comment_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlDoubleTextExpression {
+pub struct GlimmerMustacheExpression {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlDoubleTextExpression {
+impl GlimmerMustacheExpression {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -428,24 +473,28 @@ impl HtmlDoubleTextExpression {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlDoubleTextExpressionFields {
- HtmlDoubleTextExpressionFields {
- l_double_curly_token: self.l_double_curly_token(),
- expression: self.expression(),
- r_double_curly_token: self.r_double_curly_token(),
+ pub fn as_fields(&self) -> GlimmerMustacheExpressionFields {
+ GlimmerMustacheExpressionFields {
+ l_curly2_token_token: self.l_curly2_token_token(),
+ path: self.path(),
+ arguments: self.arguments(),
+ r_curly2_token_token: self.r_curly2_token_token(),
}
}
- pub fn l_double_curly_token(&self) -> SyntaxResult {
+ pub fn l_curly2_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn expression(&self) -> SyntaxResult {
+ pub fn path(&self) -> SyntaxResult {
support::required_node(&self.syntax, 1usize)
}
- pub fn r_double_curly_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 2usize)
+ pub fn arguments(&self) -> GlimmerArgumentList {
+ support::list(&self.syntax, 2usize)
+ }
+ pub fn r_curly2_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 3usize)
}
}
-impl Serialize for HtmlDoubleTextExpression {
+impl Serialize for GlimmerMustacheExpression {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -454,16 +503,17 @@ impl Serialize for HtmlDoubleTextExpression {
}
}
#[derive(Serialize)]
-pub struct HtmlDoubleTextExpressionFields {
- pub l_double_curly_token: SyntaxResult,
- pub expression: SyntaxResult,
- pub r_double_curly_token: SyntaxResult,
+pub struct GlimmerMustacheExpressionFields {
+ pub l_curly2_token_token: SyntaxResult,
+ pub path: SyntaxResult,
+ pub arguments: GlimmerArgumentList,
+ pub r_curly2_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlElement {
+pub struct GlimmerNamedArgument {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlElement {
+impl GlimmerNamedArgument {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -473,24 +523,24 @@ impl HtmlElement {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlElementFields {
- HtmlElementFields {
- opening_element: self.opening_element(),
- children: self.children(),
- closing_element: self.closing_element(),
+ pub fn as_fields(&self) -> GlimmerNamedArgumentFields {
+ GlimmerNamedArgumentFields {
+ name_token_token: self.name_token_token(),
+ eq_token_token: self.eq_token_token(),
+ value: self.value(),
}
}
- pub fn opening_element(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 0usize)
+ pub fn name_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 0usize)
}
- pub fn children(&self) -> HtmlElementList {
- support::list(&self.syntax, 1usize)
+ pub fn eq_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
}
- pub fn closing_element(&self) -> SyntaxResult {
+ pub fn value(&self) -> SyntaxResult {
support::required_node(&self.syntax, 2usize)
}
}
-impl Serialize for HtmlElement {
+impl Serialize for GlimmerNamedArgument {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -499,16 +549,16 @@ impl Serialize for HtmlElement {
}
}
#[derive(Serialize)]
-pub struct HtmlElementFields {
- pub opening_element: SyntaxResult,
- pub children: HtmlElementList,
- pub closing_element: SyntaxResult,
+pub struct GlimmerNamedArgumentFields {
+ pub name_token_token: SyntaxResult,
+ pub eq_token_token: SyntaxResult,
+ pub value: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlEmbeddedContent {
+pub struct GlimmerNamedBlock {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlEmbeddedContent {
+impl GlimmerNamedBlock {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -518,16 +568,24 @@ impl HtmlEmbeddedContent {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlEmbeddedContentFields {
- HtmlEmbeddedContentFields {
- value_token: self.value_token(),
+ pub fn as_fields(&self) -> GlimmerNamedBlockFields {
+ GlimmerNamedBlockFields {
+ opening: self.opening(),
+ children: self.children(),
+ closing: self.closing(),
}
}
- pub fn value_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 0usize)
+ pub fn opening(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 0usize)
+ }
+ pub fn children(&self) -> HtmlElementList {
+ support::list(&self.syntax, 1usize)
+ }
+ pub fn closing(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 2usize)
}
}
-impl Serialize for HtmlEmbeddedContent {
+impl Serialize for GlimmerNamedBlock {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -536,14 +594,16 @@ impl Serialize for HtmlEmbeddedContent {
}
}
#[derive(Serialize)]
-pub struct HtmlEmbeddedContentFields {
- pub value_token: SyntaxResult,
+pub struct GlimmerNamedBlockFields {
+ pub opening: SyntaxResult,
+ pub children: HtmlElementList,
+ pub closing: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlOpeningElement {
+pub struct GlimmerNamedBlockClosing {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlOpeningElement {
+impl GlimmerNamedBlockClosing {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -553,28 +613,32 @@ impl HtmlOpeningElement {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlOpeningElementFields {
- HtmlOpeningElementFields {
- l_angle_token: self.l_angle_token(),
- name: self.name(),
- attributes: self.attributes(),
- r_angle_token: self.r_angle_token(),
+ pub fn as_fields(&self) -> GlimmerNamedBlockClosingFields {
+ GlimmerNamedBlockClosingFields {
+ l_angle_token_token: self.l_angle_token_token(),
+ slash_token_token: self.slash_token_token(),
+ colon_token_token: self.colon_token_token(),
+ name_token_token: self.name_token_token(),
+ r_angle_token_token: self.r_angle_token_token(),
}
}
- pub fn l_angle_token(&self) -> SyntaxResult {
+ pub fn l_angle_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn name(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 1usize)
+ pub fn slash_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
}
- pub fn attributes(&self) -> HtmlAttributeList {
- support::list(&self.syntax, 2usize)
+ pub fn colon_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 2usize)
}
- pub fn r_angle_token(&self) -> SyntaxResult {
+ pub fn name_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 3usize)
}
+ pub fn r_angle_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 4usize)
+ }
}
-impl Serialize for HtmlOpeningElement {
+impl Serialize for GlimmerNamedBlockClosing {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -583,17 +647,18 @@ impl Serialize for HtmlOpeningElement {
}
}
#[derive(Serialize)]
-pub struct HtmlOpeningElementFields {
- pub l_angle_token: SyntaxResult,
- pub name: SyntaxResult,
- pub attributes: HtmlAttributeList,
- pub r_angle_token: SyntaxResult,
+pub struct GlimmerNamedBlockClosingFields {
+ pub l_angle_token_token: SyntaxResult,
+ pub slash_token_token: SyntaxResult,
+ pub colon_token_token: SyntaxResult,
+ pub name_token_token: SyntaxResult,
+ pub r_angle_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlRoot {
+pub struct GlimmerNamedBlockOpening {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlRoot {
+impl GlimmerNamedBlockOpening {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -603,32 +668,32 @@ impl HtmlRoot {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlRootFields {
- HtmlRootFields {
- bom_token: self.bom_token(),
- frontmatter: self.frontmatter(),
- directive: self.directive(),
- html: self.html(),
- eof_token: self.eof_token(),
+ pub fn as_fields(&self) -> GlimmerNamedBlockOpeningFields {
+ GlimmerNamedBlockOpeningFields {
+ l_angle_token_token: self.l_angle_token_token(),
+ colon_token_token: self.colon_token_token(),
+ name_token_token: self.name_token_token(),
+ attributes: self.attributes(),
+ r_angle_token_token: self.r_angle_token_token(),
}
}
- pub fn bom_token(&self) -> Option {
- support::token(&self.syntax, 0usize)
+ pub fn l_angle_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 0usize)
}
- pub fn frontmatter(&self) -> Option {
- support::node(&self.syntax, 1usize)
+ pub fn colon_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
}
- pub fn directive(&self) -> Option {
- support::node(&self.syntax, 2usize)
+ pub fn name_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 2usize)
}
- pub fn html(&self) -> HtmlElementList {
+ pub fn attributes(&self) -> HtmlAttributeList {
support::list(&self.syntax, 3usize)
}
- pub fn eof_token(&self) -> SyntaxResult {
+ pub fn r_angle_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 4usize)
}
}
-impl Serialize for HtmlRoot {
+impl Serialize for GlimmerNamedBlockOpening {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -637,18 +702,18 @@ impl Serialize for HtmlRoot {
}
}
#[derive(Serialize)]
-pub struct HtmlRootFields {
- pub bom_token: Option,
- pub frontmatter: Option,
- pub directive: Option,
- pub html: HtmlElementList,
- pub eof_token: SyntaxResult,
+pub struct GlimmerNamedBlockOpeningFields {
+ pub l_angle_token_token: SyntaxResult,
+ pub colon_token_token: SyntaxResult,
+ pub name_token_token: SyntaxResult,
+ pub attributes: HtmlAttributeList,
+ pub r_angle_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlSelfClosingElement {
+pub struct GlimmerPath {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlSelfClosingElement {
+impl GlimmerPath {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -658,32 +723,20 @@ impl HtmlSelfClosingElement {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlSelfClosingElementFields {
- HtmlSelfClosingElementFields {
- l_angle_token: self.l_angle_token(),
- name: self.name(),
- attributes: self.attributes(),
- slash_token: self.slash_token(),
- r_angle_token: self.r_angle_token(),
+ pub fn as_fields(&self) -> GlimmerPathFields {
+ GlimmerPathFields {
+ at_token_token: self.at_token_token(),
+ segments: self.segments(),
}
}
- pub fn l_angle_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 0usize)
- }
- pub fn name(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 1usize)
- }
- pub fn attributes(&self) -> HtmlAttributeList {
- support::list(&self.syntax, 2usize)
- }
- pub fn slash_token(&self) -> Option {
- support::token(&self.syntax, 3usize)
+ pub fn at_token_token(&self) -> Option {
+ support::token(&self.syntax, 0usize)
}
- pub fn r_angle_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 4usize)
+ pub fn segments(&self) -> GlimmerPathSegmentList {
+ support::list(&self.syntax, 1usize)
}
}
-impl Serialize for HtmlSelfClosingElement {
+impl Serialize for GlimmerPath {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -692,18 +745,15 @@ impl Serialize for HtmlSelfClosingElement {
}
}
#[derive(Serialize)]
-pub struct HtmlSelfClosingElementFields {
- pub l_angle_token: SyntaxResult,
- pub name: SyntaxResult,
- pub attributes: HtmlAttributeList,
- pub slash_token: Option,
- pub r_angle_token: SyntaxResult,
+pub struct GlimmerPathFields {
+ pub at_token_token: Option,
+ pub segments: GlimmerPathSegmentList,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlSingleTextExpression {
+pub struct GlimmerPathSegment {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlSingleTextExpression {
+impl GlimmerPathSegment {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -713,24 +763,16 @@ impl HtmlSingleTextExpression {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlSingleTextExpressionFields {
- HtmlSingleTextExpressionFields {
- l_curly_token: self.l_curly_token(),
- expression: self.expression(),
- r_curly_token: self.r_curly_token(),
+ pub fn as_fields(&self) -> GlimmerPathSegmentFields {
+ GlimmerPathSegmentFields {
+ value_token_token: self.value_token_token(),
}
}
- pub fn l_curly_token(&self) -> SyntaxResult {
+ pub fn value_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn expression(&self) -> SyntaxResult {
- support::required_node(&self.syntax, 1usize)
- }
- pub fn r_curly_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 2usize)
- }
}
-impl Serialize for HtmlSingleTextExpression {
+impl Serialize for GlimmerPathSegment {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -739,16 +781,14 @@ impl Serialize for HtmlSingleTextExpression {
}
}
#[derive(Serialize)]
-pub struct HtmlSingleTextExpressionFields {
- pub l_curly_token: SyntaxResult,
- pub expression: SyntaxResult,
- pub r_curly_token: SyntaxResult,
+pub struct GlimmerPathSegmentFields {
+ pub value_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlString {
+pub struct GlimmerPositionalArgument {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlString {
+impl GlimmerPositionalArgument {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -758,16 +798,16 @@ impl HtmlString {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlStringFields {
- HtmlStringFields {
- value_token: self.value_token(),
+ pub fn as_fields(&self) -> GlimmerPositionalArgumentFields {
+ GlimmerPositionalArgumentFields {
+ value: self.value(),
}
}
- pub fn value_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 0usize)
+ pub fn value(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 0usize)
}
}
-impl Serialize for HtmlString {
+impl Serialize for GlimmerPositionalArgument {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -776,14 +816,14 @@ impl Serialize for HtmlString {
}
}
#[derive(Serialize)]
-pub struct HtmlStringFields {
- pub value_token: SyntaxResult,
+pub struct GlimmerPositionalArgumentFields {
+ pub value: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlTagName {
+pub struct GlimmerSplattribute {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlTagName {
+impl GlimmerSplattribute {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -793,16 +833,20 @@ impl HtmlTagName {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlTagNameFields {
- HtmlTagNameFields {
- value_token: self.value_token(),
+ pub fn as_fields(&self) -> GlimmerSplattributeFields {
+ GlimmerSplattributeFields {
+ dotdotdot_token_token: self.dotdotdot_token_token(),
+ attributes_token_token: self.attributes_token_token(),
}
}
- pub fn value_token(&self) -> SyntaxResult {
+ pub fn dotdotdot_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
+ pub fn attributes_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 1usize)
+ }
}
-impl Serialize for HtmlTagName {
+impl Serialize for GlimmerSplattribute {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -811,14 +855,15 @@ impl Serialize for HtmlTagName {
}
}
#[derive(Serialize)]
-pub struct HtmlTagNameFields {
- pub value_token: SyntaxResult,
+pub struct GlimmerSplattributeFields {
+ pub dotdotdot_token_token: SyntaxResult,
+ pub attributes_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HtmlTextExpression {
+pub struct GlimmerStringLiteral {
pub(crate) syntax: SyntaxNode,
}
-impl HtmlTextExpression {
+impl GlimmerStringLiteral {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -828,16 +873,16 @@ impl HtmlTextExpression {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> HtmlTextExpressionFields {
- HtmlTextExpressionFields {
- html_literal_token: self.html_literal_token(),
+ pub fn as_fields(&self) -> GlimmerStringLiteralFields {
+ GlimmerStringLiteralFields {
+ value_token_token: self.value_token_token(),
}
}
- pub fn html_literal_token(&self) -> SyntaxResult {
+ pub fn value_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
}
-impl Serialize for HtmlTextExpression {
+impl Serialize for GlimmerStringLiteral {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -846,14 +891,14 @@ impl Serialize for HtmlTextExpression {
}
}
#[derive(Serialize)]
-pub struct HtmlTextExpressionFields {
- pub html_literal_token: SyntaxResult,
+pub struct GlimmerStringLiteralFields {
+ pub value_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct SvelteDebugBlock {
+pub struct GlimmerSubexpression {
pub(crate) syntax: SyntaxNode,
}
-impl SvelteDebugBlock {
+impl GlimmerSubexpression {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -863,28 +908,28 @@ impl SvelteDebugBlock {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> SvelteDebugBlockFields {
- SvelteDebugBlockFields {
- sv_curly_at_token: self.sv_curly_at_token(),
- debug_token: self.debug_token(),
- bindings: self.bindings(),
- r_curly_token: self.r_curly_token(),
+ pub fn as_fields(&self) -> GlimmerSubexpressionFields {
+ GlimmerSubexpressionFields {
+ l_paren_token_token: self.l_paren_token_token(),
+ path: self.path(),
+ arguments: self.arguments(),
+ r_paren_token_token: self.r_paren_token_token(),
}
}
- pub fn sv_curly_at_token(&self) -> SyntaxResult {
+ pub fn l_paren_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
- pub fn debug_token(&self) -> SyntaxResult {
- support::required_token(&self.syntax, 1usize)
+ pub fn path(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 1usize)
}
- pub fn bindings(&self) -> SvelteBindingList {
+ pub fn arguments(&self) -> GlimmerArgumentList {
support::list(&self.syntax, 2usize)
}
- pub fn r_curly_token(&self) -> SyntaxResult {
+ pub fn r_paren_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 3usize)
}
}
-impl Serialize for SvelteDebugBlock {
+impl Serialize for GlimmerSubexpression {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -893,17 +938,17 @@ impl Serialize for SvelteDebugBlock {
}
}
#[derive(Serialize)]
-pub struct SvelteDebugBlockFields {
- pub sv_curly_at_token: SyntaxResult,
- pub debug_token: SyntaxResult,
- pub bindings: SvelteBindingList,
- pub r_curly_token: SyntaxResult,
+pub struct GlimmerSubexpressionFields {
+ pub l_paren_token_token: SyntaxResult,
+ pub path: SyntaxResult,
+ pub arguments: GlimmerArgumentList,
+ pub r_paren_token_token: SyntaxResult,
}
#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct SvelteName {
+pub struct GlimmerTripleStashExpression {
pub(crate) syntax: SyntaxNode,
}
-impl SvelteName {
+impl GlimmerTripleStashExpression {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
@@ -913,16 +958,28 @@ impl SvelteName {
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
- pub fn as_fields(&self) -> SvelteNameFields {
- SvelteNameFields {
- svelte_ident_token: self.svelte_ident_token(),
+ pub fn as_fields(&self) -> GlimmerTripleStashExpressionFields {
+ GlimmerTripleStashExpressionFields {
+ l_curly3_token_token: self.l_curly3_token_token(),
+ path: self.path(),
+ arguments: self.arguments(),
+ r_curly3_token_token: self.r_curly3_token_token(),
}
}
- pub fn svelte_ident_token(&self) -> SyntaxResult {
+ pub fn l_curly3_token_token(&self) -> SyntaxResult {
support::required_token(&self.syntax, 0usize)
}
+ pub fn path(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 1usize)
+ }
+ pub fn arguments(&self) -> GlimmerArgumentList {
+ support::list(&self.syntax, 2usize)
+ }
+ pub fn r_curly3_token_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 3usize)
+ }
}
-impl Serialize for SvelteName {
+impl Serialize for GlimmerTripleStashExpression {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
@@ -931,204 +988,2209 @@ impl Serialize for SvelteName {
}
}
#[derive(Serialize)]
-pub struct SvelteNameFields {
- pub svelte_ident_token: SyntaxResult,
+pub struct GlimmerTripleStashExpressionFields {
+ pub l_curly3_token_token: SyntaxResult,
+ pub path: SyntaxResult,
+ pub arguments: GlimmerArgumentList,
+ pub r_curly3_token_token: SyntaxResult,
}
-#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
-pub enum AnyAstroFrontmatterElement {
- AstroBogusFrontmatter(AstroBogusFrontmatter),
- AstroFrontmatterElement(AstroFrontmatterElement),
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct HtmlAttribute {
+ pub(crate) syntax: SyntaxNode,
}
-impl AnyAstroFrontmatterElement {
- pub fn as_astro_bogus_frontmatter(&self) -> Option<&AstroBogusFrontmatter> {
- match &self {
- Self::AstroBogusFrontmatter(item) => Some(item),
- _ => None,
- }
- }
- pub fn as_astro_frontmatter_element(&self) -> Option<&AstroFrontmatterElement> {
- match &self {
- Self::AstroFrontmatterElement(item) => Some(item),
- _ => None,
- }
+impl HtmlAttribute {
+ #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
+ #[doc = r""]
+ #[doc = r" # Safety"]
+ #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
+ #[doc = r" or a match on [SyntaxNode::kind]"]
+ #[inline]
+ pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
+ Self { syntax }
}
-}
-#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
-pub enum AnyHtmlAttribute {
- HtmlAttribute(HtmlAttribute),
- HtmlBogusAttribute(HtmlBogusAttribute),
- HtmlDoubleTextExpression(HtmlDoubleTextExpression),
- HtmlSingleTextExpression(HtmlSingleTextExpression),
-}
-impl AnyHtmlAttribute {
- pub fn as_html_attribute(&self) -> Option<&HtmlAttribute> {
- match &self {
- Self::HtmlAttribute(item) => Some(item),
- _ => None,
+ pub fn as_fields(&self) -> HtmlAttributeFields {
+ HtmlAttributeFields {
+ name: self.name(),
+ initializer: self.initializer(),
}
}
- pub fn as_html_bogus_attribute(&self) -> Option<&HtmlBogusAttribute> {
- match &self {
- Self::HtmlBogusAttribute(item) => Some(item),
- _ => None,
- }
+ pub fn name(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 0usize)
}
- pub fn as_html_double_text_expression(&self) -> Option<&HtmlDoubleTextExpression> {
- match &self {
- Self::HtmlDoubleTextExpression(item) => Some(item),
- _ => None,
- }
+ pub fn initializer(&self) -> Option {
+ support::node(&self.syntax, 1usize)
}
- pub fn as_html_single_text_expression(&self) -> Option<&HtmlSingleTextExpression> {
- match &self {
- Self::HtmlSingleTextExpression(item) => Some(item),
- _ => None,
- }
+}
+impl Serialize for HtmlAttribute {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ self.as_fields().serialize(serializer)
}
}
-#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
-pub enum AnyHtmlAttributeInitializer {
- HtmlSingleTextExpression(HtmlSingleTextExpression),
- HtmlString(HtmlString),
+#[derive(Serialize)]
+pub struct HtmlAttributeFields {
+ pub name: SyntaxResult,
+ pub initializer: Option,
}
-impl AnyHtmlAttributeInitializer {
- pub fn as_html_single_text_expression(&self) -> Option<&HtmlSingleTextExpression> {
- match &self {
- Self::HtmlSingleTextExpression(item) => Some(item),
- _ => None,
- }
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct HtmlAttributeInitializerClause {
+ pub(crate) syntax: SyntaxNode,
+}
+impl HtmlAttributeInitializerClause {
+ #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
+ #[doc = r""]
+ #[doc = r" # Safety"]
+ #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
+ #[doc = r" or a match on [SyntaxNode::kind]"]
+ #[inline]
+ pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
+ Self { syntax }
}
- pub fn as_html_string(&self) -> Option<&HtmlString> {
- match &self {
- Self::HtmlString(item) => Some(item),
- _ => None,
+ pub fn as_fields(&self) -> HtmlAttributeInitializerClauseFields {
+ HtmlAttributeInitializerClauseFields {
+ eq_token: self.eq_token(),
+ value: self.value(),
}
}
+ pub fn eq_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 0usize)
+ }
+ pub fn value(&self) -> SyntaxResult {
+ support::required_node(&self.syntax, 1usize)
+ }
}
-#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
-pub enum AnyHtmlContent {
+impl Serialize for HtmlAttributeInitializerClause {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ self.as_fields().serialize(serializer)
+ }
+}
+#[derive(Serialize)]
+pub struct HtmlAttributeInitializerClauseFields {
+ pub eq_token: SyntaxResult,
+ pub value: SyntaxResult,
+}
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct HtmlAttributeName {
+ pub(crate) syntax: SyntaxNode,
+}
+impl HtmlAttributeName {
+ #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
+ #[doc = r""]
+ #[doc = r" # Safety"]
+ #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
+ #[doc = r" or a match on [SyntaxNode::kind]"]
+ #[inline]
+ pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
+ Self { syntax }
+ }
+ pub fn as_fields(&self) -> HtmlAttributeNameFields {
+ HtmlAttributeNameFields {
+ value_token: self.value_token(),
+ }
+ }
+ pub fn value_token(&self) -> SyntaxResult {
+ support::required_token(&self.syntax, 0usize)
+ }
+}
+impl Serialize for HtmlAttributeName {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ self.as_fields().serialize(serializer)
+ }
+}
+#[derive(Serialize)]
+pub struct HtmlAttributeNameFields {
+ pub value_token: SyntaxResult,
+}
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct HtmlCdataSection {
+ pub(crate) syntax: SyntaxNode,
+}
+impl HtmlCdataSection {
+ #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
+ #[doc = r""]
+ #[doc = r" # Safety"]
+ #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
+ #[doc = r" or a match on [SyntaxNode::kind]"]
+ #[inline]
+ pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
+ Self { syntax }
+ }
+ pub fn as_fields(&self) -> HtmlCdataSectionFields {
+ HtmlCdataSectionFields {
+ cdata_start_token: self.cdata_start_token(),
+ content_token: self.content_token(),
+ cdata_end_token: self.cdata_end_token(),
+ }
+ }
+ pub fn cdata_start_token(&self) -> SyntaxResult