v0.8.15
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use serde_json::{json, Value};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
use std::{env, fs, path::{Path, PathBuf}};
|
||||
|
||||
fn required_string<'a>(meta: &'a Value, key: &str, file: &str) -> &'a str {
|
||||
meta.get(key)
|
||||
@@ -8,11 +8,87 @@ fn required_string<'a>(meta: &'a Value, key: &str, file: &str) -> &'a str {
|
||||
.unwrap_or_else(|| panic!("{file}: meta.{key} must be a non-empty string"))
|
||||
}
|
||||
|
||||
|
||||
fn fnv1a_update(mut hash: u64, bytes: &[u8]) -> u64 {
|
||||
for byte in bytes {
|
||||
hash ^= u64::from(*byte);
|
||||
hash = hash.wrapping_mul(0x100000001b3);
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
fn content_hash(bytes: &[u8]) -> String {
|
||||
format!("{:016x}", fnv1a_update(0xcbf29ce484222325, bytes))
|
||||
}
|
||||
|
||||
const APP_JS_MODULES: &[&str] = &[
|
||||
"core.js",
|
||||
"forms.js",
|
||||
"bootstrap.js",
|
||||
"dashboard.js",
|
||||
"entities.js",
|
||||
"settings-ui.js",
|
||||
"router.js",
|
||||
"navigation.js",
|
||||
"charts.js",
|
||||
"history.js",
|
||||
"realtime.js",
|
||||
"events.js",
|
||||
"settings.js",
|
||||
"main.js",
|
||||
];
|
||||
|
||||
fn bundle_app_js(web_dir: &Path) -> String {
|
||||
let js_dir = web_dir.join("js");
|
||||
println!("cargo:rerun-if-changed={}", js_dir.display());
|
||||
|
||||
let mut bundle = String::new();
|
||||
for &filename in APP_JS_MODULES {
|
||||
let path = js_dir.join(filename);
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
let source = fs::read_to_string(&path)
|
||||
.unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
|
||||
bundle.push_str(&source);
|
||||
if !source.ends_with('\n') {
|
||||
bundle.push('\n');
|
||||
}
|
||||
}
|
||||
bundle
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
|
||||
let lang_dir = manifest_dir.join("lang");
|
||||
let web_dir = manifest_dir.join("web");
|
||||
println!("cargo:rerun-if-changed={}", lang_dir.display());
|
||||
|
||||
let theme_init_path = web_dir.join("theme-init.js");
|
||||
let styles_path = web_dir.join("styles.css");
|
||||
let index_path = web_dir.join("index.html");
|
||||
let not_found_path = web_dir.join("404.html");
|
||||
let sw_path = web_dir.join("sw.js");
|
||||
let manifest_path = web_dir.join("manifest.webmanifest");
|
||||
let favicon_path = web_dir.join("favicon.svg");
|
||||
for path in [&theme_init_path, &styles_path, &index_path, ¬_found_path, &sw_path, &manifest_path, &favicon_path] {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
}
|
||||
|
||||
let app_js = bundle_app_js(&web_dir);
|
||||
let app_js_hash = content_hash(app_js.as_bytes());
|
||||
let theme_init_bytes = fs::read(&theme_init_path).unwrap_or_else(|error| panic!("cannot read {}: {error}", theme_init_path.display()));
|
||||
let styles_bytes = fs::read(&styles_path).unwrap_or_else(|error| panic!("cannot read {}: {error}", styles_path.display()));
|
||||
let theme_init_hash = content_hash(&theme_init_bytes);
|
||||
let styles_hash = content_hash(&styles_bytes);
|
||||
let app_js_asset_path = format!("/app-{}.js", &app_js_hash[..12]);
|
||||
let theme_init_asset_path = format!("/theme-init-{}.js", &theme_init_hash[..12]);
|
||||
let styles_asset_path = format!("/styles-{}.css", &styles_hash[..12]);
|
||||
|
||||
let mut build_hash = fnv1a_update(0xcbf29ce484222325u64, app_js.as_bytes());
|
||||
for path in [&theme_init_path, &styles_path, &index_path, ¬_found_path, &sw_path, &manifest_path, &favicon_path] {
|
||||
let bytes = fs::read(path).unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
|
||||
build_hash = fnv1a_update(build_hash, &bytes);
|
||||
}
|
||||
|
||||
let mut files: Vec<PathBuf> = fs::read_dir(&lang_dir)
|
||||
.unwrap_or_else(|error| panic!("cannot read {}: {error}", lang_dir.display()))
|
||||
.filter_map(Result::ok)
|
||||
@@ -39,6 +115,7 @@ fn main() {
|
||||
|
||||
let source = fs::read_to_string(&path)
|
||||
.unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
|
||||
build_hash = fnv1a_update(build_hash, source.as_bytes());
|
||||
let document: Value = serde_json::from_str(&source)
|
||||
.unwrap_or_else(|error| panic!("{filename}: invalid JSON: {error}"));
|
||||
let root_object = document.as_object().unwrap_or_else(|| panic!("{filename}: language pack root must be an object"));
|
||||
@@ -85,6 +162,10 @@ fn main() {
|
||||
|
||||
let mut generated = String::new();
|
||||
generated.push_str("// @generated by build.rs - do not edit.\n");
|
||||
generated.push_str(&format!("pub const APP_JS_ASSET_PATH: &str = {:?};\n", app_js_asset_path));
|
||||
generated.push_str(&format!("pub const THEME_INIT_ASSET_PATH: &str = {:?};\n", theme_init_asset_path));
|
||||
generated.push_str(&format!("pub const STYLES_CSS_ASSET_PATH: &str = {:?};\n", styles_asset_path));
|
||||
generated.push_str(&format!("pub const ASSET_BUILD_ID: &str = \"{:016x}\";\n", build_hash));
|
||||
generated.push_str(&format!("pub const LANGUAGE_MANIFEST_JSON: &str = {:?};\n", manifest_json));
|
||||
generated.push_str("pub const LANGUAGE_ASSETS: &[(&str, &str)] = &[\n");
|
||||
for (code, path) in assets {
|
||||
@@ -97,5 +178,6 @@ fn main() {
|
||||
generated.push_str("];\n");
|
||||
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
|
||||
fs::write(out_dir.join("app.bundle.js"), app_js).expect("write generated app.bundle.js");
|
||||
fs::write(out_dir.join("languages.rs"), generated).expect("write generated languages.rs");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user