1#![warn(missing_docs)]
2
3#[macro_use]
29extern crate lazy_static;
30
31extern crate convert_case;
32
33mod transform;
35mod utils;
37pub use transform::{create_style_object, extract_html_props};
38mod statics;
40pub use statics::{HTML_PROPS, SELF_ENCLOSED_TAGS};
41
42pub fn convert_props_react(ctx: &str) -> String {
44 let mut context = ctx.to_string();
45 let props: Vec<String> = extract_html_props(&context);
46
47 for item in props.iter() {
48 if item == "style" {
49 context = create_style_object(&context);
50 } else {
51 let value = HTML_PROPS.get(&*item.to_owned()).unwrap_or(&"");
52
53 if !value.is_empty() {
54 context = context.replace(&format!("{}=", item), &format!("{}=", value));
55 }
56 }
57 }
58
59 context
60}
61
62pub fn convert_to_react(ctx: &String, component_name: String) -> String {
64 let react_html = convert_props_react(ctx);
65 let mut react_html = react_html.trim().to_owned();
66
67 if react_html.starts_with("<!DOCTYPE html>") {
69 react_html = react_html.replace("<!DOCTYPE html>", "").trim().to_owned();
70 }
71 if react_html.starts_with("<html>") {
72 react_html = react_html.replace("<html>", "");
73 react_html = react_html.replace("</html>", "");
74 }
75 if react_html.contains("<script")
77 || react_html.contains("<style")
78 || react_html.contains("<script>")
79 || react_html.contains("<style>")
80 {
81 react_html = convert_children_react(&mut react_html);
82 }
83
84 let component_name = format!(" {}", component_name.trim());
85
86 let component = format!(
87 r###"import React from "react"
88
89function{}() {{
90 return (
91 <>
92 {}
93 </>
94 )
95}}"###,
96 component_name, react_html
97 );
98
99 component
100}
101
102pub fn convert_children_react(ctx: &mut String) -> String {
104 let mut entry_start = false; let mut entry_end = false; let mut inside_tag = false; let mut store_tag = false; let mut current_prop = String::from(""); let mut result = String::from("");
111 let mut peekable = ctx.chars().peekable();
112
113 let mut empty_children = false; let mut block_self_enclose = false;
117
118 while let Some(c) = peekable.next() {
119 result.push(c);
120
121 let peeked = if c == '/' || entry_start || entry_end {
123 if let Some(cc) = peekable.peek() {
124 cc.to_string()
125 } else {
126 String::from("")
127 }
128 } else {
129 String::from("")
130 };
131
132 if c == '<' {
133 inside_tag = true;
134 store_tag = true;
135 }
136 if c == '/' && peeked == ">" {
137 block_self_enclose = true;
138 }
139 if c == '>' {
140 inside_tag = false;
141 if SELF_ENCLOSED_TAGS.contains(¤t_prop.trim_end().as_ref()) {
143 if !block_self_enclose {
144 result.pop();
145 result.push('/');
146 result.push('>');
147 } else {
148 block_self_enclose = false;
149 }
150 }
151 }
152
153 if entry_start && c == '>' {
155 entry_start = false;
156 store_tag = true;
157
158 if peeked != "<" {
159 result.push('{');
160 result.push('`');
161 empty_children = false;
162 } else {
163 empty_children = true;
164 }
165
166 current_prop.clear();
167 }
168
169 if entry_end {
171 if !empty_children {
172 for _ in 0..current_prop.len() + 1 {
173 result.pop();
174 }
175 result.push('`');
176 result.push('}');
177 result.push_str(&format!(r#"{}>"#, current_prop));
178 } else {
179 empty_children = true;
180 }
181 entry_end = false;
182
183 current_prop.clear();
184 }
185
186 if inside_tag && store_tag {
187 current_prop.push(c);
188
189 if current_prop == "<style" || current_prop == "<script" {
191 entry_start = true;
192 empty_children = false; }
194
195 if current_prop == "</style" || current_prop == "</script" {
197 entry_end = !empty_children;
198 }
199
200 if c == ' ' {
202 store_tag = false;
203 }
204
205 if current_prop.starts_with("</") && c == '>' {
207 store_tag = false;
208 }
209 } else if !inside_tag {
210 current_prop.clear();
211 }
212 }
213
214 result
215}