Browse Source

fix 2020: return None if the root nodes are empty in rsx (#2026)

Jonathan Kelley 1 year ago
parent
commit
7461a14cb4
3 changed files with 41 additions and 13 deletions
  1. 1 2
      packages/core-macro/src/lib.rs
  2. 25 0
      packages/core/tests/diff_element.rs
  3. 15 11
      packages/rsx/src/lib.rs

+ 1 - 2
packages/core-macro/src/lib.rs

@@ -4,7 +4,6 @@
 
 use proc_macro::TokenStream;
 use quote::ToTokens;
-use rsx::RenderCallBody;
 use syn::parse::Parser;
 use syn::punctuated::Punctuated;
 use syn::{parse_macro_input, Path, Token};
@@ -42,7 +41,7 @@ pub fn derive_typed_builder(input: TokenStream) -> TokenStream {
 pub fn rsx(tokens: TokenStream) -> TokenStream {
     match syn::parse::<rsx::CallBody>(tokens) {
         Err(err) => err.to_compile_error().into(),
-        Ok(body) => RenderCallBody(body).into_token_stream().into(),
+        Ok(body) => body.into_token_stream().into(),
     }
 }
 

+ 25 - 0
packages/core/tests/diff_element.rs

@@ -181,3 +181,28 @@ fn attribute_diff() {
         ]
     );
 }
+
+#[test]
+fn diff_empty() {
+    fn app() -> Element {
+        match generation() % 2 {
+            0 => rsx! { div { "hello" } },
+            1 => rsx! {},
+            _ => unreachable!(),
+        }
+    }
+
+    let mut vdom = VirtualDom::new(app);
+    vdom.rebuild(&mut NoOpMutations);
+
+    vdom.mark_dirty(ScopeId::ROOT);
+    let edits = vdom.render_immediate_to_vec().santize().edits;
+
+    assert_eq!(
+        edits,
+        [
+            CreatePlaceholder { id: ElementId(2,) },
+            ReplaceWith { id: ElementId(1,), m: 1 },
+        ]
+    )
+}

+ 15 - 11
packages/rsx/src/lib.rs

@@ -84,10 +84,13 @@ impl CallBody {
             location: Some(location),
         };
 
+        // Empty templates just are placeholders for "none"
+        if self.roots.is_empty() {
+            return quote! { None };
+        }
+
         quote! {
-            Some({
-                #body
-            })
+            Some({ #body })
         }
     }
 }
@@ -110,20 +113,20 @@ impl Parse for CallBody {
     }
 }
 
-#[derive(Default, Debug)]
-pub struct RenderCallBody(pub CallBody);
-
-impl ToTokens for RenderCallBody {
+impl ToTokens for CallBody {
     fn to_tokens(&self, out_tokens: &mut TokenStream2) {
         let body: TemplateRenderer = TemplateRenderer {
-            roots: &self.0.roots,
+            roots: &self.roots,
             location: None,
         };
 
+        // Empty templates just are placeholders for "none"
+        if self.roots.is_empty() {
+            return out_tokens.append_all(quote! { None });
+        }
+
         out_tokens.append_all(quote! {
-            Some({
-                #body
-            })
+            Some({ #body })
         })
     }
 }
@@ -145,6 +148,7 @@ impl<'a> TemplateRenderer<'a> {
         let mut context = DynamicContext::default();
 
         let mut roots = Vec::new();
+
         for (idx, root) in self.roots.iter().enumerate() {
             context.current_path.push(idx as u8);
             roots.push(context.update_node::<Ctx>(root, &mut mapping)?);