use crate::ViteDevInfo;
const LIVE_RELOAD_SCRIPT: &str = r#"<script>new EventSource("/_seam/dev/reload").onmessage=function(){location.reload()}</script>"#;
pub fn wrap_document(
skeleton: &str,
css_files: &[String],
js_files: &[String],
dev_mode: bool,
vite: Option<&ViteDevInfo>,
root_id: &str,
) -> String {
let mut doc = String::from("<!DOCTYPE html><html><head><meta charset=\"utf-8\">");
if let Some(v) = vite {
doc.push_str(&format!(
concat!(
"<script type=\"module\">",
"import RefreshRuntime from '{origin}/@react-refresh';",
"RefreshRuntime.injectIntoGlobalHook(window);",
"window.$RefreshReg$ = () => {{}};",
"window.$RefreshSig$ = () => (type) => type;",
"window.__vite_plugin_react_preamble_installed__ = true",
"</script>"
),
origin = v.origin,
));
doc.push_str(&format!(
r#"<script type="module" src="{origin}/@vite/client"></script>"#,
origin = v.origin,
));
doc.push_str(&format!(
r#"<script type="module" src="{origin}/{entry}"></script>"#,
origin = v.origin,
entry = v.entry,
));
} else {
for f in css_files {
doc.push_str(&format!(r#"<link rel="stylesheet" href="/_seam/static/{f}">"#));
}
doc.push_str("<!--seam:page-styles-->");
doc.push_str("<!--seam:prefetch-->");
}
doc.push_str(&format!("</head><body><div id=\"{root_id}\">"));
doc.push_str(skeleton);
doc.push_str("</div>");
if vite.is_none() {
for f in js_files {
doc.push_str(&format!(r#"<script type="module" src="/_seam/static/{f}"></script>"#));
}
doc.push_str("<!--seam:page-scripts-->");
}
if dev_mode && vite.is_none() {
doc.push_str(LIVE_RELOAD_SCRIPT);
}
doc.push_str("</body></html>");
doc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wraps_with_assets() {
let result = wrap_document(
"<p>Hello</p>",
&["style-abc.css".into()],
&["main-xyz.js".into()],
false,
None,
"__seam",
);
assert_eq!(
result,
concat!(
"<!DOCTYPE html><html><head><meta charset=\"utf-8\">",
"<link rel=\"stylesheet\" href=\"/_seam/static/style-abc.css\">",
"<!--seam:page-styles--><!--seam:prefetch-->",
"</head><body>",
"<div id=\"__seam\"><p>Hello</p></div>",
"<script type=\"module\" src=\"/_seam/static/main-xyz.js\"></script>",
"<!--seam:page-scripts-->",
"</body></html>"
)
);
}
#[test]
fn wraps_without_assets() {
let result = wrap_document("<p>Hi</p>", &[], &[], false, None, "__seam");
assert_eq!(
result,
concat!(
"<!DOCTYPE html><html><head><meta charset=\"utf-8\">",
"<!--seam:page-styles--><!--seam:prefetch-->",
"</head><body>",
"<div id=\"__seam\"><p>Hi</p></div>",
"<!--seam:page-scripts-->",
"</body></html>"
)
);
}
#[test]
fn skeleton_with_metadata_stays_in_body() {
let skeleton = "<title>My Page</title><meta name=\"desc\"><p>content</p>";
let result = wrap_document(skeleton, &["style.css".into()], &[], false, None, "__seam");
let root_start = result.find("__seam").unwrap();
let root_section = &result[root_start..];
assert!(root_section.contains("<title>My Page</title>"), "title stays in body");
assert!(root_section.contains("<p>content</p>"), "body content in root div");
}
#[test]
fn dev_mode_injects_live_reload_script() {
let result = wrap_document("<p>dev</p>", &[], &["app.js".into()], true, None, "__seam");
assert!(result.contains("EventSource"), "dev_mode should inject EventSource live reload");
assert!(result.contains("/_seam/dev/reload"));
let script_pos = result.find("EventSource").unwrap();
let module_pos = result.find("app.js").unwrap();
let page_scripts_pos = result.find("<!--seam:page-scripts-->").unwrap();
let body_end = result.find("</body>").unwrap();
assert!(module_pos < page_scripts_pos);
assert!(script_pos > page_scripts_pos);
assert!(script_pos < body_end);
}
#[test]
fn production_mode_no_reload_script() {
let result = wrap_document("<p>prod</p>", &[], &["app.js".into()], false, None, "__seam");
assert!(!result.contains("EventSource"), "production mode must not inject live reload");
}
#[test]
fn vite_mode_injects_three_scripts() {
let vite = ViteDevInfo {
origin: "http://localhost:5173".to_string(),
entry: "src/client/main.tsx".to_string(),
};
let result = wrap_document(
"<p>vite</p>",
&["ignored.css".into()],
&["ignored.js".into()],
false,
Some(&vite),
"__seam",
);
assert!(result.contains("@react-refresh"), "must inject React Refresh preamble");
assert!(result.contains("/@vite/client"), "must inject Vite HMR client");
assert!(result.contains("src/client/main.tsx"), "must inject app entry");
assert!(!result.contains("/_seam/static/"), "vite mode must not reference static assets");
assert!(!result.contains("ignored.css"));
assert!(!result.contains("ignored.js"));
}
#[test]
fn vite_mode_skips_sse_reload() {
let vite = ViteDevInfo {
origin: "http://localhost:5173".to_string(),
entry: "src/client/main.tsx".to_string(),
};
let result = wrap_document("<p>vite-dev</p>", &[], &[], true, Some(&vite), "__seam");
assert!(result.contains("/@vite/client"));
assert!(!result.contains("/_seam/dev/reload"), "vite mode must not inject SSE reload");
}
#[test]
fn vite_proxy_mode_uses_relative_dev_scripts() {
let vite = ViteDevInfo { origin: String::new(), entry: "src/client/main.tsx".to_string() };
let result = wrap_document("<p>vite-proxy</p>", &[], &[], true, Some(&vite), "__seam");
assert!(result.contains("import RefreshRuntime from '/@react-refresh'"));
assert!(result.contains(r#"<script type="module" src="/@vite/client"></script>"#));
assert!(result.contains(r#"<script type="module" src="/src/client/main.tsx"></script>"#));
}
#[test]
fn no_metadata_passes_through() {
let result = wrap_document("<div><p>Hello</p></div>", &[], &[], false, None, "__seam");
assert!(result.contains("<div id=\"__seam\"><div><p>Hello</p></div></div>"));
}
#[test]
fn slot_markers_present_in_production() {
let result =
wrap_document("<p>test</p>", &["a.css".into()], &["a.js".into()], false, None, "__seam");
assert!(result.contains("<!--seam:page-styles-->"));
assert!(result.contains("<!--seam:prefetch-->"));
assert!(result.contains("<!--seam:page-scripts-->"));
let head_end = result.find("</head>").unwrap();
let page_styles = result.find("<!--seam:page-styles-->").unwrap();
let page_scripts = result.find("<!--seam:page-scripts-->").unwrap();
assert!(page_styles < head_end);
assert!(page_scripts > head_end);
}
#[test]
fn slot_markers_absent_in_vite_mode() {
let vite = ViteDevInfo {
origin: "http://localhost:5173".to_string(),
entry: "src/main.tsx".to_string(),
};
let result = wrap_document("<p>test</p>", &[], &[], false, Some(&vite), "__seam");
assert!(!result.contains("<!--seam:page-styles-->"));
assert!(!result.contains("<!--seam:prefetch-->"));
assert!(!result.contains("<!--seam:page-scripts-->"));
}
#[test]
fn skeleton_with_conditional_stays_in_body() {
let skeleton =
"<!--seam:if:x--><!--seam:d:attr:content--><meta name=\"og\"><!--seam:endif:x--><p>body</p>";
let result = wrap_document(skeleton, &[], &[], false, None, "__seam");
let root_start = result.find("__seam").unwrap();
let root_section = &result[root_start..];
assert!(root_section.contains("<!--seam:if:x-->"), "conditional stays in body");
assert!(root_section.contains("<meta name=\"og\">"), "meta stays in body");
assert!(root_section.contains("<p>body</p>"), "body content in root div");
}
}