use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=ui/src");
println!("cargo:rerun-if-changed=ui/index.html");
println!("cargo:rerun-if-changed=ui/package.json");
println!("cargo:rerun-if-changed=ui/package-lock.json");
println!("cargo:rerun-if-changed=ui/vite.config.ts");
println!("cargo:rerun-if-changed=ui/tsconfig.json");
println!("cargo:rerun-if-changed=prebuilt/ui.html");
let out_dir: PathBuf = env::var_os("OUT_DIR")
.expect("OUT_DIR is set by cargo for build scripts")
.into();
let dst = out_dir.join("ui.html");
let ui_dir = Path::new("ui");
let prebuilt = Path::new("prebuilt/ui.html");
if ui_dir.join("package.json").exists() {
if !ui_dir.join("node_modules").exists() {
warn("installing ui/ dependencies (npm ci)...");
npm(&["--prefix", "ui", "ci"]);
}
warn("building ui/ via vite...");
npm(&["--prefix", "ui", "run", "build"]);
let dist = ui_dir.join("dist/index.html");
assert!(
dist.exists(),
"expected vite output at {} after `npm run build`",
dist.display()
);
fs::copy(&dist, &dst).unwrap_or_else(|e| {
panic!(
"failed to copy {} -> {}: {e}",
dist.display(),
dst.display()
)
});
} else if prebuilt.exists() {
fs::copy(prebuilt, &dst).unwrap_or_else(|e| {
panic!(
"failed to copy {} -> {}: {e}",
prebuilt.display(),
dst.display()
)
});
} else {
panic!(
"neither ui/ source nor prebuilt/ui.html is present. \
A duvis tarball must include the prebuilt UI."
);
}
}
fn warn(msg: &str) {
println!("cargo:warning={msg}");
}
fn npm(args: &[&str]) {
let cmd = if cfg!(windows) { "npm.cmd" } else { "npm" };
let status = Command::new(cmd).args(args).status().unwrap_or_else(|e| {
panic!("failed to spawn `{cmd}`: {e}. Install Node.js to build the UI.")
});
assert!(status.success(), "`{cmd} {}` failed", args.join(" "));
}