diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index c83ab0f..d3aa5b2 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -118,12 +118,17 @@ fn construct_template(template_path: &str) -> Template { }); let config_path = format!("{}/config.yaml", template_path); + let config = if let Ok(config_str) = fs::read_to_string(&config_path) { + serde_yaml::from_str(&config_str).ok() + } else { + None + }; Template::TemplateInjector { template: template_content, replace_selector: replace_selector.trim().to_string(), scraper, - config_path: Some(config_path), + config, script: post_script, style, } diff --git a/src/compiler/config.rs b/src/compiler/config.rs deleted file mode 100644 index 6aed803..0000000 --- a/src/compiler/config.rs +++ /dev/null @@ -1,50 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Deserialize, PartialEq)] -pub enum ReplaceType { - #[serde(rename = "hide")] - Hide, - #[serde(rename = "replace")] - Replace, -} - -impl Default for ReplaceType { - fn default() -> Self { - ReplaceType::Replace - } -} - -#[derive(Debug, Deserialize, Default)] -pub struct TemplateReplacer { - #[serde(default)] - pub replace_type: ReplaceType, -} - -#[derive(Debug, Deserialize, Default)] -pub struct Scraper { - #[serde(default)] - pub rerender_key: String, -} - -#[derive(Debug, Deserialize, Default)] -pub struct Config { - #[serde(default)] - pub template_replacer: TemplateReplacer, - #[serde(default)] - pub scraper: Scraper, -} - -impl Config { - pub fn load(path: &Option) -> Self { - match path { - Some(path_str) => match std::fs::File::open(path_str) { - Ok(file) => { - let reader = std::io::BufReader::new(file); - serde_yaml::from_reader(reader).unwrap_or_default() - } - Err(_) => Config::default(), - }, - None => Config::default(), - } - } -} diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 688cb55..54e88ec 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -1,3 +1,2 @@ pub mod compiler; pub mod models; -pub mod config; diff --git a/src/compiler/models.rs b/src/compiler/models.rs index d1af7e6..6762897 100644 --- a/src/compiler/models.rs +++ b/src/compiler/models.rs @@ -19,6 +19,22 @@ pub enum PageRoute { CustomScript(String), } +#[derive(serde::Deserialize, Clone)] +pub struct TemplateConfig { + pub template_replacer: Option, + pub scraper: Option, +} + +#[derive(serde::Deserialize, Clone)] +pub struct TemplateReplacer { + pub replace_type: String, // "hide" or "replace" +} + +#[derive(serde::Deserialize, Clone)] +pub struct ScraperConfig { + pub rerender_key: Option, +} + pub enum Template { CustomScript { script: String, @@ -31,7 +47,7 @@ pub enum Template { scraper: Option, style: Option, script: Option, - config_path: Option, + config: Option, }, } diff --git a/src/compiler/models_impl.rs b/src/compiler/models_impl.rs index 183a1f4..5f49c15 100644 --- a/src/compiler/models_impl.rs +++ b/src/compiler/models_impl.rs @@ -1,16 +1,25 @@ use indoc::indoc; use serde_json; -use crate::compiler::config::{Config, ReplaceType}; +// use crate::compiler::config::{Config, ReplaceType}; // Removed unused imports use crate::css::{minify_css, scope_css}; -use crate::compiler::models::{PageRoute, Project, Template}; +use crate::compiler::models::{PageRoute, Project, Template, TemplateConfig}; use crate::js::minify_javascript; impl Project { pub fn compile_script(&self) -> String { let template = indoc! { r#" (function() { + window.Rerender = window.Rerender || { + components: {}, + rerenderComponent: function(key) { + if (this.components[key]) { + this.components[key](); + } + } + }; + if (typeof Handlebars === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js'; @@ -143,7 +152,7 @@ impl Template { scraper, script, style, - config_path, + config, } => { let scope_class = format!("template-style-{}", rand::random::()); let script = if let Some(script) = script { @@ -156,7 +165,34 @@ impl Template { String::new() }; - let config = Config::load(config_path); + // Default config + let config = config.clone().unwrap_or(TemplateConfig { + template_replacer: None, + scraper: None, + }); + + let rerender_key = config + .scraper + .as_ref() + .and_then(|s| s.rerender_key.as_ref()); + let replace_type = config + .template_replacer + .as_ref() + .map(|r| r.replace_type.as_str()) + .unwrap_or("replace"); + + let rerender_registration = if let Some(key) = rerender_key { + format!( + r#" + window.Rerender.components["{key}"] = function() {{ + render(); + }}; + "#, + key = key + ) + } else { + String::new() + }; let style_script = if let Some(style) = style { if style.trim().is_empty() { @@ -167,7 +203,6 @@ impl Template { (function() {{ const styleElement = document.createElement('style'); styleElement.textContent = {style}; - console.log(styleElement.textContent); document.head.appendChild(styleElement); }})(); "# }, @@ -179,62 +214,75 @@ impl Template { String::new() }; - let script_template = - if config.template_replacer.replace_type == ReplaceType::Replace { - indoc! { r#" + let script_template = if replace_type == "replace" { + 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 render = function() { + 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 temp = document.createElement('template'); + temp.innerHTML = rendered; + const newElement = temp.content.firstElementChild; + + // Remove old + const oldElement = document.querySelector('.' + {scope_class}); + if(oldElement) oldElement.remove(); - const newElement = temp.content.firstElementChild; - - targetElement.replaceWith(newElement); - - newElement.classList.add({scope_class}); - - (function() { - {script} - })(); + targetElement.replaceWith(newElement); + newElement.classList.add({scope_class}); + + (function() { + {script} + })(); + }; + render(); + {rerender_registration} })(); "# - } - } else { - indoc! { r#" + } + } else { + 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 render = function() { + const parsedData = eval({scraper} || "(() => ({}))"); + const template = Handlebars.compile({template}); + const rendered = template(parsedData); + const targetElement = document.querySelector({replace_selector}); - targetElement.style.display = 'none'; + targetElement.style.display = 'none'; - const temp = document.createElement('template'); - temp.innerHTML = rendered; - const newElement = temp.content.firstElementChild; + const temp = document.createElement('template'); + temp.innerHTML = rendered; + const newElement = temp.content.firstElementChild; + + // Remove old + const oldElement = document.querySelector('.' + {scope_class}); + if(oldElement) oldElement.remove(); - targetElement.after(newElement); + targetElement.after(newElement); + newElement.classList.add({scope_class}); - newElement.classList.add({scope_class}); - - (function() { - {script} - })(); + (function() { + {script} + })(); + }; + render(); + {rerender_registration} })(); "# } - }; + }; script_template .replace("{style_script}", &style_script.trim()) .replace("{script}", &script) + .replace("{rerender_registration}", &rerender_registration) .replace( "{template}", &serde_json::to_string(&template.trim()).unwrap(),