use std::fs;
use std::path::Path;
use std::process::Command;
use anyhow::{bail, Context, Result};
use serde_json::json;
use crate::catalog::place_template;
use crate::config::PackageWorkflow;
use crate::steps::{capture, run};
use crate::ui;
const STARTER_FILES: &[(&str, &str)] = &[
("src/shared/hello.luau", "--!strict\n\nreturn {\n\tgreeting = \"hello from shared\",\n}\n"),
(
"src/server/hello.server.luau",
"--!strict\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal hello = require(ReplicatedStorage.shared.hello)\n\nprint(hello.greeting, \"- server\")\n",
),
(
"src/client/hello.client.luau",
"--!strict\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal hello = require(ReplicatedStorage.shared.hello)\n\nprint(hello.greeting, \"- client\")\n",
),
];
pub fn scaffold_project_json(
project_dir: &Path,
project_name: &str,
package_workflow: PackageWorkflow,
testez_selected: bool,
has_server_packages: bool,
) -> Result<()> {
let path = project_dir.join("default.project.json");
if path.exists() {
ui::ok("default.project.json already exists");
return Ok(());
}
for (rel_path, contents) in STARTER_FILES {
let file = project_dir.join(rel_path);
fs::create_dir_all(file.parent().expect("starter paths have a parent"))?;
if !file.exists() {
fs::write(&file, contents)?;
}
}
let mut replicated_storage = serde_json::Map::new();
replicated_storage.insert("shared".to_string(), json!({ "$path": "src/shared" }));
let mut project = serde_json::Map::new();
project.insert("name".to_string(), json!(project_name));
match package_workflow {
PackageWorkflow::Wally => {
replicated_storage.insert("packages".to_string(), json!({ "$path": "Packages" }));
}
PackageWorkflow::GitSubmodules => {
replicated_storage.insert("modules".to_string(), json!({ "$path": "modules" }));
}
}
let mut server_scripts = serde_json::Map::new();
server_scripts.insert("server".to_string(), json!({ "$path": "src/server" }));
if has_server_packages {
server_scripts.insert("serverPackages".to_string(), json!({ "$path": "ServerPackages" }));
}
let mut client_scripts = serde_json::Map::new();
client_scripts.insert("client".to_string(), json!({ "$path": "src/client" }));
if testez_selected {
replicated_storage.insert("test".to_string(), json!({ "$path": "tests/shared" }));
server_scripts.insert("test".to_string(), json!({ "$path": "tests/server" }));
client_scripts.insert("test".to_string(), json!({ "$path": "tests/client" }));
}
let mut tree = serde_json::Map::new();
tree.insert("$className".to_string(), json!("DataModel"));
tree.insert("ReplicatedStorage".to_string(), json!(replicated_storage));
tree.insert("ServerScriptService".to_string(), json!(server_scripts));
tree.insert("StarterPlayer".to_string(), json!({ "StarterPlayerScripts": client_scripts }));
for (name, node) in place_template::render() {
tree.insert(name, node);
}
project.insert("tree".to_string(), serde_json::Value::Object(tree));
fs::write(&path, serde_json::to_string_pretty(&project)?)?;
ui::ok("wrote default.project.json");
Ok(())
}
pub fn install_studio_plugin() -> Result<()> {
run("rojo", &["plugin", "install"])
}
pub fn generate_sourcemap(project_dir: &Path) -> Result<()> {
let output = capture(
"rojo",
&["sourcemap", "default.project.json", "-o", "sourcemap.json"],
Some(project_dir),
)?;
if !output.success || ui::is_verbose() {
ui::passthrough(&output.stdout, &output.stderr);
}
if !output.success {
bail!("rojo could not generate a sourcemap (see above)");
}
ui::ok("generated sourcemap.json");
Ok(())
}
pub fn watch_sourcemap(project_dir: &Path) -> Result<()> {
let args = ["sourcemap", "--watch", "default.project.json", "-o", "sourcemap.json"];
ui::command("rojo", &args);
let status = Command::new("rojo")
.args(args)
.current_dir(project_dir)
.status()
.context("failed to start `rojo sourcemap --watch`")?;
let _ = status;
Ok(())
}