diff --git a/src/html_element.rs b/src/html_element.rs index 166f1f5..cb0a04c 100644 --- a/src/html_element.rs +++ b/src/html_element.rs @@ -26,6 +26,7 @@ pub(crate) struct HtmlPartWithLine { pub(crate) struct Element { pub(crate) name: String, pub(crate) self_closing: bool, + pub(crate) is_component: bool, pub(crate) open_attrs: Vec<(String, String)>, pub(crate) close_attrs: Vec<(String, String)>, pub(crate) children: Vec, diff --git a/src/sink.rs b/src/sink.rs index 2e44cac..ca7caf7 100644 --- a/src/sink.rs +++ b/src/sink.rs @@ -17,34 +17,51 @@ impl<'a> TokenSink for HtmlSink<'a> { let mut element = Element { name: tag.name.to_string(), self_closing: tag.self_closing, + is_component: false, open_attrs: tag.attrs.into_iter().map(|a| (a.name.local.to_string(), a.value.to_string())).collect(), close_attrs: Vec::new(), children: Vec::new(), }; + if element.name == "comp" || element.name == "component" { let Some(real_name) = element.open_attrs.iter().find(|(k, _)| k == "name").map(|(_, v)| v) else { abort!(self.args.path_span, "Missing name attribute on component tag at line {line_number}"); }; + element.name = real_name.to_owned(); + element.is_component = true; element.open_attrs.retain(|(k, _)| k != "name"); } - match element.self_closing { - true => match self.opened_elements.last_mut() { + + if element.self_closing { + match self.opened_elements.last_mut() { Some(container) => container.children.push(HtmlPartWithLine { part: HtmlPart::Element(element), line: line_number as usize }), None => self.html_parts.push(HtmlPartWithLine { part: HtmlPart::Element(element), line: line_number as usize }), - }, - false => self.opened_elements.push(element) + } + } else { + self.opened_elements.push(element) } }, TagKind::EndTag => { let mut element = self.opened_elements.pop().unwrap_or_else(|| abort!(self.args.path_span, "Unexpected closing tag {} at line {line_number}", tag.name)); - if tag.name != element.name { - abort!(self.args.path_span, "Unexpected closing tag {} at line {line_number}", tag.name); + + if element.is_component { + if !["comp", "component"].contains(&tag.name.to_string().as_str()) { + abort!(self.args.path_span, "Unexpected closing tag {} at line {line_number}", tag.name); + } + } else { + if tag.name != element.name { + abort!(self.args.path_span, "Unexpected closing tag {} at line {line_number}", tag.name); + } } + element.close_attrs = tag.attrs.into_iter().map(|a| (a.name.local.to_string(), a.value.to_string())).collect(); + + let html_part = HtmlPartWithLine { part: HtmlPart::Element(element), line: line_number as usize }; + match self.opened_elements.last_mut() { - Some(container) => container.children.push(HtmlPartWithLine { part: HtmlPart::Element(element), line: line_number as usize }), - None => self.html_parts.push(HtmlPartWithLine { part: HtmlPart::Element(element), line: line_number as usize }), + Some(container) => container.children.push(html_part), + None => self.html_parts.push(html_part), } }, }, @@ -64,6 +81,7 @@ pub(crate) fn read_template(args: &Args) -> Element { Ok(template) => template, Err(e) => abort!(args.path_span, "Failed to read template file at {}: {}", args.path, e), }; + let mut html_parts = Vec::new(); let html_sink = HtmlSink { html_parts: &mut html_parts, opened_elements: Vec::new(), args }; let mut html_tokenizer = Tokenizer::new(html_sink, TokenizerOpts::default()); @@ -71,13 +89,16 @@ pub(crate) fn read_template(args: &Args) -> Element { buffer_queue.push_back(template.into()); let _ = html_tokenizer.feed(&mut buffer_queue); html_tokenizer.end(); + let mut root = Element { name: "".to_string(), open_attrs: Vec::new(), close_attrs: Vec::new(), self_closing: false, + is_component: false, children: html_parts, }; + root.clean_text(); root }