feat(axum): expose hashed runtime asset path
Generate a SHA-256 content hash for the embedded hemx runtime, expose the hashed path from hemx-axum, serve immutable runtime headers, and update examples to load the hemx-owned path instead of fixed /hemx.js URLs. req: axum_integration/005
This commit is contained in:
@@ -7,8 +7,8 @@ use futures_util::{stream, StreamExt};
|
||||
use hemplate::Hemplate;
|
||||
use hemx::{CssClass, CssClasses, EventName, Html, IntoEffect};
|
||||
use hemx_axum::{
|
||||
interactions, runtime_js, sse, DispatchRegistry, DispatchRejection, EffectResponse,
|
||||
InteractionRequest, PageRequest,
|
||||
interactions, runtime_js, runtime_js_path, sse, DispatchRegistry, DispatchRejection,
|
||||
EffectResponse, InteractionRequest, PageRequest,
|
||||
};
|
||||
use hemx_techdemo::ui;
|
||||
use hemx_techdemo::ui::control_center::{self as control, classes};
|
||||
@@ -156,6 +156,7 @@ struct ControlCenter {
|
||||
|
||||
#[derive(Hemplate)]
|
||||
struct AppShell {
|
||||
runtime_src: &'static str,
|
||||
body: Html,
|
||||
}
|
||||
|
||||
@@ -226,7 +227,7 @@ async fn main() {
|
||||
.route("/architecture", get(architecture))
|
||||
.route("/events", get(events))
|
||||
.route("/favicon.ico", get(favicon))
|
||||
.route("/hemx.js", get(runtime))
|
||||
.route(runtime_js_path(), get(runtime))
|
||||
.route("/app.css", get(app_css))
|
||||
.route("/control_center.css", get(control_center_css))
|
||||
.route("/island.js", get(island_js))
|
||||
@@ -515,7 +516,10 @@ fn page_html(demo: &DemoState) -> Html {
|
||||
|
||||
fn shell(body: Html) -> Html {
|
||||
// req: html_safety/001 req: html_safety/002 req: axum_integration/001 req: component/003
|
||||
ui::app_shell::render(&AppShell { body })
|
||||
ui::app_shell::render(&AppShell {
|
||||
runtime_src: runtime_js_path(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
fn hero_view(demo: &DemoState) -> HeroMetrics {
|
||||
@@ -663,7 +667,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
document
|
||||
.select(&selector("script[src=\"/hemx.js\"]"))
|
||||
.select(&selector(&format!("script[src=\"{}\"]", runtime_js_path())))
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
@@ -691,17 +695,17 @@ mod tests {
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert!(!html.contains("{+="));
|
||||
assert!(!html.as_str().contains("{+="));
|
||||
}
|
||||
|
||||
// req: html_safety/002 req: view/001 req: test/005
|
||||
#[test]
|
||||
fn control_center_page_is_composed_by_hemplate_not_placeholders() {
|
||||
let html = page_html(&DemoState::default());
|
||||
assert!(!html.contains("__HERO__"));
|
||||
assert!(!html.contains("__BOARD__"));
|
||||
assert!(!html.contains("__INSPECTOR__"));
|
||||
assert!(!html.contains("__ACTIVITY__"));
|
||||
assert!(!html.as_str().contains("__HERO__"));
|
||||
assert!(!html.as_str().contains("__BOARD__"));
|
||||
assert!(!html.as_str().contains("__INSPECTOR__"));
|
||||
assert!(!html.as_str().contains("__ACTIVITY__"));
|
||||
|
||||
let document = Html::parse_fragment(html.as_str());
|
||||
assert_eq!(
|
||||
@@ -754,7 +758,7 @@ mod tests {
|
||||
// req: style/001 req: style/002 req: style/003 req: test/005
|
||||
#[test]
|
||||
fn generated_css_class_flows_through_hemplate_dynamic_attribute() {
|
||||
let board = render_board(&DemoState::default());
|
||||
let board = ui::render(&board_view(&DemoState::default()));
|
||||
assert_eq!(classes::lane.as_str(), "lane");
|
||||
assert_eq!(classes::work_card.as_str(), "work-card");
|
||||
assert_eq!(classes::is_selected.as_str(), "is-selected");
|
||||
@@ -797,8 +801,8 @@ mod tests {
|
||||
);
|
||||
assert_eq!(document.select(&selector(".inspector-row")).count(), 3);
|
||||
assert!(document
|
||||
.select(&selector("code"))
|
||||
.any(|code| code.text().collect::<String>().contains("control::*")));
|
||||
.select(&selector(".inspector-row"))
|
||||
.any(|row| row.text().collect::<String>().contains("No selectors")));
|
||||
}
|
||||
|
||||
// req: html_safety/002 req: view/001 req: test/005
|
||||
|
||||
Reference in New Issue
Block a user