Repository: Y2Z/monolith
Pull request #444 (2025-03-16 02:55:48)

Embed JS as plain text instead of relying on data URLs


Patch:
diff --git a/src/core.rs b/src/core.rs
index ae7fff7..41f114c 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -107,6 +107,7 @@ const FILE_SIGNATURES: [[&[u8]; 2]; 18] = [
 ];
 // All known non-"text/..." plaintext media types
 const PLAINTEXT_MEDIA_TYPES: &[&str] = &[
+    "application/javascript",          // .js
     "application/json",                // .json
     "application/ld+json",             // .jsonld
     "application/x-sh",                // .sh
diff --git a/src/html.rs b/src/html.rs
index 9cccd98..6bcd22c 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -637,18 +637,32 @@ pub fn retrieve_and_embed_asset(
                     // Every other type of element gets processed here
 
                     // Parse media type for SCRIPT elements
-                    if node_name == "script" && get_node_attr(node, "src").is_some() {
+                    if node_name == "script" {
                         let script_media_type =
                             get_node_attr(node, "type").unwrap_or(String::from("text/javascript"));
 
-                        if script_media_type == "text/javascript" {
-                            // TODO: embed content here instead of using data URLs
+                        if script_media_type == "text/javascript"
+                            || script_media_type == "application/javascript"
+                        {
+                            // Embed javascript code instead of using data URLs
+                            let script_dom: RcDom =
+                                parse_document(RcDom::default(), Default::default())
+                                    .one("<script>;</script>");
+                            for script_node in
+                                find_nodes(&script_dom.document, vec!["html", "head", "script"])
+                                    .iter()
+                            {
+                                let text_node = &script_node.children.borrow()[0];
 
-                            // Create and embed data URL
-                            let mut data_url =
-                                create_data_url(&script_media_type, &charset, &data, &final_url);
-                            data_url.set_fragment(resolved_url.fragment());
-                            set_node_attr(node, attr_name, Some(data_url.to_string()));
+                                if let NodeData::Text { ref contents } = text_node.data {
+                                    let mut tendril = contents.borrow_mut();
+                                    tendril.clear();
+                                    tendril.push_slice(&String::from_utf8_lossy(&data));
+                                }
+
+                                node.children.borrow_mut().push(text_node.clone());
+                                set_node_attr(node, attr_name, None);
+                            }
                         } else {
                             // Create and embed data URL
                             let mut data_url =


Test patch:
diff --git a/tests/cli/data_url.rs b/tests/cli/data_url.rs
index 56cb0be..78ed811 100644
--- a/tests/cli/data_url.rs
+++ b/tests/cli/data_url.rs
@@ -221,7 +221,7 @@ mod failing {
         // STDOUT should contain HTML without contents of local JS file
         assert_eq!(
             String::from_utf8_lossy(&out.stdout),
-            "<html><head><script src=\"data:text/javascript;base64,\"></script></head><body></body></html>\n"
+            "<html><head><script></script></head><body></body></html>\n"
         );
 
         // Exit code should be 0
diff --git a/tests/cli/local_files.rs b/tests/cli/local_files.rs
index bcbccf4..0013267 100644
--- a/tests/cli/local_files.rs
+++ b/tests/cli/local_files.rs
@@ -62,7 +62,7 @@ mod passing {
             <img alt=\"\">\n  \
             <a href=\"file://local-file.html/\">Tricky href</a>\n  \
             <a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n  \
-            <script src=\"data:text/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==\"></script>\n\n\n\n\
+            <script>document.body.style.backgroundColor = \"green\";\ndocument.body.style.color = \"red\";\n</script>\n\n\n\n\
             </body></html>\n\
             "
         );
@@ -289,7 +289,7 @@ mod passing {
                 <link href=\"data:text/css;base64,Ym9keSB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogICAgY29sb3I6ICNGRkY7Cn0K\" rel=\"stylesheet\" type=\"text/css\" crossorigin=\"anonymous\">\n  \
                 <link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\" crossorigin=\"anonymous\">\n</head>\n\n<body>\n  \
                 <p>This page should have black background and white foreground, but only when served via http: (not via file:)</p>\n  \
-                <script src=\"data:text/javascript;base64,ZnVuY3Rpb24gbm9vcCgpIHsKICAgIGNvbnNvbGUubG9nKCJtb25vbGl0aCIpOwp9Cg==\"></script>\n  \
+                <script>function noop() {{\n    console.log(\"monolith\");\n}}\n</script>\n  \
                 <script src=\"script.js\"></script>\n\n\n\n\
                 </body></html>\n\
                 "