use anyhow::Result;
use std::fs;
use std::path::Path;
use super::template::render;
use crate::template_vars;
pub const FORGE_VERSION: &str = env!("CARGO_PKG_VERSION");
const PACKAGE_JSON: &str = include_str!("../../templates/runtime/package.json.tmpl");
const TYPES_TS: &str = include_str!("../../templates/runtime/types.ts.tmpl");
const CLIENT_TS: &str = include_str!("../../templates/runtime/client.ts.tmpl");
const CONTEXT_TS: &str = include_str!("../../templates/runtime/context.ts.tmpl");
const STORES_TS: &str = include_str!("../../templates/runtime/stores.ts.tmpl");
const API_TS: &str = include_str!("../../templates/runtime/api.ts.tmpl");
const FORGE_PROVIDER: &str = include_str!("../../templates/runtime/ForgeProvider.svelte.tmpl");
const INDEX_TS: &str = include_str!("../../templates/runtime/index.ts.tmpl");
pub fn has_legacy_runtime(frontend_dir: &Path) -> bool {
frontend_dir.join("src/lib/forge/runtime/index.ts").exists()
}
pub fn get_installed_version(frontend_dir: &Path) -> Option<String> {
let version_file = frontend_dir.join(".forge/version");
fs::read_to_string(version_file)
.ok()
.map(|s| s.trim().to_string())
}
pub fn needs_update(frontend_dir: &Path) -> bool {
match get_installed_version(frontend_dir) {
Some(installed) => installed != FORGE_VERSION,
None => true, }
}
pub fn generate_runtime(frontend_dir: &Path) -> Result<()> {
let forge_dir = frontend_dir.join(".forge");
let svelte_dir = forge_dir.join("svelte");
fs::create_dir_all(&svelte_dir)?;
fs::write(forge_dir.join("version"), FORGE_VERSION)?;
let vars = template_vars!("version" => FORGE_VERSION);
fs::write(svelte_dir.join("package.json"), render(PACKAGE_JSON, &vars))?;
fs::write(svelte_dir.join("types.ts"), render(TYPES_TS, &vars))?;
fs::write(svelte_dir.join("client.ts"), render(CLIENT_TS, &vars))?;
fs::write(svelte_dir.join("context.ts"), render(CONTEXT_TS, &vars))?;
fs::write(svelte_dir.join("stores.ts"), render(STORES_TS, &vars))?;
fs::write(svelte_dir.join("api.ts"), render(API_TS, &vars))?;
fs::write(
svelte_dir.join("ForgeProvider.svelte"),
render(FORGE_PROVIDER, &vars),
)?;
fs::write(svelte_dir.join("index.ts"), render(INDEX_TS, &vars))?;
Ok(())
}
pub fn update_frontend_package_json(frontend_dir: &Path) -> Result<()> {
let package_json_path = frontend_dir.join("package.json");
if !package_json_path.exists() {
return Ok(()); }
let content = fs::read_to_string(&package_json_path)?;
if content.contains("\"@forge/svelte\"") {
return Ok(());
}
let mut json: serde_json::Value = serde_json::from_str(&content)?;
if let Some(deps) = json.get_mut("dependencies") {
if let Some(obj) = deps.as_object_mut() {
obj.insert(
"@forge/svelte".to_string(),
serde_json::Value::String("file:./.forge/svelte".to_string()),
);
}
} else {
let mut deps = serde_json::Map::new();
deps.insert(
"@forge/svelte".to_string(),
serde_json::Value::String("file:./.forge/svelte".to_string()),
);
json["dependencies"] = serde_json::Value::Object(deps);
}
let formatted = serde_json::to_string_pretty(&json)?;
fs::write(&package_json_path, formatted)?;
Ok(())
}
pub fn remove_legacy_runtime(frontend_dir: &Path) -> Result<()> {
let legacy_dir = frontend_dir.join("src/lib/forge/runtime");
if legacy_dir.exists() {
fs::remove_dir_all(legacy_dir)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_generate_runtime() {
let dir = tempdir().unwrap();
generate_runtime(dir.path()).unwrap();
assert!(dir.path().join(".forge/version").exists());
assert!(dir.path().join(".forge/svelte/package.json").exists());
assert!(dir.path().join(".forge/svelte/index.ts").exists());
assert!(dir.path().join(".forge/svelte/types.ts").exists());
assert!(dir.path().join(".forge/svelte/client.ts").exists());
assert!(dir.path().join(".forge/svelte/context.ts").exists());
assert!(dir.path().join(".forge/svelte/stores.ts").exists());
assert!(dir.path().join(".forge/svelte/api.ts").exists());
assert!(dir
.path()
.join(".forge/svelte/ForgeProvider.svelte")
.exists());
}
#[test]
fn test_version_file() {
let dir = tempdir().unwrap();
generate_runtime(dir.path()).unwrap();
let version = fs::read_to_string(dir.path().join(".forge/version")).unwrap();
assert_eq!(version.trim(), FORGE_VERSION);
}
#[test]
fn test_needs_update() {
let dir = tempdir().unwrap();
assert!(needs_update(dir.path()));
generate_runtime(dir.path()).unwrap();
assert!(!needs_update(dir.path()));
fs::write(dir.path().join(".forge/version"), "0.0.0").unwrap();
assert!(needs_update(dir.path()));
}
#[test]
fn test_has_legacy_runtime() {
let dir = tempdir().unwrap();
assert!(!has_legacy_runtime(dir.path()));
let legacy_dir = dir.path().join("src/lib/forge/runtime");
fs::create_dir_all(&legacy_dir).unwrap();
fs::write(legacy_dir.join("index.ts"), "// legacy").unwrap();
assert!(has_legacy_runtime(dir.path()));
}
}