Fix hot reload diffing to empty rsx (#3567)
* Fix hot reloading to empty rsx * Only normalize the template when hot reloading and rendering * add a test for emptying out an rsx block in hot reloading
This commit is contained in:
1 parent
2c6a5212fc
commit
f99e50b0aa
3 files changed
+61
-26
No files matched your search
@@ -122,7 +122,10 @@ impl HotReloadResult {
|
|||||||
new: &TemplateBody,
|
new: &TemplateBody,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let full_rebuild_state = LastBuildState::new(full_rebuild_state, name);
|
// Normalize both the full rebuild state and the new state for rendering
|
||||||
|
let full_rebuild_state = full_rebuild_state.normalized();
|
||||||
|
let new = new.normalized();
|
||||||
|
let full_rebuild_state = LastBuildState::new(&full_rebuild_state, name);
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
full_rebuild_state,
|
full_rebuild_state,
|
||||||
templates: Default::default(),
|
templates: Default::default(),
|
||||||
@@ -131,7 +134,7 @@ impl HotReloadResult {
|
|||||||
literal_component_properties: Default::default(),
|
literal_component_properties: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
s.hotreload_body::<Ctx>(new)?;
|
s.hotreload_body::<Ctx>(&new)?;
|
||||||
|
|
||||||
Some(s)
|
Some(s)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,6 +467,32 @@ fn invalid_cases() {
|
|||||||
assert!(added.is_none());
|
assert!(added.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_empty_rsx() {
|
||||||
|
let old_template = quote! {
|
||||||
|
div {
|
||||||
|
for item in vec![1, 2, 3, 4] {
|
||||||
|
div { "asasddasdasd" }
|
||||||
|
div { "123" }
|
||||||
|
}
|
||||||
|
for item in vec![4, 5, 6] {
|
||||||
|
span { "asasddasdasd" }
|
||||||
|
span { "123" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// empty out the whole rsx block
|
||||||
|
let new_template = quote! {};
|
||||||
|
|
||||||
|
let location = "file:line:col:0";
|
||||||
|
|
||||||
|
let old_template: CallBody = syn::parse2(old_template).unwrap();
|
||||||
|
let new_template: CallBody = syn::parse2(new_template).unwrap();
|
||||||
|
|
||||||
|
assert!(hotreload_callbody::<Mock>(&old_template, &new_template).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attributes_reload() {
|
fn attributes_reload() {
|
||||||
let old = quote! {
|
let old = quote! {
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ impl Parse for TemplateBody {
|
|||||||
myself
|
myself
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.extend(children.diagnostics.into_diagnostics());
|
.extend(children.diagnostics.into_diagnostics());
|
||||||
|
|
||||||
Ok(myself)
|
Ok(myself)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,51 +100,39 @@ impl Parse for TemplateBody {
|
|||||||
/// This is because the parsing phase filled in all the additional metadata we need
|
/// This is because the parsing phase filled in all the additional metadata we need
|
||||||
impl ToTokens for TemplateBody {
|
impl ToTokens for TemplateBody {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
// If the nodes are completely empty, insert a placeholder node
|
// First normalize the template body for rendering
|
||||||
// Core expects at least one node in the template to make it easier to replace
|
let node = self.normalized();
|
||||||
if self.is_empty() {
|
|
||||||
// Create an empty template body with a placeholder and diagnostics + the template index from the original
|
|
||||||
let empty = Self::new(vec![BodyNode::RawExpr(parse_quote! {()})]);
|
|
||||||
let default = Self {
|
|
||||||
diagnostics: self.diagnostics.clone(),
|
|
||||||
template_idx: self.template_idx.clone(),
|
|
||||||
..empty
|
|
||||||
};
|
|
||||||
// And then render the default template body
|
|
||||||
default.to_tokens(tokens);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have an implicit key, then we need to write its tokens
|
// If we have an implicit key, then we need to write its tokens
|
||||||
let key_tokens = match self.implicit_key() {
|
let key_tokens = match node.implicit_key() {
|
||||||
Some(tok) => quote! { Some( #tok.to_string() ) },
|
Some(tok) => quote! { Some( #tok.to_string() ) },
|
||||||
None => quote! { None },
|
None => quote! { None },
|
||||||
};
|
};
|
||||||
|
|
||||||
let roots = self.quote_roots();
|
let roots = node.quote_roots();
|
||||||
|
|
||||||
// Print paths is easy - just print the paths
|
// Print paths is easy - just print the paths
|
||||||
let node_paths = self.node_paths.iter().map(|it| quote!(&[#(#it),*]));
|
let node_paths = node.node_paths.iter().map(|it| quote!(&[#(#it),*]));
|
||||||
let attr_paths = self.attr_paths.iter().map(|(it, _)| quote!(&[#(#it),*]));
|
let attr_paths = node.attr_paths.iter().map(|(it, _)| quote!(&[#(#it),*]));
|
||||||
|
|
||||||
// For printing dynamic nodes, we rely on the ToTokens impl
|
// For printing dynamic nodes, we rely on the ToTokens impl
|
||||||
// Elements have a weird ToTokens - they actually are the entrypoint for Template creation
|
// Elements have a weird ToTokens - they actually are the entrypoint for Template creation
|
||||||
let dynamic_nodes: Vec<_> = self.dynamic_nodes().collect();
|
let dynamic_nodes: Vec<_> = node.dynamic_nodes().collect();
|
||||||
let dynamic_nodes_len = dynamic_nodes.len();
|
let dynamic_nodes_len = dynamic_nodes.len();
|
||||||
|
|
||||||
// We could add a ToTokens for Attribute but since we use that for both components and elements
|
// We could add a ToTokens for Attribute but since we use that for both components and elements
|
||||||
// They actually need to be different, so we just localize that here
|
// They actually need to be different, so we just localize that here
|
||||||
let dyn_attr_printer: Vec<_> = self
|
let dyn_attr_printer: Vec<_> = node
|
||||||
.dynamic_attributes()
|
.dynamic_attributes()
|
||||||
.map(|attr| attr.rendered_as_dynamic_attr())
|
.map(|attr| attr.rendered_as_dynamic_attr())
|
||||||
.collect();
|
.collect();
|
||||||
let dynamic_attr_len = dyn_attr_printer.len();
|
let dynamic_attr_len = dyn_attr_printer.len();
|
||||||
|
|
||||||
let dynamic_text = self.dynamic_text_segments.iter();
|
let dynamic_text = node.dynamic_text_segments.iter();
|
||||||
|
|
||||||
let diagnostics = &self.diagnostics;
|
let diagnostics = &node.diagnostics;
|
||||||
let index = self.template_idx.get();
|
let index = node.template_idx.get();
|
||||||
let hot_reload_mapping = self.hot_reload_mapping();
|
let hot_reload_mapping = node.hot_reload_mapping();
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
dioxus_core::Element::Ok({
|
dioxus_core::Element::Ok({
|
||||||
@@ -255,6 +244,23 @@ impl TemplateBody {
|
|||||||
body
|
body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize the Template body for rendering. If the body is completely empty, insert a placeholder node
|
||||||
|
pub fn normalized(&self) -> Self {
|
||||||
|
// If the nodes are completely empty, insert a placeholder node
|
||||||
|
// Core expects at least one node in the template to make it easier to replace
|
||||||
|
if self.is_empty() {
|
||||||
|
// Create an empty template body with a placeholder and diagnostics + the template index from the original
|
||||||
|
let empty = Self::new(vec![BodyNode::RawExpr(parse_quote! {()})]);
|
||||||
|
let default = Self {
|
||||||
|
diagnostics: self.diagnostics.clone(),
|
||||||
|
template_idx: self.template_idx.clone(),
|
||||||
|
..empty
|
||||||
|
};
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.roots.is_empty()
|
self.roots.is_empty()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user