v0.9.5
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
use serde_json::{json, Value};
|
||||
use std::{env, fs, path::{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,7 +11,6 @@ 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);
|
||||
@@ -69,14 +71,24 @@ fn main() {
|
||||
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] {
|
||||
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_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]);
|
||||
@@ -84,8 +96,17 @@ fn main() {
|
||||
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()));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -107,9 +128,18 @@ fn main() {
|
||||
|
||||
for path in files {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
let filename = path.file_name().and_then(|v| v.to_str()).expect("UTF-8 language filename");
|
||||
let stem = path.file_stem().and_then(|v| v.to_str()).expect("UTF-8 language code");
|
||||
if !stem.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_') {
|
||||
let filename = path
|
||||
.file_name()
|
||||
.and_then(|v| v.to_str())
|
||||
.expect("UTF-8 language filename");
|
||||
let stem = path
|
||||
.file_stem()
|
||||
.and_then(|v| v.to_str())
|
||||
.expect("UTF-8 language code");
|
||||
if !stem
|
||||
.chars()
|
||||
.all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_')
|
||||
{
|
||||
panic!("{filename}: filename may only contain ASCII letters, digits, '-' and '_'");
|
||||
}
|
||||
|
||||
@@ -118,15 +148,20 @@ fn main() {
|
||||
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"));
|
||||
let unexpected: Vec<&str> = root_object.keys()
|
||||
let root_object = document
|
||||
.as_object()
|
||||
.unwrap_or_else(|| panic!("{filename}: language pack root must be an object"));
|
||||
let unexpected: Vec<&str> = root_object
|
||||
.keys()
|
||||
.filter(|key| key.as_str() != "meta" && key.as_str() != "translations")
|
||||
.map(|key| key.as_str())
|
||||
.collect();
|
||||
if !unexpected.is_empty() {
|
||||
panic!("{filename}: unexpected top-level keys {:?}; translation keys must be inside 'translations'", unexpected);
|
||||
}
|
||||
let meta = document.get("meta").unwrap_or_else(|| panic!("{filename}: missing meta object"));
|
||||
let meta = document
|
||||
.get("meta")
|
||||
.unwrap_or_else(|| panic!("{filename}: missing meta object"));
|
||||
let code = required_string(meta, "code", filename);
|
||||
let name = required_string(meta, "name", filename);
|
||||
let native_name = required_string(meta, "native_name", filename);
|
||||
@@ -158,15 +193,31 @@ fn main() {
|
||||
let manifest_json = serde_json::to_string(&json!({
|
||||
"default": "en",
|
||||
"languages": manifest_languages
|
||||
})).expect("serialize language manifest");
|
||||
}))
|
||||
.expect("serialize language manifest");
|
||||
|
||||
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(&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 {
|
||||
generated.push_str(&format!(
|
||||
|
||||
Reference in New Issue
Block a user