autumn_plugin_markstream/
script.rs1use autumn_web::{Markup, html};
15
16use crate::assets::{CORE_JS, INIT_JS, MARKSTREAM_ASSETS, MARKSTREAM_CSS, PLUGIN_CSS};
17use crate::vendored::EAGER_MODULES;
18
19#[must_use]
29pub fn markstream_head() -> Markup {
30 html! {
31 (markstream_stylesheet())
32 (markstream_preloads())
33 (markstream_script())
34 }
35}
36
37#[must_use]
43pub fn markstream_script() -> Markup {
44 html! {
45 @if let Some(init) = MARKSTREAM_ASSETS.get(INIT_JS) {
46 script type="module" src=(init.url()) integrity=(init.integrity())
47 crossorigin="anonymous" {}
48 }
49 }
50}
51
52#[must_use]
58pub fn markstream_preloads() -> Markup {
59 let paths = std::iter::once(CORE_JS).chain(EAGER_MODULES.iter().copied());
60 html! {
61 @for asset in paths.filter_map(|p| MARKSTREAM_ASSETS.get(p)) {
62 link rel="modulepreload" href=(asset.plain_url())
63 integrity=(asset.integrity()) crossorigin="anonymous";
64 }
65 }
66}
67
68#[must_use]
70pub fn markstream_stylesheet() -> Markup {
71 html! {
72 (MARKSTREAM_ASSETS.stylesheet_tag(MARKSTREAM_CSS))
73 (MARKSTREAM_ASSETS.stylesheet_tag(PLUGIN_CSS))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use crate::assets::{CORE_JS, INIT_JS, MARKSTREAM_ASSETS, MARKSTREAM_CSS, PLUGIN_CSS};
81 use crate::vendored::EAGER_MODULES;
82
83 fn asset(path: &str) -> &'static autumn_web::assets::PluginAsset {
84 MARKSTREAM_ASSETS.get(path).expect("bundled")
85 }
86
87 #[test]
88 fn script_is_one_module_tag_with_sri_and_hashed_url() {
89 let html = markstream_script().into_string();
90 let init = asset(INIT_JS);
91 assert_eq!(
92 html,
93 format!(
94 r#"<script type="module" src="{}" integrity="{}" crossorigin="anonymous"></script>"#,
95 init.url(),
96 init.integrity()
97 )
98 );
99 }
100
101 #[test]
102 fn preloads_cover_core_and_each_eager_module_at_plain_urls() {
103 let html = markstream_preloads().into_string();
104 let mut paths = vec![CORE_JS];
105 paths.extend(EAGER_MODULES);
106 assert_eq!(
107 html.matches(r#"rel="modulepreload""#).count(),
108 paths.len(),
109 "{html}"
110 );
111 for path in paths {
112 let a = asset(path);
113 let tag = format!(
114 r#"<link rel="modulepreload" href="{}" integrity="{}" crossorigin="anonymous">"#,
115 a.plain_url(),
116 a.integrity()
117 );
118 assert!(html.contains(&tag), "missing {tag}");
119 }
120 }
121
122 #[test]
123 fn stylesheet_links_both_files_with_sri() {
124 let html = markstream_stylesheet().into_string();
125 for path in [MARKSTREAM_CSS, PLUGIN_CSS] {
126 let a = asset(path);
127 assert!(html.contains(&format!(r#"href="{}""#, a.url())), "{html}");
128 assert!(
129 html.contains(&format!(r#"integrity="{}""#, a.integrity())),
130 "{html}"
131 );
132 }
133 let vendored_at = html
134 .find(asset(MARKSTREAM_CSS).url())
135 .expect("vendored css");
136 let plugin_at = html.find(asset(PLUGIN_CSS).url()).expect("plugin css");
137 assert!(vendored_at < plugin_at, "plugin styles must win: {html}");
138 }
139
140 #[test]
141 fn head_has_styles_then_preloads_then_the_script() {
142 let html = markstream_head().into_string();
143 let css = html.find("stylesheet").expect("css");
144 let preload = html.find("modulepreload").expect("preload");
145 let script = html.find("<script").expect("script");
146 assert!(css < preload && preload < script, "{html}");
147 assert_eq!(html.matches("<script").count(), 1, "{html}");
148 assert!(html.contains("></script>"), "no inline script body: {html}");
149 assert!(!html.contains("importmap"), "{html}");
150 }
151}