Fix hydration of suspense fragments that contain text nodes (#3287)

* Fix hydration of suspense fragments that contain text nodes

* fix release builds

* disable link deduplication

* restore meta tag deduplication

* Add a test for the failed hydration case

* make changes to the document trait non-breaking

* remove info log from server future hook

* restore default implementation - this was also a breaking change

* fix semver breakage

* clean up server_future

* Cleanup unused code

* fix clippy

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-01-07 19:47:23 -08:00
1 parent 57d637184f
commit d41ab3a756
33 files changed
+644 -222

No files matched your search

+10 -3
View File
@@ -25,7 +25,8 @@ pub struct LinkProps {
}
impl LinkProps {
pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> {
/// Get all the attributes for the link tag
pub fn attributes(&self) -> Vec<(&'static str, String)> {
let mut attributes = Vec::new();
if let Some(rel) = &self.rel {
attributes.push(("rel", rel.clone()));
@@ -104,12 +105,18 @@ pub fn Link(props: LinkProps) -> Element {
use_update_warning(&props, "Link {}");
use_hook(|| {
let document = document();
let mut insert_link = document.create_head_component();
if let Some(href) = &props.href {
if !should_insert_link(href) {
return;
insert_link = false;
}
}
let document = document();
if !insert_link {
return;
}
document.create_link(props);
});
+2 -1
View File
@@ -16,7 +16,8 @@ pub struct MetaProps {
}
impl MetaProps {
pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> {
/// Get all the attributes for the meta tag
pub fn attributes(&self) -> Vec<(&'static str, String)> {
let mut attributes = Vec::new();
if let Some(property) = &self.property {
attributes.push(("property", property.clone()));
+15 -3
View File
@@ -22,7 +22,8 @@ pub struct ScriptProps {
}
impl ScriptProps {
pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> {
/// Get all the attributes for the script tag
pub fn attributes(&self) -> Vec<(&'static str, String)> {
let mut attributes = Vec::new();
if let Some(defer) = &self.defer {
attributes.push(("defer", defer.to_string()));
@@ -90,13 +91,24 @@ pub fn Script(props: ScriptProps) -> Element {
use_update_warning(&props, "Script {}");
use_hook(|| {
let document = document();
let mut insert_script = document.create_head_component();
if let Some(src) = &props.src {
if !should_insert_script(src) {
return;
insert_script = false;
}
}
let document = document();
if !insert_script {
return;
}
// Make sure the props are in a valid form - they must either have a source or children
if let (None, Err(err)) = (&props.src, props.script_contents()) {
// If the script has neither contents nor src, log an error
err.log("Script")
}
document.create_script(props);
});
+36 -4
View File
@@ -17,7 +17,8 @@ pub struct StyleProps {
}
impl StyleProps {
pub(crate) fn attributes(&self) -> Vec<(&'static str, String)> {
/// Get all the attributes for the style tag
pub fn attributes(&self) -> Vec<(&'static str, String)> {
let mut attributes = Vec::new();
if let Some(href) = &self.href {
attributes.push(("href", href.clone()));
@@ -71,13 +72,44 @@ pub fn Style(props: StyleProps) -> Element {
use_update_warning(&props, "Style {}");
use_hook(|| {
let document = document();
let mut insert_style = document.create_head_component();
if let Some(href) = &props.href {
if !should_insert_style(href) {
return;
insert_style = false;
}
}
let document = document();
document.create_style(props);
if !insert_style {
return;
}
let mut attributes = props.attributes();
match (&props.href, props.style_contents()) {
// The style has inline contents, render it as a style tag
(_, Ok(_)) => document.create_style(props),
// The style has a src, render it as a link tag
(Some(_), _) => {
attributes.push(("type", "text/css".into()));
document.create_link(LinkProps {
media: props.media,
title: props.title,
r#type: Some("text/css".to_string()),
additional_attributes: props.additional_attributes,
href: props.href,
rel: None,
disabled: None,
r#as: None,
sizes: None,
crossorigin: None,
referrerpolicy: None,
fetchpriority: None,
hreflang: None,
integrity: None,
blocking: None,
});
}
// The style has neither contents nor src, log an error
(None, Err(err)) => err.log("Style"),
};
});
VNode::empty()