This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/crates/maestro-web/build.rs
T
naps62-yoloandClaude Opus 4.8 c355f9bd2e fix(maestro): tie maestro-web recompile to web/dist + harden bundle build
Watch the embedded web/dist output (not just frontend inputs) so a
rebuilt bundle is actually re-embedded; assert pnpm build produced
web/dist/index.html; strengthen the embed test to require a vite-built
hashed asset reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 22:02:11 +00:00

92 lines
3.1 KiB
Rust

//! Build the embedded SPA bundle before the crate compiles.
//!
//! `static_bundle.rs` embeds `web/dist` at compile time via `include_dir!`.
//! This script guarantees that directory holds a freshly built bundle by
//! running `pnpm install --frozen-lockfile && pnpm build` in `web/`. pnpm and
//! node are HARD requirements — a missing toolchain fails the build with an
//! actionable message rather than silently shipping the placeholder.
use std::env;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let manifest_dir =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR set by cargo"));
let web_dir = manifest_dir.join("../../web");
// Rebuild the bundle only when a frontend input changes — not on every
// unrelated Rust recompile.
for rel in [
"src",
"index.html",
"package.json",
"pnpm-lock.yaml",
"vite.config.ts",
] {
println!("cargo:rerun-if-changed={}", web_dir.join(rel).display());
}
// Also watch the embedded OUTPUT directory. The input watches above decide
// whether THIS script re-runs and regenerates `web/dist`; but `include_dir!`
// in `static_bundle.rs` reads `web/dist`, and cargo has no other declared
// dependency on those output files. Without this watch, an incremental build
// could regenerate `web/dist` yet keep the OLD bundle embedded. Tying the
// crate's recompilation to the regenerated output makes the fresh assets
// actually get re-embedded.
let dist_dir = web_dir.join("dist");
println!("cargo:rerun-if-changed={}", dist_dir.display());
require_tool("pnpm");
require_tool("node");
run(
Command::new("pnpm")
.args(["install", "--frozen-lockfile"])
.current_dir(&web_dir),
"pnpm install --frozen-lockfile",
);
run(
Command::new("pnpm").arg("build").current_dir(&web_dir),
"pnpm build",
);
// Sanity-check that pnpm build actually produced the bundle `include_dir!`
// expects. A green build that embeds an empty/placeholder dir is worse than
// a loud failure here.
let index_html = dist_dir.join("index.html");
let produced = std::fs::metadata(&index_html)
.map(|m| m.is_file() && m.len() > 0)
.unwrap_or(false);
if !produced {
panic!(
"`pnpm build` did not produce the expected bundle at {} \
(missing or empty) — check the vite `outDir`.",
index_html.display()
);
}
}
fn require_tool(tool: &str) {
let ok = Command::new(tool)
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !ok {
panic!(
"maestro-web requires `{tool}` to build its embedded SPA bundle. \
Install pnpm + node (https://pnpm.io) or use a prebuilt binary."
);
}
}
fn run(cmd: &mut Command, label: &str) {
let status = cmd
.status()
.unwrap_or_else(|e| panic!("failed to spawn `{label}`: {e}"));
if !status.success() {
panic!("`{label}` failed with {status}");
}
}