cipher-gate 0.3.0

Proxy RPC that routes signing requests to a browser wallet UI
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");
}