orbiton/
hmr_inject.rs

1// Module for handling HMR client code injection
2
3use anyhow::Result;
4use log::debug;
5use std::io::Read;
6use std::path::Path;
7
8/// The HMR client script as a static string
9pub const HMR_CLIENT_SCRIPT: &str = include_str!("hmr_client.js");
10
11/// Checks if a file is an HTML file based on extension
12pub fn is_html_file(path: &Path) -> bool {
13    if let Some(ext) = path.extension() {
14        ext.to_string_lossy().eq_ignore_ascii_case("html")
15    } else {
16        false
17    }
18}
19
20/// Inject HMR client code into an HTML response
21pub fn inject_hmr_client(html_content: &str, _port: u16) -> Result<String> {
22    debug!("Injecting HMR client code into HTML response");
23
24    // Check if the HTML content already has the HMR client script
25    if html_content.contains("__ORBIT_REGISTER_HMR_HANDLER") {
26        debug!("HMR client code already present in HTML");
27        return Ok(html_content.to_owned());
28    }
29
30    // Find where to inject the script (before closing </body> tag)
31    if let Some(pos) = html_content.to_lowercase().rfind("</body>") {
32        let (before, after) = html_content.split_at(pos);
33
34        // We can either embed the script or reference it as an external file
35        // Using external file is often better for debugging
36        let script = format!(
37            "<script type=\"text/javascript\" src=\"/__orbit_hmr_client.js?v={}\"></script>\n",
38            std::time::SystemTime::now()
39                .duration_since(std::time::UNIX_EPOCH)
40                .unwrap_or_default()
41                .as_secs()
42        );
43
44        // Inject the script
45        let injected_html = format!("{before}{script}{after}");
46        debug!("HMR client code injected successfully");
47
48        Ok(injected_html)
49    } else {
50        // If no </body> tag is found, append the script at the end
51        debug!("No </body> tag found, appending HMR client code at the end");
52        let script = format!(
53            "<script type=\"text/javascript\" src=\"/__orbit_hmr_client.js?v={}\"></script>\n",
54            std::time::SystemTime::now()
55                .duration_since(std::time::UNIX_EPOCH)
56                .unwrap_or_default()
57                .as_secs()
58        );
59        let injected_html = format!("{html_content}{script}");
60
61        Ok(injected_html)
62    }
63}
64
65/// Serve HMR client code as a standalone JavaScript file
66pub fn get_hmr_client_js() -> &'static str {
67    HMR_CLIENT_SCRIPT
68}
69
70/// Process HTML file and inject HMR client code
71pub fn process_html_file(path: &Path, port: u16) -> Result<Vec<u8>> {
72    debug!("Processing HTML file: {path:?}");
73
74    // Read HTML file content
75    let mut file = std::fs::File::open(path)?;
76    let mut content = String::new();
77    file.read_to_string(&mut content)?;
78
79    // Inject HMR client code
80    let injected_content = inject_hmr_client(&content, port)?;
81
82    Ok(injected_content.into_bytes())
83}