diff --git a/compiler/ds-cli/src/main.rs b/compiler/ds-cli/src/main.rs index 9ad7afc..3d934b2 100644 --- a/compiler/ds-cli/src/main.rs +++ b/compiler/ds-cli/src/main.rs @@ -8,6 +8,8 @@ use clap::{Parser, Subcommand}; use std::fs; use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex, atomic::{AtomicU64, Ordering}}; +use std::time::{Duration, Instant}; #[derive(Parser)] #[command(name = "dreamstack")] @@ -97,7 +99,7 @@ fn cmd_build(file: &Path, output: &Path) { fs::write(&out_path, &html).unwrap(); println!(" output: {}", out_path.display()); println!("✅ Build complete! ({} bytes)", html.len()); - println!(""); + println!(); println!(" Open in browser:"); println!(" file://{}", fs::canonicalize(&out_path).unwrap().display()); } @@ -108,12 +110,66 @@ fn cmd_build(file: &Path, output: &Path) { } } +/// HMR client script injected into every page served by `dreamstack dev`. +/// Uses Server-Sent Events to receive reload notifications from the dev server. +const HMR_CLIENT_SCRIPT: &str = r#" + +"#; + +fn inject_hmr(html: &str) -> String { + // Inject the HMR script just before
+{e}
++ if let Some(pos) = html.rfind("") { + format!("{}{}{}", &html[..pos], HMR_CLIENT_SCRIPT, &html[pos..]) + } else { + // No tag — just append + format!("{html}{HMR_CLIENT_SCRIPT}") + } +} + fn cmd_dev(file: &Path, port: u16) { + use notify::{Watcher, RecursiveMode}; + use std::sync::mpsc; + use std::thread; + println!("🚀 DreamStack dev server"); println!(" watching: {}", file.display()); println!(" serving: http://localhost:{port}"); - println!(""); + println!(); + // Shared state: compiled HTML + version counter + let version = Arc::new(AtomicU64::new(1)); + let compiled_html = Arc::new(Mutex::new(String::new())); + + // Initial compile let source = match fs::read_to_string(file) { Ok(s) => s, Err(e) => { @@ -122,36 +178,150 @@ fn cmd_dev(file: &Path, port: u16) { } }; - let html = match compile(&source) { - Ok(html) => html, - Err(e) => { - eprintln!("❌ Compile error: {e}"); - std::process::exit(1); + let start = Instant::now(); + match compile(&source) { + Ok(html) => { + let ms = start.elapsed().as_millis(); + let html_with_hmr = inject_hmr(&html); + *compiled_html.lock().unwrap() = html_with_hmr; + println!("✅ Compiled in {ms}ms ({} bytes)", html.len()); } - }; + Err(e) => { + eprintln!("⚠️ Compile error: {e}"); + let error_html = format!( + r#" +