initial commit

This commit is contained in:
2026-05-21 20:46:11 +02:00
commit b47d99ba2b
11 changed files with 1662 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
use indoc::indoc;
use crate::css::scope_css;
use crate::compiler::models::{PageRoute, Project, Template};
use crate::js::minify_javascript;
impl Project {
pub fn compile_script(&self) -> String {
let template = indoc! { r#"
(function() {
if (typeof Handlebars === 'undefined') {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js';
document.head.appendChild(script);
}
const tempDefine = window.define;
window.define = undefined;
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js';
script.onload = function() {
window.define = tempDefine;
{pages_script}
};
document.head.appendChild(script);
})()
"# };
let mut script = String::new();
for page in &self.pages {
let template_script = indoc! { r#"
if (eval({page_route})) {
{content_script}
}
"# };
let route_script = page.route.as_script();
let content_script = page
.content
.iter()
.map(|template| template.as_script())
.collect::<Vec<String>>()
.join("\n");
script.push_str(
&template_script
.replace("{page_route}", &format!("{:?}", minify_javascript(&route_script)))
.replace("{content_script}", &content_script),
);
}
minify_javascript(&template.replace("{pages_script}", &script))
}
}
impl PageRoute {
fn as_script(&self) -> String {
match self {
PageRoute::Regexpr(expr) => {
let template = indoc! { r#"
(function() {
const regex = new RegExp({expr});
return regex.test(window.location.pathname);
})()
"# };
template.replace("{expr}", &format!("{:?}", expr.trim()))
}
PageRoute::Static(route) => {
let template = indoc! { r#"
(function() {
return window.location.pathname === {route};
})()
"# };
template.replace("{route}", &format!("{:?}", route))
}
PageRoute::CustomScript(script) => script.clone(),
}
}
}
impl Template {
pub fn as_script(&self) -> String {
match self {
Template::CustomScript { script, style } => {
if style.is_none() || style.as_ref().unwrap().trim().is_empty() {
return script.clone();
}
let style_script = style.as_ref().map_or(String::new(), |s| {
format!(
r#"
const styleElement = document.createElement('style');
styleElement.textContent = {style};
document.head.appendChild(styleElement);
"#,
style = format!("{:?}", s)
)
});
format!("{}\n{}", style_script, script)
}
Template::TemplateInjector {
template,
replace_selector,
scraper,
style,
} => {
let scope_class = format!("template-style-{}", rand::random::<u32>());
let style_script = if let Some(style) = style {
if style.trim().is_empty() {
String::new()
} else {
format!(
indoc! {r#"
const styleElement = document.createElement('style');
styleElement.textContent = {style};
document.head.appendChild(styleElement);
"# },
style = &scope_css(&style, &scope_class)
)
}
} else {
String::new()
};
let script_template = indoc! { r#"
(function() {
{style_script}
const parsedData = eval({scraper} || "(() => ({}))");
const template = Handlebars.compile({template});
const rendered = template(parsedData);
console.log("Rendered template:", rendered);
const targetElement = document.querySelector({replace_selector});
const temp = document.createElement('template');
temp.innerHTML = rendered;
const newElement = temp.content.firstElementChild;
targetElement.replaceWith(newElement);
newElement.classList.add({scope_class});
})();
"# };
script_template
.replace("{style_script}", &style_script)
.replace("{template}", &format!("{:?}", template.trim()))
.replace("{replace_selector}", &format!("{:?}", replace_selector))
.replace("{scope_class}", &format!("{:?}", scope_class))
.replace(
"{scraper}",
&format!("{:?}", minify_javascript(scraper.as_deref().unwrap_or(""))),
)
}
}
}
}