use crate::error::Result;
use colored::*;
use include_dir::{include_dir, Dir};
use std::fs;
use std::path::Path;
static TEMPLATES: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
fn get_template_name(simple: bool) -> &'static str {
if simple {
"boilerplate-simple"
} else {
"boilerplate"
}
}
pub fn copy_boilerplate(project_path: &Path, simple: bool) -> Result<()> {
let template_name = get_template_name(simple);
let template_dir = TEMPLATES
.get_dir(template_name)
.ok_or_else(|| anyhow::anyhow!("Boilerplate template '{}' not found in embedded templates", template_name))?;
let message = if simple {
"Setting up simple boilerplate project (no counter/chat demos)..."
} else {
"Setting up boilerplate project..."
};
println!("{}", message.blue());
copy_embedded_dir(template_dir, project_path, "")?;
Ok(())
}
fn copy_embedded_dir(dir: &include_dir::Dir, dest: &Path, prefix: &str) -> Result<()> {
for subdir in dir.dirs() {
let dir_name = subdir.path().file_name().unwrap().to_str().unwrap();
if matches!(dir_name, ".DS_Store" | "elm-stuff" | "node_modules" | ".git" | ".lamdera") {
continue;
}
let subdir_path = dest.join(prefix).join(dir_name);
fs::create_dir_all(&subdir_path)?;
let new_prefix = if prefix.is_empty() {
dir_name.to_string()
} else {
format!("{}/{}", prefix, dir_name)
};
copy_embedded_dir(subdir, dest, &new_prefix)?;
}
for file in dir.files() {
let file_name = file.path().file_name().unwrap().to_str().unwrap();
if file_name == ".DS_Store" {
continue;
}
let final_name = if file_name == "gitignore" {
".gitignore"
} else {
file_name
};
let file_path = dest.join(prefix).join(final_name);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&file_path, file.contents())?;
}
Ok(())
}
pub fn update_package_json(project_path: &Path, package_manager: &str) -> Result<()> {
let package_json_path = project_path.join("package.json");
if !package_json_path.exists() {
return Ok(());
}
let content = fs::read_to_string(&package_json_path)?;
let mut package_json: serde_json::Value = serde_json::from_str(&content)?;
let runner = if package_manager == "bun" { "bunx" } else { "npx" };
if let Some(scripts) = package_json.get_mut("scripts").and_then(|s| s.as_object_mut()) {
if let Some(start) = scripts.get_mut("start") {
*start = serde_json::Value::String(format!(
"concurrently \"{} tailwindcss -i ./src/styles.css -o ./public/styles.css --watch\" \"./lamdera-dev-watch.sh\"",
runner
));
}
if let Some(start_hot) = scripts.get_mut("start:hot") {
*start_hot = serde_json::Value::String(format!(
"concurrently \"{} tailwindcss -i ./src/styles.css -o ./public/styles.css --watch\" \"PORT=8001 ./lamdera-dev-watch.sh\"",
runner
));
}
}
let updated_content = serde_json::to_string_pretty(&package_json)?;
fs::write(&package_json_path, updated_content)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_template_name() {
assert_eq!(get_template_name(false), "boilerplate");
assert_eq!(get_template_name(true), "boilerplate-simple");
}
#[test]
fn test_embedded_templates_exist() {
assert!(TEMPLATES.get_dir("boilerplate").is_some());
assert!(TEMPLATES.get_dir("boilerplate-simple").is_some());
}
}