From 1798ba349086d6ce56814fa890413994c1fbc828 Mon Sep 17 00:00:00 2001 From: puneetdixit200 Date: Thu, 4 Jun 2026 05:45:30 +0530 Subject: [PATCH] Ensure fullstack HTML has doctype --- packages/fullstack-server/src/index_html.rs | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/fullstack-server/src/index_html.rs b/packages/fullstack-server/src/index_html.rs index bde3f5530b..a3c74e378a 100644 --- a/packages/fullstack-server/src/index_html.rs +++ b/packages/fullstack-server/src/index_html.rs @@ -1,6 +1,9 @@ use anyhow::Context; +use std::borrow::Cow; use std::path::Path; +const HTML5_DOCTYPE: &str = ""; + /// An `IndexHtml` represents the contents of an `index.html` file used to serve a web application. /// /// This defines the static portion of your web application, typically generated by a tool like `dx` @@ -44,6 +47,9 @@ impl IndexHtml { /// The `root_id` parameter specifies the id of the main application container (e.g., "main") /// which your app will render into. pub fn new(contents: &str, root_id: &str) -> Result { + let contents = normalize_doctype(contents); + let contents = contents.as_ref(); + let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")) .with_context(|| format!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."))?; @@ -114,3 +120,34 @@ impl IndexHtml { Self::new(DEFAULT, "main").expect("Failed to load default index.html") } } + +fn normalize_doctype(contents: &str) -> Cow<'_, str> { + let trimmed = contents.trim_start(); + let has_doctype = trimmed + .as_bytes() + .get(..HTML5_DOCTYPE.len()) + .is_some_and(|prefix| prefix.eq_ignore_ascii_case(HTML5_DOCTYPE.as_bytes())); + + if has_doctype { + Cow::Borrowed(contents) + } else { + Cow::Owned(format!("{HTML5_DOCTYPE}\n{contents}")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn templates_without_a_doctype_are_normalized() { + let template = r#" + app +
+ "#; + + let index = IndexHtml::new(template, "main").unwrap(); + + assert!(index.head_before_title.starts_with("")); + } +}