192 lines
6.6 KiB
Rust
192 lines
6.6 KiB
Rust
use indoc::indoc;
|
|
use serde_json;
|
|
|
|
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}
|
|
|
|
const domReadyEvent = new Event('DOMContentLoaded', {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
});
|
|
|
|
document.dispatchEvent(domReadyEvent);
|
|
window.dispatchEvent(domReadyEvent);
|
|
};
|
|
|
|
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}",
|
|
&serde_json::to_string(&minify_javascript(&route_script)).unwrap(),
|
|
)
|
|
.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}", &serde_json::to_string(&expr.trim()).unwrap())
|
|
}
|
|
PageRoute::Static(route) => {
|
|
let template = indoc! { r#"
|
|
(function() {
|
|
return window.location.pathname === {route};
|
|
})()
|
|
"# };
|
|
|
|
template.replace("{route}", &serde_json::to_string(&route).unwrap())
|
|
}
|
|
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 = serde_json::to_string(s).unwrap()
|
|
)
|
|
});
|
|
|
|
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 =
|
|
&serde_json::to_string(&scope_css(&style, &scope_class)).unwrap()
|
|
)
|
|
}
|
|
} else {
|
|
String::new()
|
|
};
|
|
|
|
let script_template = indoc! { r#"
|
|
(function() {
|
|
{style_script}
|
|
|
|
const parsedData = eval({scraper} || "(() => ({}))");
|
|
const template = Handlebars.compile({template});
|
|
const rendered = template(parsedData);
|
|
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}",
|
|
&serde_json::to_string(&template.trim()).unwrap(),
|
|
)
|
|
.replace(
|
|
"{replace_selector}",
|
|
&serde_json::to_string(&replace_selector).unwrap(),
|
|
)
|
|
.replace(
|
|
"{scope_class}",
|
|
&serde_json::to_string(&scope_class).unwrap(),
|
|
)
|
|
.replace(
|
|
"{scraper}",
|
|
&serde_json::to_string(&minify_javascript(
|
|
scraper.as_deref().unwrap_or(""),
|
|
))
|
|
.unwrap(),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|