1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use std::env;
use std::path::Path;
use std::process::Command;
fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let frontend_dir = Path::new(&manifest_dir).join("frontend");
let out_dir = frontend_dir.join("out");
println!("cargo:rerun-if-changed=frontend/src");
println!("cargo:rerun-if-changed=frontend/package.json");
println!("cargo:rerun-if-changed=frontend/next.config.ts");
// Rebuild the binary when the static export changes (e.g. after a manual rebuild).
println!("cargo:rerun-if-changed=frontend/out");
// Escape hatch: force a fresh npm build even when frontend/out is present.
println!("cargo:rerun-if-env-changed=CIPHER_GATE_REBUILD_FRONTEND");
// If a prebuilt static export is already present (the published crate ships
// `frontend/out`), embed it as-is and skip the Node toolchain entirely. This
// makes `cargo install cipher-gate` work with only a Rust toolchain — no Node, npm,
// or network access needed. Set CIPHER_GATE_REBUILD_FRONTEND=1 to force a rebuild.
let force_rebuild = env::var_os("CIPHER_GATE_REBUILD_FRONTEND").is_some();
if out_dir.join("index.html").exists() && !force_rebuild {
println!(
"cargo:warning=Using prebuilt frontend/out (set CIPHER_GATE_REBUILD_FRONTEND=1 to rebuild)"
);
return;
}
// No prebuilt assets — build the frontend from source.
// Clean-install strictly from the lock file. `npm ci` wipes node_modules and
// installs the exact locked tree, picking the platform-correct native deps
// (e.g. @tailwindcss/oxide) via the lock file's os/cpu entries. Do NOT use
// `npm install --force`: it drops the `peer` flag on optional peer deps
// (@base-org/account, @coinbase/cdp-sdk), which then pull unresolved modules
// (@solana/kit, axios) into the bundle and break `next build`.
let status = Command::new("npm")
.arg("ci")
.current_dir(&frontend_dir)
.status()
.expect("Failed to run npm ci. Is Node.js installed?");
assert!(status.success(), "npm ci failed");
let status = Command::new("npm")
.args(["run", "build"])
.current_dir(&frontend_dir)
.status()
.expect("Failed to run npm run build");
assert!(status.success(), "npm run build failed");
}