dioxus_rsx_rosetta/
lib.rs1#![doc = include_str!("../README.md")]
2#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
3#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
4
5use convert_case::{Case, Casing};
6use dioxus_html::{map_html_attribute_to_rsx, map_html_element_to_rsx};
7use dioxus_rsx::{
8 Attribute, AttributeName, AttributeValue, BodyNode, CallBody, Component, Element, ElementName,
9 HotLiteral, TemplateBody, TextNode,
10};
11pub use html_parser::{Dom, Node};
12use htmlentity::entity::ICodedDataTrait;
13use proc_macro2::{Ident, Span};
14use syn::{punctuated::Punctuated, LitStr};
15
16pub fn rsx_from_html(dom: &Dom) -> CallBody {
18 let nodes = dom
19 .children
20 .iter()
21 .filter_map(rsx_node_from_html)
22 .collect::<Vec<_>>();
23
24 let template = TemplateBody::new(nodes);
25
26 CallBody::new(template)
27}
28
29pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
33 use AttributeName::*;
34 use AttributeValue::*;
35
36 match node {
37 Node::Text(text) => Some(BodyNode::Text(TextNode::from_text(
38 &htmlentity::entity::decode(text.as_bytes())
39 .to_string()
40 .ok()?,
41 ))),
42
43 Node::Element(el) => {
44 let el_name = if let Some(name) = map_html_element_to_rsx(&el.name) {
45 ElementName::Ident(Ident::new(name, Span::call_site()))
46 } else {
47 if el.name.contains('-') {
49 ElementName::Custom(LitStr::new(&el.name, Span::call_site()))
50 } else {
51 ElementName::Ident(Ident::new(&el.name.to_case(Case::Snake), Span::call_site()))
53 }
54 };
55
56 let mut attributes: Vec<_> = el
57 .attributes
58 .iter()
59 .map(|(name, value)| {
60 let (_namespace, name) = name.split_once(':').unwrap_or(("", name));
63
64 let value = HotLiteral::from_raw_text(value.as_deref().unwrap_or("false"));
65 let attr = if let Some(name) = map_html_attribute_to_rsx(name) {
66 let name = if let Some(name) = name.strip_prefix("r#") {
67 Ident::new_raw(name, Span::call_site())
68 } else {
69 Ident::new(name, Span::call_site())
70 };
71 BuiltIn(name)
72 } else {
73 Custom(LitStr::new(name, Span::call_site()))
75 };
76
77 Attribute::from_raw(attr, AttrLiteral(value))
78 })
79 .collect();
80
81 let class = el.classes.join(" ");
82 if !class.is_empty() {
83 attributes.push(Attribute::from_raw(
84 BuiltIn(Ident::new("class", Span::call_site())),
85 AttrLiteral(HotLiteral::from_raw_text(&class)),
86 ));
87 }
88
89 if let Some(id) = &el.id {
90 attributes.push(Attribute::from_raw(
91 BuiltIn(Ident::new("id", Span::call_site())),
92 AttrLiteral(HotLiteral::from_raw_text(id)),
93 ));
94 }
95
96 attributes.sort_by(|a, b| a.name.to_string().cmp(&b.name.to_string()));
100
101 let children = el.children.iter().filter_map(rsx_node_from_html).collect();
102
103 Some(BodyNode::Element(Element {
104 name: el_name,
105 children,
106 raw_attributes: attributes,
107 merged_attributes: Default::default(),
108 diagnostics: Default::default(),
109 spreads: Default::default(),
110 brace: Default::default(),
111 }))
112 }
113
114 Node::Comment(_) => None,
116 }
117}
118
119pub fn collect_svgs(children: &mut [BodyNode], out: &mut Vec<BodyNode>) {
121 for child in children {
122 match child {
123 BodyNode::Component(comp) => collect_svgs(&mut comp.children.roots, out),
124
125 BodyNode::Element(el) if el.name == "svg" => {
126 let mut segments = Punctuated::new();
128
129 segments.push(Ident::new("icons", Span::call_site()).into());
130
131 let new_name: Ident = Ident::new(&format!("icon_{}", out.len()), Span::call_site());
132
133 segments.push(new_name.clone().into());
134
135 let mut new_comp = BodyNode::Component(Component {
137 name: syn::Path {
138 leading_colon: None,
139 segments,
140 },
141 generics: None,
142 spreads: Default::default(),
143 diagnostics: Default::default(),
144 fields: vec![],
145 children: TemplateBody::new(vec![]),
146 brace: Some(Default::default()),
147 dyn_idx: Default::default(),
148 component_literal_dyn_idx: vec![],
149 });
150
151 std::mem::swap(child, &mut new_comp);
152
153 out.push(new_comp);
155 }
156
157 BodyNode::Element(el) => collect_svgs(&mut el.children, out),
158
159 _ => {}
160 }
161 }
162}