This commit is contained in:
Josh Thomas 2025-01-04 22:10:22 -06:00
parent 92ecfcb83c
commit baeed3ed66
20 changed files with 617 additions and 778 deletions

View file

@ -36,9 +36,6 @@ impl Ast {
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub enum Node { pub enum Node {
Django(DjangoNode), Django(DjangoNode),
Html(HtmlNode),
Script(ScriptNode),
Style(StyleNode),
Text(String), Text(String),
} }
@ -82,56 +79,6 @@ impl DjangoFilter {
} }
} }
#[derive(Clone, Debug, Serialize)]
pub enum HtmlNode {
Comment(String),
Doctype(String),
Element {
tag_name: String,
attributes: Attributes,
children: Vec<Node>,
},
Void {
tag_name: String,
attributes: Attributes,
},
}
#[derive(Clone, Debug, Serialize)]
pub enum ScriptNode {
Comment {
content: String,
kind: ScriptCommentKind,
},
Element {
attributes: Attributes,
children: Vec<Node>,
},
}
#[derive(Clone, Debug, Serialize)]
pub enum ScriptCommentKind {
SingleLine, // //
MultiLine, // /* */
}
#[derive(Clone, Debug, Serialize)]
pub enum StyleNode {
Comment(String),
Element {
attributes: Attributes,
children: Vec<Node>,
},
}
#[derive(Clone, Debug, Serialize)]
pub enum AttributeValue {
Value(String),
Boolean,
}
pub type Attributes = BTreeMap<String, AttributeValue>;
#[derive(Clone, Debug, Error, Serialize)] #[derive(Clone, Debug, Error, Serialize)]
pub enum AstError { pub enum AstError {
#[error("Empty AST")] #[error("Empty AST")]

View file

@ -53,107 +53,47 @@ impl Lexer {
self.consume_n(2)?; // #} self.consume_n(2)?; // #}
TokenType::Comment(content, "{#".to_string(), Some("#}".to_string())) TokenType::Comment(content, "{#".to_string(), Some("#}".to_string()))
} }
_ => {
self.consume()?; // {
TokenType::Text(String::from("{"))
}
},
'<' => match self.peek_next()? {
'/' => {
self.consume_n(2)?; // </
let tag = self.consume_until(">")?;
self.consume()?; // >
TokenType::HtmlTagClose(tag)
}
'!' if self.matches("<!--")? => {
self.consume_n(4)?; // <!--
let content = self.consume_until("-->")?;
self.consume_n(3)?; // -->
TokenType::Comment(content, "<!--".to_string(), Some("-->".to_string()))
}
_ => {
self.consume()?; // consume <
let tag = self.consume_until(">")?;
self.consume()?; // consume >
if tag.starts_with("script") {
TokenType::ScriptTagOpen(tag)
} else if tag.starts_with("style") {
TokenType::StyleTagOpen(tag)
} else if tag.ends_with("/") {
TokenType::HtmlTagVoid(tag.trim_end_matches("/").to_string())
} else {
TokenType::HtmlTagOpen(tag)
}
}
},
'/' => match self.peek_next()? {
'/' => {
self.consume_n(2)?; // //
let content = self.consume_until("\n")?;
TokenType::Comment(content, "//".to_string(), None)
}
'*' => {
self.consume_n(2)?; // /*
let content = self.consume_until("*/")?;
self.consume_n(2)?; // */
TokenType::Comment(content, "/*".to_string(), Some("*/".to_string()))
}
_ => { _ => {
self.consume()?; self.consume()?;
TokenType::Text("/".to_string()) TokenType::Text("{".to_string())
} }
}, },
'\n' => {
c if c.is_whitespace() => { self.consume()?;
if c == '\n' || c == '\r' { self.line += 1;
self.consume()?; // \r or \n TokenType::Newline
if c == '\r' && self.peek()? == '\n' { }
self.consume()?; // \n of \r\n ' ' | '\t' | '\r' => {
} let mut count = 1;
TokenType::Newline self.consume()?;
} else { while let Ok(c) = self.peek() {
self.consume()?; // Consume the first whitespace if c != ' ' && c != '\t' && c != '\r' {
while !self.is_at_end() && self.peek()?.is_whitespace() { break;
if self.peek()? == '\n' || self.peek()? == '\r' { }
break; self.consume()?;
} count += 1;
self.consume()?; }
} TokenType::Whitespace(count)
let whitespace_count = self.current - self.start;
TokenType::Whitespace(whitespace_count)
}
} }
_ => { _ => {
let mut text = String::new(); let mut text = String::new();
while !self.is_at_end() { while !self.is_at_end() {
let c = self.peek()?; match self.peek()? {
if c == '{' || c == '<' || c == '\n' { '{' => break,
break; '\n' | ' ' | '\t' | '\r' => break,
_ => {
text.push(self.consume()?);
}
} }
text.push(c); }
self.consume()?; if text.is_empty() {
return Err(LexerError::EmptyToken(self.line));
} }
TokenType::Text(text) TokenType::Text(text)
} }
}; };
let token = Token::new(token_type, self.line, Some(self.start)); Ok(Token::new(token_type, self.line, Some(self.start)))
match self.peek_previous()? {
'\n' => self.line += 1,
'\r' => {
self.line += 1;
if self.peek()? == '\n' {
self.current += 1;
}
}
_ => {}
}
Ok(token)
} }
fn peek(&self) -> Result<char, LexerError> { fn peek(&self) -> Result<char, LexerError> {
@ -310,15 +250,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_comments() { fn test_tokenize_comments() {
let source = r#"<!-- HTML comment --> let source = r#"<!-- HTML comment -->
{# Django comment #} {# Django comment #}"#;
<script>
// JS single line comment
/* JS multi-line
comment */
</script>
<style>
/* CSS comment */
</style>"#;
let mut lexer = Lexer::new(source); let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize().unwrap(); let tokens = lexer.tokenize().unwrap();
insta::assert_yaml_snapshot!(tokens); insta::assert_yaml_snapshot!(tokens);
@ -357,7 +289,7 @@ mod tests {
assert!(Lexer::new("{{ user.name").tokenize().is_err()); // No closing }} assert!(Lexer::new("{{ user.name").tokenize().is_err()); // No closing }}
assert!(Lexer::new("{% if").tokenize().is_err()); // No closing %} assert!(Lexer::new("{% if").tokenize().is_err()); // No closing %}
assert!(Lexer::new("{#").tokenize().is_err()); // No closing #} assert!(Lexer::new("{#").tokenize().is_err()); // No closing #}
assert!(Lexer::new("<div").tokenize().is_err()); // No closing > assert!(Lexer::new("<div").tokenize().is_ok()); // No closing >, but HTML is treated as text
// Invalid characters or syntax within tokens // Invalid characters or syntax within tokens
assert!(Lexer::new("{{}}").tokenize().is_ok()); // Empty but valid assert!(Lexer::new("{{}}").tokenize().is_ok()); // Empty but valid

View file

@ -1,10 +1,6 @@
use crate::ast::{ use crate::ast::{Ast, AstError, DjangoFilter, DjangoNode, Node, TagNode};
Ast, AstError, AttributeValue, DjangoFilter, DjangoNode, HtmlNode, Node, ScriptCommentKind,
ScriptNode, StyleNode, TagNode,
};
use crate::tagspecs::TagSpec; use crate::tagspecs::TagSpec;
use crate::tokens::{Token, TokenStream, TokenType}; use crate::tokens::{Token, TokenStream, TokenType};
use std::collections::BTreeMap;
use thiserror::Error; use thiserror::Error;
pub struct Parser { pub struct Parser {
@ -19,18 +15,13 @@ impl Parser {
pub fn parse(&mut self) -> Result<Ast, ParserError> { pub fn parse(&mut self) -> Result<Ast, ParserError> {
let mut ast = Ast::default(); let mut ast = Ast::default();
let mut had_nodes = false;
while !self.is_at_end() { while !self.is_at_end() {
match self.next_node() { match self.next_node() {
Ok(node) => { Ok(node) => {
ast.add_node(node); ast.add_node(node);
had_nodes = true;
} }
Err(ParserError::Ast(AstError::StreamError(kind))) if kind == "AtEnd" => { Err(ParserError::Ast(AstError::StreamError(kind))) if kind == "AtEnd" => {
if !had_nodes {
return Ok(ast.finalize()?);
}
break; break;
} }
Err(ParserError::ErrorSignal(Signal::SpecialTag(_))) => { Err(ParserError::ErrorSignal(Signal::SpecialTag(_))) => {
@ -58,43 +49,31 @@ impl Parser {
return Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string()))); return Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string())));
} }
let token = self.consume()?; let token = self.peek()?;
let node = match token.token_type() { let node = match token.token_type() {
TokenType::Comment(s, start, end) => self.parse_comment(s, start, end.as_deref()), TokenType::Comment(content, start, end) => {
TokenType::DjangoBlock(s) => self.parse_django_block(s), self.consume()?;
TokenType::DjangoVariable(s) => self.parse_django_variable(s), self.parse_comment(content, start, end.as_deref())
TokenType::Eof => {
if self.is_at_end() {
Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string())))
} else {
self.next_node()
}
} }
TokenType::HtmlTagClose(tag) => { TokenType::DjangoBlock(content) => {
self.backtrack(1)?; self.consume()?;
Err(ParserError::ErrorSignal(Signal::ClosingTagFound( self.parse_django_block(content)
tag.to_string(),
)))
} }
TokenType::HtmlTagOpen(s) => self.parse_tag_open(s), TokenType::DjangoVariable(content) => {
TokenType::HtmlTagVoid(s) => self.parse_html_tag_void(s), self.consume()?;
TokenType::Newline => self.next_node(), self.parse_django_variable(content)
TokenType::ScriptTagClose(_) => {
self.backtrack(1)?;
Err(ParserError::ErrorSignal(Signal::ClosingTagFound(
"script".to_string(),
)))
} }
TokenType::ScriptTagOpen(s) => self.parse_tag_open(s), TokenType::Text(_)
TokenType::StyleTagClose(_) => { | TokenType::Whitespace(_)
self.backtrack(1)?; | TokenType::Newline
Err(ParserError::ErrorSignal(Signal::ClosingTagFound( | TokenType::HtmlTagOpen(_)
"style".to_string(), | TokenType::HtmlTagClose(_)
))) | TokenType::HtmlTagVoid(_)
} | TokenType::ScriptTagOpen(_)
TokenType::StyleTagOpen(s) => self.parse_tag_open(s), | TokenType::ScriptTagClose(_)
TokenType::Text(s) => Ok(Node::Text(s.to_string())), | TokenType::StyleTagOpen(_)
TokenType::Whitespace(_) => self.next_node(), | TokenType::StyleTagClose(_) => self.parse_text(),
TokenType::Eof => Err(ParserError::Ast(AstError::StreamError("AtEnd".to_string()))),
}?; }?;
Ok(node) Ok(node)
} }
@ -107,49 +86,12 @@ impl Parser {
) -> Result<Node, ParserError> { ) -> Result<Node, ParserError> {
match start { match start {
"{#" => Ok(Node::Django(DjangoNode::Comment(content.to_string()))), "{#" => Ok(Node::Django(DjangoNode::Comment(content.to_string()))),
"<!--" => Ok(Node::Html(HtmlNode::Comment(content.to_string()))), _ => Ok(Node::Text(format!(
"//" => Ok(Node::Script(ScriptNode::Comment { "{}{}{}",
content: content.to_string(), start,
kind: ScriptCommentKind::SingleLine, content,
})), end.unwrap_or("")
"/*" => { ))),
// Look back for script/style context
let token_type = self
.peek_back(self.current)?
.iter()
.find_map(|token| match token.token_type() {
TokenType::ScriptTagOpen(_) => {
Some(TokenType::ScriptTagOpen(String::new()))
}
TokenType::StyleTagOpen(_) => Some(TokenType::StyleTagOpen(String::new())),
TokenType::ScriptTagClose(_) | TokenType::StyleTagClose(_) => None,
_ => None,
})
.ok_or(ParserError::InvalidMultiLineComment)?;
match token_type {
TokenType::ScriptTagOpen(_) => Ok(Node::Script(ScriptNode::Comment {
content: content.to_string(),
kind: ScriptCommentKind::MultiLine,
})),
TokenType::StyleTagOpen(_) => {
Ok(Node::Style(StyleNode::Comment(content.to_string())))
}
_ => unreachable!(),
}
}
_ => Err(ParserError::token_error(
"valid token",
Token::new(
TokenType::Comment(
content.to_string(),
start.to_string(),
end.map(String::from),
),
0,
None,
),
)),
} }
} }
@ -274,100 +216,39 @@ impl Parser {
Ok(Node::Django(DjangoNode::Variable { bits, filters })) Ok(Node::Django(DjangoNode::Variable { bits, filters }))
} }
fn parse_tag_open(&mut self, s: &str) -> Result<Node, ParserError> { fn parse_text(&mut self) -> Result<Node, ParserError> {
let mut parts = s.split_whitespace(); let mut text = String::new();
let token_type = self.peek_previous()?.token_type().clone(); while let Ok(token) = self.peek() {
match token.token_type() {
let tag_name = match token_type { TokenType::DjangoBlock(_)
TokenType::HtmlTagOpen(_) => { | TokenType::DjangoVariable(_)
let name = parts | TokenType::Comment(_, _, _) => break,
.next() TokenType::Text(s) => {
.ok_or(ParserError::Ast(AstError::EmptyTag))? self.consume()?;
.to_string(); text.push_str(s);
if name.to_lowercase() == "!doctype" {
return Ok(Node::Html(HtmlNode::Doctype("!DOCTYPE html".to_string())));
} }
name TokenType::HtmlTagOpen(s)
} | TokenType::HtmlTagClose(s)
TokenType::ScriptTagOpen(_) => { | TokenType::HtmlTagVoid(s)
parts.next(); // Skip the tag name | TokenType::ScriptTagOpen(s)
"script".to_string() | TokenType::ScriptTagClose(s)
} | TokenType::StyleTagOpen(s)
TokenType::StyleTagOpen(_) => { | TokenType::StyleTagClose(s) => {
parts.next(); // Skip the tag name self.consume()?;
"style".to_string() text.push_str(s);
}
_ => return Err(ParserError::invalid_tag("Unknown tag type".to_string())),
};
let mut attributes = BTreeMap::new();
for attr in parts {
if let Some((key, value)) = parse_attribute(attr)? {
attributes.insert(key, value);
}
}
let mut children = Vec::new();
let mut found_closing_tag = false;
while !self.is_at_end() {
match self.next_node() {
Ok(node) => {
children.push(node);
} }
Err(ParserError::ErrorSignal(Signal::ClosingTagFound(tag))) => { TokenType::Whitespace(len) => {
if tag == tag_name { self.consume()?;
found_closing_tag = true; text.push_str(&" ".repeat(*len));
self.consume()?;
break;
}
} }
Err(e) => return Err(e), TokenType::Newline => {
self.consume()?;
text.push('\n');
}
TokenType::Eof => break,
} }
} }
Ok(Node::Text(text))
if !found_closing_tag {
return Err(ParserError::Ast(AstError::UnclosedTag(tag_name.clone())));
}
Ok(match token_type {
TokenType::HtmlTagOpen(_) => Node::Html(HtmlNode::Element {
tag_name,
attributes,
children,
}),
TokenType::ScriptTagOpen(_) => Node::Script(ScriptNode::Element {
attributes,
children,
}),
TokenType::StyleTagOpen(_) => Node::Style(StyleNode::Element {
attributes,
children,
}),
_ => return Err(ParserError::invalid_tag("Unknown tag type".to_string())),
})
}
fn parse_html_tag_void(&mut self, s: &str) -> Result<Node, ParserError> {
let mut parts = s.split_whitespace();
let tag_name = parts
.next()
.ok_or(ParserError::Ast(AstError::EmptyTag))?
.to_string();
let mut attributes = BTreeMap::new();
for attr in parts {
if let Some((key, value)) = parse_attribute(attr)? {
attributes.insert(key, value);
}
}
Ok(Node::Html(HtmlNode::Void {
tag_name,
attributes,
}))
} }
fn peek(&self) -> Result<Token, ParserError> { fn peek(&self) -> Result<Token, ParserError> {
@ -429,20 +310,17 @@ impl Parser {
} }
fn synchronize(&mut self) -> Result<(), ParserError> { fn synchronize(&mut self) -> Result<(), ParserError> {
const SYNC_TYPES: &[TokenType] = &[ let sync_types = [
TokenType::DjangoBlock(String::new()), TokenType::DjangoBlock(String::new()),
TokenType::HtmlTagOpen(String::new()), TokenType::DjangoVariable(String::new()),
TokenType::HtmlTagVoid(String::new()), TokenType::Comment(String::new(), String::from("{#"), Some(String::from("#}"))),
TokenType::ScriptTagOpen(String::new()),
TokenType::StyleTagOpen(String::new()),
TokenType::Newline,
TokenType::Eof, TokenType::Eof,
]; ];
while !self.is_at_end() { while !self.is_at_end() {
let current = self.peek()?; let current = self.peek()?;
for sync_type in SYNC_TYPES { for sync_type in &sync_types {
if current.token_type() == sync_type { if current.token_type() == sync_type {
return Ok(()); return Ok(());
} }
@ -463,17 +341,6 @@ pub enum Signal {
ClosingTag, ClosingTag,
} }
fn parse_attribute(attr: &str) -> Result<Option<(String, AttributeValue)>, ParserError> {
if let Some((key, value)) = attr.split_once('=') {
Ok(Some((
key.to_string(),
AttributeValue::Value(value.trim_matches('"').to_string()),
)))
} else {
Ok(Some((attr.to_string(), AttributeValue::Boolean)))
}
}
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ParserError { pub enum ParserError {
#[error(transparent)] #[error(transparent)]
@ -694,8 +561,7 @@ mod tests {
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let ast = parser.parse().unwrap(); let ast = parser.parse().unwrap();
insta::assert_yaml_snapshot!(ast); insta::assert_yaml_snapshot!(ast);
assert_eq!(ast.errors().len(), 1); assert_eq!(ast.errors().len(), 0);
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "div"));
} }
#[test] #[test]
@ -727,8 +593,7 @@ mod tests {
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let ast = parser.parse().unwrap(); let ast = parser.parse().unwrap();
insta::assert_yaml_snapshot!(ast); insta::assert_yaml_snapshot!(ast);
assert_eq!(ast.errors().len(), 1); assert_eq!(ast.errors().len(), 0);
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "script"));
} }
#[test] #[test]
@ -738,8 +603,7 @@ mod tests {
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let ast = parser.parse().unwrap(); let ast = parser.parse().unwrap();
insta::assert_yaml_snapshot!(ast); insta::assert_yaml_snapshot!(ast);
assert_eq!(ast.errors().len(), 1); assert_eq!(ast.errors().len(), 0);
assert!(matches!(&ast.errors()[0], AstError::UnclosedTag(tag) if tag == "style"));
} }
#[test] #[test]
@ -747,13 +611,13 @@ mod tests {
let source = r#"<div class="container"> let source = r#"<div class="container">
<h1>Header</h1> <h1>Header</h1>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{# This if is unclosed which does matter #}
<p>Welcome {{ user.name }}</p> <p>Welcome {{ user.name }}</p>
<div> <div>
{# This div is unclosed #} {# This div is unclosed which doesn't matter #}
{% for item in items %} {% for item in items %}
<span>{{ item }}</span> <span>{{ item }}</span>
{% endfor %} {% endfor %}
{% endif %}
<footer>Page Footer</footer> <footer>Page Footer</footer>
</div>"#; </div>"#;
let tokens = Lexer::new(source).tokenize().unwrap(); let tokens = Lexer::new(source).tokenize().unwrap();
@ -790,7 +654,7 @@ mod tests {
<div class="header" id="main" data-value="123" disabled> <div class="header" id="main" data-value="123" disabled>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{# Welcome message #} {# Welcome message #}
<h1>Welcome, {{ user.name|default:"Guest"|title }}!</h1> <h1>Welcome, {{ user.name|title|default:'Guest' }}!</h1>
{% if user.is_staff %} {% if user.is_staff %}
<span>Admin</span> <span>Admin</span>
{% else %} {% else %}

View file

@ -3,14 +3,35 @@ source: crates/djls-template-ast/src/lexer.rs
expression: tokens expression: tokens
--- ---
- token_type: - token_type:
Comment: Text: "<!--"
- HTML comment
- "<!--"
- "-->"
line: 1 line: 1
start: 0 start: 0
- token_type: Newline - token_type:
Whitespace: 1
line: 1 line: 1
start: 4
- token_type:
Text: HTML
line: 1
start: 5
- token_type:
Whitespace: 1
line: 1
start: 9
- token_type:
Text: comment
line: 1
start: 10
- token_type:
Whitespace: 1
line: 1
start: 17
- token_type:
Text: "-->"
line: 1
start: 18
- token_type: Newline
line: 2
start: 21 start: 21
- token_type: - token_type:
Comment: Comment:
@ -19,76 +40,6 @@ expression: tokens
- "#}" - "#}"
line: 2 line: 2
start: 22 start: 22
- token_type: Newline
line: 2
start: 42
- token_type:
ScriptTagOpen: script
line: 3
start: 43
- token_type: Newline
line: 3
start: 51
- token_type:
Whitespace: 4
line: 4
start: 52
- token_type:
Comment:
- JS single line comment
- //
- ~
line: 4
start: 56
- token_type: Newline
line: 4
start: 81
- token_type:
Whitespace: 4
line: 5
start: 82
- token_type:
Comment:
- "JS multi-line\n comment"
- /*
- "*/"
line: 5
start: 86
- token_type: Newline
line: 5
start: 120
- token_type:
HtmlTagClose: script
line: 6
start: 121
- token_type: Newline
line: 6
start: 130
- token_type:
StyleTagOpen: style
line: 7
start: 131
- token_type: Newline
line: 7
start: 138
- token_type:
Whitespace: 4
line: 8
start: 139
- token_type:
Comment:
- CSS comment
- /*
- "*/"
line: 8
start: 143
- token_type: Newline
line: 8
start: 160
- token_type:
HtmlTagClose: style
line: 9
start: 161
- token_type: Eof - token_type: Eof
line: 9 line: 2
start: ~ start: ~

View file

@ -3,59 +3,100 @@ source: crates/djls-template-ast/src/lexer.rs
expression: tokens expression: tokens
--- ---
- token_type: - token_type:
HtmlTagOpen: "!DOCTYPE html" Text: "<!DOCTYPE"
line: 1 line: 1
start: 0 start: 0
- token_type: Newline - token_type:
Whitespace: 1
line: 1 line: 1
start: 9
- token_type:
Text: html>
line: 1
start: 10
- token_type: Newline
line: 2
start: 15 start: 15
- token_type: - token_type:
HtmlTagOpen: html Text: "<html>"
line: 2 line: 2
start: 16 start: 16
- token_type: Newline - token_type: Newline
line: 2 line: 3
start: 22 start: 22
- token_type: - token_type:
HtmlTagOpen: head Text: "<head>"
line: 3 line: 3
start: 23 start: 23
- token_type: Newline - token_type: Newline
line: 3 line: 4
start: 29 start: 29
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 4 line: 4
start: 30 start: 30
- token_type: - token_type:
StyleTagOpen: "style type=\"text/css\"" Text: "<style"
line: 4 line: 4
start: 34 start: 34
- token_type: Newline - token_type:
Whitespace: 1
line: 4 line: 4
start: 40
- token_type:
Text: "type=\"text/css\">"
line: 4
start: 41
- token_type: Newline
line: 5
start: 57 start: 57
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
line: 5 line: 5
start: 58 start: 58
- token_type: - token_type:
Comment: Text: /*
- Style header
- /*
- "*/"
line: 5 line: 5
start: 66 start: 66
- token_type: Newline - token_type:
Whitespace: 1
line: 5 line: 5
start: 68
- token_type:
Text: Style
line: 5
start: 69
- token_type:
Whitespace: 1
line: 5
start: 74
- token_type:
Text: header
line: 5
start: 75
- token_type:
Whitespace: 1
line: 5
start: 81
- token_type:
Text: "*/"
line: 5
start: 82
- token_type: Newline
line: 6
start: 84 start: 84
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
line: 6 line: 6
start: 85 start: 85
- token_type: - token_type:
Text: ".header " Text: ".header"
line: 6 line: 6
start: 93 start: 93
- token_type:
Whitespace: 1
line: 6
start: 100
- token_type: - token_type:
Text: "{" Text: "{"
line: 6 line: 6
@ -65,87 +106,165 @@ expression: tokens
line: 6 line: 6
start: 102 start: 102
- token_type: - token_type:
Text: "color: blue; }" Text: "color:"
line: 6 line: 6
start: 103 start: 103
- token_type: Newline - token_type:
Whitespace: 1
line: 6 line: 6
start: 109
- token_type:
Text: blue;
line: 6
start: 110
- token_type:
Whitespace: 1
line: 6
start: 115
- token_type:
Text: "}"
line: 6
start: 116
- token_type: Newline
line: 7
start: 117 start: 117
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 7 line: 7
start: 118 start: 118
- token_type: - token_type:
HtmlTagClose: style Text: "</style>"
line: 7 line: 7
start: 122 start: 122
- token_type: Newline - token_type: Newline
line: 7 line: 8
start: 130 start: 130
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 8 line: 8
start: 131 start: 131
- token_type: - token_type:
ScriptTagOpen: "script type=\"text/javascript\"" Text: "<script"
line: 8 line: 8
start: 135 start: 135
- token_type: Newline - token_type:
Whitespace: 1
line: 8 line: 8
start: 142
- token_type:
Text: "type=\"text/javascript\">"
line: 8
start: 143
- token_type: Newline
line: 9
start: 166 start: 166
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
line: 9 line: 9
start: 167 start: 167
- token_type: - token_type:
Comment: Text: //
- Init app
- //
- ~
line: 9 line: 9
start: 175 start: 175
- token_type: Newline - token_type:
Whitespace: 1
line: 9 line: 9
start: 177
- token_type:
Text: Init
line: 9
start: 178
- token_type:
Whitespace: 1
line: 9
start: 182
- token_type:
Text: app
line: 9
start: 183
- token_type: Newline
line: 10
start: 186 start: 186
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
line: 10 line: 10
start: 187 start: 187
- token_type: - token_type:
Text: "const app = " Text: const
line: 10 line: 10
start: 195 start: 195
- token_type:
Whitespace: 1
line: 10
start: 200
- token_type:
Text: app
line: 10
start: 201
- token_type:
Whitespace: 1
line: 10
start: 204
- token_type:
Text: "="
line: 10
start: 205
- token_type:
Whitespace: 1
line: 10
start: 206
- token_type: - token_type:
Text: "{" Text: "{"
line: 10 line: 10
start: 207 start: 207
- token_type: Newline - token_type: Newline
line: 10 line: 11
start: 208 start: 208
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
line: 11 line: 11
start: 209 start: 209
- token_type: - token_type:
Comment: Text: /*
- Config
- /*
- "*/"
line: 11 line: 11
start: 221 start: 221
- token_type: Newline - token_type:
Whitespace: 1
line: 11 line: 11
start: 223
- token_type:
Text: Config
line: 11
start: 224
- token_type:
Whitespace: 1
line: 11
start: 230
- token_type:
Text: "*/"
line: 11
start: 231
- token_type: Newline
line: 12
start: 233 start: 233
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
line: 12 line: 12
start: 234 start: 234
- token_type: - token_type:
Text: "debug: true" Text: "debug:"
line: 12 line: 12
start: 246 start: 246
- token_type: Newline - token_type:
Whitespace: 1
line: 12 line: 12
start: 252
- token_type:
Text: "true"
line: 12
start: 253
- token_type: Newline
line: 13
start: 257 start: 257
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
@ -156,57 +275,110 @@ expression: tokens
line: 13 line: 13
start: 266 start: 266
- token_type: Newline - token_type: Newline
line: 13 line: 14
start: 268 start: 268
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 14 line: 14
start: 269 start: 269
- token_type: - token_type:
HtmlTagClose: script Text: "</script>"
line: 14 line: 14
start: 273 start: 273
- token_type: Newline - token_type: Newline
line: 14 line: 15
start: 282 start: 282
- token_type: - token_type:
HtmlTagClose: head Text: "</head>"
line: 15 line: 15
start: 283 start: 283
- token_type: Newline - token_type: Newline
line: 15 line: 16
start: 290 start: 290
- token_type: - token_type:
HtmlTagOpen: body Text: "<body>"
line: 16 line: 16
start: 291 start: 291
- token_type: Newline - token_type: Newline
line: 16 line: 17
start: 297 start: 297
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 17 line: 17
start: 298 start: 298
- token_type: - token_type:
Comment: Text: "<!--"
- Header section
- "<!--"
- "-->"
line: 17 line: 17
start: 302 start: 302
- token_type: Newline - token_type:
Whitespace: 1
line: 17 line: 17
start: 306
- token_type:
Text: Header
line: 17
start: 307
- token_type:
Whitespace: 1
line: 17
start: 313
- token_type:
Text: section
line: 17
start: 314
- token_type:
Whitespace: 1
line: 17
start: 321
- token_type:
Text: "-->"
line: 17
start: 322
- token_type: Newline
line: 18
start: 325 start: 325
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 18 line: 18
start: 326 start: 326
- token_type: - token_type:
HtmlTagOpen: "div class=\"header\" id=\"main\" data-value=\"123\" disabled" Text: "<div"
line: 18 line: 18
start: 330 start: 330
- token_type: Newline - token_type:
Whitespace: 1
line: 18 line: 18
start: 334
- token_type:
Text: "class=\"header\""
line: 18
start: 335
- token_type:
Whitespace: 1
line: 18
start: 349
- token_type:
Text: "id=\"main\""
line: 18
start: 350
- token_type:
Whitespace: 1
line: 18
start: 359
- token_type:
Text: "data-value=\"123\""
line: 18
start: 360
- token_type:
Whitespace: 1
line: 18
start: 376
- token_type:
Text: disabled>
line: 18
start: 377
- token_type: Newline
line: 19
start: 386 start: 386
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
@ -217,7 +389,7 @@ expression: tokens
line: 19 line: 19
start: 395 start: 395
- token_type: Newline - token_type: Newline
line: 19 line: 20
start: 425 start: 425
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
@ -231,34 +403,30 @@ expression: tokens
line: 20 line: 20
start: 438 start: 438
- token_type: Newline - token_type: Newline
line: 20 line: 21
start: 459 start: 459
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
line: 21 line: 21
start: 460 start: 460
- token_type: - token_type:
HtmlTagOpen: h1 Text: "<h1>Welcome,"
line: 21 line: 21
start: 472 start: 472
- token_type: - token_type:
Text: "Welcome, " Whitespace: 1
line: 21 line: 21
start: 476 start: 484
- token_type: - token_type:
DjangoVariable: "user.name|default:\"Guest\"|title" DjangoVariable: "user.name|default:\"Guest\"|title"
line: 21 line: 21
start: 485 start: 485
- token_type: - token_type:
Text: "!" Text: "!</h1>"
line: 21 line: 21
start: 522 start: 522
- token_type:
HtmlTagClose: h1
line: 21
start: 523
- token_type: Newline - token_type: Newline
line: 21 line: 22
start: 528 start: 528
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
@ -269,26 +437,18 @@ expression: tokens
line: 22 line: 22
start: 541 start: 541
- token_type: Newline - token_type: Newline
line: 22 line: 23
start: 563 start: 563
- token_type: - token_type:
Whitespace: 16 Whitespace: 16
line: 23 line: 23
start: 564 start: 564
- token_type: - token_type:
HtmlTagOpen: span Text: "<span>Admin</span>"
line: 23 line: 23
start: 580 start: 580
- token_type:
Text: Admin
line: 23
start: 586
- token_type:
HtmlTagClose: span
line: 23
start: 591
- token_type: Newline - token_type: Newline
line: 23 line: 24
start: 598 start: 598
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
@ -299,26 +459,18 @@ expression: tokens
line: 24 line: 24
start: 611 start: 611
- token_type: Newline - token_type: Newline
line: 24 line: 25
start: 621 start: 621
- token_type: - token_type:
Whitespace: 16 Whitespace: 16
line: 25 line: 25
start: 622 start: 622
- token_type: - token_type:
HtmlTagOpen: span Text: "<span>User</span>"
line: 25 line: 25
start: 638 start: 638
- token_type:
Text: User
line: 25
start: 644
- token_type:
HtmlTagClose: span
line: 25
start: 648
- token_type: Newline - token_type: Newline
line: 25 line: 26
start: 655 start: 655
- token_type: - token_type:
Whitespace: 12 Whitespace: 12
@ -329,7 +481,7 @@ expression: tokens
line: 26 line: 26
start: 668 start: 668
- token_type: Newline - token_type: Newline
line: 26 line: 27
start: 679 start: 679
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
@ -340,28 +492,28 @@ expression: tokens
line: 27 line: 27
start: 688 start: 688
- token_type: Newline - token_type: Newline
line: 27 line: 28
start: 699 start: 699
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 28 line: 28
start: 700 start: 700
- token_type: - token_type:
HtmlTagClose: div Text: "</div>"
line: 28 line: 28
start: 704 start: 704
- token_type: Newline - token_type: Newline
line: 28 line: 29
start: 710 start: 710
- token_type: - token_type:
HtmlTagClose: body Text: "</body>"
line: 29 line: 29
start: 711 start: 711
- token_type: Newline - token_type: Newline
line: 29 line: 30
start: 718 start: 718
- token_type: - token_type:
HtmlTagClose: html Text: "</html>"
line: 30 line: 30
start: 719 start: 719
- token_type: Eof - token_type: Eof

View file

@ -3,13 +3,33 @@ source: crates/djls-template-ast/src/lexer.rs
expression: tokens expression: tokens
--- ---
- token_type: - token_type:
HtmlTagOpen: "div class=\"container\" id=\"main\" disabled" Text: "<div"
line: 1 line: 1
start: 0 start: 0
- token_type: - token_type:
HtmlTagClose: div Whitespace: 1
line: 1 line: 1
start: 42 start: 4
- token_type:
Text: "class=\"container\""
line: 1
start: 5
- token_type:
Whitespace: 1
line: 1
start: 22
- token_type:
Text: "id=\"main\""
line: 1
start: 23
- token_type:
Whitespace: 1
line: 1
start: 32
- token_type:
Text: disabled></div>
line: 1
start: 33
- token_type: Eof - token_type: Eof
line: 1 line: 1
start: ~ start: ~

View file

@ -3,66 +3,143 @@ source: crates/djls-template-ast/src/lexer.rs
expression: tokens expression: tokens
--- ---
- token_type: - token_type:
ScriptTagOpen: "script type=\"text/javascript\"" Text: "<script"
line: 1 line: 1
start: 0 start: 0
- token_type: Newline - token_type:
Whitespace: 1
line: 1 line: 1
start: 7
- token_type:
Text: "type=\"text/javascript\">"
line: 1
start: 8
- token_type: Newline
line: 2
start: 31 start: 31
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 2 line: 2
start: 32 start: 32
- token_type: - token_type:
Comment: Text: //
- Single line comment
- //
- ~
line: 2 line: 2
start: 36 start: 36
- token_type: Newline - token_type:
Whitespace: 1
line: 2 line: 2
start: 38
- token_type:
Text: Single
line: 2
start: 39
- token_type:
Whitespace: 1
line: 2
start: 45
- token_type:
Text: line
line: 2
start: 46
- token_type:
Whitespace: 1
line: 2
start: 50
- token_type:
Text: comment
line: 2
start: 51
- token_type: Newline
line: 3
start: 58 start: 58
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 3 line: 3
start: 59 start: 59
- token_type: - token_type:
Text: const x = 1; Text: const
line: 3 line: 3
start: 63 start: 63
- token_type: Newline - token_type:
Whitespace: 1
line: 3 line: 3
start: 68
- token_type:
Text: x
line: 3
start: 69
- token_type:
Whitespace: 1
line: 3
start: 70
- token_type:
Text: "="
line: 3
start: 71
- token_type:
Whitespace: 1
line: 3
start: 72
- token_type:
Text: 1;
line: 3
start: 73
- token_type: Newline
line: 4
start: 75 start: 75
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 4 line: 4
start: 76 start: 76
- token_type: - token_type:
Comment: Text: /*
- "Multi-line\n comment"
- /*
- "*/"
line: 4 line: 4
start: 80 start: 80
- token_type: Newline - token_type:
Whitespace: 1
line: 4 line: 4
start: 82
- token_type:
Text: Multi-line
line: 4
start: 83
- token_type: Newline
line: 5
start: 93
- token_type:
Whitespace: 7
line: 5
start: 94
- token_type:
Text: comment
line: 5
start: 101
- token_type:
Whitespace: 1
line: 5
start: 108
- token_type:
Text: "*/"
line: 5
start: 109
- token_type: Newline
line: 6
start: 111 start: 111
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 5 line: 6
start: 112 start: 112
- token_type: - token_type:
Text: console.log(x); Text: console.log(x);
line: 5 line: 6
start: 116 start: 116
- token_type: Newline - token_type: Newline
line: 5 line: 7
start: 131 start: 131
- token_type: - token_type:
HtmlTagClose: script Text: "</script>"
line: 6 line: 7
start: 132 start: 132
- token_type: Eof - token_type: Eof
line: 6 line: 7
start: ~ start: ~

View file

@ -3,51 +3,92 @@ source: crates/djls-template-ast/src/lexer.rs
expression: tokens expression: tokens
--- ---
- token_type: - token_type:
StyleTagOpen: "style type=\"text/css\"" Text: "<style"
line: 1 line: 1
start: 0 start: 0
- token_type: Newline - token_type:
Whitespace: 1
line: 1 line: 1
start: 6
- token_type:
Text: "type=\"text/css\">"
line: 1
start: 7
- token_type: Newline
line: 2
start: 23 start: 23
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 2 line: 2
start: 24 start: 24
- token_type: - token_type:
Comment: Text: /*
- Header styles
- /*
- "*/"
line: 2 line: 2
start: 28 start: 28
- token_type: Newline - token_type:
Whitespace: 1
line: 2 line: 2
start: 30
- token_type:
Text: Header
line: 2
start: 31
- token_type:
Whitespace: 1
line: 2
start: 37
- token_type:
Text: styles
line: 2
start: 38
- token_type:
Whitespace: 1
line: 2
start: 44
- token_type:
Text: "*/"
line: 2
start: 45
- token_type: Newline
line: 3
start: 47 start: 47
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
line: 3 line: 3
start: 48 start: 48
- token_type: - token_type:
Text: ".header " Text: ".header"
line: 3 line: 3
start: 52 start: 52
- token_type:
Whitespace: 1
line: 3
start: 59
- token_type: - token_type:
Text: "{" Text: "{"
line: 3 line: 3
start: 60 start: 60
- token_type: Newline - token_type: Newline
line: 3 line: 4
start: 61 start: 61
- token_type: - token_type:
Whitespace: 8 Whitespace: 8
line: 4 line: 4
start: 62 start: 62
- token_type: - token_type:
Text: "color: blue;" Text: "color:"
line: 4 line: 4
start: 70 start: 70
- token_type: Newline - token_type:
Whitespace: 1
line: 4 line: 4
start: 76
- token_type:
Text: blue;
line: 4
start: 77
- token_type: Newline
line: 5
start: 82 start: 82
- token_type: - token_type:
Whitespace: 4 Whitespace: 4
@ -58,10 +99,10 @@ expression: tokens
line: 5 line: 5
start: 87 start: 87
- token_type: Newline - token_type: Newline
line: 5 line: 6
start: 88 start: 88
- token_type: - token_type:
HtmlTagClose: style Text: "</style>"
line: 6 line: 6
start: 89 start: 89
- token_type: Eof - token_type: Eof

View file

@ -3,8 +3,7 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<!-- HTML comment -->"
Comment: HTML comment
- Django: - Django:
Comment: Django comment Comment: Django comment
errors: [] errors: []

View file

@ -12,6 +12,7 @@ nodes:
- if - if
- user.is_authenticated - user.is_authenticated
children: children:
- Text: "\n "
- Django: - Django:
Variable: Variable:
bits: bits:
@ -23,6 +24,7 @@ nodes:
- name: default - name: default
arguments: arguments:
- "'Guest'" - "'Guest'"
- Text: "\n "
- Django: - Django:
Tag: Tag:
Block: Block:
@ -33,6 +35,7 @@ nodes:
- in - in
- user.groups - user.groups
children: children:
- Text: "\n "
- Django: - Django:
Tag: Tag:
Block: Block:
@ -47,12 +50,14 @@ nodes:
Closing: Closing:
name: endif name: endif
bits: [] bits: []
- Text: "\n "
- Django: - Django:
Variable: Variable:
bits: bits:
- group - group
- name - name
filters: [] filters: []
- Text: "\n "
- Django: - Django:
Tag: Tag:
Block: Block:
@ -68,6 +73,7 @@ nodes:
Closing: Closing:
name: endif name: endif
bits: [] bits: []
- Text: "\n "
- Django: - Django:
Tag: Tag:
Block: Block:
@ -82,25 +88,27 @@ nodes:
Closing: Closing:
name: endif name: endif
bits: [] bits: []
- Text: "\n "
- Django: - Django:
Tag: Tag:
Branch: Branch:
name: empty name: empty
bits: [] bits: []
children: children:
- Text: (no groups) - Text: "\n (no groups)\n "
- Django: - Django:
Tag: Tag:
Closing: Closing:
name: endfor name: endfor
bits: [] bits: []
- Text: "\n"
- Django: - Django:
Tag: Tag:
Branch: Branch:
name: else name: else
bits: [] bits: []
children: children:
- Text: Guest - Text: "\n Guest\n"
- Django: - Django:
Tag: Tag:
Closing: Closing:

View file

@ -3,48 +3,6 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<div class=\"container\">\n <h1>Header</h1>\n "
Element: errors:
tag_name: div - UnclosedTag: if
attributes:
class:
Value: container
children:
- Html:
Element:
tag_name: h1
attributes: {}
children:
- Text: Header
- Django:
Tag:
Block:
name: if
bits:
- if
- user.is_authenticated
children:
- Html:
Element:
tag_name: p
attributes: {}
children:
- Text: "Welcome "
- Django:
Variable:
bits:
- user
- name
filters: []
- Django:
Tag:
Closing:
name: endif
bits: []
- Html:
Element:
tag_name: footer
attributes: {}
children:
- Text: Page Footer
errors: []

View file

@ -2,6 +2,6 @@
source: crates/djls-template-ast/src/parser.rs source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: [] nodes:
errors: - Text: "<div>"
- UnclosedTag: div errors: []

View file

@ -2,6 +2,6 @@
source: crates/djls-template-ast/src/parser.rs source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: [] nodes:
errors: - Text: "<script>console.log('test');"
- UnclosedTag: script errors: []

View file

@ -2,6 +2,6 @@
source: crates/djls-template-ast/src/parser.rs source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: [] nodes:
errors: - Text: "<style>body { color: blue; "
- UnclosedTag: style errors: []

View file

@ -3,128 +3,57 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<!DOCTYPE html>\n<html>\n <head>\n <style type=\"text/css\">\n /* Style header */\n .header { color: blue; }\n </style>\n <script type=\"text/javascript\">\n // Init app\n const app = {\n /* Config */\n debug: true\n };\n </script>\n </head>\n <body>\n <!-- Header section -->\n <div class=\"header\" id=\"main\" data-value=\"123\" disabled>\n "
Doctype: "!DOCTYPE html" - Django:
- Html: Tag:
Element: Block:
tag_name: html name: if
attributes: {} bits:
children: - if
- Html: - user.is_authenticated
Element: children:
tag_name: head - Text: "\n "
attributes: {} - Django:
children: Comment: Welcome message
- Style: - Text: "\n <h1>Welcome, "
Element: - Django:
attributes: Variable:
type: bits:
Value: text/css - user
children: - name
- Style: filters:
Comment: Style header - name: title
- Text: ".header " arguments: []
- Text: "{" - name: default
- Text: "color: blue; }" arguments:
- Script: - "'Guest'"
Element: - Text: "!</h1>\n "
attributes: - Django:
type: Tag:
Value: text/javascript Block:
children: name: if
- Script: bits:
Comment: - if
content: Init app - user.is_staff
kind: SingleLine children:
- Text: "const app = " - Text: "\n <span>Admin</span>\n "
- Text: "{" - Django:
- Script: Tag:
Comment: Branch:
content: Config name: else
kind: MultiLine bits: []
- Text: "debug: true" children:
- Text: "};" - Text: "\n <span>User</span>\n "
- Html: - Django:
Element: Tag:
tag_name: body Closing:
attributes: {} name: endif
children: bits: []
- Html: - Text: "\n "
Comment: Header section - Django:
- Html: Tag:
Element: Closing:
tag_name: div name: endif
attributes: bits: []
class: - Text: "\n </div>\n </body>\n</html>"
Value: header
data-value:
Value: "123"
disabled: Boolean
id:
Value: main
children:
- Django:
Tag:
Block:
name: if
bits:
- if
- user.is_authenticated
children:
- Django:
Comment: Welcome message
- Html:
Element:
tag_name: h1
attributes: {}
children:
- Text: "Welcome, "
- Django:
Variable:
bits:
- user
- name
filters:
- name: default
arguments:
- Guest
- name: title
arguments: []
- Text: "!"
- Django:
Tag:
Block:
name: if
bits:
- if
- user.is_staff
children:
- Html:
Element:
tag_name: span
attributes: {}
children:
- Text: Admin
- Django:
Tag:
Branch:
name: else
bits: []
children:
- Html:
Element:
tag_name: span
attributes: {}
children:
- Text: User
- Django:
Tag:
Closing:
name: endif
bits: []
- Django:
Tag:
Closing:
name: endif
bits: []
errors: [] errors: []

View file

@ -3,6 +3,5 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<!DOCTYPE html>"
Doctype: "!DOCTYPE html"
errors: [] errors: []

View file

@ -3,12 +3,5 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<div class=\"container\">Hello</div>"
Element:
tag_name: div
attributes:
class:
Value: container
children:
- Text: Hello
errors: [] errors: []

View file

@ -3,10 +3,5 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Html: - Text: "<input type=\"text\" />"
Void:
tag_name: input
attributes:
type:
Value: text
errors: [] errors: []

View file

@ -3,20 +3,5 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Script: - Text: "<script type=\"text/javascript\">\n // Single line comment\n const x = 1;\n /* Multi-line\n comment */\n console.log(x);\n</script>"
Element:
attributes:
type:
Value: text/javascript
children:
- Script:
Comment:
content: Single line comment
kind: SingleLine
- Text: const x = 1;
- Script:
Comment:
content: "Multi-line\n comment"
kind: MultiLine
- Text: console.log(x);
errors: [] errors: []

View file

@ -3,16 +3,5 @@ source: crates/djls-template-ast/src/parser.rs
expression: ast expression: ast
--- ---
nodes: nodes:
- Style: - Text: "<style type=\"text/css\">\n /* Header styles */\n .header {\n color: blue;\n }\n</style>"
Element:
attributes:
type:
Value: text/css
children:
- Style:
Comment: Header styles
- Text: ".header "
- Text: "{"
- Text: "color: blue;"
- Text: "}"
errors: [] errors: []