1
0
Эх сурвалжийг харах

fix boolean attribute rendering in SSR

Evan Almloff 1 жил өмнө
parent
commit
aaded7981f

+ 6 - 0
packages/ssr/src/cache.rs

@@ -1,6 +1,8 @@
 use dioxus_core::prelude::*;
 use std::fmt::Write;
 
+use crate::renderer::{str_truthy, BOOL_ATTRS};
+
 #[derive(Debug)]
 pub struct StringCache {
     pub segments: Vec<Segment>,
@@ -86,6 +88,10 @@ impl StringCache {
                                 inner_html = Some(value);
                             } else if let Some("style") = namespace {
                                 styles.push((name, value));
+                            } else if BOOL_ATTRS.contains(&name) {
+                                if str_truthy(value) {
+                                    write!(chain, " {name}=\"{value}\"",)?;
+                                }
                             } else {
                                 write!(chain, " {name}=\"{value}\"")?;
                             }

+ 78 - 22
packages/ssr/src/renderer.rs

@@ -1,5 +1,6 @@
 use super::cache::Segment;
 use crate::cache::StringCache;
+
 use dioxus_core::{prelude::*, AttributeValue, DynamicNode, RenderReturn};
 use std::collections::HashMap;
 use std::fmt::Write;
@@ -83,18 +84,14 @@ impl Renderer {
                         inner_html = Some(attr);
                     } else if attr.namespace == Some("style") {
                         accumulated_dynamic_styles.push(attr);
+                    } else if BOOL_ATTRS.contains(&attr.name) {
+                        if truthy(&attr.value) {
+                            write!(buf, " {}=", attr.name)?;
+                            write_value(buf, &attr.value)?;
+                        }
                     } else {
-                        match attr.value {
-                            AttributeValue::Text(value) => {
-                                write!(buf, " {}=\"{}\"", attr.name, value)?
-                            }
-                            AttributeValue::Bool(value) => write!(buf, " {}={}", attr.name, value)?,
-                            AttributeValue::Int(value) => write!(buf, " {}={}", attr.name, value)?,
-                            AttributeValue::Float(value) => {
-                                write!(buf, " {}={}", attr.name, value)?
-                            }
-                            _ => {}
-                        };
+                        write!(buf, " {}=", attr.name)?;
+                        write_value(buf, &attr.value)?;
                     }
                 }
                 Segment::Node(idx) => match &template.dynamic_nodes[*idx] {
@@ -153,17 +150,9 @@ impl Renderer {
                             write!(buf, " style=\"")?;
                         }
                         for attr in &accumulated_dynamic_styles {
-                            match attr.value {
-                                AttributeValue::Text(value) => {
-                                    write!(buf, "{}:{};", attr.name, value)?
-                                }
-                                AttributeValue::Bool(value) => {
-                                    write!(buf, "{}:{};", attr.name, value)?
-                                }
-                                AttributeValue::Float(f) => write!(buf, "{}:{};", attr.name, f)?,
-                                AttributeValue::Int(i) => write!(buf, "{}:{};", attr.name, i)?,
-                                _ => {}
-                            };
+                            write!(buf, "{}:", attr.name)?;
+                            write_value_unquoted(buf, &attr.value)?;
+                            write!(buf, ";")?;
                         }
                         if !*inside_style_tag {
                             write!(buf, "\"")?;
@@ -248,3 +237,70 @@ fn to_string_works() {
 
     assert_eq!(out, "<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 --&gt;123&lt;-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>&lt;/diiiiiiiiv&gt;<div>finalize 0</div><div>finalize 1</div><div>finalize 2</div><div>finalize 3</div><div>finalize 4</div></div>");
 }
+
+pub(crate) const BOOL_ATTRS: &[&str] = &[
+    "allowfullscreen",
+    "allowpaymentrequest",
+    "async",
+    "autofocus",
+    "autoplay",
+    "checked",
+    "controls",
+    "default",
+    "defer",
+    "disabled",
+    "formnovalidate",
+    "hidden",
+    "ismap",
+    "itemscope",
+    "loop",
+    "multiple",
+    "muted",
+    "nomodule",
+    "novalidate",
+    "open",
+    "playsinline",
+    "readonly",
+    "required",
+    "reversed",
+    "selected",
+    "truespeed",
+    "webkitdirectory",
+];
+
+pub(crate) fn str_truthy(value: &str) -> bool {
+    !value.is_empty() && value != "0" && value.to_lowercase() != "false"
+}
+
+pub(crate) fn truthy(value: &AttributeValue) -> bool {
+    match value {
+        AttributeValue::Text(value) => str_truthy(value),
+        AttributeValue::Bool(value) => *value,
+        AttributeValue::Int(value) => *value != 0,
+        AttributeValue::Float(value) => *value != 0.0,
+        _ => false,
+    }
+}
+
+pub(crate) fn write_value(buf: &mut impl Write, value: &AttributeValue) -> std::fmt::Result {
+    match value {
+        AttributeValue::Text(value) => write!(buf, "\"{}\"", value),
+        AttributeValue::Bool(value) => write!(buf, "{}", value),
+        AttributeValue::Int(value) => write!(buf, "{}", value),
+        AttributeValue::Float(value) => write!(buf, "{}", value),
+        _ => Ok(()),
+    }
+}
+
+pub(crate) fn write_value_unquoted(
+    buf: &mut impl Write,
+    value: &AttributeValue,
+) -> std::fmt::Result {
+    match value {
+        AttributeValue::Text(value) => write!(buf, "{}", value),
+        AttributeValue::Bool(value) => write!(buf, "{}", value),
+        AttributeValue::Int(value) => write!(buf, "{}", value),
+        AttributeValue::Float(value) => write!(buf, "{}", value),
+        _ => Ok(()),
+    }
+}

+ 38 - 0
packages/ssr/tests/bool_attr.rs

@@ -0,0 +1,38 @@
+use dioxus::prelude::*;
+
+#[test]
+fn static_boolean_attributs() {
+    fn app(cx: Scope) -> Element {
+        render! {
+            div { hidden: "false" }
+            div { hidden: "true" }
+        }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(
+        dioxus_ssr::render(&dom),
+        r#"<div></div><div hidden="true"></div>"#
+    );
+}
+
+#[test]
+fn dynamic_boolean_attributs() {
+    fn app(cx: Scope) -> Element {
+        let inner_html = "<div>1234</div>";
+        render! {
+            div { hidden: false }
+            div { hidden: true }
+        }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(
+        dioxus_ssr::render(&dom),
+        r#"<div></div><div hidden=true></div>"#
+    );
+}