From f3e2c8392a5b40a6d8da22f6f31d61998fd8f4f3 Mon Sep 17 00:00:00 2001
From: Alix-007
Date: Wed, 18 Mar 2026 01:38:40 +0800
Subject: [PATCH] fix(build): rerun embedded web assets when dist changes
(#3799)
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
---
build.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 2 deletions(-)
diff --git a/build.rs b/build.rs
index 01afed645..2320503bb 100644
--- a/build.rs
+++ b/build.rs
@@ -1,22 +1,30 @@
+use std::fs;
use std::path::Path;
use std::process::Command;
+use std::time::SystemTime;
fn main() {
let dist_dir = Path::new("web/dist");
let web_dir = Path::new("web");
- // Tell Cargo to re-run this script when web source files change.
+ // Tell Cargo to re-run this script when web sources or bundled assets change.
println!("cargo:rerun-if-changed=web/src");
+ println!("cargo:rerun-if-changed=web/public");
println!("cargo:rerun-if-changed=web/index.html");
println!("cargo:rerun-if-changed=web/package.json");
+ println!("cargo:rerun-if-changed=web/package-lock.json");
+ println!("cargo:rerun-if-changed=web/tsconfig.json");
+ println!("cargo:rerun-if-changed=web/tsconfig.app.json");
+ println!("cargo:rerun-if-changed=web/tsconfig.node.json");
println!("cargo:rerun-if-changed=web/vite.config.ts");
+ println!("cargo:rerun-if-changed=web/dist");
// Attempt to build the web frontend if npm is available and web/dist is
// missing or stale. The build is best-effort: when Node.js is not
// installed (e.g. CI containers, cross-compilation, minimal dev setups)
// we fall back to the existing stub/empty dist directory so the Rust
// build still succeeds.
- let needs_build = !dist_dir.join("index.html").exists();
+ let needs_build = web_build_required(web_dir, dist_dir);
if needs_build && web_dir.join("package.json").exists() {
if let Ok(npm) = which_npm() {
@@ -77,6 +85,49 @@ fn main() {
ensure_dist_dir(dist_dir);
}
+fn web_build_required(web_dir: &Path, dist_dir: &Path) -> bool {
+ let Some(dist_mtime) = latest_modified(dist_dir) else {
+ return true;
+ };
+
+ [
+ web_dir.join("src"),
+ web_dir.join("public"),
+ web_dir.join("index.html"),
+ web_dir.join("package.json"),
+ web_dir.join("package-lock.json"),
+ web_dir.join("tsconfig.json"),
+ web_dir.join("tsconfig.app.json"),
+ web_dir.join("tsconfig.node.json"),
+ web_dir.join("vite.config.ts"),
+ ]
+ .into_iter()
+ .filter_map(|path| latest_modified(&path))
+ .any(|mtime| mtime > dist_mtime)
+}
+
+fn latest_modified(path: &Path) -> Option {
+ let metadata = fs::metadata(path).ok()?;
+ if metadata.is_file() {
+ return metadata.modified().ok();
+ }
+ if !metadata.is_dir() {
+ return None;
+ }
+
+ let mut latest = metadata.modified().ok();
+ let entries = fs::read_dir(path).ok()?;
+ for entry in entries.flatten() {
+ if let Some(child_mtime) = latest_modified(&entry.path()) {
+ latest = Some(match latest {
+ Some(current) if current >= child_mtime => current,
+ _ => child_mtime,
+ });
+ }
+ }
+ latest
+}
+
/// Ensure the dist directory exists so `rust-embed` does not fail at compile
/// time even when the web frontend is not built.
fn ensure_dist_dir(dist_dir: &Path) {