#![allow(clippy::unwrap_used, clippy::expect_used)]
use anyhow::Result;
use ssg::cache::BuildCache;
use ssg::livereload::LiveReloadPlugin;
use ssg::plugin::{PluginContext, PluginManager};
use ssg::plugins::MinifyPlugin;
use ssg::postprocess::{HtmlFixPlugin, ManifestFixPlugin};
use ssg::search::SearchPlugin;
use ssg::seo::{CanonicalPlugin, RobotsPlugin, SeoPlugin};
use staticdatagen::compiler::service::compile;
use std::fs;
use std::path::Path;
fn main() -> Result<()> {
let build_dir = Path::new("./examples/plugins/build");
let site_dir = Path::new("./examples/plugins/public");
let content_dir = Path::new("./examples/plugins/content");
let template_dir = Path::new("./examples/templates");
let cache_dir = Path::new("target/.ssg-cache");
fs::create_dir_all(cache_dir)?;
let cache_path = &cache_dir.join("plugins.json");
fs::create_dir_all(build_dir)?;
fs::create_dir_all(site_dir)?;
let mut cache = BuildCache::load(cache_path)?;
let changed = cache.changed_files(content_dir)?;
let output_missing = !site_dir.join("index.html").exists();
if changed.is_empty() && !output_missing {
println!(" ✅ No content changes detected — skipping build");
} else {
if output_missing && changed.is_empty() {
println!(" 🔨 Output missing — forcing rebuild");
}
println!(" 🔨 {} file(s) changed — rebuilding", changed.len());
for path in &changed {
println!(" ↳ {}", path.display());
}
match compile(build_dir, content_dir, site_dir, template_dir) {
Ok(()) => println!(" ✅ Site compiled successfully"),
Err(e) => {
println!(" ❌ Compilation error: {e:?}");
return Ok(());
}
}
cache.update(content_dir)?;
cache.save()?;
println!(" 💾 Build cache updated ({} entries)", cache.len());
}
let base_url = "https://example.com";
let mut plugins = PluginManager::new();
plugins.register(HtmlFixPlugin);
plugins.register(ManifestFixPlugin);
plugins.register(SeoPlugin);
plugins.register(CanonicalPlugin::new(base_url));
plugins.register(RobotsPlugin::new(base_url));
plugins.register(SearchPlugin);
plugins.register(MinifyPlugin);
plugins.register(LiveReloadPlugin::new());
let ctx =
PluginContext::new(content_dir, build_dir, site_dir, template_dir);
println!("\n 🔌 Running {} plugins:", plugins.len());
for name in plugins.names() {
println!(" ↳ {name}");
}
plugins.run_before_compile(&ctx)?;
plugins.run_after_compile(&ctx)?;
plugins.run_fused_transforms(&ctx)?;
plugins.run_on_serve(&ctx)?;
println!("\n ✅ Plugin pipeline complete");
let index_path = site_dir.join("search-index.json");
if index_path.exists() {
let size = fs::metadata(&index_path)?.len();
println!(" 🔍 Search index: {size} bytes");
}
let robots_path = site_dir.join("robots.txt");
if robots_path.exists() {
println!(" 🤖 robots.txt generated");
}
let safe = ssg::fs_ops::is_safe_path(site_dir)?;
println!(
" \u{1f512} Path safety check: {}",
if safe { "PASS" } else { "FAIL" }
);
println!(
" \u{1f4be} Stream buffer: {} KB",
ssg::stream::STREAM_BUFFER_SIZE / 1024
);
let css_kind = ssg::watch::classify_change(Path::new("style.css"));
let md_kind = ssg::watch::classify_change(Path::new("post.md"));
println!(
" \u{1f441} Watch: style.css → {css_kind:?}, post.md → {md_kind:?}"
);
let md = "| Col A | Col B |\n|-------|-------|\n| 1 | 2 |";
let html = ssg::markdown_ext::expand_gfm(md);
println!(" \u{1f4dd} GFM table \u{2192} {} bytes HTML", html.len());
let schema = ssg::schema::generate_schema();
println!(
" \u{1f4cb} Config schema: {} properties",
schema
.get("properties")
.and_then(|p| p.as_object())
.map_or(0, |o| o.len())
);
println!(" \u{1f3d7} Scaffold: ssg::scaffold::scaffold_project(\"my-site\") creates a starter");
println!(" \u{1f4c4} Logging: ssg::logging::create_log_file(\"build.log\") for structured logs");
println!(" \u{2699} Process: ssg::process::args() handles CLI \u{2192} compilation workflow");
println!("\n Done. Site ready at {}", site_dir.display());
Ok(())
}