浏览代码

wip: start to refactor out hot reloading from packages

Also implement alternative simpler template system
Jonathan Kelley 2 年之前
父节点
当前提交
90982e0ccb
共有 44 个文件被更改,包括 3907 次插入5175 次删除
  1. 3 2
      Cargo.toml
  2. 1 1
      packages/autofmt/src/buffer.rs
  3. 1 1
      packages/autofmt/src/element.rs
  4. 1 5
      packages/core/Cargo.toml
  5. 18 423
      packages/core/src/arbitrary_value.rs
  6. 297 615
      packages/core/src/diff.rs
  7. 0 190
      packages/core/src/dynamic_template_context.rs
  8. 69 69
      packages/core/src/lazynodes.rs
  9. 24 36
      packages/core/src/lib.rs
  10. 52 395
      packages/core/src/mutations.rs
  11. 136 219
      packages/core/src/nodes.old.rs
  12. 172 0
      packages/core/src/nodes/arbitrary_value.rs
  13. 61 0
      packages/core/src/nodes/component.rs
  14. 150 0
      packages/core/src/nodes/element.rs
  15. 366 0
      packages/core/src/nodes/factory.rs
  16. 16 0
      packages/core/src/nodes/fragment.rs
  17. 204 0
      packages/core/src/nodes/mod.rs
  18. 0 0
      packages/core/src/nodes/suspense.rs
  19. 60 0
      packages/core/src/nodes/template.rs
  20. 25 0
      packages/core/src/nodes/text.rs
  21. 30 30
      packages/core/src/properties.rs
  22. 90 185
      packages/core/src/scopes.rs
  23. 0 1287
      packages/core/src/template.rs
  24. 3 46
      packages/core/src/util.rs
  25. 126 169
      packages/core/src/virtual_dom.rs
  26. 114 0
      packages/core/tests/interpreter.rs
  27. 0 1
      packages/desktop/Cargo.toml
  28. 23 21
      packages/desktop/src/controller.rs
  29. 6 5
      packages/desktop/src/hot_reload.rs
  30. 2 3
      packages/dioxus/Cargo.toml
  31. 1 1
      packages/dioxus/tests/create_dom.rs
  32. 17 0
      packages/dioxus/tests/rsx_syntax.rs
  33. 9 0
      packages/edit-stream/Cargo.toml
  34. 378 0
      packages/edit-stream/src/lib.rs
  35. 25 79
      packages/html/src/elements.rs
  36. 22 31
      packages/html/src/global_attributes.rs
  37. 0 1
      packages/router/Cargo.toml
  38. 1 4
      packages/rsx/Cargo.toml
  39. 9 3
      packages/rsx/src/error.rs
  40. 9 0
      packages/rsx/src/ifmt.rs
  41. 144 40
      packages/rsx/src/lib.rs
  42. 1059 1080
      packages/rsx/src/template.rs
  43. 183 232
      packages/ssr/src/lib.rs
  44. 0 1
      packages/web/Cargo.toml

+ 3 - 2
Cargo.toml

@@ -19,6 +19,7 @@ members = [
     "packages/rsx",
     "packages/native-core",
     "packages/native-core-macro",
+    "packages/edit-stream",
     "docs/guide",
 ]
 
@@ -41,7 +42,7 @@ rust-version = "1.60.0"
 
 [dev-dependencies]
 dioxus = { path = "./packages/dioxus" }
-dioxus-desktop = { path = "./packages/desktop", features = ["hot-reload"] }
+dioxus-desktop = { path = "./packages/desktop" }
 dioxus-ssr = { path = "./packages/ssr" }
 dioxus-router = { path = "./packages/router" }
 fermi = { path = "./packages/fermi" }
@@ -63,4 +64,4 @@ env_logger = "0.9.0"
 [profile.release]
 opt-level = 3
 lto = true
-debug = true
+debug = true

+ 1 - 1
packages/autofmt/src/buffer.rs

@@ -67,7 +67,7 @@ impl Buffer {
         match node {
             BodyNode::Element(el) => self.write_element(el),
             BodyNode::Component(component) => self.write_component(component),
-            BodyNode::Text(text) => self.write_text(text),
+            BodyNode::DynamicText(text) => self.write_text(text),
             BodyNode::RawExpr(exp) => self.write_raw_expr(exp),
         }
     }

+ 1 - 1
packages/autofmt/src/element.rs

@@ -280,7 +280,7 @@ impl Buffer {
         }
 
         match children {
-            [BodyNode::Text(ref text)] => Some(text.source.as_ref().unwrap().value().len()),
+            [BodyNode::DynamicText(ref text)] => Some(text.source.as_ref().unwrap().value().len()),
             [BodyNode::Component(ref comp)] => {
                 let attr_len = self.field_len(&comp.fields, &comp.manual_props);
 

+ 1 - 5
packages/core/Cargo.toml

@@ -18,7 +18,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 bumpalo = { version = "3.6", features = ["collections", "boxed"] }
 
 # faster hashmaps
-rustc-hash = "1.1.0"
+fxhash = "0.2"
 
 # Used in diffing
 longest-increasing-subsequence = "0.1.0"
@@ -45,11 +45,7 @@ serde = { version = "1", features = ["derive"], optional = true }
 # todo: I want to get rid of this
 backtrace = "0.3"
 
-# allows cloing trait objects
-dyn-clone = "1.0.9"
-
 [features]
 default = []
 serialize = ["serde"]
 debug_vdom = []
-hot-reload = []

+ 18 - 423
packages/core/src/arbitrary_value.rs

@@ -1,7 +1,4 @@
-use std::fmt::{Arguments, Formatter};
-
-use bumpalo::Bump;
-use dyn_clone::{clone_box, DynClone};
+use std::{any::Any, fmt::Formatter};
 
 /// Possible values for an attribute
 // trying to keep values at 3 bytes
@@ -28,134 +25,7 @@ pub enum AttributeValue<'a> {
     Vec4Uint(u32, u32, u32, u32),
 
     Bytes(&'a [u8]),
-    Any(ArbitraryAttributeValue<'a>),
-}
-
-/// A value that can be converted into an attribute value
-pub trait IntoAttributeValue<'a> {
-    /// Convert into an attribute value
-    fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
-}
-
-impl<'a> IntoAttributeValue<'a> for u32 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Uint32(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for u64 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Uint64(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for i32 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Int32(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for i64 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Int64(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for f32 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Float32(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for f64 {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Float64(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for bool {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Bool(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for &'a str {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Text(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
-    fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
-        use bumpalo::core_alloc::fmt::Write;
-        let mut str_buf = bumpalo::collections::String::new_in(bump);
-        str_buf.write_fmt(self).unwrap();
-        AttributeValue::Text(str_buf.into_bump_str())
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for &'a [u8] {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Bytes(self)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (f32, f32, f32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec3Float(self.0, self.1, self.2)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (i32, i32, i32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec3Int(self.0, self.1, self.2)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (u32, u32, u32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec3Uint(self.0, self.1, self.2)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (f32, f32, f32, f32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec4Float(self.0, self.1, self.2, self.3)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (i32, i32, i32, i32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec4Int(self.0, self.1, self.2, self.3)
-    }
-}
-
-impl<'a> IntoAttributeValue<'a> for (u32, u32, u32, u32) {
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Vec4Uint(self.0, self.1, self.2, self.3)
-    }
-}
-
-impl<'a, T> IntoAttributeValue<'a> for &'a T
-where
-    T: AnyClone + PartialEq,
-{
-    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
-        AttributeValue::Any(ArbitraryAttributeValue {
-            value: self,
-            cmp: |a, b| {
-                if let Some(a) = a.as_any().downcast_ref::<T>() {
-                    if let Some(b) = b.as_any().downcast_ref::<T>() {
-                        a == b
-                    } else {
-                        false
-                    }
-                } else {
-                    false
-                }
-            },
-        })
-    }
+    // Any(ArbitraryAttributeValue<'a>),
 }
 
 // todo
@@ -196,7 +66,7 @@ impl<'a> std::fmt::Display for AttributeValue<'a> {
             AttributeValue::Vec4Int(_, _, _, _) => todo!(),
             AttributeValue::Vec4Uint(_, _, _, _) => todo!(),
             AttributeValue::Bytes(a) => write!(f, "{:?}", a),
-            AttributeValue::Any(a) => write!(f, "{:?}", a),
+            // AttributeValue::Any(a) => write!(f, "{:?}", a),
         }
     }
 }
@@ -204,15 +74,17 @@ impl<'a> std::fmt::Display for AttributeValue<'a> {
 #[derive(Clone, Copy)]
 #[allow(missing_docs)]
 pub struct ArbitraryAttributeValue<'a> {
-    pub value: &'a dyn AnyClone,
-    pub cmp: fn(&dyn AnyClone, &dyn AnyClone) -> bool,
+    pub value: &'a dyn Any,
+    pub cmp: fn(&'a dyn std::any::Any, &'a dyn std::any::Any) -> bool,
 }
 
-impl PartialEq for ArbitraryAttributeValue<'_> {
-    fn eq(&self, other: &Self) -> bool {
-        (self.cmp)(self.value, other.value)
-    }
-}
+// pub trait AnyAttrValue {}
+
+// impl PartialEq for ArbitraryAttributeValue<'_> {
+//     fn eq(&self, other: &Self) -> bool {
+//         (self.cmp)(self.value, other.value)
+//     }
+// }
 
 impl std::fmt::Debug for ArbitraryAttributeValue<'_> {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@@ -248,79 +120,6 @@ impl<'de, 'a> serde::Deserialize<'de> for ArbitraryAttributeValue<'a> {
     }
 }
 
-/// A clone, sync and send version of `Any`
-// we only need the Sync + Send bound when hot reloading is enabled
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-pub trait AnyClone: std::any::Any + DynClone + Send + Sync {
-    fn as_any(&self) -> &dyn std::any::Any;
-}
-#[cfg(not(any(feature = "hot-reload", debug_assertions)))]
-pub trait AnyClone: std::any::Any + DynClone {
-    fn as_any(&self) -> &dyn std::any::Any;
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-impl<T: std::any::Any + DynClone + Send + Sync> AnyClone for T {
-    fn as_any(&self) -> &dyn std::any::Any {
-        self
-    }
-}
-#[cfg(not(any(feature = "hot-reload", debug_assertions)))]
-impl<T: std::any::Any + DynClone> AnyClone for T {
-    fn as_any(&self) -> &dyn std::any::Any {
-        self
-    }
-}
-
-dyn_clone::clone_trait_object!(AnyClone);
-
-#[derive(Clone)]
-#[allow(missing_docs)]
-pub struct OwnedArbitraryAttributeValue {
-    pub value: Box<dyn AnyClone>,
-    pub cmp: fn(&dyn AnyClone, &dyn AnyClone) -> bool,
-}
-
-impl PartialEq for OwnedArbitraryAttributeValue {
-    fn eq(&self, other: &Self) -> bool {
-        (self.cmp)(&*self.value, &*other.value)
-    }
-}
-
-impl std::fmt::Debug for OwnedArbitraryAttributeValue {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("OwnedArbitraryAttributeValue").finish()
-    }
-}
-
-#[cfg(feature = "serialize")]
-impl serde::Serialize for OwnedArbitraryAttributeValue {
-    fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        panic!("OwnedArbitraryAttributeValue should not be serialized")
-    }
-}
-#[cfg(feature = "serialize")]
-impl<'de> serde::Deserialize<'de> for &OwnedArbitraryAttributeValue {
-    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        panic!("OwnedArbitraryAttributeValue is not deserializable!")
-    }
-}
-#[cfg(feature = "serialize")]
-impl<'de> serde::Deserialize<'de> for OwnedArbitraryAttributeValue {
-    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        panic!("OwnedArbitraryAttributeValue is not deserializable!")
-    }
-}
-
 // todo
 #[allow(missing_docs)]
 impl<'a> AttributeValue<'a> {
@@ -429,214 +228,10 @@ impl<'a> AttributeValue<'a> {
         }
     }
 
-    pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
-        match self {
-            AttributeValue::Any(a) => Some(a),
-            _ => None,
-        }
-    }
-}
-
-/// A owned attribute value.
-#[derive(Debug, Clone, PartialEq)]
-#[cfg_attr(
-    all(feature = "serialize"),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-#[allow(missing_docs)]
-pub enum OwnedAttributeValue {
-    Text(String),
-    Float32(f32),
-    Float64(f64),
-    Int32(i32),
-    Int64(i64),
-    Uint32(u32),
-    Uint64(u64),
-    Bool(bool),
-
-    Vec3Float(f32, f32, f32),
-    Vec3Int(i32, i32, i32),
-    Vec3Uint(u32, u32, u32),
-
-    Vec4Float(f32, f32, f32, f32),
-    Vec4Int(i32, i32, i32, i32),
-    Vec4Uint(u32, u32, u32, u32),
-
-    Bytes(Vec<u8>),
-    // TODO: support other types
-    Any(OwnedArbitraryAttributeValue),
-}
-
-impl PartialEq<AttributeValue<'_>> for OwnedAttributeValue {
-    fn eq(&self, other: &AttributeValue<'_>) -> bool {
-        match (self, other) {
-            (Self::Text(l0), AttributeValue::Text(r0)) => l0 == r0,
-            (Self::Float32(l0), AttributeValue::Float32(r0)) => l0 == r0,
-            (Self::Float64(l0), AttributeValue::Float64(r0)) => l0 == r0,
-            (Self::Int32(l0), AttributeValue::Int32(r0)) => l0 == r0,
-            (Self::Int64(l0), AttributeValue::Int64(r0)) => l0 == r0,
-            (Self::Uint32(l0), AttributeValue::Uint32(r0)) => l0 == r0,
-            (Self::Uint64(l0), AttributeValue::Uint64(r0)) => l0 == r0,
-            (Self::Bool(l0), AttributeValue::Bool(r0)) => l0 == r0,
-            (Self::Vec3Float(l0, l1, l2), AttributeValue::Vec3Float(r0, r1, r2)) => {
-                l0 == r0 && l1 == r1 && l2 == r2
-            }
-            (Self::Vec3Int(l0, l1, l2), AttributeValue::Vec3Int(r0, r1, r2)) => {
-                l0 == r0 && l1 == r1 && l2 == r2
-            }
-            (Self::Vec3Uint(l0, l1, l2), AttributeValue::Vec3Uint(r0, r1, r2)) => {
-                l0 == r0 && l1 == r1 && l2 == r2
-            }
-            (Self::Vec4Float(l0, l1, l2, l3), AttributeValue::Vec4Float(r0, r1, r2, r3)) => {
-                l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3
-            }
-            (Self::Vec4Int(l0, l1, l2, l3), AttributeValue::Vec4Int(r0, r1, r2, r3)) => {
-                l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3
-            }
-            (Self::Vec4Uint(l0, l1, l2, l3), AttributeValue::Vec4Uint(r0, r1, r2, r3)) => {
-                l0 == r0 && l1 == r1 && l2 == r2 && l3 == r3
-            }
-            (Self::Bytes(l0), AttributeValue::Bytes(r0)) => l0 == r0,
-            (_, _) => false,
-        }
-    }
-}
-
-impl<'a> From<AttributeValue<'a>> for OwnedAttributeValue {
-    fn from(attr: AttributeValue<'a>) -> Self {
-        match attr {
-            AttributeValue::Text(t) => OwnedAttributeValue::Text(t.to_owned()),
-            AttributeValue::Float32(f) => OwnedAttributeValue::Float32(f),
-            AttributeValue::Float64(f) => OwnedAttributeValue::Float64(f),
-            AttributeValue::Int32(i) => OwnedAttributeValue::Int32(i),
-            AttributeValue::Int64(i) => OwnedAttributeValue::Int64(i),
-            AttributeValue::Uint32(u) => OwnedAttributeValue::Uint32(u),
-            AttributeValue::Uint64(u) => OwnedAttributeValue::Uint64(u),
-            AttributeValue::Bool(b) => OwnedAttributeValue::Bool(b),
-            AttributeValue::Vec3Float(f1, f2, f3) => OwnedAttributeValue::Vec3Float(f1, f2, f3),
-            AttributeValue::Vec3Int(f1, f2, f3) => OwnedAttributeValue::Vec3Int(f1, f2, f3),
-            AttributeValue::Vec3Uint(f1, f2, f3) => OwnedAttributeValue::Vec3Uint(f1, f2, f3),
-            AttributeValue::Vec4Float(f1, f2, f3, f4) => {
-                OwnedAttributeValue::Vec4Float(f1, f2, f3, f4)
-            }
-            AttributeValue::Vec4Int(f1, f2, f3, f4) => OwnedAttributeValue::Vec4Int(f1, f2, f3, f4),
-            AttributeValue::Vec4Uint(f1, f2, f3, f4) => {
-                OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4)
-            }
-            AttributeValue::Bytes(b) => OwnedAttributeValue::Bytes(b.to_owned()),
-            AttributeValue::Any(a) => OwnedAttributeValue::Any(OwnedArbitraryAttributeValue {
-                value: clone_box(a.value),
-                cmp: a.cmp,
-            }),
-        }
-    }
-}
-
-// todo
-#[allow(missing_docs)]
-impl OwnedAttributeValue {
-    pub fn as_text(&self) -> Option<&str> {
-        match self {
-            OwnedAttributeValue::Text(s) => Some(s),
-            _ => None,
-        }
-    }
-
-    pub fn as_float32(&self) -> Option<f32> {
-        match self {
-            OwnedAttributeValue::Float32(f) => Some(*f),
-            _ => None,
-        }
-    }
-
-    pub fn as_float64(&self) -> Option<f64> {
-        match self {
-            OwnedAttributeValue::Float64(f) => Some(*f),
-            _ => None,
-        }
-    }
-
-    pub fn as_int32(&self) -> Option<i32> {
-        match self {
-            OwnedAttributeValue::Int32(i) => Some(*i),
-            _ => None,
-        }
-    }
-
-    pub fn as_int64(&self) -> Option<i64> {
-        match self {
-            OwnedAttributeValue::Int64(i) => Some(*i),
-            _ => None,
-        }
-    }
-
-    pub fn as_uint32(&self) -> Option<u32> {
-        match self {
-            OwnedAttributeValue::Uint32(i) => Some(*i),
-            _ => None,
-        }
-    }
-
-    pub fn as_uint64(&self) -> Option<u64> {
-        match self {
-            OwnedAttributeValue::Uint64(i) => Some(*i),
-            _ => None,
-        }
-    }
-
-    pub fn as_bool(&self) -> Option<bool> {
-        match self {
-            OwnedAttributeValue::Bool(b) => Some(*b),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec3_float(&self) -> Option<(f32, f32, f32)> {
-        match self {
-            OwnedAttributeValue::Vec3Float(x, y, z) => Some((*x, *y, *z)),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec3_int(&self) -> Option<(i32, i32, i32)> {
-        match self {
-            OwnedAttributeValue::Vec3Int(x, y, z) => Some((*x, *y, *z)),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec3_uint(&self) -> Option<(u32, u32, u32)> {
-        match self {
-            OwnedAttributeValue::Vec3Uint(x, y, z) => Some((*x, *y, *z)),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec4_float(&self) -> Option<(f32, f32, f32, f32)> {
-        match self {
-            OwnedAttributeValue::Vec4Float(x, y, z, w) => Some((*x, *y, *z, *w)),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec4_int(&self) -> Option<(i32, i32, i32, i32)> {
-        match self {
-            OwnedAttributeValue::Vec4Int(x, y, z, w) => Some((*x, *y, *z, *w)),
-            _ => None,
-        }
-    }
-
-    pub fn as_vec4_uint(&self) -> Option<(u32, u32, u32, u32)> {
-        match self {
-            OwnedAttributeValue::Vec4Uint(x, y, z, w) => Some((*x, *y, *z, *w)),
-            _ => None,
-        }
-    }
-
-    pub fn as_bytes(&self) -> Option<&[u8]> {
-        match self {
-            OwnedAttributeValue::Bytes(b) => Some(b),
-            _ => None,
-        }
-    }
+    // pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
+    //     match self {
+    //         AttributeValue::Any(a) => Some(a),
+    //         _ => None,
+    //     }
+    // }
 }

+ 297 - 615
packages/core/src/diff.rs

@@ -38,20 +38,6 @@
 //! Other implementations either don't support fragments or use a "child + sibling" pattern to represent them. Our code is
 //! vastly simpler and more performant when we can just create a placeholder element while the fragment has no children.
 //!
-//! ### Suspense
-//! ------------
-//! Dioxus implements Suspense slightly differently than React. In React, each fiber is manually progressed until it runs
-//! into a promise-like value. React will then work on the next "ready" fiber, checking back on the previous fiber once
-//! it has finished its new work. In Dioxus, we use a similar approach, but try to completely render the tree before
-//! switching sub-fibers. Instead, each future is submitted into a futures-queue and the node is manually loaded later on.
-//! Due to the frequent calls to [`crate::virtual_dom::VirtualDom::work_with_deadline`] we can get the pure "fetch-as-you-render" behavior of React Fiber.
-//!
-//! We're able to use this approach because we use placeholder nodes - futures that aren't ready still get submitted to
-//! DOM, but as a placeholder.
-//!
-//! Right now, the "suspense" queue is intertwined with hooks. In the future, we should allow any future to drive attributes
-//! and contents, without the need for a `use_suspense` hook. In the interim, this is the quickest way to get Suspense working.
-//!
 //! ## Subtree Memoization
 //! -----------------------
 //! We also employ "subtree memoization" which saves us from having to check trees which hold no dynamic content. We can
@@ -91,128 +77,95 @@
 //! More info on how to improve this diffing algorithm:
 //!  - <https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/>
 
-use crate::{
-    dynamic_template_context::TemplateContext,
-    innerlude::{
-        AnyProps, ElementId, Mutations, ScopeArena, ScopeId, VComponent, VElement, VFragment,
-        VNode, VPlaceholder, VText,
-    },
-    template::{
-        Template, TemplateAttribute, TemplateElement, TemplateNode, TemplateNodeId,
-        TemplateNodeType, TemplateValue, TextTemplateSegment, VTemplateRef,
-    },
-    Attribute, TemplateAttributeValue,
+use crate::innerlude::{
+    AnyProps, ElementId, Renderer, ScopeArena, ScopeId, TemplateNode, VComponent, VElement,
+    VFragment, VNode, VTemplate, VText,
 };
-use bumpalo::Bump;
-use rustc_hash::{FxHashMap, FxHashSet};
+use fxhash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
 
-pub(crate) struct DiffState<'bump> {
+pub(crate) struct DiffState<'a, 'bump, R: Renderer<'bump>> {
     pub(crate) scopes: &'bump ScopeArena,
-    pub(crate) mutations: Mutations<'bump>,
+    pub(crate) mutations: &'a mut R,
     pub(crate) force_diff: bool,
+    pub(crate) element_stack: SmallVec<[ElementId; 10]>,
     pub(crate) scope_stack: SmallVec<[ScopeId; 5]>,
 }
 
-impl<'b> DiffState<'b> {
-    pub fn new(scopes: &'b ScopeArena) -> Self {
+impl<'a, 'b, R: Renderer<'b>> DiffState<'a, 'b, R> {
+    pub fn new(scopes: &'b ScopeArena, renderer: &'a mut R) -> Self {
         Self {
             scopes,
-            mutations: Mutations::new(),
+            mutations: renderer,
             force_diff: false,
+            element_stack: smallvec![],
             scope_stack: smallvec![],
         }
     }
 
-    pub fn diff_scope(&mut self, parent: ElementId, scopeid: ScopeId) {
+    pub fn diff_scope(&mut self, scopeid: ScopeId) {
         let (old, new) = (self.scopes.wip_head(scopeid), self.scopes.fin_head(scopeid));
+        let scope = self.scopes.get_scope(scopeid).unwrap();
 
         self.scope_stack.push(scopeid);
+        self.element_stack.push(scope.container);
         {
-            self.diff_node(parent, old, new);
+            self.diff_node(old, new);
         }
+        self.element_stack.pop();
         self.scope_stack.pop();
 
         self.mutations.mark_dirty_scope(scopeid);
     }
 
-    pub fn diff_node(
-        &mut self,
-        parent: ElementId,
-        old_node: &'b VNode<'b>,
-        new_node: &'b VNode<'b>,
-    ) {
-        use VNode::{Component, Element, Fragment, Placeholder, TemplateRef, Text};
+    pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) {
+        use VNode::{Component, Element, Fragment, Template, Text};
         match (old_node, new_node) {
             (Text(old), Text(new)) => {
                 self.diff_text_nodes(old, new, old_node, new_node);
             }
 
-            (Placeholder(old), Placeholder(new)) => {
-                self.diff_placeholder_nodes(old, new, old_node, new_node);
-            }
-
             (Element(old), Element(new)) => {
                 self.diff_element_nodes(old, new, old_node, new_node);
             }
 
             (Component(old), Component(new)) => {
-                self.diff_component_nodes(parent, old, new, old_node, new_node);
+                self.diff_component_nodes(old_node, new_node, *old, *new);
             }
 
             (Fragment(old), Fragment(new)) => {
-                self.diff_fragment_nodes(parent, old, new);
+                self.diff_fragment_nodes(old, new);
             }
 
-            (TemplateRef(old), TemplateRef(new)) => {
-                self.diff_template_ref_nodes(parent, old, new, old_node, new_node);
+            (Template(old), Template(new)) => {
+                self.diff_templates(old, new, old_node, new_node);
             }
 
             (
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_) | TemplateRef(_),
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_) | TemplateRef(_),
-            ) => self.replace_node(parent, old_node, new_node),
+                Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
+                Component(_) | Text(_) | Element(_) | Template(_) | Fragment(_),
+            ) => self.replace_node(old_node, new_node),
         }
     }
 
-    pub fn create_node(&mut self, parent: ElementId, node: &'b VNode<'b>, nodes: &mut Vec<u64>) {
+    pub fn create_node(&mut self, node: &'b VNode<'b>) -> usize {
         match node {
-            VNode::Text(vtext) => self.create_text_node(vtext, node, nodes),
-            VNode::Placeholder(anchor) => self.create_anchor_node(anchor, node, nodes),
-            VNode::Element(element) => self.create_element_node(parent, element, node, nodes),
-            VNode::Fragment(frag) => self.create_fragment_node(parent, frag, nodes),
-            VNode::Component(component) => self.create_component_node(parent, component, nodes),
-            VNode::TemplateRef(temp) => self.create_template_ref_node(parent, temp, node, nodes),
+            VNode::Text(vtext) => self.create_text_node(vtext, node),
+            VNode::Element(element) => self.create_element_node(element, node),
+            VNode::Fragment(frag) => self.create_fragment_node(*frag),
+            VNode::Component(component) => self.create_component_node(*component),
+            VNode::Template(template) => self.create_template_node(template, node),
         }
     }
 
-    fn create_text_node(&mut self, text: &'b VText<'b>, node: &'b VNode<'b>, nodes: &mut Vec<u64>) {
+    fn create_text_node(&mut self, text: &'b VText<'b>, node: &'b VNode<'b>) -> usize {
         let real_id = self.scopes.reserve_node(node);
         text.id.set(Some(real_id));
-        self.mutations
-            .create_text_node(text.text, Some(real_id.as_u64()));
-        nodes.push(real_id.0 as u64);
+        self.mutations.create_text_node(text.text, real_id);
+        1
     }
 
-    fn create_anchor_node(
-        &mut self,
-        anchor: &'b VPlaceholder,
-        node: &'b VNode<'b>,
-        nodes: &mut Vec<u64>,
-    ) {
-        let real_id = self.scopes.reserve_node(node);
-        anchor.id.set(Some(real_id));
-        self.mutations.create_placeholder(Some(real_id.as_u64()));
-        nodes.push(real_id.0 as u64);
-    }
-
-    fn create_element_node(
-        &mut self,
-        parent: ElementId,
-        element: &'b VElement<'b>,
-        node: &'b VNode<'b>,
-        nodes: &mut Vec<u64>,
-    ) {
+    fn create_element_node(&mut self, element: &'b VElement<'b>, node: &'b VNode<'b>) -> usize {
         let VElement {
             tag: tag_name,
             listeners,
@@ -224,15 +177,15 @@ impl<'b> DiffState<'b> {
             ..
         } = &element;
 
-        parent_id.set(Some(parent));
+        parent_id.set(self.element_stack.last().copied());
 
         let real_id = self.scopes.reserve_node(node);
 
         dom_id.set(Some(real_id));
 
+        self.element_stack.push(real_id);
         {
-            self.mutations
-                .create_element(tag_name, *namespace, Some(real_id.as_u64()), 0);
+            self.mutations.create_element(tag_name, *namespace, real_id);
 
             let cur_scope_id = self.current_scope();
 
@@ -242,32 +195,23 @@ impl<'b> DiffState<'b> {
             }
 
             for attr in attributes.iter() {
-                self.mutations.set_attribute(attr, Some(real_id.as_u64()));
+                self.mutations.set_attribute(attr, real_id);
             }
 
             if !children.is_empty() {
-                self.create_and_append_children(real_id, children);
+                self.create_and_append_children(children);
             }
         }
+        self.element_stack.pop();
 
-        nodes.push(real_id.0 as u64);
+        1
     }
 
-    fn create_fragment_node(
-        &mut self,
-        parent: ElementId,
-        frag: &'b VFragment<'b>,
-        nodes: &mut Vec<u64>,
-    ) {
-        self.create_children(parent, frag.children, nodes);
+    fn create_fragment_node(&mut self, frag: &'b VFragment<'b>) -> usize {
+        self.create_children(frag.children)
     }
 
-    fn create_component_node(
-        &mut self,
-        parent: ElementId,
-        vcomponent: &'b VComponent<'b>,
-        nodes: &mut Vec<u64>,
-    ) {
+    fn create_component_node(&mut self, vcomponent: &'b VComponent<'b>) -> usize {
         let parent_idx = self.current_scope();
 
         // the component might already exist - if it does, we need to reuse it
@@ -279,8 +223,13 @@ impl<'b> DiffState<'b> {
             // Insert a new scope into our component list
             let props: Box<dyn AnyProps + 'b> = vcomponent.props.borrow_mut().take().unwrap();
             let props: Box<dyn AnyProps + 'static> = unsafe { std::mem::transmute(props) };
-            self.scopes
-                .new_with_key(vcomponent.user_fc, props, Some(parent_idx), parent)
+            self.scopes.new_with_key(
+                vcomponent.user_fc,
+                props,
+                Some(parent_idx),
+                self.element_stack.last().copied().unwrap(),
+                0,
+            )
         };
 
         // Actually initialize the caller's slot with the right address
@@ -301,57 +250,19 @@ impl<'b> DiffState<'b> {
 
         self.enter_scope(new_idx);
 
-        // Run the scope for one iteration to initialize it
-        self.scopes.run_scope(new_idx);
-        self.mutations.mark_dirty_scope(new_idx);
-
-        // Take the node that was just generated from running the component
-        let nextnode = self.scopes.fin_head(new_idx);
-        self.create_node(parent, nextnode, nodes);
-
-        self.leave_scope();
-    }
-
-    pub(crate) fn create_template_ref_node(
-        &mut self,
-        parent: ElementId,
-        new: &'b VTemplateRef<'b>,
-        _node: &'b VNode<'b>,
-        nodes: &mut Vec<u64>,
-    ) {
-        let (id, just_created) = {
-            let mut resolver = self.scopes.template_resolver.borrow_mut();
-            resolver.get_or_create_client_id(&new.template_id, self.scopes)
-        };
+        let created = {
+            // Run the scope for one iteration to initialize it
+            self.scopes.run_scope(new_idx);
+            self.mutations.mark_dirty_scope(new_idx);
 
-        let template = {
-            let templates = self.scopes.templates.borrow();
-            templates.get(&new.template_id).unwrap().clone()
+            // Take the node that was just generated from running the component
+            let nextnode = self.scopes.fin_head(new_idx);
+            self.create_node(nextnode)
         };
-        let template = template.borrow();
-
-        if just_created {
-            self.register_template(&template, id);
-        }
-
-        new.template_ref_id
-            .set(Some(self.scopes.reserve_template_ref(new)));
-
-        let template_ref_id = new.template_ref_id.get().unwrap();
 
-        let root_nodes = template.root_nodes();
-        nodes.extend(root_nodes.iter().map(|node_id| {
-            let real_id = self.scopes.reserve_template_node(template_ref_id, *node_id);
-            new.set_node_id(*node_id, real_id);
-            real_id.as_u64()
-        }));
-
-        self.mutations.clone_node_children(
-            Some(id.as_u64()),
-            nodes[(nodes.len() - root_nodes.len())..].to_vec(),
-        );
+        self.leave_scope();
 
-        new.hydrate(parent, &template, self);
+        created
     }
 
     pub(crate) fn diff_text_nodes(
@@ -372,7 +283,7 @@ impl<'b> DiffState<'b> {
         };
 
         if old.text != new.text {
-            self.mutations.set_text(new.text, Some(root.as_u64()));
+            self.mutations.set_text(new.text, root);
         }
 
         self.scopes.update_node(new_node, root);
@@ -380,25 +291,110 @@ impl<'b> DiffState<'b> {
         new.id.set(Some(root));
     }
 
-    pub(crate) fn diff_placeholder_nodes(
+    fn diff_templates(
         &mut self,
-        old: &'b VPlaceholder,
-        new: &'b VPlaceholder,
-        _old_node: &'b VNode<'b>,
+        old: &'b VTemplate<'b>,
+        new: &'b VTemplate<'b>,
+        old_node: &'b VNode<'b>,
         new_node: &'b VNode<'b>,
     ) {
-        if std::ptr::eq(old, new) {
-            return;
+        if old.template.id == new.template.id {
+            // if they're the same, just diff the dynamic nodes directly
+            for (left, right) in old.dynamic_nodes.iter().zip(new.dynamic_nodes.iter()) {
+                self.diff_node(left, right);
+            }
+        } else {
+            // else, diff them manually, taking the slow path
+            self.replace_node(old_node, new_node);
         }
+    }
 
-        // if the node is comming back not assigned, that means it was borrowed but removed
-        let root = match old.id.get() {
-            Some(id) => id,
-            None => self.scopes.reserve_node(new_node),
-        };
+    /// Create the template from scratch using instructions, cache it, and then use the instructions to build it
+    fn create_template_node(
+        &mut self,
+        template: &'b VTemplate<'b>,
+        temp_node: &'b VNode<'b>,
+    ) -> usize {
+        /*
+        - Use a document fragment for holding nodes
+        - Assign IDs to any root nodes so we can find them later for shuffling around
+        - Build dynamic nodes in reverse order so indexing is preserved
+        */
 
-        self.scopes.update_node(new_node, root);
-        new.id.set(Some(root));
+        todo!()
+
+        // let def = template.template;
+
+        // let mut templates = self.scopes.template_cache.borrow_mut();
+
+        // if !templates.contains(&def) {
+        //     // self.create_template_def(def);
+        //     templates.insert(def);
+        // }
+
+        // let mut nodes_created = 0;
+        // let mut dynamic_nodes = template.dynamic_nodes.iter().enumerate().rev();
+
+        // for node in template.rendered_nodes {
+        //     match node {
+        //         // Give it an ID
+        //         crate::innerlude::TemplateRoots::Static(_) => todo!(),
+
+        //         // let the creation step give it an ID
+        //         crate::innerlude::TemplateRoots::Runtime(_) => todo!(),
+        //     }
+        // }
+
+        // // Create all the dynamic nodes and merge them into the template
+        // for (index, node) in dynamic_nodes {
+        //     let new_created = self.create_node(node);
+        //     self.mutations.edits.push(DomEdit::MergeTemplate {
+        //         index: index as u32,
+        //         num_children: new_created as u32,
+        //     })
+        // }
+
+        // nodes_created
+    }
+
+    fn create_template_static_node(&mut self, nodes: &'static [TemplateNode]) -> usize {
+        todo!()
+        // let mut created = 0;
+        // for node in nodes {
+        //     match *node {
+        //         TemplateNode::Element(el) => {
+        //             for attr in el.attributes {
+        //                 match attr {
+        //                     crate::template::TemplateAttribute::Dynamic => todo!(),
+        //                     crate::template::TemplateAttribute::Static { attr } => {
+        //                         self.mutations.set_attribute(attr, 0);
+        //                     }
+        //                 }
+        //             }
+
+        //             self.mutations.create_element(el.tag, None, ElementId(0));
+
+        //             if !nodes.is_empty() {
+        //                 let res = self.create_template_static_node(nodes);
+        //                 self.mutations.append_children(res as u32);
+        //             }
+
+        //             created += 1;
+        //         }
+        //         TemplateNode::StaticText(text) => {
+        //             self.mutations.create_text_node(text, ElementId(0));
+        //             created += 1;
+        //         }
+        //         TemplateNode::DynamicText(_)
+        //         | TemplateNode::DynamicExpr(_)
+        //         | TemplateNode::DynamicComponent(_) => {
+        //             self.mutations.create_placeholder(ElementId(0));
+        //             created += 1;
+        //         }
+        //     }
+        // }
+
+        // created
     }
 
     fn diff_element_nodes(
@@ -423,7 +419,7 @@ impl<'b> DiffState<'b> {
         //
         // This case is rather rare (typically only in non-keyed lists)
         if new.tag != old.tag || new.namespace != old.namespace {
-            self.replace_node(root, old_node, new_node);
+            self.replace_node(old_node, new_node);
             return;
         }
 
@@ -445,19 +441,16 @@ impl<'b> DiffState<'b> {
         // TODO: take a more efficient path than this
         if old.attributes.len() == new.attributes.len() {
             for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
-                if !old_attr.is_static && old_attr.value != new_attr.value
-                    || new_attr.attribute.volatile
-                {
-                    self.mutations.set_attribute(new_attr, Some(root.as_u64()));
+                if old_attr.value != new_attr.value || new_attr.volatile {
+                    self.mutations.set_attribute(new_attr, root);
                 }
             }
         } else {
             for attribute in old.attributes {
-                self.mutations
-                    .remove_attribute(attribute, Some(root.as_u64()));
+                self.mutations.remove_attribute(attribute, root);
             }
             for attribute in new.attributes {
-                self.mutations.set_attribute(attribute, Some(root.as_u64()));
+                self.mutations.set_attribute(attribute, root);
             }
         }
 
@@ -475,15 +468,13 @@ impl<'b> DiffState<'b> {
             for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                 new_l.mounted_node.set(old_l.mounted_node.get());
                 if old_l.event != new_l.event {
-                    self.mutations
-                        .remove_event_listener(old_l.event, Some(root.as_u64()));
+                    self.mutations.remove_event_listener(old_l.event, root);
                     self.mutations.new_event_listener(new_l, cur_scope_id);
                 }
             }
         } else {
             for listener in old.listeners {
-                self.mutations
-                    .remove_event_listener(listener.event, Some(root.as_u64()));
+                self.mutations.remove_event_listener(listener.event, root);
             }
             for listener in new.listeners {
                 listener.mounted_node.set(Some(root));
@@ -494,21 +485,21 @@ impl<'b> DiffState<'b> {
         match (old.children.len(), new.children.len()) {
             (0, 0) => {}
             (0, _) => {
-                let mut created = Vec::new();
-                self.create_children(root, new.children, &mut created);
-                self.mutations.append_children(Some(root.as_u64()), created);
+                self.mutations.push_root(root);
+                let created = self.create_children(new.children);
+                self.mutations.append_children(created as u32);
+                self.mutations.pop_root();
             }
-            (_, _) => self.diff_children(root, old.children, new.children),
+            (_, _) => self.diff_children(old.children, new.children),
         };
     }
 
     fn diff_component_nodes(
         &mut self,
-        parent: ElementId,
-        old: &'b VComponent<'b>,
-        new: &'b VComponent<'b>,
         old_node: &'b VNode<'b>,
         new_node: &'b VNode<'b>,
+        old: &'b VComponent<'b>,
+        new: &'b VComponent<'b>,
     ) {
         let scope_addr = old
             .scope
@@ -563,7 +554,6 @@ impl<'b> DiffState<'b> {
                     self.mutations.mark_dirty_scope(scope_addr);
 
                     self.diff_node(
-                        parent,
                         self.scopes.wip_head(scope_addr),
                         self.scopes.fin_head(scope_addr),
                     );
@@ -574,240 +564,29 @@ impl<'b> DiffState<'b> {
             }
             self.leave_scope();
         } else {
-            self.replace_node(parent, old_node, new_node);
+            self.replace_node(old_node, new_node);
         }
     }
 
-    fn diff_fragment_nodes(
-        &mut self,
-        parent: ElementId,
-        old: &'b VFragment<'b>,
-        new: &'b VFragment<'b>,
-    ) {
+    fn diff_fragment_nodes(&mut self, old: &'b VFragment<'b>, new: &'b VFragment<'b>) {
         if std::ptr::eq(old, new) {
             return;
         }
 
-        // This is the case where options or direct vnodes might be used.
-        // In this case, it's faster to just skip ahead to their diff
-        if old.children.len() == 1 && new.children.len() == 1 {
-            if !std::ptr::eq(old, new) {
-                self.diff_node(parent, &old.children[0], &new.children[0]);
-            }
-            return;
-        }
-
-        debug_assert!(!old.children.is_empty());
-        debug_assert!(!new.children.is_empty());
-
-        self.diff_children(parent, old.children, new.children);
-    }
-
-    #[allow(clippy::too_many_lines)]
-    fn diff_template_ref_nodes(
-        &mut self,
-        parent: ElementId,
-        old: &'b VTemplateRef<'b>,
-        new: &'b VTemplateRef<'b>,
-        old_node: &'b VNode<'b>,
-        new_node: &'b VNode<'b>,
-    ) {
-        fn diff_attributes<'b, Nodes, Attributes, V, Children, Listeners, TextSegments, Text>(
-            nodes: &Nodes,
-            ctx: (
-                &mut DiffState<'b>,
-                &'b Bump,
-                &'b VTemplateRef<'b>,
-                &Template,
-                usize,
-            ),
-        ) where
-            Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (diff_state, scope_bump, new, template, idx) = ctx;
-            for (node_id, attr_idx) in template.get_dynamic_nodes_for_attribute_index(idx) {
-                if let TemplateNodeType::Element(el) = &nodes.as_ref()[node_id.0].node_type {
-                    let TemplateElement { attributes, .. } = el;
-                    let attr = &attributes.as_ref()[*attr_idx];
-                    let attribute = Attribute {
-                        attribute: attr.attribute,
-                        value: new.dynamic_context.resolve_attribute(idx).clone(),
-                        is_static: false,
-                    };
-                    let real_id = new.get_node_id(*node_id);
-                    diff_state
-                        .mutations
-                        .set_attribute(scope_bump.alloc(attribute), Some(real_id.as_u64()));
-                } else {
-                    panic!("expected element node");
-                }
-            }
-        }
-
-        fn set_attribute<'b, Attributes, V, Children, Listeners, TextSegments, Text>(
-            node: &TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>,
-            ctx: (&mut DiffState<'b>, &'b Bump, &'b VTemplateRef<'b>, usize),
-        ) where
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (diff_state, scope_bump, new, template_attr_idx) = ctx;
-            if let TemplateNodeType::Element(el) = &node.node_type {
-                let TemplateElement { attributes, .. } = el;
-                let attr = &attributes.as_ref()[template_attr_idx];
-                let value = match &attr.value {
-                    TemplateAttributeValue::Dynamic(idx) => {
-                        new.dynamic_context.resolve_attribute(*idx).clone()
-                    }
-                    TemplateAttributeValue::Static(value) => value.allocate(scope_bump),
-                };
-                let attribute = Attribute {
-                    attribute: attr.attribute,
-                    value,
-                    is_static: false,
-                };
-                let real_id = new.get_node_id(node.id);
-                diff_state
-                    .mutations
-                    .set_attribute(scope_bump.alloc(attribute), Some(real_id.as_u64()));
-            } else {
-                panic!("expected element node");
-            }
-        }
-
-        fn diff_text<'b, Attributes, V, Children, Listeners, TextSegments, Text>(
-            node: &TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>,
-            ctx: (
-                &mut DiffState<'b>,
-                &'b VTemplateRef<'b>,
-                &TemplateContext<'b>,
-            ),
-        ) where
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (diff, new, dynamic_context) = ctx;
-            if let TemplateNodeType::Text(text) = &node.node_type {
-                let text = dynamic_context.resolve_text(text);
-                let real_id = new.get_node_id(node.id);
-                diff.mutations.set_text(
-                    diff.current_scope_bump().alloc(text),
-                    Some(real_id.as_u64()),
-                );
-            } else {
-                panic!("expected text node");
-            }
-        }
-
-        if std::ptr::eq(old, new) {
-            return;
-        }
-
-        // if the templates are different, just rebuild it
-        if old.template_id != new.template_id
-            || self
-                .scopes
-                .template_resolver
-                .borrow()
-                .is_dirty(&new.template_id)
-        {
-            self.replace_node(parent, old_node, new_node);
-            return;
-        }
-
-        if let Some(template_ref_id) = old.template_ref_id.get() {
-            self.scopes.update_template_ref(template_ref_id, new);
-            new.template_ref_id.set(Some(template_ref_id));
-        } else {
-            new.template_ref_id
-                .set(Some(self.scopes.reserve_template_ref(new)));
-        }
-        new.parent.set(Some(parent));
-        new.node_ids.replace(old.node_ids.take());
-
-        let scope_bump = &self.current_scope_bump();
-
-        let template = {
-            let templates = self.scopes.templates.borrow();
-            templates.get(&new.template_id).unwrap().clone()
-        };
-        let template = template.borrow();
-
-        // diff dynamic attributes
-        for (idx, (old_attr, new_attr)) in old
-            .dynamic_context
-            .attributes
-            .iter()
-            .zip(new.dynamic_context.attributes.iter())
-            .enumerate()
-        {
-            if old_attr != new_attr {
-                template.with_nodes(
-                    diff_attributes,
-                    diff_attributes,
-                    (self, scope_bump, new, &template, idx),
-                );
-            }
-        }
-
-        // set all volatile attributes
-        for (id, idx) in template.volatile_attributes() {
-            template.with_node(
-                id,
-                set_attribute,
-                set_attribute,
-                (self, scope_bump, new, idx),
-            );
-        }
+        todo!()
+        // // This is the case where options or direct vnodes might be used.
+        // // In this case, it's faster to just skip ahead to their diff
+        // if old.children.len() == 1 && new.children.len() == 1 {
+        //     if !std::ptr::eq(old, new) {
+        //         self.diff_node(&old.children[0], &new.children[0]);
+        //     }
+        //     return;
+        // }
 
-        // diff dynmaic nodes
-        for (old_node, new_node) in old
-            .dynamic_context
-            .nodes
-            .iter()
-            .zip(new.dynamic_context.nodes.iter())
-        {
-            self.diff_node(parent, old_node, new_node);
-        }
+        // debug_assert!(!old.children.is_empty());
+        // debug_assert!(!new.children.is_empty());
 
-        // diff dynamic text
-        // text nodes could rely on multiple dynamic text parts, so we keep a record of which ones to rerender and send the diffs at the end
-        let mut dirty_text_nodes = FxHashSet::default();
-        for (idx, (old_text, new_text)) in old
-            .dynamic_context
-            .text_segments
-            .iter()
-            .zip(new.dynamic_context.text_segments.iter())
-            .enumerate()
-        {
-            if old_text != new_text {
-                for node_id in template.get_dynamic_nodes_for_text_index(idx) {
-                    dirty_text_nodes.insert(*node_id);
-                }
-            }
-        }
-        for node_id in dirty_text_nodes {
-            template.with_node(
-                node_id,
-                diff_text,
-                diff_text,
-                (self, new, &new.dynamic_context),
-            );
-        }
+        // self.diff_children(old.children, new.children);
     }
 
     // Diff the given set of old and new children.
@@ -825,7 +604,7 @@ impl<'b> DiffState<'b> {
     //
     // Fragment nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
     // to an element, and appending makes sense.
-    fn diff_children(&mut self, parent: ElementId, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
+    fn diff_children(&mut self, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
         if std::ptr::eq(old, new) {
             return;
         }
@@ -833,7 +612,7 @@ impl<'b> DiffState<'b> {
         // Remember, fragments can never be empty (they always have a single child)
         match (old, new) {
             ([], []) => {}
-            ([], _) => self.create_and_append_children(parent, new),
+            ([], _) => self.create_and_append_children(new),
             (_, []) => self.remove_nodes(old, true),
             _ => {
                 let new_is_keyed = new[0].key().is_some();
@@ -849,9 +628,9 @@ impl<'b> DiffState<'b> {
                 );
 
                 if new_is_keyed && old_is_keyed {
-                    self.diff_keyed_children(parent, old, new);
+                    self.diff_keyed_children(old, new);
                 } else {
-                    self.diff_non_keyed_children(parent, old, new);
+                    self.diff_non_keyed_children(old, new);
                 }
             }
         }
@@ -865,12 +644,7 @@ impl<'b> DiffState<'b> {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_non_keyed_children(
-        &mut self,
-        parent: ElementId,
-        old: &'b [VNode<'b>],
-        new: &'b [VNode<'b>],
-    ) {
+    fn diff_non_keyed_children(&mut self, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
         use std::cmp::Ordering;
 
         // Handled these cases in `diff_children` before calling this function.
@@ -879,14 +653,12 @@ impl<'b> DiffState<'b> {
 
         match old.len().cmp(&new.len()) {
             Ordering::Greater => self.remove_nodes(&old[new.len()..], true),
-            Ordering::Less => {
-                self.create_and_insert_after(parent, &new[old.len()..], old.last().unwrap());
-            }
+            Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()),
             Ordering::Equal => {}
         }
 
         for (new, old) in new.iter().zip(old.iter()) {
-            self.diff_node(parent, old, new);
+            self.diff_node(old, new);
         }
     }
 
@@ -906,14 +678,9 @@ impl<'b> DiffState<'b> {
     // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
     //
     // The stack is empty upon entry.
-    fn diff_keyed_children(
-        &mut self,
-        parent: ElementId,
-        old: &'b [VNode<'b>],
-        new: &'b [VNode<'b>],
-    ) {
+    fn diff_keyed_children(&mut self, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
         if cfg!(debug_assertions) {
-            let mut keys = rustc_hash::FxHashSet::default();
+            let mut keys = fxhash::FxHashSet::default();
             let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
                 keys.clear();
                 for child in children {
@@ -939,7 +706,7 @@ impl<'b> DiffState<'b> {
         //
         // `shared_prefix_count` is the count of how many nodes at the start of
         // `new` and `old` share the same keys.
-        let (left_offset, right_offset) = match self.diff_keyed_ends(parent, old, new) {
+        let (left_offset, right_offset) = match self.diff_keyed_ends(old, new) {
             Some(count) => count,
             None => return,
         };
@@ -965,18 +732,18 @@ impl<'b> DiffState<'b> {
             if left_offset == 0 {
                 // insert at the beginning of the old list
                 let foothold = &old[old.len() - right_offset];
-                self.create_and_insert_before(parent, new_middle, foothold);
+                self.create_and_insert_before(new_middle, foothold);
             } else if right_offset == 0 {
                 // insert at the end  the old list
                 let foothold = old.last().unwrap();
-                self.create_and_insert_after(parent, new_middle, foothold);
+                self.create_and_insert_after(new_middle, foothold);
             } else {
                 // inserting in the middle
                 let foothold = &old[left_offset - 1];
-                self.create_and_insert_after(parent, new_middle, foothold);
+                self.create_and_insert_after(new_middle, foothold);
             }
         } else {
-            self.diff_keyed_middle(parent, old_middle, new_middle);
+            self.diff_keyed_middle(old_middle, new_middle);
         }
     }
 
@@ -987,7 +754,6 @@ impl<'b> DiffState<'b> {
     /// If there is no offset, then this function returns None and the diffing is complete.
     fn diff_keyed_ends(
         &mut self,
-        parent: ElementId,
         old: &'b [VNode<'b>],
         new: &'b [VNode<'b>],
     ) -> Option<(usize, usize)> {
@@ -998,14 +764,14 @@ impl<'b> DiffState<'b> {
             if old.key() != new.key() {
                 break;
             }
-            self.diff_node(parent, old, new);
+            self.diff_node(old, new);
             left_offset += 1;
         }
 
         // If that was all of the old children, then create and append the remaining
         // new children and we're finished.
         if left_offset == old.len() {
-            self.create_and_insert_after(parent, &new[left_offset..], old.last().unwrap());
+            self.create_and_insert_after(&new[left_offset..], old.last().unwrap());
             return None;
         }
 
@@ -1023,7 +789,7 @@ impl<'b> DiffState<'b> {
             if old.key() != new.key() {
                 break;
             }
-            self.diff_node(parent, old, new);
+            self.diff_node(old, new);
             right_offset += 1;
         }
 
@@ -1044,7 +810,7 @@ impl<'b> DiffState<'b> {
     //
     // Upon exit from this function, it will be restored to that same self.
     #[allow(clippy::too_many_lines)]
-    fn diff_keyed_middle(&mut self, parent: ElementId, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
+    fn diff_keyed_middle(&mut self, old: &'b [VNode<'b>], new: &'b [VNode<'b>]) {
         /*
         1. Map the old keys into a numerical ordering based on indices.
         2. Create a map of old key to its index
@@ -1102,13 +868,12 @@ impl<'b> DiffState<'b> {
         if shared_keys.is_empty() {
             if let Some(first_old) = old.get(0) {
                 self.remove_nodes(&old[1..], true);
-                let mut nodes_created = Vec::new();
-                self.create_children(parent, new, &mut nodes_created);
+                let nodes_created = self.create_children(new);
                 self.replace_inner(first_old, nodes_created);
             } else {
                 // I think this is wrong - why are we appending?
                 // only valid of the if there are no trailing elements
-                self.create_and_append_children(parent, new);
+                self.create_and_append_children(new);
             }
             return;
         }
@@ -1146,10 +911,10 @@ impl<'b> DiffState<'b> {
         }
 
         for idx in &lis_sequence {
-            self.diff_node(parent, &old[new_index_to_old_index[*idx]], &new[*idx]);
+            self.diff_node(&old[new_index_to_old_index[*idx]], &new[*idx]);
         }
 
-        let mut nodes_created = Vec::new();
+        let mut nodes_created = 0;
 
         // add mount instruction for the first items not covered by the lis
         let last = *lis_sequence.last().unwrap();
@@ -1158,16 +923,18 @@ impl<'b> DiffState<'b> {
                 let new_idx = idx + last + 1;
                 let old_index = new_index_to_old_index[new_idx];
                 if old_index == u32::MAX as usize {
-                    self.create_node(parent, new_node, &mut nodes_created);
+                    nodes_created += self.create_node(new_node);
                 } else {
-                    self.diff_node(parent, &old[old_index], new_node);
-                    self.get_all_real_nodes(new_node, &mut nodes_created);
+                    self.diff_node(&old[old_index], new_node);
+                    nodes_created += self.push_all_real_nodes(new_node);
                 }
             }
 
-            let last = Some(self.find_last_element(&new[last]).unwrap().as_u64());
-            self.mutations.insert_after(last, nodes_created);
-            nodes_created = Vec::new();
+            self.mutations.insert_after(
+                self.find_last_element(&new[last]).unwrap(),
+                nodes_created as u32,
+            );
+            nodes_created = 0;
         }
 
         // for each spacing, generate a mount instruction
@@ -1179,17 +946,19 @@ impl<'b> DiffState<'b> {
                     let new_idx = idx + next + 1;
                     let old_index = new_index_to_old_index[new_idx];
                     if old_index == u32::MAX as usize {
-                        self.create_node(parent, new_node, &mut nodes_created);
+                        nodes_created += self.create_node(new_node);
                     } else {
-                        self.diff_node(parent, &old[old_index], new_node);
-                        self.get_all_real_nodes(new_node, &mut nodes_created);
+                        self.diff_node(&old[old_index], new_node);
+                        nodes_created += self.push_all_real_nodes(new_node);
                     }
                 }
 
-                let first = Some(self.find_first_element(&new[last]).unwrap().as_u64());
-                self.mutations.insert_before(first, nodes_created);
+                self.mutations.insert_before(
+                    self.find_first_element(&new[last]).unwrap(),
+                    nodes_created as u32,
+                );
 
-                nodes_created = Vec::new();
+                nodes_created = 0;
             }
             last = *next;
         }
@@ -1200,44 +969,43 @@ impl<'b> DiffState<'b> {
             for (idx, new_node) in new[..first_lis].iter().enumerate() {
                 let old_index = new_index_to_old_index[idx];
                 if old_index == u32::MAX as usize {
-                    self.create_node(parent, new_node, &mut nodes_created);
+                    nodes_created += self.create_node(new_node);
                 } else {
-                    self.diff_node(parent, &old[old_index], new_node);
-                    self.get_all_real_nodes(new_node, &mut nodes_created);
+                    self.diff_node(&old[old_index], new_node);
+                    nodes_created += self.push_all_real_nodes(new_node);
                 }
             }
 
-            let first = Some(self.find_first_element(&new[first_lis]).unwrap().as_u64());
-            self.mutations.insert_before(first, nodes_created);
+            self.mutations.insert_before(
+                self.find_first_element(&new[first_lis]).unwrap(),
+                nodes_created as u32,
+            );
         }
     }
 
-    pub fn replace_node(&mut self, parent: ElementId, old: &'b VNode<'b>, new: &'b VNode<'b>) {
-        let mut nodes_vec = Vec::new();
-        self.create_node(parent, new, &mut nodes_vec);
-        self.replace_inner(old, nodes_vec);
+    fn replace_node(&mut self, old: &'b VNode<'b>, new: &'b VNode<'b>) {
+        let nodes_created = self.create_node(new);
+        self.replace_inner(old, nodes_created);
     }
 
-    fn replace_inner(&mut self, old: &'b VNode<'b>, nodes_created: Vec<u64>) {
+    fn replace_inner(&mut self, old: &'b VNode<'b>, nodes_created: usize) {
         match old {
             VNode::Element(el) => {
                 let id = old
                     .try_mounted_id()
                     .unwrap_or_else(|| panic!("broke on {:?}", old));
 
-                self.mutations
-                    .replace_with(Some(id.as_u64()), nodes_created);
+                self.mutations.replace_with(id, nodes_created as u32);
                 self.remove_nodes(el.children, false);
                 self.scopes.collect_garbage(id);
             }
 
-            VNode::Text(_) | VNode::Placeholder(_) => {
+            VNode::Text(_) => {
                 let id = old
                     .try_mounted_id()
                     .unwrap_or_else(|| panic!("broke on {:?}", old));
 
-                self.mutations
-                    .replace_with(Some(id.as_u64()), nodes_created);
+                self.mutations.replace_with(id, nodes_created as u32);
                 self.scopes.collect_garbage(id);
             }
 
@@ -1245,50 +1013,34 @@ impl<'b> DiffState<'b> {
                 self.replace_inner(&f.children[0], nodes_created);
                 self.remove_nodes(f.children.iter().skip(1), true);
             }
-
             VNode::Component(c) => {
-                log::trace!("Replacing component {:?}", old);
                 let scope_id = c.scope.get().unwrap();
                 let node = self.scopes.fin_head(scope_id);
 
                 self.enter_scope(scope_id);
                 {
                     self.replace_inner(node, nodes_created);
-
-                    log::trace!("Replacing component x2 {:?}", old);
-
                     let scope = self.scopes.get_scope(scope_id).unwrap();
                     c.scope.set(None);
                     let props = scope.props.take().unwrap();
                     c.props.borrow_mut().replace(props);
                     self.scopes.try_remove(scope_id);
                 }
+
                 self.leave_scope();
             }
-
-            VNode::TemplateRef(template_ref) => {
-                let templates = self.scopes.templates.borrow();
-                let template = templates.get(&template_ref.template_id).unwrap().borrow();
-                let mut root_iter = template.root_nodes().iter();
-                let first_real_id = template_ref.get_node_id(*root_iter.next().unwrap());
-                self.mutations
-                    .replace_with(Some(first_real_id.as_u64()), nodes_created);
-                for id in root_iter {
-                    let real_id = template_ref.get_node_id(*id);
-                    self.mutations.remove(Some(real_id.as_u64()));
-                }
-
-                self.remove_nodes(template_ref.dynamic_context.nodes, true);
-
-                if let Some(id) = template_ref.template_ref_id.get() {
-                    self.scopes.template_refs.borrow_mut().remove(id.0);
-                }
-
-                for id in template_ref.node_ids.borrow().iter() {
-                    if let Some(id) = id.get() {
-                        self.scopes.collect_garbage(*id);
-                    }
-                }
+            VNode::Template(c) => {
+                todo!()
+                // // let ids = c.root_keys.as_slice_of_cells();
+
+                // self.mutations
+                //     .replace_with(ids[0].get(), nodes_created as u32);
+                // self.scopes.collect_garbage(ids[0].get());
+
+                // for id in ids.iter().skip(1) {
+                //     self.mutations.remove(id.get());
+                //     self.scopes.collect_garbage(id.get());
+                // }
             }
         }
     }
@@ -1303,24 +1055,24 @@ impl<'b> DiffState<'b> {
                         t.id.set(None);
 
                         if gen_muts {
-                            self.mutations.remove(Some(id.as_u64()));
+                            self.mutations.remove(id);
                         }
                     }
                 }
-                VNode::Placeholder(a) => {
-                    let id = a.id.get().unwrap();
-                    self.scopes.collect_garbage(id);
-                    a.id.set(None);
-
-                    if gen_muts {
-                        self.mutations.remove(Some(id.as_u64()));
-                    }
-                }
+                // VNode::Placeholder(a) => {
+                //     let id = a.id.get().unwrap();
+                //     self.scopes.collect_garbage(id);
+                //     a.id.set(None);
+
+                //     if gen_muts {
+                //         self.mutations.remove(id);
+                //     }
+                // }
                 VNode::Element(e) => {
                     let id = e.id.get().unwrap();
 
                     if gen_muts {
-                        self.mutations.remove(Some(id.as_u64()));
+                        self.mutations.remove(id);
                     }
 
                     self.scopes.collect_garbage(id);
@@ -1332,7 +1084,6 @@ impl<'b> DiffState<'b> {
                 VNode::Fragment(f) => {
                     self.remove_nodes(f.children, gen_muts);
                 }
-
                 VNode::Component(c) => {
                     self.enter_scope(c.scope.get().unwrap());
                     {
@@ -1350,77 +1101,37 @@ impl<'b> DiffState<'b> {
                     self.leave_scope();
                 }
 
-                VNode::TemplateRef(template_ref) => {
-                    let templates = self.scopes.templates.borrow();
-                    let template = templates.get(&template_ref.template_id).unwrap().borrow();
-                    if gen_muts {
-                        for id in template.root_nodes() {
-                            let real_id = template_ref.get_node_id(*id);
-                            self.mutations.remove(Some(real_id.as_u64()));
-                        }
-                    }
-
-                    self.remove_nodes(template_ref.dynamic_context.nodes, gen_muts);
-
-                    if let Some(id) = template_ref.template_ref_id.get() {
-                        self.scopes.template_refs.borrow_mut().remove(id.0);
-                    }
-
-                    for id in template_ref.node_ids.borrow().iter() {
-                        if let Some(id) = id.get() {
-                            self.scopes.collect_garbage(*id);
-                        }
-                    }
-                }
+                VNode::Template(c) => {}
             }
         }
     }
 
-    fn create_children(
-        &mut self,
-        parent: ElementId,
-        nodes: &'b [VNode<'b>],
-        nodes_vec: &mut Vec<u64>,
-    ) {
-        nodes_vec.reserve(nodes.len());
+    fn create_children(&mut self, nodes: &'b [VNode<'b>]) -> usize {
+        let mut created = 0;
         for node in nodes {
-            self.create_node(parent, node, nodes_vec);
+            created += self.create_node(node);
         }
+        created
     }
 
-    fn create_and_append_children(&mut self, parent: ElementId, nodes: &'b [VNode<'b>]) {
-        let mut nodes_vec = Vec::with_capacity(nodes.len());
-        self.create_children(parent, nodes, &mut nodes_vec);
-        self.mutations
-            .append_children(Some(parent.as_u64()), nodes_vec);
+    fn create_and_append_children(&mut self, nodes: &'b [VNode<'b>]) {
+        let created = self.create_children(nodes);
+        self.mutations.append_children(created as u32);
     }
 
-    fn create_and_insert_after(
-        &mut self,
-        parent: ElementId,
-        nodes: &'b [VNode<'b>],
-        after: &'b VNode<'b>,
-    ) {
-        let mut nodes_vec = Vec::with_capacity(nodes.len());
-        self.create_children(parent, nodes, &mut nodes_vec);
+    fn create_and_insert_after(&mut self, nodes: &'b [VNode<'b>], after: &'b VNode<'b>) {
+        let created = self.create_children(nodes);
         let last = self.find_last_element(after).unwrap();
-        self.mutations.insert_after(Some(last.as_u64()), nodes_vec);
+        self.mutations.insert_after(last, created as u32);
     }
 
-    fn create_and_insert_before(
-        &mut self,
-        parent: ElementId,
-        nodes: &'b [VNode<'b>],
-        before: &'b VNode<'b>,
-    ) {
-        let mut nodes_vec = Vec::with_capacity(nodes.len());
-        self.create_children(parent, nodes, &mut nodes_vec);
+    fn create_and_insert_before(&mut self, nodes: &'b [VNode<'b>], before: &'b VNode<'b>) {
+        let created = self.create_children(nodes);
         let first = self.find_first_element(before).unwrap();
-        self.mutations
-            .insert_before(Some(first.as_u64()), nodes_vec);
+        self.mutations.insert_before(first, created as u32);
     }
 
-    pub fn current_scope(&self) -> ScopeId {
+    fn current_scope(&self) -> ScopeId {
         self.scope_stack.last().copied().expect("no current scope")
     }
 
@@ -1432,95 +1143,66 @@ impl<'b> DiffState<'b> {
         self.scope_stack.pop();
     }
 
-    fn find_last_element(&mut self, vnode: &'b VNode<'b>) -> Option<ElementId> {
+    fn find_last_element(&self, vnode: &'b VNode<'b>) -> Option<ElementId> {
         let mut search_node = Some(vnode);
         loop {
             match &search_node.take().unwrap() {
                 VNode::Text(t) => break t.id.get(),
                 VNode::Element(t) => break t.id.get(),
-                VNode::Placeholder(t) => break t.id.get(),
                 VNode::Fragment(frag) => search_node = frag.children.last(),
                 VNode::Component(el) => {
                     let scope_id = el.scope.get().unwrap();
                     search_node = Some(self.scopes.root_node(scope_id));
                 }
-                VNode::TemplateRef(t) => {
-                    let templates = self.scopes.templates.borrow();
-                    let template = templates.get(&t.template_id).unwrap();
-                    let template = template.borrow();
-                    break template.root_nodes().last().map(|id| t.get_node_id(*id));
+                VNode::Template(c) => {
+                    todo!()
                 }
             }
         }
     }
 
-    fn find_first_element(&mut self, vnode: &'b VNode<'b>) -> Option<ElementId> {
+    fn find_first_element(&self, vnode: &'b VNode<'b>) -> Option<ElementId> {
         let mut search_node = Some(vnode);
         loop {
             match &search_node.take().expect("search node to have an ID") {
                 VNode::Text(t) => break t.id.get(),
                 VNode::Element(t) => break t.id.get(),
-                VNode::Placeholder(t) => break t.id.get(),
                 VNode::Fragment(frag) => search_node = Some(&frag.children[0]),
                 VNode::Component(el) => {
                     let scope = el.scope.get().expect("element to have a scope assigned");
                     search_node = Some(self.scopes.root_node(scope));
                 }
-                VNode::TemplateRef(t) => {
-                    let templates = self.scopes.templates.borrow();
-                    let template = templates.get(&t.template_id).unwrap();
-                    let template = template.borrow();
-                    break template.root_nodes().first().map(|id| t.get_node_id(*id));
+                VNode::Template(t) => {
+                    todo!()
                 }
             }
         }
     }
 
     // recursively push all the nodes of a tree onto the stack and return how many are there
-    fn get_all_real_nodes(&mut self, node: &'b VNode<'b>, nodes: &mut Vec<u64>) {
+    fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
         match node {
-            VNode::Text(_) | VNode::Placeholder(_) | VNode::Element(_) => {
-                nodes.push(node.mounted_id().0 as u64);
-            }
-
-            VNode::TemplateRef(template_ref) => {
-                let templates = self.scopes.templates.borrow();
-                let template = templates.get(&template_ref.template_id).unwrap();
-                let template = template.borrow();
-                nodes.extend(
-                    template
-                        .root_nodes()
-                        .iter()
-                        .map(|id| template_ref.get_node_id(*id).as_u64()),
-                );
+            VNode::Text(_) | VNode::Element(_) => {
+                self.mutations.push_root(node.mounted_id());
+                1
             }
 
             VNode::Fragment(frag) => {
-                nodes.reserve(frag.children.len());
+                let mut added = 0;
                 for child in frag.children {
-                    self.get_all_real_nodes(child, nodes);
+                    added += self.push_all_real_nodes(child);
                 }
+                added
             }
-
             VNode::Component(c) => {
                 let scope_id = c.scope.get().unwrap();
                 let root = self.scopes.root_node(scope_id);
-                self.get_all_real_nodes(root, nodes);
+                self.push_all_real_nodes(root)
             }
-        }
-    }
-
-    pub(crate) fn current_scope_bump(&self) -> &'b Bump {
-        &self
-            .scopes
-            .get_scope(self.current_scope())
-            .unwrap()
-            .fin_frame()
-            .bump
-    }
 
-    pub fn register_template(&mut self, template: &Template, id: ElementId) {
-        let bump = &self.scopes.template_bump;
-        template.create(&mut self.mutations, bump, id);
+            VNode::Template(c) => {
+                todo!()
+            }
+        }
     }
 }

+ 0 - 190
packages/core/src/dynamic_template_context.rs

@@ -1,190 +0,0 @@
-use std::{fmt::Write, marker::PhantomData, ops::Deref};
-
-use once_cell::sync::Lazy;
-
-use crate::{
-    template::{TemplateNodeId, TextTemplateSegment},
-    AttributeValue, Listener, TextTemplate, VNode,
-};
-
-/// A lazily initailized vector
-#[derive(Debug, Clone, Copy)]
-pub struct LazyStaticVec<T: 'static>(pub &'static Lazy<Vec<T>>);
-
-impl<T: 'static> AsRef<[T]> for LazyStaticVec<T> {
-    fn as_ref(&self) -> &[T] {
-        let v: &Vec<_> = self.0.deref();
-        v.as_ref()
-    }
-}
-
-impl<T> PartialEq for LazyStaticVec<T> {
-    fn eq(&self, other: &Self) -> bool {
-        std::ptr::eq(self.0, other.0)
-    }
-}
-
-/// Stores what nodes depend on specific dynamic parts of the template to allow the diffing algorithm to jump to that part of the template instead of travering it
-/// This makes adding constant template nodes add no additional cost to diffing.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-pub struct DynamicNodeMapping<
-    Nodes,
-    TextOuter,
-    TextInner,
-    AttributesOuter,
-    AttributesInner,
-    Volatile,
-    Listeners,
-> where
-    Nodes: AsRef<[Option<TemplateNodeId>]>,
-    TextOuter: AsRef<[TextInner]>,
-    TextInner: AsRef<[TemplateNodeId]>,
-    AttributesOuter: AsRef<[AttributesInner]>,
-    AttributesInner: AsRef<[(TemplateNodeId, usize)]>,
-    Volatile: AsRef<[(TemplateNodeId, usize)]>,
-    Listeners: AsRef<[TemplateNodeId]>,
-{
-    /// The node that depend on each node in the dynamic template
-    pub nodes: Nodes,
-    text_inner: PhantomData<TextInner>,
-    /// The text nodes that depend on each text segment of the dynamic template
-    pub text: TextOuter,
-    /// The attributes along with the attribute index in the template that depend on each attribute of the dynamic template
-    pub attributes: AttributesOuter,
-    attributes_inner: PhantomData<AttributesInner>,
-    /// The attributes that are marked as volatile in the template
-    pub volatile_attributes: Volatile,
-    /// The listeners that depend on each listener of the dynamic template
-    pub nodes_with_listeners: Listeners,
-}
-
-impl<Nodes, TextOuter, TextInner, AttributesOuter, AttributesInner, Volatile, Listeners>
-    DynamicNodeMapping<
-        Nodes,
-        TextOuter,
-        TextInner,
-        AttributesOuter,
-        AttributesInner,
-        Volatile,
-        Listeners,
-    >
-where
-    Nodes: AsRef<[Option<TemplateNodeId>]>,
-    TextOuter: AsRef<[TextInner]>,
-    TextInner: AsRef<[TemplateNodeId]>,
-    AttributesOuter: AsRef<[AttributesInner]>,
-    AttributesInner: AsRef<[(TemplateNodeId, usize)]>,
-    Volatile: AsRef<[(TemplateNodeId, usize)]>,
-    Listeners: AsRef<[TemplateNodeId]>,
-{
-    /// Creates a new dynamic node mapping
-    pub const fn new(
-        nodes: Nodes,
-        text: TextOuter,
-        attributes: AttributesOuter,
-        volatile_attributes: Volatile,
-        listeners: Listeners,
-    ) -> Self {
-        DynamicNodeMapping {
-            nodes,
-            text_inner: PhantomData,
-            text,
-            attributes,
-            attributes_inner: PhantomData,
-            volatile_attributes,
-            nodes_with_listeners: listeners,
-        }
-    }
-}
-
-/// A dynamic node mapping that is stack allocated
-pub type StaticDynamicNodeMapping = DynamicNodeMapping<
-    &'static [Option<TemplateNodeId>],
-    &'static [&'static [TemplateNodeId]],
-    &'static [TemplateNodeId],
-    &'static [&'static [(TemplateNodeId, usize)]],
-    &'static [(TemplateNodeId, usize)],
-    // volatile attribute information is available at compile time, but there is no way for the macro to generate it, so we initialize it lazily instead
-    LazyStaticVec<(TemplateNodeId, usize)>,
-    &'static [TemplateNodeId],
->;
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A dynamic node mapping that is heap allocated
-pub type OwnedDynamicNodeMapping = DynamicNodeMapping<
-    Vec<Option<TemplateNodeId>>,
-    Vec<Vec<TemplateNodeId>>,
-    Vec<TemplateNodeId>,
-    Vec<Vec<(TemplateNodeId, usize)>>,
-    Vec<(TemplateNodeId, usize)>,
-    Vec<(TemplateNodeId, usize)>,
-    Vec<TemplateNodeId>,
->;
-
-/// The dynamic parts used to saturate a template durring runtime
-pub struct TemplateContext<'b> {
-    /// The dynamic nodes
-    pub nodes: &'b [VNode<'b>],
-    /// The dynamic text
-    pub text_segments: &'b [&'b str],
-    /// The dynamic attributes
-    pub attributes: &'b [AttributeValue<'b>],
-    /// The dynamic attributes
-    // The listeners must not change during the lifetime of the context, use a dynamic node if the listeners change
-    pub listeners: &'b [Listener<'b>],
-    /// A optional key for diffing
-    pub key: Option<&'b str>,
-}
-
-impl<'b> TemplateContext<'b> {
-    /// Resolve text segments to a string
-    pub fn resolve_text<TextSegments, Text>(
-        &self,
-        text: &TextTemplate<TextSegments, Text>,
-    ) -> String
-    where
-        TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-        Text: AsRef<str>,
-    {
-        let mut result = String::with_capacity(text.min_size);
-        self.resolve_text_into(text, &mut result);
-        result
-    }
-
-    /// Resolve text and writes the result
-    pub fn resolve_text_into<TextSegments, Text>(
-        &self,
-        text: &TextTemplate<TextSegments, Text>,
-        result: &mut impl Write,
-    ) where
-        TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-        Text: AsRef<str>,
-    {
-        for seg in text.segments.as_ref() {
-            match seg {
-                TextTemplateSegment::Static(s) => {
-                    let _ = result.write_str(s.as_ref());
-                }
-                TextTemplateSegment::Dynamic(idx) => {
-                    let _ = result.write_str(self.text_segments[*idx]);
-                }
-            }
-        }
-    }
-
-    /// Resolve an attribute value
-    pub fn resolve_attribute(&self, idx: usize) -> &'b AttributeValue<'b> {
-        &self.attributes[idx]
-    }
-
-    /// Resolve a listener
-    pub fn resolve_listener(&self, idx: usize) -> &'b Listener<'b> {
-        &self.listeners[idx]
-    }
-
-    /// Resolve a node
-    pub fn resolve_node(&self, idx: usize) -> &'b VNode<'b> {
-        &self.nodes[idx]
-    }
-}

+ 69 - 69
packages/core/src/lazynodes.rs

@@ -251,72 +251,72 @@ fn round_to_words(len: usize) -> usize {
     (len + mem::size_of::<usize>() - 1) / mem::size_of::<usize>()
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::innerlude::{Element, Scope, VirtualDom};
-
-    #[test]
-    fn it_works() {
-        fn app(cx: Scope<()>) -> Element {
-            cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
-        }
-
-        let mut dom = VirtualDom::new(app);
-        dom.rebuild();
-
-        let g = dom.base_scope().root_node();
-        dbg!(g);
-    }
-
-    #[test]
-    fn it_drops() {
-        use std::rc::Rc;
-
-        struct AppProps {
-            inner: Rc<i32>,
-        }
-
-        fn app(cx: Scope<AppProps>) -> Element {
-            struct DropInner {
-                id: i32,
-            }
-            impl Drop for DropInner {
-                fn drop(&mut self) {
-                    eprintln!("dropping inner");
-                }
-            }
-
-            let caller = {
-                let it = (0..10).map(|i| {
-                    let val = cx.props.inner.clone();
-                    LazyNodes::new(move |f| {
-                        eprintln!("hell closure");
-                        let inner = DropInner { id: i };
-                        f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
-                    })
-                });
-
-                LazyNodes::new(|f| {
-                    eprintln!("main closure");
-                    f.fragment_from_iter(it)
-                })
-            };
-
-            cx.render(caller)
-        }
-
-        let inner = Rc::new(0);
-        let mut dom = VirtualDom::new_with_props(
-            app,
-            AppProps {
-                inner: inner.clone(),
-            },
-        );
-        dom.rebuild();
-
-        drop(dom);
-
-        assert_eq!(Rc::strong_count(&inner), 1);
-    }
-}
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use crate::innerlude::{Element, Scope, VirtualDom};
+
+//     #[test]
+//     fn it_works() {
+//         fn app(cx: Scope<()>) -> Element {
+//             cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
+//         }
+
+//         let mut dom = VirtualDom::new(app);
+//         dom.rebuild();
+
+//         let g = dom.base_scope().root_node();
+//         dbg!(g);
+//     }
+
+//     #[test]
+//     fn it_drops() {
+//         use std::rc::Rc;
+
+//         struct AppProps {
+//             inner: Rc<i32>,
+//         }
+
+//         fn app(cx: Scope<AppProps>) -> Element {
+//             struct DropInner {
+//                 id: i32,
+//             }
+//             impl Drop for DropInner {
+//                 fn drop(&mut self) {
+//                     eprintln!("dropping inner");
+//                 }
+//             }
+
+//             let caller = {
+//                 let it = (0..10).map(|i| {
+//                     let val = cx.props.inner.clone();
+//                     LazyNodes::new(move |f| {
+//                         eprintln!("hell closure");
+//                         let inner = DropInner { id: i };
+//                         f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
+//                     })
+//                 });
+
+//                 LazyNodes::new(|f| {
+//                     eprintln!("main closure");
+//                     f.fragment_from_iter(it)
+//                 })
+//             };
+
+//             cx.render(caller)
+//         }
+
+//         let inner = Rc::new(0);
+//         let mut dom = VirtualDom::new_with_props(
+//             app,
+//             AppProps {
+//                 inner: inner.clone(),
+//             },
+//         );
+//         dom.rebuild();
+
+//         drop(dom);
+
+//         assert_eq!(Rc::strong_count(&inner), 1);
+//     }
+// }

+ 24 - 36
packages/core/src/lib.rs

@@ -1,31 +1,23 @@
 #![allow(non_snake_case)]
 #![doc = include_str!("../README.md")]
-#![deny(missing_docs)]
+#![warn(missing_docs)]
 
-pub(crate) mod arbitrary_value;
 pub(crate) mod diff;
-pub(crate) mod dynamic_template_context;
 pub(crate) mod events;
 pub(crate) mod lazynodes;
 pub(crate) mod mutations;
 pub(crate) mod nodes;
 pub(crate) mod properties;
 pub(crate) mod scopes;
-pub(crate) mod template;
-pub(crate) mod util;
 pub(crate) mod virtual_dom;
 
 pub(crate) mod innerlude {
-    pub use crate::arbitrary_value::*;
-    pub use crate::dynamic_template_context::*;
     pub use crate::events::*;
     pub use crate::lazynodes::*;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::properties::*;
     pub use crate::scopes::*;
-    pub use crate::template::*;
-    pub use crate::util::*;
     pub use crate::virtual_dom::*;
 
     /// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
@@ -67,38 +59,19 @@ pub(crate) mod innerlude {
 }
 
 pub use crate::innerlude::{
-    AnyEvent, ArbitraryAttributeValue, Attribute, AttributeDiscription, AttributeValue,
-    CodeLocation, Component, DioxusElement, DomEdit, DynamicNodeMapping, Element, ElementId,
-    ElementIdIterator, EventHandler, EventPriority, IntoAttributeValue, IntoVNode, LazyNodes,
-    Listener, Mutations, NodeFactory, OwnedAttributeValue, PathSeg, Properties, RendererTemplateId,
-    SchedulerMsg, Scope, ScopeId, ScopeState, StaticCodeLocation, StaticDynamicNodeMapping,
-    StaticPathSeg, StaticTemplateNode, StaticTemplateNodes, StaticTraverse, TaskId, Template,
-    TemplateAttribute, TemplateAttributeValue, TemplateContext, TemplateElement, TemplateId,
-    TemplateNode, TemplateNodeId, TemplateNodeType, TemplateValue, TextTemplate,
-    TextTemplateSegment, UiEvent, UpdateOp, UserEvent, VComponent, VElement, VFragment, VNode,
-    VPlaceholder, VText, VirtualDom,
-};
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-pub use crate::innerlude::{
-    OwnedCodeLocation, OwnedDynamicNodeMapping, OwnedPathSeg, OwnedTemplateNode,
-    OwnedTemplateNodes, OwnedTraverse, SetTemplateMsg,
+    AnyEvent, ArbitraryAttributeValue, Attribute, AttributeValue, Component, Element, ElementId,
+    EventHandler, EventPriority, IntoVNode, LazyNodes, Listener, NodeFactory, Properties, Renderer,
+    SchedulerMsg, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode,
+    UiEvent, UserEvent, VComponent, VElement, VNode, VTemplate, VText, VirtualDom,
 };
 
 /// The purpose of this module is to alleviate imports of many common types
 ///
 /// This includes types like [`Scope`], [`Element`], and [`Component`].
 pub mod prelude {
-    pub use crate::get_line_num;
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub use crate::innerlude::OwnedTemplate;
     pub use crate::innerlude::{
-        fc_to_builder, AttributeDiscription, AttributeValue, Attributes, CodeLocation, Component,
-        DioxusElement, Element, EventHandler, Fragment, IntoAttributeValue, LazyNodes,
-        LazyStaticVec, NodeFactory, Properties, Scope, ScopeId, ScopeState, StaticAttributeValue,
-        StaticCodeLocation, StaticDynamicNodeMapping, StaticPathSeg, StaticTemplate,
-        StaticTemplateNodes, StaticTraverse, Template, TemplateAttribute, TemplateAttributeValue,
-        TemplateContext, TemplateElement, TemplateId, TemplateNode, TemplateNodeId,
-        TemplateNodeType, TextTemplate, TextTemplateSegment, UpdateOp, VNode, VirtualDom,
+        fc_to_builder, Attributes, Component, Element, EventHandler, LazyNodes, NodeFactory,
+        Properties, Scope, ScopeId, ScopeState, Template, VNode, VirtualDom,
     };
 }
 
@@ -107,7 +80,6 @@ pub mod exports {
     //! Feel free to just add the dependencies in your own Crates.toml
     pub use bumpalo;
     pub use futures_channel;
-    pub use once_cell;
 }
 
 /// Functions that wrap unsafe functionality to prevent us from misusing it at the callsite
@@ -136,8 +108,24 @@ pub(crate) mod unsafe_utils {
 /// }
 /// ```
 macro_rules! to_owned {
-    ($($es:ident),+$(,)?) => {$(
+    ($($es:ident),+) => {$(
         #[allow(unused_mut)]
         let mut $es = $es.to_owned();
     )*}
 }
+
+/// get the code location of the code that called this function
+#[macro_export]
+macro_rules! get_line_num {
+    () => {
+        concat!(
+            file!(),
+            ":",
+            line!(),
+            ":",
+            column!(),
+            ":",
+            env!("CARGO_MANIFEST_DIR")
+        )
+    };
+}

+ 52 - 395
packages/core/src/mutations.rs

@@ -6,402 +6,59 @@
 //! interpreters for these types of DomEdits.
 
 use crate::innerlude::*;
-use std::{any::Any, fmt::Debug};
 
-/// ## Mutations
+/// A renderer for Dioxus to modify during diffing
 ///
-/// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
-/// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
-/// applied the edits.
+/// The renderer should implement a Stack Machine. IE each call to the below methods are modifications to the renderer's
+/// internal stack for creating and modifying nodes.
 ///
-/// Mutations are the only link between the RealDOM and the VirtualDOM.
-pub struct Mutations<'a> {
-    /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
-    pub edits: Vec<DomEdit<'a>>,
-
-    /// The list of Scopes that were diffed, created, and removed during the Diff process.
-    pub dirty_scopes: FxHashSet<ScopeId>,
-
-    /// The list of nodes to connect to the RealDOM.
-    pub refs: Vec<NodeRefMutation<'a>>,
-}
-
-impl Debug for Mutations<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Mutations")
-            .field("edits", &self.edits)
-            .field("noderefs", &self.refs)
-            .finish()
-    }
-}
-
-/// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
-/// network or through FFI boundaries.
-#[derive(Debug, PartialEq)]
-#[cfg_attr(
-    feature = "serialize",
-    derive(serde::Serialize, serde::Deserialize),
-    serde(tag = "type")
-)]
-pub enum DomEdit<'bump> {
-    /// Pop the topmost node from our stack and append them to the node
-    /// at the top of the stack.
-    AppendChildren {
-        /// The parent to append nodes to.
-        root: Option<u64>,
-
-        /// The ids of the children to append.
-        children: Vec<u64>,
-    },
-
-    /// Replace a given (single) node with a handful of nodes currently on the stack.
-    ReplaceWith {
-        /// The ID of the node to be replaced.
-        root: Option<u64>,
-
-        /// The ids of the nodes to replace the root with.
-        nodes: Vec<u64>,
-    },
-
-    /// Insert a number of nodes after a given node.
-    InsertAfter {
-        /// The ID of the node to insert after.
-        root: Option<u64>,
-
-        /// The ids of the nodes to insert after the target node.
-        nodes: Vec<u64>,
-    },
-
-    /// Insert a number of nodes before a given node.
-    InsertBefore {
-        /// The ID of the node to insert before.
-        root: Option<u64>,
-
-        /// The ids of the nodes to insert before the target node.
-        nodes: Vec<u64>,
-    },
-
-    /// Remove a particular node from the DOM
-    Remove {
-        /// The ID of the node to remove.
-        root: Option<u64>,
-    },
-
-    /// Create a new purely-text node
-    CreateTextNode {
-        /// The ID the new node should have.
-        root: Option<u64>,
-
-        /// The textcontent of the node
-        text: &'bump str,
-    },
-
-    /// Create a new purely-element node
-    CreateElement {
-        /// The ID the new node should have.
-        root: Option<u64>,
-
-        /// The tagname of the node
-        tag: &'bump str,
-
-        /// The number of children nodes that will follow this message.
-        children: u32,
-    },
-
-    /// Create a new purely-comment node with a given namespace
-    CreateElementNs {
-        /// The ID the new node should have.
-        root: Option<u64>,
-
-        /// The namespace of the node
-        tag: &'bump str,
-
-        /// The namespace of the node (like `SVG`)
-        ns: &'static str,
-
-        /// The number of children nodes that will follow this message.
-        children: u32,
-    },
-
-    /// Create a new placeholder node.
-    /// In most implementations, this will either be a hidden div or a comment node.
-    CreatePlaceholder {
-        /// The ID the new node should have.
-        root: Option<u64>,
-    },
-
-    /// Create a new Event Listener.
-    NewEventListener {
-        /// The name of the event to listen for.
-        event_name: &'static str,
-
-        /// The ID of the node to attach the listener to.
-        scope: ScopeId,
-
-        /// The ID of the node to attach the listener to.
-        root: Option<u64>,
-    },
-
-    /// Remove an existing Event Listener.
-    RemoveEventListener {
-        /// The ID of the node to remove.
-        root: Option<u64>,
-
-        /// The name of the event to remove.
-        event: &'static str,
-    },
-
-    /// Set the textcontent of a node.
-    SetText {
-        /// The ID of the node to set the textcontent of.
-        root: Option<u64>,
-
-        /// The textcontent of the node
-        text: &'bump str,
-    },
-
-    /// Set the value of a node's attribute.
-    SetAttribute {
-        /// The ID of the node to set the attribute of.
-        root: Option<u64>,
-
-        /// The name of the attribute to set.
-        field: &'static str,
-
-        /// The value of the attribute.
-        value: AttributeValue<'bump>,
-
-        // value: &'bump str,
-        /// The (optional) namespace of the attribute.
-        /// For instance, "style" is in the "style" namespace.
-        ns: Option<&'bump str>,
-    },
-
-    /// Remove an attribute from a node.
-    RemoveAttribute {
-        /// The ID of the node to remove.
-        root: Option<u64>,
-
-        /// The name of the attribute to remove.
-        name: &'static str,
-
-        /// The namespace of the attribute.
-        ns: Option<&'bump str>,
-    },
-
-    /// Clones a node.
-    CloneNode {
-        /// The ID of the node to clone.
-        id: Option<u64>,
-
-        /// The ID of the new node.
-        new_id: u64,
-    },
-
-    /// Clones the children of a node. (allows cloning fragments)
-    CloneNodeChildren {
-        /// The ID of the node to clone.
-        id: Option<u64>,
-
-        /// The ID of the new node.
-        new_ids: Vec<u64>,
-    },
-
-    /// Navigates to the last node to the first child of the current node.
-    FirstChild {},
-
-    /// Navigates to the last node to the last child of the current node.
-    NextSibling {},
-
-    /// Navigates to the last node to the parent of the current node.
-    ParentNode {},
-
-    /// Stores the last node with a new id.
-    StoreWithId {
-        /// The ID of the node to store.
-        id: u64,
-    },
-
-    /// Manually set the last node.
-    SetLastNode {
-        /// The ID to set the last node to.
-        id: u64,
-    },
-}
-
-use rustc_hash::FxHashSet;
-use DomEdit::*;
-
-#[allow(unused)]
-impl<'a> Mutations<'a> {
-    pub(crate) fn new() -> Self {
-        Self {
-            edits: Vec::new(),
-            refs: Vec::new(),
-            dirty_scopes: Default::default(),
-        }
-    }
-
-    pub(crate) fn replace_with(&mut self, root: Option<u64>, nodes: Vec<u64>) {
-        self.edits.push(ReplaceWith { nodes, root });
-    }
-
-    pub(crate) fn insert_after(&mut self, root: Option<u64>, nodes: Vec<u64>) {
-        self.edits.push(InsertAfter { nodes, root });
-    }
-
-    pub(crate) fn insert_before(&mut self, root: Option<u64>, nodes: Vec<u64>) {
-        self.edits.push(InsertBefore { nodes, root });
-    }
-
-    pub(crate) fn append_children(&mut self, root: Option<u64>, children: Vec<u64>) {
-        self.edits.push(AppendChildren { root, children });
-    }
-
-    // Remove Nodes from the dom
-    pub(crate) fn remove(&mut self, id: Option<u64>) {
-        self.edits.push(Remove { root: id });
-    }
-
-    // Create
-    pub(crate) fn create_text_node(&mut self, text: &'a str, id: Option<u64>) {
-        self.edits.push(CreateTextNode { text, root: id });
-    }
-
-    pub(crate) fn create_element(
-        &mut self,
-        tag: &'static str,
-        ns: Option<&'static str>,
-        id: Option<u64>,
-        children: u32,
-    ) {
-        match ns {
-            Some(ns) => self.edits.push(CreateElementNs {
-                root: id,
-                ns,
-                tag,
-                children,
-            }),
-            None => self.edits.push(CreateElement {
-                root: id,
-                tag,
-                children,
-            }),
-        }
-    }
-
-    // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
-    pub(crate) fn create_placeholder(&mut self, id: Option<u64>) {
-        self.edits.push(CreatePlaceholder { root: id });
-    }
-
-    // events
-    pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
-        let Listener {
-            event,
-            mounted_node,
-            ..
-        } = listener;
-
-        let element_id = Some(mounted_node.get().unwrap().into());
-
-        self.edits.push(NewEventListener {
-            scope,
-            event_name: event,
-            root: element_id,
-        });
-    }
-
-    pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: Option<u64>) {
-        self.edits.push(RemoveEventListener { event, root });
-    }
-
-    // modify
-    pub(crate) fn set_text(&mut self, text: &'a str, root: Option<u64>) {
-        self.edits.push(SetText { text, root });
-    }
-
-    pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: Option<u64>) {
-        let Attribute {
-            value, attribute, ..
-        } = attribute;
-
-        self.edits.push(SetAttribute {
-            field: attribute.name,
-            value: value.clone(),
-            ns: attribute.namespace,
-            root,
-        });
-    }
-
-    pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: Option<u64>) {
-        let Attribute { attribute, .. } = attribute;
-
-        self.edits.push(RemoveAttribute {
-            name: attribute.name,
-            ns: attribute.namespace,
-            root,
-        });
-    }
-
-    pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) {
-        self.dirty_scopes.insert(scope);
-    }
-
-    pub(crate) fn clone_node(&mut self, id: Option<u64>, new_id: u64) {
-        self.edits.push(CloneNode { id, new_id });
-    }
-
-    pub(crate) fn clone_node_children(&mut self, id: Option<u64>, new_ids: Vec<u64>) {
-        self.edits.push(CloneNodeChildren { id, new_ids });
-    }
-
-    pub(crate) fn first_child(&mut self) {
-        self.edits.push(FirstChild {});
-    }
-
-    pub(crate) fn next_sibling(&mut self) {
-        self.edits.push(NextSibling {});
-    }
-
-    pub(crate) fn parent_node(&mut self) {
-        self.edits.push(ParentNode {});
-    }
-
-    pub(crate) fn store_with_id(&mut self, id: u64) {
-        self.edits.push(StoreWithId { id });
-    }
-
-    pub(crate) fn set_last_node(&mut self, id: u64) {
-        self.edits.push(SetLastNode { id });
-    }
-}
-
-// refs are only assigned once
-pub struct NodeRefMutation<'a> {
-    pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
-    pub element_id: ElementId,
-}
-
-impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("NodeRefMutation")
-            .field("element_id", &self.element_id)
-            .finish()
-    }
-}
-
-impl<'a> NodeRefMutation<'a> {
-    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
-        self.element
-            .as_ref()
-            .and_then(|f| f.get())
-            .and_then(|f| f.downcast_ref::<T>())
-    }
-    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
-        self.element
-            .as_mut()
-            .and_then(|f| f.get_mut())
-            .and_then(|f| f.downcast_mut::<T>())
-    }
+/// Dioxus guarantees that the stack is always in a valid state.
+pub trait Renderer<'a> {
+    /// Load this element onto the stack
+    fn push_root(&mut self, root: ElementId);
+    /// Pop the topmost element from the stack
+    fn pop_root(&mut self);
+    /// Replace the given element with the next m elements on the stack
+    fn replace_with(&mut self, root: ElementId, m: u32);
+    /// Insert the next m elements on the stack after the given element
+    fn insert_after(&mut self, root: ElementId, n: u32);
+    /// Insert the next m elements on the stack before the given element
+    fn insert_before(&mut self, root: ElementId, n: u32);
+    /// Append the next n elements on the stack to the n+1 element on the stack
+    fn append_children(&mut self, n: u32);
+
+    /// Create a new element with the given text and ElementId
+    fn create_text_node(&mut self, text: &'a str, root: ElementId);
+    /// Create an element with the given tag name, optional namespace, and ElementId
+    /// Note that namespaces do not cascade down the tree, so the renderer must handle this if it implements namespaces
+    fn create_element(&mut self, tag: &'static str, ns: Option<&'static str>, id: ElementId);
+    /// Create a hidden element to be used later for replacement.
+    /// Used in suspense, lists, and other places where we need to hide a node before it is ready to be shown.
+    /// This is up to the renderer to implement, but it should not be visible to the user.
+    fn create_placeholder(&mut self, id: ElementId);
+
+    /// Remove the targeted node from the DOM
+    fn remove(&mut self, root: ElementId);
+    /// Remove an attribute from an existing element
+    fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId);
+
+    /// Attach a new listener to the dom
+    fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId);
+    /// Remove an existing listener from the dom
+    fn remove_event_listener(&mut self, event: &'static str, root: ElementId);
+
+    /// Set the text content of a node
+    fn set_text(&mut self, text: &'a str, root: ElementId);
+    /// Set an attribute on an element
+    fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: ElementId);
+
+    /// Save the current n nodes to the ID to be loaded later
+    fn save(&mut self, id: &str, num: u32);
+    /// Loads a set of saved nodes from the ID
+    fn load(&mut self, id: &str);
+
+    // General statistics for doing things that extend outside of the renderer
+
+    ///
+    fn mark_dirty_scope(&mut self, scope: ScopeId);
 }

+ 136 - 219
packages/core/src/nodes.rs → packages/core/src/nodes.old.rs

@@ -3,22 +3,18 @@
 //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
 //! cheap and *very* fast to construct - building a full tree should be quick.
 use crate::{
-    dynamic_template_context::TemplateContext,
-    innerlude::{
-        AttributeValue, ComponentPtr, Element, IntoAttributeValue, Properties, Scope, ScopeId,
-        ScopeState, Template, TemplateId,
-    },
+    innerlude::{AttributeValue, ComponentPtr, Element, Properties, Scope, ScopeId, ScopeState},
     lazynodes::LazyNodes,
-    template::VTemplateRef,
     AnyEvent, Component,
 };
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use std::{
     cell::{Cell, RefCell},
     fmt::{Arguments, Debug, Formatter},
-    rc::Rc,
 };
 
+use crate::template::*;
+
 /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
 ///
 /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
@@ -101,26 +97,10 @@ pub enum VNode<'src> {
     /// ```
     Component(&'src VComponent<'src>),
 
-    /// Placeholders are a type of placeholder VNode used when fragments don't contain any children.
-    ///
-    /// Placeholders cannot be directly constructed via public APIs.
-    ///
-    /// # Example
-    ///
-    /// ```rust, ignore
-    /// let mut vdom = VirtualDom::new();
+    /// Templates are containers for other nodes
     ///
-    /// let node = vdom.render_vnode(rsx!( Fragment {} ));
     ///
-    /// if let VNode::Fragment(frag) = node {
-    ///     let root = &frag.children[0];
-    ///     assert_eq!(root, VNode::Anchor);
-    /// }
-    /// ```
-    Placeholder(&'src VPlaceholder),
-
-    /// Templetes ase generated by the rsx macro to eleminate diffing static nodes.
-    TemplateRef(&'src VTemplateRef<'src>),
+    Template(&'src VTemplate<'src>),
 }
 
 impl<'src> VNode<'src> {
@@ -129,10 +109,9 @@ impl<'src> VNode<'src> {
         match &self {
             VNode::Element(el) => el.key,
             VNode::Component(c) => c.key,
-            VNode::Fragment(f) => f.key,
             VNode::Text(_t) => None,
-            VNode::Placeholder(_f) => None,
-            VNode::TemplateRef(t) => t.dynamic_context.key,
+            VNode::Template(_t) => None,
+            VNode::Fragment(_f) => None,
         }
     }
 
@@ -150,10 +129,9 @@ impl<'src> VNode<'src> {
         match &self {
             VNode::Text(el) => el.id.get(),
             VNode::Element(el) => el.id.get(),
-            VNode::Placeholder(el) => el.id.get(),
             VNode::Fragment(_) => None,
             VNode::Component(_) => None,
-            VNode::TemplateRef(_) => None,
+            VNode::Template(_) => todo!(),
         }
     }
 
@@ -163,9 +141,8 @@ impl<'src> VNode<'src> {
             VNode::Text(t) => VNode::Text(t),
             VNode::Element(e) => VNode::Element(e),
             VNode::Component(c) => VNode::Component(c),
-            VNode::Placeholder(a) => VNode::Placeholder(a),
             VNode::Fragment(f) => VNode::Fragment(f),
-            VNode::TemplateRef(t) => VNode::TemplateRef(t),
+            VNode::Template(_) => todo!(),
         }
     }
 }
@@ -186,10 +163,6 @@ impl Debug for VNode<'_> {
                 .field("text", &t.text)
                 .field("id", &t.id)
                 .finish(),
-            VNode::Placeholder(t) => s
-                .debug_struct("VNode::Placholder")
-                .field("id", &t.id)
-                .finish(),
             VNode::Fragment(frag) => s
                 .debug_struct("VNode::Fragment")
                 .field("children", &frag.children)
@@ -201,10 +174,7 @@ impl Debug for VNode<'_> {
                 .field("key", &comp.key)
                 .field("scope", &comp.scope)
                 .finish(),
-            VNode::TemplateRef(temp) => s
-                .debug_struct("VNode::TemplateRef")
-                .field("template_id", &temp.template_id)
-                .finish(),
+            VNode::Template(_) => todo!(),
         }
     }
 }
@@ -214,8 +184,6 @@ impl Debug for VNode<'_> {
 /// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
 /// unmounted, then the `ElementId` will be reused for a new component.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serialize", serde(transparent))]
 pub struct ElementId(pub usize);
 impl std::fmt::Display for ElementId {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -224,19 +192,13 @@ impl std::fmt::Display for ElementId {
 }
 
 impl ElementId {
-    /// Convert the ElementId to a `u64`.
-    pub fn as_u64(&self) -> u64 {
-        (*self).into()
-    }
-}
-
-impl From<ElementId> for u64 {
-    fn from(el: ElementId) -> u64 {
-        el.0 as u64
+    /// Convertt the ElementId to a `u64`.
+    pub fn as_u64(self) -> u64 {
+        self.0 as u64
     }
 }
 
-fn empty_cell<T>() -> Cell<Option<T>> {
+fn empty_cell() -> Cell<Option<ElementId>> {
     Cell::new(None)
 }
 
@@ -264,12 +226,20 @@ pub struct VFragment<'src> {
     /// The key of the fragment to be used during keyed diffing.
     pub key: Option<&'src str>,
 
+    pub memo: Option<FragmentMemo<'src>>,
+
     /// Fragments can never have zero children. Enforced by NodeFactory.
     ///
     /// You *can* make a fragment with no children, but it's not a valid fragment and your VDom will panic.
     pub children: &'src [VNode<'src>],
 }
 
+pub struct FragmentMemo<'a> {
+    pub template: TemplateDef<'static>,
+
+    pub dynamic_nodes: &'a Cell<[ElementId]>,
+}
+
 /// An element like a "div" with children, listeners, and attributes.
 pub struct VElement<'a> {
     /// The [`ElementId`] of the VText.
@@ -354,52 +324,30 @@ pub trait DioxusElement {
     }
 }
 
-type StaticStr = &'static str;
-
-/// A discription of the attribute
-#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
-#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Deserialize)
-)]
-pub struct AttributeDiscription {
-    /// The name of the attribute.
-    #[cfg_attr(
-        all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-        serde(deserialize_with = "crate::util::deserialize_static_leaky")
-    )]
-    pub name: StaticStr,
-
-    /// The namespace of the attribute.
-    ///
-    /// Doesn't exist in the html spec.
-    /// Used in Dioxus to denote "style" tags and other attribute groups.
-    #[cfg_attr(
-        all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-        serde(deserialize_with = "crate::util::deserialize_static_leaky_ns")
-    )]
-    pub namespace: Option<StaticStr>,
-
-    /// An indication of we should always try and set the attribute.
-    /// Used in controlled components to ensure changes are propagated.
-    pub volatile: bool,
-}
-
 /// An attribute on a DOM node, such as `id="my-thing"` or
 /// `href="https://example.com"`.
 #[derive(Clone, Debug)]
 pub struct Attribute<'a> {
-    /// The discription of the attribute.
-    pub attribute: AttributeDiscription,
+    /// The name of the attribute.
+    pub name: &'static str,
+
+    /// The value of the attribute.
+    pub value: AttributeValue<'a>,
 
     /// An indication if this attribute can be ignored during diffing
     ///
     /// Usually only when there are no strings to be formatted (so the value is &'static str)
     pub is_static: bool,
 
-    /// The value of the attribute.
-    pub value: AttributeValue<'a>,
+    /// An indication of we should always try and set the attribute.
+    /// Used in controlled components to ensure changes are propagated.
+    pub is_volatile: bool,
+
+    /// The namespace of the attribute.
+    ///
+    /// Doesn't exist in the html spec.
+    /// Used in Dioxus to denote "style" tags and other attribute groups.
+    pub namespace: Option<&'static str>,
 }
 
 /// An event listener.
@@ -466,11 +414,9 @@ impl<'a, T> Default for EventHandler<'a, T> {
 impl<T> EventHandler<'_, T> {
     /// Call this event handler with the appropriate event type
     pub fn call(&self, event: T) {
-        log::trace!("calling event handler");
         if let Some(callback) = self.callback.borrow_mut().as_mut() {
             callback(event);
         }
-        log::trace!("done");
     }
 
     /// Forcibly drop the internal handler callback, releasing memory
@@ -647,35 +593,21 @@ impl<'a> NodeFactory<'a> {
         }))
     }
 
-    /// Create a new [`Attribute`] from a attribute discrimination and a value
-    pub fn attr_disciption(
-        &self,
-        discription: AttributeDiscription,
-        val: impl IntoAttributeValue<'a>,
-    ) -> Attribute<'a> {
-        Attribute {
-            attribute: discription,
-            is_static: false,
-            value: val.into_value(self.bump),
-        }
-    }
-
     /// Create a new [`Attribute`]
     pub fn attr(
         &self,
         name: &'static str,
-        val: impl IntoAttributeValue<'a>,
+        val: Arguments,
         namespace: Option<&'static str>,
         is_volatile: bool,
     ) -> Attribute<'a> {
+        let (value, is_static) = self.raw_text(val);
         Attribute {
-            attribute: AttributeDiscription {
-                name,
-                namespace,
-                volatile: is_volatile,
-            },
-            is_static: false,
-            value: val.into_value(self.bump),
+            name,
+            value: AttributeValue::Text(value),
+            is_static,
+            namespace,
+            is_volatile,
         }
     }
 
@@ -689,13 +621,11 @@ impl<'a> NodeFactory<'a> {
         is_static: bool,
     ) -> Attribute<'a> {
         Attribute {
-            attribute: AttributeDiscription {
-                name,
-                namespace,
-                volatile: is_volatile,
-            },
-            is_static,
+            name,
             value,
+            is_static,
+            namespace,
+            is_volatile,
         }
     }
 
@@ -746,59 +676,67 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
-    /// Create a new [`VNode::Fragment`] from a root of the rsx! call
-    pub fn fragment_root<'b, 'c>(
-        self,
-        node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
-    ) -> VNode<'a> {
-        let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
-
-        for node in node_iter {
-            nodes.push(node.into_vnode(self));
-        }
-
-        if nodes.is_empty() {
-            VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
-        } else {
-            VNode::Fragment(self.bump.alloc(VFragment {
-                children: nodes.into_bump_slice(),
-                key: None,
-            }))
-        }
-    }
-
-    /// Create a new [`VNode::Fragment`] from any iterator
-    pub fn fragment_from_iter<'c, I, J>(
-        self,
-        node_iter: impl IntoVNode<'a, I, J> + 'c,
-    ) -> VNode<'a> {
-        node_iter.into_vnode(self)
+    pub fn template(&self, def: &'static TemplateDef, dynamic_nodes: &'a [VNode<'a>]) -> VNode<'a> {
+        VNode::Template(self.bump.alloc(VTemplate {
+            def,
+            dynamic_nodes,
+            rendered_nodes: &[],
+        }))
     }
 
-    /// Create a new [`VNode`] from any iterator of children
-    pub fn create_children(
-        self,
-        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
-    ) -> Element<'a> {
-        let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
-
-        for node in node_iter {
-            nodes.push(node.into_vnode(self));
-        }
-
-        if nodes.is_empty() {
-            Some(VNode::Placeholder(
-                self.bump.alloc(VPlaceholder { id: empty_cell() }),
-            ))
-        } else {
-            let children = nodes.into_bump_slice();
-
-            Some(VNode::Fragment(self.bump.alloc(VFragment {
-                children,
-                key: None,
-            })))
-        }
-    }
+    // /// Create a new [`VNode::Fragment`] from a root of the rsx! call
+    // pub fn fragment_root<'b, 'c>(
+    //     self,
+    //     node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
+    // ) -> VNode<'a> {
+    //     let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
+
+    //     for node in node_iter {
+    //         nodes.push(node.into_vnode(self));
+    //     }
+
+    //     if nodes.is_empty() {
+    //         VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
+    //     } else {
+    //         VNode::Fragment(self.bump.alloc(VFragment {
+    //             children: nodes.into_bump_slice(),
+    //             key: None,
+    //         }))
+    //     }
+    // }
+
+    // /// Create a new [`VNode::Fragment`] from any iterator
+    // pub fn fragment_from_iter<'c, I, J>(
+    //     self,
+    //     node_iter: impl IntoVNode<'a, I, J> + 'c,
+    // ) -> VNode<'a> {
+    //     node_iter.into_vnode(self)
+    // }
+
+    // /// Create a new [`VNode`] from any iterator of children
+    // pub fn create_children(
+    //     self,
+    //     node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
+    // ) -> Element<'a> {
+    //     let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
+
+    //     for node in node_iter {
+    //         nodes.push(node.into_vnode(self));
+    //     }
+
+    //     if nodes.is_empty() {
+    //         Some(VNode::Placeholder(
+    //             self.bump.alloc(VPlaceholder { id: empty_cell() }),
+    //         ))
+    //     } else {
+    //         let children = nodes.into_bump_slice();
+
+    //         Some(VNode::Fragment(self.bump.alloc(VFragment {
+    //             children,
+    //             key: None,
+    //         })))
+    //     }
+    // }
 
     /// Create a new [`EventHandler`] from an [`FnMut`]
     pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
@@ -807,29 +745,6 @@ impl<'a> NodeFactory<'a> {
         let callback = RefCell::new(Some(caller));
         EventHandler { callback }
     }
-
-    /// Create a refrence to a template
-    pub fn template_ref(
-        &self,
-        id: TemplateId,
-        template: Template,
-        dynamic_context: TemplateContext<'a>,
-    ) -> VNode<'a> {
-        let borrow_ref = self.scope.templates.borrow();
-        // We only create the template if it doesn't already exist to allow for hot reloading
-        if !borrow_ref.contains_key(&id) {
-            drop(borrow_ref);
-            let mut borrow_mut = self.scope.templates.borrow_mut();
-            borrow_mut.insert(id.clone(), Rc::new(RefCell::new(template)));
-        }
-        VNode::TemplateRef(self.bump.alloc(VTemplateRef {
-            dynamic_context,
-            template_id: id,
-            node_ids: RefCell::new(Vec::new()),
-            parent: Cell::new(None),
-            template_ref_id: Cell::new(None),
-        }))
-    }
 }
 
 impl Debug for NodeFactory<'_> {
@@ -869,7 +784,8 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
 // Conveniently, we also support "null" (nothing) passed in
 impl IntoVNode<'_> for () {
     fn into_vnode(self, cx: NodeFactory) -> VNode {
-        VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
+        todo!()
+        // VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
     }
 }
 
@@ -918,32 +834,33 @@ where
             nodes.push(node.into_vnode(cx));
         }
 
-        if nodes.is_empty() {
-            VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
-        } else {
-            let children = nodes.into_bump_slice();
-
-            if cfg!(debug_assertions)
-                && children.len() > 1
-                && children.last().unwrap().key().is_none()
-            {
-                // todo: make the backtrace prettier or remove it altogether
-                log::error!(
-                    r#"
-                Warning: Each child in an array or iterator should have a unique "key" prop.
-                Not providing a key will lead to poor performance with lists.
-                See docs.rs/dioxus for more information.
-                -------------
-                {:?}
-                "#,
-                    backtrace::Backtrace::new()
-                );
-            }
-
-            VNode::Fragment(cx.bump.alloc(VFragment {
-                children,
-                key: None,
-            }))
-        }
+        todo!()
+        // if nodes.is_empty() {
+        //     VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() }))
+        // } else {
+        //     let children = nodes.into_bump_slice();
+
+        //     if cfg!(debug_assertions)
+        //         && children.len() > 1
+        //         && children.last().unwrap().key().is_none()
+        //     {
+        //         // todo: make the backtrace prettier or remove it altogether
+        //         log::error!(
+        //             r#"
+        //         Warning: Each child in an array or iterator should have a unique "key" prop.
+        //         Not providing a key will lead to poor performance with lists.
+        //         See docs.rs/dioxus for more information.
+        //         -------------
+        //         {:?}
+        //         "#,
+        //             backtrace::Backtrace::new()
+        //         );
+        //     }
+
+        //     VNode::Fragment(cx.bump.alloc(VFragment {
+        //         children,
+        //         key: None,
+        //     }))
+        // }
     }
 }

+ 172 - 0
packages/core/src/nodes/arbitrary_value.rs

@@ -0,0 +1,172 @@
+use std::{
+    any::Any,
+    fmt::{Arguments, Display, Formatter},
+};
+
+use bumpalo::Bump;
+use serde::{Deserialize, Serialize};
+
+/// Possible values for an attribute
+// trying to keep values at 3 bytes
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serialize", serde(untagged))]
+#[derive(Clone, PartialEq)]
+#[allow(missing_docs)]
+pub enum AttributeValue<'a> {
+    Text(&'a str),
+    Float32(f32),
+    Bool(bool),
+    Any(ArbitraryAttributeValue<'a>),
+}
+
+impl<'a> Display for AttributeValue<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        todo!()
+    }
+}
+
+impl std::fmt::Debug for AttributeValue<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        todo!()
+        // match self {
+        //     AttributeValue::Text(s) => write!(f, "AttributeValue::Text({:?})", s),
+        //     AttributeValue::Float32(f) => write!(f, "AttributeValue::Float32({:?})", f),
+        //     AttributeValue::Bool(b) => write!(f, "AttributeValue::Bool({:?})", b),
+        //     AttributeValue::Any(a) => write!(f, "AttributeValue::Any({:?})", a),
+        // }
+    }
+}
+
+/// A value that can be converted into an attribute value
+pub trait IntoAttributeValue<'a> {
+    /// Convert into an attribute value
+    fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
+}
+impl<'a> IntoAttributeValue<'a> for f32 {
+    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
+        AttributeValue::Float32(self)
+    }
+}
+
+impl<'a> IntoAttributeValue<'a> for bool {
+    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
+        AttributeValue::Bool(self)
+    }
+}
+
+impl<'a> IntoAttributeValue<'a> for &'a str {
+    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
+        AttributeValue::Text(self)
+    }
+}
+
+impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
+    fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
+        use bumpalo::core_alloc::fmt::Write;
+        let mut str_buf = bumpalo::collections::String::new_in(bump);
+        str_buf.write_fmt(self).unwrap();
+        AttributeValue::Text(str_buf.into_bump_str())
+    }
+}
+
+impl<'a, T> IntoAttributeValue<'a> for &'a T
+where
+    T: PartialEq,
+{
+    fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
+        todo!()
+        // AttributeValue::Any(ArbitraryAttributeValue {
+        //     value: self,
+        //     cmp: |a, b| {
+        //         if let Some(a) = a.as_any().downcast_ref::<T>() {
+        //             if let Some(b) = b.as_any().downcast_ref::<T>() {
+        //                 a == b
+        //             } else {
+        //                 false
+        //             }
+        //         } else {
+        //             false
+        //         }
+        //     },
+        // })
+    }
+}
+
+// todo
+#[allow(missing_docs)]
+impl<'a> AttributeValue<'a> {
+    pub fn is_truthy(&self) -> bool {
+        match self {
+            AttributeValue::Text(t) => *t == "true",
+            AttributeValue::Bool(t) => *t,
+            _ => false,
+        }
+    }
+
+    pub fn is_falsy(&self) -> bool {
+        match self {
+            AttributeValue::Text(t) => *t == "false",
+            AttributeValue::Bool(t) => !(*t),
+            _ => false,
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+#[allow(missing_docs)]
+pub struct ArbitraryAttributeValue<'a> {
+    pub value: &'a dyn Any,
+    // pub value: &'a dyn AnyClone,
+    // pub cmp: fn(&dyn AnyClone, &dyn AnyClone) -> bool,
+}
+
+#[cfg(feature = "serialize")]
+impl<'a> Serialize for ArbitraryAttributeValue<'a> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        todo!()
+    }
+}
+#[cfg(feature = "serialize")]
+impl<'a, 'de> Deserialize<'de> for ArbitraryAttributeValue<'a> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        todo!()
+    }
+}
+
+impl PartialEq for ArbitraryAttributeValue<'_> {
+    fn eq(&self, other: &Self) -> bool {
+        todo!()
+        // (self.cmp)(self.value, other.value)
+    }
+}
+
+// todo
+#[allow(missing_docs)]
+impl<'a> AttributeValue<'a> {
+    pub fn as_text(&self) -> Option<&'a str> {
+        match self {
+            AttributeValue::Text(s) => Some(s),
+            _ => None,
+        }
+    }
+
+    pub fn as_float32(&self) -> Option<f32> {
+        match self {
+            AttributeValue::Float32(f) => Some(*f),
+            _ => None,
+        }
+    }
+
+    pub fn as_any(&self) -> Option<&'a ArbitraryAttributeValue> {
+        match self {
+            AttributeValue::Any(a) => Some(a),
+            _ => None,
+        }
+    }
+}

+ 61 - 0
packages/core/src/nodes/component.rs

@@ -0,0 +1,61 @@
+use crate::{
+    innerlude::{ComponentPtr, Element, Scope, ScopeId, ScopeState},
+    Component,
+};
+use std::cell::{Cell, RefCell};
+
+/// Virtual Components for custom user-defined components
+/// Only supports the functional syntax
+pub struct VComponent<'src> {
+    /// The key of the component to be used during keyed diffing.
+    pub key: Option<&'src str>,
+
+    /// The ID of the component.
+    /// Will not be assigned until after the component has been initialized.
+    pub scope: Cell<Option<ScopeId>>,
+
+    /// An indication if the component is static (can be memozied)
+    pub can_memoize: bool,
+
+    /// The function pointer to the component's render function.
+    pub user_fc: ComponentPtr,
+
+    /// The actual name of the component.
+    pub fn_name: &'static str,
+
+    /// The props of the component.
+    pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
+}
+
+pub(crate) struct VComponentProps<P> {
+    pub render_fn: Component<P>,
+    pub memo: unsafe fn(&P, &P) -> bool,
+    pub props: P,
+}
+
+pub trait AnyProps {
+    fn as_ptr(&self) -> *const ();
+    fn render<'a>(&'a self, bump: &'a ScopeState) -> Element<'a>;
+    unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
+}
+
+impl<P> AnyProps for VComponentProps<P> {
+    fn as_ptr(&self) -> *const () {
+        &self.props as *const _ as *const ()
+    }
+
+    // Safety:
+    // this will downcast the other ptr as our swallowed type!
+    // you *must* make this check *before* calling this method
+    // if your functions are not the same, then you will downcast a pointer into a different type (UB)
+    unsafe fn memoize(&self, other: &dyn AnyProps) -> bool {
+        let real_other: &P = &*(other.as_ptr() as *const _ as *const P);
+        let real_us: &P = &*(self.as_ptr() as *const _ as *const P);
+        (self.memo)(real_us, real_other)
+    }
+
+    fn render<'a>(&'a self, scope: &'a ScopeState) -> Element<'a> {
+        let props = unsafe { std::mem::transmute::<&P, &P>(&self.props) };
+        (self.render_fn)(Scope { scope, props })
+    }
+}

+ 150 - 0
packages/core/src/nodes/element.rs

@@ -0,0 +1,150 @@
+use crate::{innerlude::AttributeValue, AnyEvent, ElementId, VNode};
+use bumpalo::boxed::Box as BumpBox;
+use std::{
+    cell::{Cell, RefCell},
+    fmt::{Debug, Formatter},
+};
+
+/// An element like a "div" with children, listeners, and attributes.
+pub struct VElement<'a> {
+    /// The [`ElementId`] of the VText.
+    pub id: Cell<Option<ElementId>>,
+
+    /// The key of the element to be used during keyed diffing.
+    pub key: Option<&'a str>,
+
+    /// The tag name of the element.
+    ///
+    /// IE "div"
+    pub tag: &'static str,
+
+    /// The namespace of the VElement
+    ///
+    /// IE "svg"
+    pub namespace: Option<&'static str>,
+
+    /// The parent of the Element (if any).
+    ///
+    /// Used when bubbling events
+    pub parent: Cell<Option<ElementId>>,
+
+    /// The Listeners of the VElement.
+    pub listeners: &'a [Listener<'a>],
+
+    /// The attributes of the VElement.
+    pub attributes: &'a [Attribute<'a>],
+
+    /// The children of the VElement.
+    pub children: &'a [VNode<'a>],
+}
+
+impl Debug for VElement<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("VElement")
+            .field("tag_name", &self.tag)
+            .field("namespace", &self.namespace)
+            .field("key", &self.key)
+            .field("id", &self.id)
+            .field("parent", &self.parent)
+            .field("listeners", &self.listeners.len())
+            .field("attributes", &self.attributes)
+            .field("children", &self.children)
+            .finish()
+    }
+}
+
+/// An attribute on a DOM node, such as `id="my-thing"` or
+/// `href="https://example.com"`.
+#[derive(Clone, Debug)]
+pub struct Attribute<'a> {
+    /// The name of the attribute.
+    pub name: &'static str,
+
+    /// The namespace of the attribute.
+    ///
+    /// Doesn't exist in the html spec.
+    /// Used in Dioxus to denote "style" tags and other attribute groups.
+    pub namespace: Option<&'static str>,
+
+    /// An indication of we should always try and set the attribute.
+    /// Used in controlled components to ensure changes are propagated.
+    pub volatile: bool,
+
+    /// The value of the attribute.
+    pub value: AttributeValue<'a>,
+}
+
+/// An event listener.
+/// IE onclick, onkeydown, etc
+pub struct Listener<'bump> {
+    /// The ID of the node that this listener is mounted to
+    /// Used to generate the event listener's ID on the DOM
+    pub mounted_node: Cell<Option<ElementId>>,
+
+    /// The type of event to listen for.
+    ///
+    /// IE "click" - whatever the renderer needs to attach the listener by name.
+    pub event: &'static str,
+
+    /// The actual callback that the user specified
+    pub(crate) callback: InternalHandler<'bump>,
+}
+
+pub type InternalHandler<'bump> = &'bump RefCell<Option<InternalListenerCallback<'bump>>>;
+type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(AnyEvent) + 'bump>;
+type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
+
+/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
+///
+/// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
+///
+///
+/// # Example
+///
+/// ```rust, ignore
+///
+/// rsx!{
+///     MyComponent { onclick: move |evt| log::info!("clicked"), }
+/// }
+///
+/// #[derive(Props)]
+/// struct MyProps<'a> {
+///     onclick: EventHandler<'a, MouseEvent>,
+/// }
+///
+/// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element {
+///     cx.render(rsx!{
+///         button {
+///             onclick: move |evt| cx.props.onclick.call(evt),
+///         }
+///     })
+/// }
+///
+/// ```
+pub struct EventHandler<'bump, T = ()> {
+    /// The (optional) callback that the user specified
+    /// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
+    pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
+}
+
+impl<'a, T> Default for EventHandler<'a, T> {
+    fn default() -> Self {
+        Self {
+            callback: RefCell::new(None),
+        }
+    }
+}
+
+impl<T> EventHandler<'_, T> {
+    /// Call this event handler with the appropriate event type
+    pub fn call(&self, event: T) {
+        if let Some(callback) = self.callback.borrow_mut().as_mut() {
+            callback(event);
+        }
+    }
+
+    /// Forcibly drop the internal handler callback, releasing memory
+    pub fn release(&self) {
+        self.callback.replace(None);
+    }
+}

+ 366 - 0
packages/core/src/nodes/factory.rs

@@ -0,0 +1,366 @@
+use crate::{innerlude::*, Attribute, Listener, VElement, VNode, VText};
+use bumpalo::{boxed::Box as BumpBox, Bump};
+use std::{
+    cell::{Cell, RefCell},
+    fmt::{Arguments, Debug},
+};
+
+/// This struct provides an ergonomic API to quickly build VNodes.
+///
+/// NodeFactory is used to build VNodes in the component's memory space.
+/// This struct adds metadata to the final VNode about listeners, attributes, and children
+#[derive(Copy, Clone)]
+pub struct NodeFactory<'a> {
+    pub(crate) scope: &'a ScopeState,
+    pub(crate) bump: &'a Bump,
+}
+
+impl<'a> NodeFactory<'a> {
+    /// Create a new [`NodeFactory`] from a [`Scope`] or [`ScopeState`]
+    pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
+        NodeFactory {
+            scope,
+            bump: &scope.wip_frame().bump,
+        }
+    }
+
+    /// Get the custom allocator for this component
+    #[inline]
+    pub fn bump(&self) -> &'a bumpalo::Bump {
+        self.bump
+    }
+
+    /// Directly pass in text blocks without the need to use the format_args macro.
+    pub fn static_text(&self, text: &'static str) -> VNode<'a> {
+        VNode::Text(self.bump.alloc(VText {
+            id: Default::default(),
+            text,
+            is_static: true,
+        }))
+    }
+
+    /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
+    ///
+    /// Text that's static may be pointer compared, making it cheaper to diff
+    pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
+        match args.as_str() {
+            Some(static_str) => (static_str, true),
+            None => {
+                use bumpalo::core_alloc::fmt::Write;
+                let mut str_buf = bumpalo::collections::String::new_in(self.bump);
+                str_buf.write_fmt(args).unwrap();
+                (str_buf.into_bump_str(), false)
+            }
+        }
+    }
+
+    /// Create some text that's allocated along with the other vnodes
+    ///
+    pub fn text(&self, args: Arguments) -> VNode<'a> {
+        let (text, is_static) = self.raw_text(args);
+
+        VNode::Text(self.bump.alloc(VText {
+            text,
+            is_static,
+            id: Default::default(),
+        }))
+    }
+
+    /// Create a new [`VNode::Element`] without the trait bound
+    ///
+    /// IE pass in "div" instead of `div`
+    pub fn raw_element(
+        &self,
+        tag_name: &'static str,
+        namespace: Option<&'static str>,
+        listeners: &'a [Listener<'a>],
+        attributes: &'a [Attribute<'a>],
+        children: &'a [VNode<'a>],
+        key: Option<Arguments>,
+    ) -> VNode<'a> {
+        let key = key.map(|f| self.raw_text(f).0);
+
+        let mut items = self.scope.items.borrow_mut();
+        for listener in listeners {
+            let long_listener = unsafe { std::mem::transmute(listener) };
+            items.listeners.push(long_listener);
+        }
+
+        VNode::Element(self.bump.alloc(VElement {
+            tag: tag_name,
+            key,
+            namespace,
+            listeners,
+            attributes,
+            children,
+            id: Default::default(),
+            parent: Default::default(),
+        }))
+    }
+
+    /// Create a new [`Attribute`]
+    pub fn attr(
+        &self,
+        name: &'static str,
+        val: impl IntoAttributeValue<'a>,
+        namespace: Option<&'static str>,
+        is_volatile: bool,
+    ) -> Attribute<'a> {
+        Attribute {
+            name,
+            namespace,
+            volatile: is_volatile,
+            value: val.into_value(self.bump),
+        }
+    }
+
+    /// Create a new [`Attribute`] using non-arguments
+    pub fn custom_attr(
+        &self,
+        name: &'static str,
+        value: AttributeValue<'a>,
+        namespace: Option<&'static str>,
+        is_volatile: bool,
+    ) -> Attribute<'a> {
+        Attribute {
+            name,
+            namespace,
+            volatile: is_volatile,
+            value,
+        }
+    }
+
+    /// Create a new [`VNode::Component`]
+    pub fn component<P>(
+        &self,
+        component: fn(Scope<'a, P>) -> Element,
+        props: P,
+        key: Option<Arguments>,
+        fn_name: &'static str,
+    ) -> VNode<'a>
+    where
+        P: Properties + 'a,
+    {
+        let vcomp = self.bump.alloc(VComponent {
+            key: key.map(|f| self.raw_text(f).0),
+            scope: Default::default(),
+            can_memoize: P::IS_STATIC,
+            user_fc: component as ComponentPtr,
+            fn_name,
+            props: RefCell::new(Some(Box::new(VComponentProps {
+                props,
+                memo: P::memoize, // smuggle the memoization function across borders
+
+                // i'm sorry but I just need to bludgeon the lifetimes into place here
+                // this is safe because we're managing all lifetimes to originate from previous calls
+                // the intricacies of Rust's lifetime system make it difficult to properly express
+                // the transformation from this specific lifetime to the for<'a> lifetime
+                render_fn: unsafe { std::mem::transmute(component) },
+            }))),
+        });
+
+        if !P::IS_STATIC {
+            let vcomp = &*vcomp;
+            let vcomp = unsafe { std::mem::transmute(vcomp) };
+            self.scope.items.borrow_mut().borrowed_props.push(vcomp);
+        }
+
+        VNode::Component(vcomp)
+    }
+
+    /// Create a new [`Listener`]
+    pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
+        Listener {
+            event,
+            mounted_node: Cell::new(None),
+            callback,
+        }
+    }
+
+    /// Create a new [`VNode::Fragment`] from a root of the rsx! call
+    pub fn fragment_root<'b, 'c>(
+        self,
+        node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
+    ) -> VNode<'a> {
+        let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
+
+        for node in node_iter {
+            nodes.push(node.into_vnode(self));
+        }
+
+        VNode::Fragment(self.bump.alloc(VFragment {
+            children: nodes.into_bump_slice(),
+            placeholder: Default::default(),
+            key: None,
+        }))
+    }
+
+    /// Create a new [`VNode::Fragment`] from any iterator
+    pub fn fragment_from_iter<'c, I, J>(
+        self,
+        node_iter: impl IntoVNode<'a, I, J> + 'c,
+    ) -> VNode<'a> {
+        node_iter.into_vnode(self)
+    }
+
+    /// Create a new [`VNode`] from any iterator of children
+    pub fn create_children(
+        self,
+        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
+    ) -> Element<'a> {
+        let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
+
+        for node in node_iter {
+            nodes.push(node.into_vnode(self));
+        }
+
+        let children = nodes.into_bump_slice();
+
+        Some(VNode::Fragment(self.bump.alloc(VFragment {
+            children,
+            key: None,
+            placeholder: Default::default(),
+        })))
+    }
+
+    /// Create a new [`EventHandler`] from an [`FnMut`]
+    pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
+        let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
+        let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
+        let callback = RefCell::new(Some(caller));
+        EventHandler { callback }
+    }
+
+    /// Create a refrence to a template
+    pub fn template_ref(
+        &self,
+        template: Template,
+        nodes: &'a [VNode<'a>],
+        attributes: &'a [Attribute<'a>],
+        listeners: &'a [Listener<'a>],
+        key: Option<Arguments>,
+    ) -> VNode<'a> {
+        // let borrow_ref = self.scope.templates.borrow();
+        // // We only create the template if it doesn't already exist to allow for hot reloading
+        // if !borrow_ref.contains_key(&id) {
+        //     drop(borrow_ref);
+        //     let mut borrow_mut = self.scope.templates.borrow_mut();
+        //     borrow_mut.insert(id.clone(), Rc::new(RefCell::new(template)));
+        // }
+        todo!()
+        // VNode::TemplateRef(self.bump.alloc(VTemplate {
+        //     dynamic_context,
+        //     template_id: id,
+        //     node_ids: RefCell::new(Vec::new()),
+        //     parent: Cell::new(None),
+        //     template_ref_id: Cell::new(None),
+        // }))
+    }
+}
+
+impl Debug for NodeFactory<'_> {
+    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        Ok(())
+    }
+}
+
+/// Trait implementations for use in the rsx! and html! macros.
+///
+/// ## Details
+///
+/// This section provides convenience methods and trait implementations for converting common structs into a format accepted
+/// by the macros.
+///
+/// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
+/// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
+/// ```rust, ignore
+/// impl IntoIterator<Item = impl IntoVNode<'a>>
+/// ```
+///
+/// As such, all node creation must go through the factory, which is only available in the component context.
+/// These strict requirements make it possible to manage lifetimes and state.
+pub trait IntoVNode<'a, I = (), J = ()> {
+    /// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
+}
+
+// TODO: do we even need this? It almost seems better not to
+// // For the case where a rendered VNode is passed into the rsx! macro through curly braces
+impl<'a> IntoVNode<'a> for VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
+        self
+    }
+}
+
+impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, '_> {
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        self.call(cx)
+    }
+}
+
+impl<'b> IntoVNode<'_> for &'b str {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        cx.text(format_args!("{}", self))
+    }
+}
+
+impl IntoVNode<'_> for String {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        cx.text(format_args!("{}", self))
+    }
+}
+
+impl IntoVNode<'_> for Arguments<'_> {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        cx.text(self)
+    }
+}
+
+impl<'a> IntoVNode<'a> for &VNode<'a> {
+    fn into_vnode(self, _cx: NodeFactory<'a>) -> VNode<'a> {
+        // borrowed nodes are strange
+        self.decouple()
+    }
+}
+
+// Note that we're using the E as a generic but this is never crafted anyways.
+pub struct FromNodeIterator;
+impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T
+where
+    T: IntoIterator<Item = I>,
+    I: IntoVNode<'a, E>,
+{
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        let mut nodes = bumpalo::collections::Vec::new_in(cx.bump);
+
+        for node in self {
+            nodes.push(node.into_vnode(cx));
+        }
+
+        let children = nodes.into_bump_slice();
+
+        if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
+        {
+            // let bt = backtrace::Backtrace::new();
+            let bt = "no backtrace available";
+
+            // todo: make the backtrace prettier or remove it altogether
+            log::error!(
+                r#"
+                Warning: Each child in an array or iterator should have a unique "key" prop.
+                Not providing a key will lead to poor performance with lists.
+                See docs.rs/dioxus for more information.
+                -------------
+                {:?}
+                "#,
+                bt
+            );
+        }
+
+        VNode::Fragment(cx.bump.alloc(VFragment {
+            children,
+            placeholder: Default::default(),
+            key: None,
+        }))
+    }
+}

+ 16 - 0
packages/core/src/nodes/fragment.rs

@@ -0,0 +1,16 @@
+use crate::{ElementId, VNode};
+use std::cell::Cell;
+
+/// A list of VNodes with no single root.
+pub struct VFragment<'src> {
+    /// The key of the fragment to be used during keyed diffing.
+    pub key: Option<&'src str>,
+
+    /// The [`ElementId`] of the placeholder.
+    pub placeholder: Cell<Option<ElementId>>,
+
+    /// Fragments can never have zero children. Enforced by NodeFactory.
+    ///
+    /// You *can* make a fragment with no children, but it's not a valid fragment and your VDom will panic.
+    pub children: &'src [VNode<'src>],
+}

+ 204 - 0
packages/core/src/nodes/mod.rs

@@ -0,0 +1,204 @@
+//! Virtual Node Support
+//!
+//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
+//! cheap and *very* fast to construct - building a full tree should be quick.
+
+use std::fmt::{Debug, Formatter};
+
+mod arbitrary_value;
+mod component;
+mod element;
+mod factory;
+mod fragment;
+mod suspense;
+mod template;
+mod text;
+
+pub use arbitrary_value::*;
+pub use component::*;
+pub use element::*;
+pub use factory::*;
+pub use fragment::*;
+pub use suspense::*;
+pub use template::*;
+pub use text::*;
+
+/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
+///
+/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
+///
+/// - the `rsx!` macro
+/// - the [`NodeFactory`] API
+pub enum VNode<'src> {
+    /// Text VNodes are simply bump-allocated (or static) string slices
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// let mut vdom = VirtualDom::new();
+    /// let node = vdom.render_vnode(rsx!( "hello" ));
+    ///
+    /// if let VNode::Text(vtext) = node {
+    ///     assert_eq!(vtext.text, "hello");
+    ///     assert_eq!(vtext.dom_id.get(), None);
+    ///     assert_eq!(vtext.is_static, true);
+    /// }
+    /// ```
+    Text(&'src VText<'src>),
+
+    /// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children.
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// let mut vdom = VirtualDom::new();
+    ///
+    /// let node = vdom.render_vnode(rsx!{
+    ///     div {
+    ///         key: "a",
+    ///         onclick: |e| log::info!("clicked"),
+    ///         hidden: "true",
+    ///         style: { background_color: "red" },
+    ///         "hello"
+    ///     }
+    /// });
+    ///
+    /// if let VNode::Element(velement) = node {
+    ///     assert_eq!(velement.tag_name, "div");
+    ///     assert_eq!(velement.namespace, None);
+    ///     assert_eq!(velement.key, Some("a"));
+    /// }
+    /// ```
+    Element(&'src VElement<'src>),
+
+    /// Fragment nodes may contain many VNodes without a single root.
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// rsx!{
+    ///     a {}
+    ///     link {}
+    ///     style {}
+    ///     "asd"
+    ///     Example {}
+    /// }
+    /// ```
+    Fragment(&'src VFragment<'src>),
+
+    /// Component nodes represent a mounted component with props, children, and a key.
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// fn Example(cx: Scope) -> Element {
+    ///     ...
+    /// }
+    ///
+    /// let mut vdom = VirtualDom::new();
+    ///
+    /// let node = vdom.render_vnode(rsx!( Example {} ));
+    ///
+    /// if let VNode::Component(vcomp) = node {
+    ///     assert_eq!(vcomp.user_fc, Example as *const ());
+    /// }
+    /// ```
+    Component(&'src VComponent<'src>),
+
+    /// Templetes ase generated by the rsx macro to eleminate diffing static nodes.
+    ///
+    ///
+    ///
+    ///
+    ///
+    ///
+    Template(&'src VTemplate<'src>),
+}
+
+/// An Element's unique identifier.
+///
+/// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
+/// unmounted, then the `ElementId` will be reused for a new component.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "serialize", serde(transparent))]
+pub struct ElementId(pub usize);
+
+impl<'src> VNode<'src> {
+    /// Get the VNode's "key" used in the keyed diffing algorithm.
+    pub fn key(&self) -> Option<&'src str> {
+        match &self {
+            VNode::Element(el) => el.key,
+            VNode::Component(c) => c.key,
+            VNode::Fragment(f) => f.key,
+            VNode::Text(_t) => None,
+            VNode::Template(t) => t.key,
+        }
+    }
+
+    /// Get the ElementID of the mounted VNode.
+    ///
+    /// Panics if the mounted ID is None or if the VNode is not represented by a single Element.
+    pub fn mounted_id(&self) -> ElementId {
+        self.try_mounted_id().unwrap()
+    }
+
+    /// Try to get the ElementID of the mounted VNode.
+    ///
+    /// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
+    pub fn try_mounted_id(&self) -> Option<ElementId> {
+        match &self {
+            VNode::Text(el) => el.id.get(),
+            VNode::Element(el) => el.id.get(),
+            VNode::Fragment(_) => None,
+            VNode::Component(_) => None,
+            VNode::Template(_) => None,
+        }
+    }
+
+    // Create an "owned" version of the vnode.
+    pub(crate) fn decouple(&self) -> VNode<'src> {
+        match *self {
+            VNode::Text(t) => VNode::Text(t),
+            VNode::Element(e) => VNode::Element(e),
+            VNode::Component(c) => VNode::Component(c),
+            VNode::Fragment(f) => VNode::Fragment(f),
+            VNode::Template(t) => VNode::Template(t),
+        }
+    }
+}
+
+impl Debug for VNode<'_> {
+    fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+        match &self {
+            VNode::Element(el) => s
+                .debug_struct("VNode::Element")
+                .field("name", &el.tag)
+                .field("key", &el.key)
+                .field("attrs", &el.attributes)
+                .field("children", &el.children)
+                .field("id", &el.id)
+                .finish(),
+            VNode::Text(t) => s
+                .debug_struct("VNode::Text")
+                .field("text", &t.text)
+                .field("id", &t.id)
+                .finish(),
+            VNode::Fragment(frag) => s
+                .debug_struct("VNode::Fragment")
+                .field("children", &frag.children)
+                .finish(),
+            VNode::Component(comp) => s
+                .debug_struct("VNode::Component")
+                .field("name", &comp.fn_name)
+                .field("fnptr", &comp.user_fc)
+                .field("key", &comp.key)
+                .field("scope", &comp.scope)
+                .finish(),
+            VNode::Template(temp) => s
+                .debug_struct("VNode::Templates")
+                .field("template_id", &temp.template.id)
+                .finish(),
+        }
+    }
+}

+ 0 - 0
packages/core/src/nodes/suspense.rs


+ 60 - 0
packages/core/src/nodes/template.rs

@@ -0,0 +1,60 @@
+use std::hash::Hash;
+
+use crate::{Attribute, ElementId, Listener, VNode};
+
+/// A reference to a template along with any context needed to hydrate it
+pub struct VTemplate<'a> {
+    pub key: Option<&'a str>,
+
+    pub template: Template<'static>,
+
+    pub dynamic_nodes: &'a [VNode<'a>],
+
+    pub dynamic_attrs: &'a [Attribute<'a>],
+
+    pub listeners: &'a [Listener<'a>],
+}
+
+/// A template that is created at compile time
+#[derive(Clone, Copy)]
+pub struct Template<'a> {
+    /// name, line, col, or some sort of identifier
+    pub id: &'static str,
+
+    /// All the roots of the template. ie rsx! { div {} div{} } would have two roots
+    pub roots: &'a [TemplateNode<'a>],
+}
+
+impl<'a> Eq for Template<'a> {}
+
+impl<'a> PartialEq for Template<'a> {
+    fn eq(&self, other: &Self) -> bool {
+        self.id == other.id
+    }
+}
+
+impl<'a> Hash for Template<'a> {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.id.hash(state);
+    }
+}
+
+/// A weird-ish variant of VNodes with way more limited types
+pub enum TemplateNode<'a> {
+    Element {
+        tag: &'static str,
+        attrs: &'a [TemplateAttribute],
+        children: &'a [TemplateNode<'a>],
+    },
+    Text(&'static str),
+    Dynamic(usize),
+}
+
+pub enum TemplateAttribute {
+    // todo: more values
+    Static {
+        name: &'static str,
+        value: &'static str,
+    },
+    Dynamic(usize),
+}

+ 25 - 0
packages/core/src/nodes/text.rs

@@ -0,0 +1,25 @@
+use crate::{
+    innerlude::{
+        AttributeValue, ComponentPtr, Element, IntoAttributeValue, Properties, Scope, ScopeId,
+        ScopeState, Template,
+    },
+    AnyEvent, Component, ElementId,
+};
+use bumpalo::{boxed::Box as BumpBox, Bump};
+use std::{
+    cell::{Cell, RefCell},
+    fmt::{Arguments, Debug, Formatter},
+};
+
+/// A bump-allocated string slice and metadata.
+pub struct VText<'src> {
+    /// The [`ElementId`] of the VText.
+    pub id: Cell<Option<ElementId>>,
+
+    /// The text of the VText.
+    pub text: &'src str,
+
+    /// An indiciation if this VText can be ignored during diffing
+    /// Is usually only when there are no strings to be formatted (so the text is &'static str)
+    pub is_static: bool,
+}

+ 30 - 30
packages/core/src/properties.rs

@@ -64,36 +64,36 @@ impl<'a> Properties for FragmentProps<'a> {
     }
 }
 
-/// Create inline fragments using Component syntax.
-///
-/// ## Details
-///
-/// Fragments capture a series of children without rendering extra nodes.
-///
-/// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
-/// a key is needed to identify each item.
-///
-/// ## Example
-///
-/// ```rust, ignore
-/// rsx!{
-///     Fragment { key: "abc" }
-/// }
-/// ```
-///
-/// ## Usage
-///
-/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
-/// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
-///
-/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
-///
-/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
-#[allow(non_upper_case_globals, non_snake_case)]
-pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
-    let i = cx.props.0.as_ref().map(|f| f.decouple());
-    cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
-}
+// /// Create inline fragments using Component syntax.
+// ///
+// /// ## Details
+// ///
+// /// Fragments capture a series of children without rendering extra nodes.
+// ///
+// /// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
+// /// a key is needed to identify each item.
+// ///
+// /// ## Example
+// ///
+// /// ```rust, ignore
+// /// rsx!{
+// ///     Fragment { key: "abc" }
+// /// }
+// /// ```
+// ///
+// /// ## Usage
+// ///
+// /// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
+// /// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
+// ///
+// /// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
+// ///
+// /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
+// #[allow(non_upper_case_globals, non_snake_case)]
+// pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
+//     let i = cx.props.0.as_ref().map(|f| f.decouple());
+//     cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
+// }
 
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
 /// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the

+ 90 - 185
packages/core/src/scopes.rs

@@ -1,10 +1,11 @@
-use crate::{innerlude::*, template::TemplateNodeId, unsafe_utils::extend_vnode};
+use crate::{innerlude::*, unsafe_utils::extend_vnode};
 use bumpalo::Bump;
 use futures_channel::mpsc::UnboundedSender;
-use rustc_hash::FxHashMap;
+use fxhash::FxHashMap;
 use slab::Slab;
 use std::{
     any::{Any, TypeId},
+    borrow::Borrow,
     cell::{Cell, RefCell},
     collections::{HashMap, HashSet},
     future::Future,
@@ -13,17 +14,6 @@ use std::{
     sync::Arc,
 };
 
-pub(crate) enum NodePtr {
-    VNode(*const VNode<'static>),
-    TemplateNode {
-        template_ref: TemplateRefId,
-        node_id: TemplateNodeId,
-    },
-    Phantom,
-}
-
-pub(crate) type NodeSlab = Slab<NodePtr>;
-
 /// for traceability, we use the raw fn pointer to identify the function
 /// we also get the component name, but that's not necessarily unique in the app
 pub(crate) type ComponentPtr = *mut std::os::raw::c_void;
@@ -43,16 +33,9 @@ pub(crate) struct ScopeArena {
     pub scopes: RefCell<FxHashMap<ScopeId, *mut ScopeState>>,
     pub heuristics: RefCell<FxHashMap<ComponentPtr, Heuristic>>,
     pub free_scopes: RefCell<Vec<*mut ScopeState>>,
-    // All nodes are stored here. This mimics the allocations needed on the renderer side.
-    // Some allocations are needed on the renderer to render nodes in templates that are
-    // not needed in dioxus, for these allocations None is used so that the id is preseved and passive memory managment is preserved.
-    pub nodes: RefCell<NodeSlab>,
+    pub nodes: RefCell<Slab<*const VNode<'static>>>,
     pub tasks: Rc<TaskQueue>,
-    pub template_resolver: RefCell<TemplateResolver>,
-    pub templates: Rc<RefCell<FxHashMap<TemplateId, Rc<RefCell<Template>>>>>,
-    // this is used to store intermidiate artifacts of creating templates, so that the lifetime aligns with Mutations<'bump>.
-    pub template_bump: Bump,
-    pub template_refs: RefCell<Slab<*const VTemplateRef<'static>>>,
+    pub template_cache: RefCell<HashSet<Template<'static>>>,
 }
 
 impl ScopeArena {
@@ -75,9 +58,7 @@ impl ScopeArena {
 
         let node = bump.alloc(VNode::Element(el));
         let mut nodes = Slab::new();
-        let root_id = nodes.insert(NodePtr::VNode(unsafe {
-            std::mem::transmute(node as *const _)
-        }));
+        let root_id = nodes.insert(unsafe { std::mem::transmute(node as *const _) });
 
         debug_assert_eq!(root_id, 0);
 
@@ -94,10 +75,7 @@ impl ScopeArena {
                 gen: Cell::new(0),
                 sender,
             }),
-            template_resolver: RefCell::new(TemplateResolver::default()),
-            templates: Rc::new(RefCell::new(FxHashMap::default())),
-            template_bump: Bump::new(),
-            template_refs: RefCell::new(Slab::new()),
+            template_cache: RefCell::new(HashSet::new()),
         }
     }
 
@@ -118,6 +96,7 @@ impl ScopeArena {
         vcomp: Box<dyn AnyProps>,
         parent_scope: Option<ScopeId>,
         container: ElementId,
+        subtree: u32,
     ) -> ScopeId {
         // Increment the ScopeId system. ScopeIDs are never reused
         let new_scope_id = ScopeId(self.scope_gen.get());
@@ -147,6 +126,7 @@ impl ScopeArena {
             scope.height = height;
             scope.fnptr = fc_ptr;
             scope.props.get_mut().replace(vcomp);
+            scope.subtree.set(subtree);
             scope.frames[0].reset();
             scope.frames[1].reset();
             scope.shared_contexts.get_mut().clear();
@@ -177,6 +157,10 @@ impl ScopeArena {
                     props: RefCell::new(Some(vcomp)),
                     frames: [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)],
 
+                    // todo: subtrees
+                    subtree: Cell::new(0),
+                    is_subtree_root: Cell::default(),
+
                     generation: 0.into(),
 
                     tasks: self.tasks.clone(),
@@ -190,8 +174,6 @@ impl ScopeArena {
                     hook_arena: Bump::new(),
                     hook_vals: RefCell::new(Vec::with_capacity(hook_capacity)),
                     hook_idx: Cell::default(),
-
-                    templates: self.templates.clone(),
                 }),
             );
         }
@@ -227,61 +209,13 @@ impl ScopeArena {
         let key = entry.key();
         let id = ElementId(key);
         let node = unsafe { extend_vnode(node) };
-        entry.insert(NodePtr::VNode(node as *const _));
-        id
-    }
-
-    pub fn reserve_template_ref<'a>(&self, template_ref: &'a VTemplateRef<'a>) -> TemplateRefId {
-        let mut refs = self.template_refs.borrow_mut();
-        let entry = refs.vacant_entry();
-        let key = entry.key();
-        let id = TemplateRefId(key);
-        let static_ref: &VTemplateRef<'static> = unsafe { std::mem::transmute(template_ref) };
-        entry.insert(static_ref as *const _);
-        id
-    }
-
-    pub fn reserve_template_node(
-        &self,
-        template_ref_id: TemplateRefId,
-        node_id: TemplateNodeId,
-    ) -> ElementId {
-        let mut els = self.nodes.borrow_mut();
-        let entry = els.vacant_entry();
-        let key = entry.key();
-        let id = ElementId(key);
-        entry.insert(NodePtr::TemplateNode {
-            template_ref: template_ref_id,
-            node_id,
-        });
-        id
-    }
-
-    pub fn reserve_phantom_node(&self) -> ElementId {
-        let mut els = self.nodes.borrow_mut();
-        let entry = els.vacant_entry();
-        let key = entry.key();
-        let id = ElementId(key);
-        entry.insert(NodePtr::Phantom);
+        entry.insert(node as *const _);
         id
     }
 
     pub fn update_node<'a>(&self, node: &'a VNode<'a>, id: ElementId) {
         let node = unsafe { extend_vnode(node) };
-        *self.nodes.borrow_mut().get_mut(id.0).unwrap() = NodePtr::VNode(node);
-    }
-
-    pub fn update_template_ref<'a>(
-        &self,
-        template_ref_id: TemplateRefId,
-        template_ref: &'a VTemplateRef<'a>,
-    ) {
-        let template_ref = unsafe { std::mem::transmute(template_ref) };
-        *self
-            .template_refs
-            .borrow_mut()
-            .get_mut(template_ref_id.0)
-            .unwrap() = template_ref;
+        *self.nodes.borrow_mut().get_mut(id.0).unwrap() = node;
     }
 
     pub fn collect_garbage(&self, id: ElementId) {
@@ -369,11 +303,11 @@ impl ScopeArena {
             frame.node.set(unsafe { extend_vnode(node) });
         } else {
             let frame = scope.wip_frame();
-            let node = frame
-                .bump
-                .alloc(VNode::Placeholder(frame.bump.alloc(VPlaceholder {
-                    id: Cell::default(),
-                })));
+            let node = frame.bump.alloc(VNode::Text(frame.bump.alloc(VText {
+                id: Cell::default(),
+                text: "asd",
+                is_static: false,
+            })));
             frame.node.set(unsafe { extend_vnode(node) });
         }
 
@@ -389,92 +323,16 @@ impl ScopeArena {
         let state = Rc::new(BubbleState::new());
 
         while let Some(id) = cur_el.take() {
-            if state.canceled.get() {
-                // stop bubbling if canceled
-                return;
-            }
-            if let Some(ptr) = nodes.get(id.0) {
-                match ptr {
-                    NodePtr::VNode(ptr) => {
-                        let real_el = unsafe { &**ptr };
-                        log::trace!("looking for listener on {:?}", real_el);
-
-                        if let VNode::Element(real_el) = real_el {
-                            for listener in real_el.listeners.iter() {
-                                if listener.event == event.name {
-                                    log::trace!("calling listener {:?}", listener.event);
-
-                                    let mut cb = listener.callback.borrow_mut();
-                                    if let Some(cb) = cb.as_mut() {
-                                        // todo: arcs are pretty heavy to clone
-                                        // we really want to convert arc to rc
-                                        // unfortunately, the SchedulerMsg must be send/sync to be sent across threads
-                                        // we could convert arc to rc internally or something
-                                        (cb)(AnyEvent {
-                                            bubble_state: state.clone(),
-                                            data: event.data.clone(),
-                                        });
-                                    }
-                                    break;
-                                }
-                            }
-
-                            cur_el = real_el.parent.get();
-                        }
-                    }
-                    NodePtr::TemplateNode {
-                        node_id,
-                        template_ref,
-                    } => {
-                        let template_refs = self.template_refs.borrow();
-                        let template_ptr = template_refs.get(template_ref.0).unwrap();
-                        let template_ref = unsafe { &**template_ptr };
-                        log::trace!("looking for listener in node {:?}", node_id);
-                        let templates = self.templates.borrow();
-                        let template = templates.get(&template_ref.template_id).unwrap();
-                        cur_el = template.borrow().with_nodes(
-                            bubble_template,
-                            bubble_template,
-                            (*node_id, template_ref, event, &state),
-                        );
-                    }
-                    _ => panic!("Expected Real Node"),
-                }
-            }
+            if let Some(el) = nodes.get(id.0) {
+                let real_el = unsafe { &**el };
 
-            if !event.bubbles {
-                return;
-            }
-        }
-
-        fn bubble_template<'b, Attributes, V, Children, Listeners, TextSegments, Text, Nodes>(
-            nodes: &Nodes,
-            ctx: (
-                TemplateNodeId,
-                &VTemplateRef<'b>,
-                &UserEvent,
-                &Rc<BubbleState>,
-            ),
-        ) -> Option<ElementId>
-        where
-            Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (start, template_ref, event, state) = ctx;
-            let dynamic_context = &template_ref.dynamic_context;
-            let mut current = &nodes.as_ref()[start.0];
-            loop {
-                if let TemplateNodeType::Element(el) = &current.node_type {
-                    let TemplateElement { listeners, .. } = el;
-                    for listener_idx in listeners.as_ref() {
-                        let listener = dynamic_context.resolve_listener(*listener_idx);
+                if let VNode::Element(real_el) = real_el {
+                    for listener in real_el.listeners.borrow().iter() {
                         if listener.event == event.name {
-                            log::trace!("calling listener {:?}", listener.event);
+                            if state.canceled.get() {
+                                // stop bubbling if canceled
+                                return;
+                            }
 
                             let mut cb = listener.callback.borrow_mut();
                             if let Some(cb) = cb.as_mut() {
@@ -487,17 +345,14 @@ impl ScopeArena {
                                     data: event.data.clone(),
                                 });
                             }
-                            break;
+
+                            if !event.bubbles {
+                                return;
+                            }
                         }
                     }
 
-                    if let Some(id) = current.parent {
-                        current = &nodes.as_ref()[id.0];
-                    } else {
-                        return template_ref.parent.get();
-                    }
-                } else {
-                    return None;
+                    cur_el = real_el.parent.get();
                 }
             }
         }
@@ -525,10 +380,11 @@ impl ScopeArena {
 
     // this is totally okay since all our nodes are always in a valid state
     pub fn get_element(&self, id: ElementId) -> Option<&VNode> {
-        self.nodes.borrow().get(id.0).and_then(|ptr| match ptr {
-            NodePtr::VNode(ptr) => Some(unsafe { extend_vnode(&**ptr) }),
-            _ => None,
-        })
+        self.nodes
+            .borrow()
+            .get(id.0)
+            .copied()
+            .map(|ptr| unsafe { extend_vnode(&*ptr) })
     }
 }
 
@@ -613,6 +469,10 @@ pub struct ScopeState {
     pub(crate) our_arena_idx: ScopeId,
     pub(crate) height: u32,
     pub(crate) fnptr: ComponentPtr,
+
+    // todo: subtrees
+    pub(crate) is_subtree_root: Cell<bool>,
+    pub(crate) subtree: Cell<u32>,
     pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
 
     // nodes, items
@@ -628,9 +488,6 @@ pub struct ScopeState {
     // shared state -> todo: move this out of scopestate
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
     pub(crate) tasks: Rc<TaskQueue>,
-
-    // templates
-    pub(crate) templates: Rc<RefCell<FxHashMap<TemplateId, Rc<RefCell<Template>>>>>,
 }
 
 pub struct SelfReferentialItems<'a> {
@@ -640,6 +497,52 @@ pub struct SelfReferentialItems<'a> {
 
 // Public methods exposed to libraries and components
 impl ScopeState {
+    /// Get the subtree ID that this scope belongs to.
+    ///
+    /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
+    /// the mutations to the correct window/portal/subtree.
+    ///
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// let mut dom = VirtualDom::new(|cx| cx.render(rsx!{ div {} }));
+    /// dom.rebuild();
+    ///
+    /// let base = dom.base_scope();
+    ///
+    /// assert_eq!(base.subtree(), 0);
+    /// ```
+    ///
+    /// todo: enable
+    pub(crate) fn _subtree(&self) -> u32 {
+        self.subtree.get()
+    }
+
+    /// Create a new subtree with this scope as the root of the subtree.
+    ///
+    /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
+    /// the mutations to the correct window/portal/subtree.
+    ///
+    /// This method
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// fn App(cx: Scope) -> Element {
+    ///     render!(div { "Subtree {id}"})
+    /// };
+    /// ```
+    ///
+    /// todo: enable subtree
+    pub(crate) fn _create_subtree(&self) -> Option<u32> {
+        if self.is_subtree_root.get() {
+            None
+        } else {
+            todo!()
+        }
+    }
+
     /// Get the height of this Scope - IE the number of scopes above it.
     ///
     /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
@@ -1002,6 +905,8 @@ impl ScopeState {
         self.hook_idx.set(0);
         self.parent_scope = None;
         self.generation.set(0);
+        self.is_subtree_root.set(false);
+        self.subtree.set(0);
 
         // next: shared context data
         self.shared_contexts.get_mut().clear();

+ 0 - 1287
packages/core/src/template.rs

@@ -1,1287 +0,0 @@
-//! Templates are used to skip diffing on any static parts of the rsx.
-//! TemplateNodes are different from VNodes in that they can contain partial dynamic and static content in the same node.
-//! For example:
-//! ```
-//! rsx! {
-//!     div {
-//!         color: "{color}",
-//!         "Hello, world",
-//!         "{dynamic_text_1}",
-//!         "{dynamic_text_2}",
-//!         dynamic_iterator
-//!     }
-//! }
-//! ```
-//! The above will turn into a template that contains information on how to build div { "Hello, world" } and then every refrence to the template will hydrate with the value of dynamic_text_1, dynamic_text_2, dynamic_iterator, and the color property.
-//! The rsx macro will both generate the template and the `DynamicNodeMapping` struct that contains the information on what parts of the template depend on each value of the dynamic context.
-//! In templates with many dynamic parts, this allows the diffing algorithm to skip traversing the template to find what part to hydrate.
-//! Each dynamic part will contain a index into the dynamic context to determine what value to use. The indexes are origionally ordered by traversing the tree depth first from the root.
-//! The indexes for the above would be as follows:
-//! ```
-//! rsx! {
-//!     div {
-//!         color: "{color}", // attribute index 0
-//!         "Hello, world",
-//!         "{dynamic_text_1}", // text index 0
-//!         "{dynamic_text_2}", // text index 1
-//!         dynamic_iterator // node index 0
-//!     }
-//! }
-//! ```
-//! Including these indexes allows hot reloading to move the dynamic parts of the template around.
-//! The templates generated by rsx are stored as 'static refrences, but you can change the template at runtime to allow hot reloading.
-//! The template could be replaced with a new one at runtime:
-//! ```
-//! rsx! {
-//!     div {
-//!         "Hello, world",
-//!         dynamic_iterator // node index 0
-//!         h1 {
-//!             background_color: "{color}" // attribute index 0
-//!             "{dynamic_text_2}", // text index 1
-//!         }
-//!         h1 {
-//!            color: "{color}", // attribute index 0
-//!            "{dynamic_text_1}", // text index 0
-//!         }
-//!     }
-//! }
-//! ```
-//! Notice how the indecies are no longer in depth first traversal order, and indecies are no longer unique. Attributes and dynamic parts of the text can be duplicated, but dynamic vnodes and componets cannot.
-//! To minimize the cost of allowing hot reloading on applications that do not use it there are &'static and owned versions of template nodes, and dynamic node mapping.
-//!
-//! Notes:
-//! 1) The template allow diffing to scale with reactivity.
-//! With a virtual dom the diffing cost scales with the number of nodes in the dom. With templates the cost scales with the number of dynamic parts of the dom. The dynamic template context links any parts of the template that can change which allows the diffing algorithm to skip traversing the template and find what part to hydrate in constant time.
-
-use once_cell::unsync::OnceCell;
-use std::{
-    cell::{Cell, RefCell},
-    hash::Hash,
-    marker::PhantomData,
-    ptr,
-};
-
-use rustc_hash::FxHashMap;
-
-use bumpalo::Bump;
-
-use crate::{
-    diff::DiffState, dynamic_template_context::TemplateContext, nodes::AttributeDiscription,
-    scopes::ScopeArena, Attribute, AttributeValue, ElementId, Mutations, OwnedAttributeValue,
-    OwnedDynamicNodeMapping, StaticDynamicNodeMapping,
-};
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) struct TemplateRefId(pub usize);
-
-/// The location of a charicter. Used to track the location of rsx calls for hot reloading.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize)
-)]
-pub struct StaticCodeLocation {
-    /// the path to the crate that contains the location
-    pub crate_path: &'static str,
-    /// the path within the crate to the file that contains the location
-    pub file_path: &'static str,
-    /// the line number of the location
-    pub line: u32,
-    /// the column number of the location
-    pub column: u32,
-}
-
-/// The location of a charicter. Used to track the location of rsx calls for hot reloading.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct OwnedCodeLocation {
-    /// the path to the crate that contains the location
-    pub crate_path: String,
-    /// the path within the crate to the file that contains the location
-    pub file_path: String,
-    /// the line number of the location
-    pub line: u32,
-    /// the column number of the location
-    pub column: u32,
-}
-
-/// The location of a charicter. Used to track the location of rsx calls for hot reloading.
-#[derive(Clone, Eq, Debug)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize)
-)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    serde(untagged)
-)]
-pub enum CodeLocation {
-    /// A loctation that is created at compile time.
-    Static(&'static StaticCodeLocation),
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    /// A loctation that is created at runtime.
-    Dynamic(Box<OwnedCodeLocation>),
-}
-
-#[cfg(all(feature = "serialize", any(feature = "hot-reload", debug_assertions)))]
-impl<'de> serde::Deserialize<'de> for CodeLocation {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        Ok(Self::Dynamic(Box::new(OwnedCodeLocation::deserialize(
-            deserializer,
-        )?)))
-    }
-}
-
-impl Hash for CodeLocation {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        match self {
-            CodeLocation::Static(loc) => {
-                let loc: &'static _ = *loc;
-                state.write_usize((loc as *const _) as usize);
-            }
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => {
-                let (crate_path, file_path): (&str, &str) = (&loc.crate_path, &loc.file_path);
-                crate_path.hash(state);
-                file_path.hash(state);
-                state.write_u32(loc.line);
-                state.write_u32(loc.column);
-            }
-        }
-    }
-}
-
-impl PartialEq for CodeLocation {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (Self::Static(l), Self::Static(r)) => ptr::eq(*l, *r),
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            (Self::Dynamic(l), Self::Dynamic(r)) => l == r,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            (Self::Static(l), Self::Dynamic(r)) => **r == **l,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            (Self::Dynamic(l), Self::Static(r)) => **l == **r,
-        }
-    }
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-impl PartialEq<StaticCodeLocation> for OwnedCodeLocation {
-    fn eq(&self, other: &StaticCodeLocation) -> bool {
-        self.crate_path == other.crate_path
-            && self.file_path == other.file_path
-            && self.line == other.line
-            && self.column == other.column
-    }
-}
-
-impl CodeLocation {
-    /// Get the line number of the location.
-    pub fn line(&self) -> u32 {
-        match self {
-            CodeLocation::Static(loc) => loc.line,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => loc.line,
-        }
-    }
-
-    /// Get the column number of the location.
-    pub fn column(&self) -> u32 {
-        match self {
-            CodeLocation::Static(loc) => loc.column,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => loc.column,
-        }
-    }
-
-    /// Get the path within the crate to the location.
-    pub fn file_path(&self) -> &str {
-        match self {
-            CodeLocation::Static(loc) => loc.file_path,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => loc.file_path.as_str(),
-        }
-    }
-
-    /// Get the path of the crate to the location.
-    pub fn crate_path(&self) -> &str {
-        match self {
-            CodeLocation::Static(loc) => loc.crate_path,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => loc.crate_path.as_str(),
-        }
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    /// Create an owned code location from a code location.
-    pub fn to_owned(&self) -> OwnedCodeLocation {
-        match self {
-            CodeLocation::Static(loc) => OwnedCodeLocation {
-                crate_path: loc.crate_path.to_owned(),
-                file_path: loc.file_path.to_owned(),
-                line: loc.line,
-                column: loc.column,
-            },
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            CodeLocation::Dynamic(loc) => *loc.clone(),
-        }
-    }
-}
-
-/// get the code location of the code that called this function
-#[macro_export]
-macro_rules! get_line_num {
-    () => {{
-        const LOC: CodeLocation = CodeLocation::Static(&StaticCodeLocation {
-            crate_path: env!("CARGO_MANIFEST_DIR"),
-            file_path: file!(),
-            line: line!(),
-            column: column!(),
-        });
-        LOC
-    }};
-}
-
-/// An Template's unique identifier within the vdom.
-///
-/// `TemplateId` is a refrence to the location in the code the template was created.
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct TemplateId(pub CodeLocation);
-
-/// An Template's unique identifier within the renderer.
-///
-/// `RendererTemplateId` is a unique id of the template sent to the renderer. It is unique across time.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub struct RendererTemplateId(pub usize);
-
-impl From<RendererTemplateId> for u64 {
-    fn from(id: RendererTemplateId) -> u64 {
-        id.0 as u64
-    }
-}
-
-/// A TemplateNode's unique identifier.
-///
-/// `TemplateNodeId` is a `usize` that is only unique across the template that contains it, it is not unique across multaple instances of that template.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[cfg_attr(feature = "serialize", serde(transparent))]
-pub struct TemplateNodeId(pub usize);
-
-/// A refrence to a template along with any context needed to hydrate it
-pub struct VTemplateRef<'a> {
-    pub(crate) template_ref_id: Cell<Option<TemplateRefId>>,
-    pub template_id: TemplateId,
-    pub dynamic_context: TemplateContext<'a>,
-    /// The parent of the template
-    pub(crate) parent: Cell<Option<ElementId>>,
-    // any nodes that already have ids assigned to them in the renderer
-    pub node_ids: RefCell<Vec<OnceCell<ElementId>>>,
-}
-
-impl<'a> VTemplateRef<'a> {
-    // update the template with content from the dynamic context
-    pub(crate) fn hydrate<'b: 'a>(
-        &'b self,
-        parent: ElementId,
-        template: &Template,
-        diff_state: &mut DiffState<'a>,
-    ) {
-        fn traverse_seg<'b, T, O, Nodes, Attributes, V, Children, Listeners, TextSegments, Text>(
-            seg: &PathSeg<T, O>,
-            nodes: &Nodes,
-            diff_state: &mut DiffState<'b>,
-            template_ref: &'b VTemplateRef<'b>,
-            parent: ElementId,
-        ) where
-            Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-            T: Traversable<O>,
-            O: AsRef<[UpdateOp]>,
-        {
-            let mut current_node_id = None;
-            let mut temp_id = false;
-            for op in seg.ops.as_ref() {
-                match op {
-                    UpdateOp::StoreNode(id) => {
-                        if let Some(real_id) = template_ref.try_get_node_id(*id) {
-                            current_node_id = Some(real_id);
-                            nodes.as_ref()[id.0].hydrate(real_id, diff_state, template_ref);
-                        } else {
-                            let real_id = diff_state.scopes.reserve_template_node(
-                                template_ref.template_ref_id.get().unwrap(),
-                                *id,
-                            );
-                            current_node_id = Some(real_id);
-                            template_ref.set_node_id(*id, real_id);
-                            diff_state.mutations.store_with_id(real_id.as_u64());
-                            nodes.as_ref()[id.0].hydrate(real_id, diff_state, template_ref);
-                        }
-                    }
-                    UpdateOp::AppendChild(id) => {
-                        let node = &nodes.as_ref()[id.0];
-                        match &node.node_type {
-                            TemplateNodeType::DynamicNode(idx) => {
-                                if current_node_id.is_none() {
-                                    // create a temporary node to come back to later
-                                    let id = diff_state.scopes.reserve_phantom_node();
-                                    diff_state.mutations.store_with_id(id.as_u64());
-                                    temp_id = true;
-                                    current_node_id = Some(id);
-                                }
-                                let id = current_node_id.unwrap();
-                                let mut created = Vec::new();
-                                let node = template_ref.dynamic_context.resolve_node(*idx);
-                                diff_state.create_node(id, node, &mut created);
-                                diff_state.mutations.set_last_node(id.as_u64());
-                                diff_state.mutations.append_children(None, created);
-                            }
-                            _ => panic!("can only insert dynamic nodes"),
-                        }
-                    }
-                    UpdateOp::InsertBefore(id) | UpdateOp::InsertAfter(id) => {
-                        let node = &nodes.as_ref()[id.0];
-                        match &node.node_type {
-                            TemplateNodeType::DynamicNode(idx) => {
-                                if current_node_id.is_none() {
-                                    // create a temporary node to come back to later
-                                    let id = diff_state.scopes.reserve_phantom_node();
-                                    diff_state.mutations.store_with_id(id.as_u64());
-                                    temp_id = true;
-                                    current_node_id = Some(id);
-                                }
-                                let id = current_node_id.unwrap();
-                                let mut created = Vec::new();
-                                let node = template_ref.dynamic_context.resolve_node(*idx);
-                                diff_state.create_node(parent, node, &mut created);
-                                diff_state.mutations.set_last_node(id.as_u64());
-                                match op {
-                                    UpdateOp::InsertBefore(_) => {
-                                        diff_state.mutations.insert_before(None, created);
-                                    }
-                                    UpdateOp::InsertAfter(_) => {
-                                        diff_state.mutations.insert_after(None, created);
-                                    }
-                                    _ => unreachable!(),
-                                }
-                            }
-                            _ => panic!("can only insert dynamic nodes"),
-                        }
-                    }
-                }
-            }
-            match (seg.traverse.first_child(), seg.traverse.next_sibling()) {
-                (Some(child), Some(sibling)) => {
-                    if current_node_id.is_none() {
-                        // create a temporary node to come back to later
-                        let id = diff_state.scopes.reserve_phantom_node();
-                        diff_state.mutations.store_with_id(id.as_u64());
-                        temp_id = true;
-                        current_node_id = Some(id);
-                    }
-                    let id = current_node_id.unwrap();
-                    diff_state.mutations.first_child();
-                    traverse_seg(child, nodes, diff_state, template_ref, id);
-                    diff_state.mutations.set_last_node(id.as_u64());
-                    diff_state.mutations.next_sibling();
-                    traverse_seg(sibling, nodes, diff_state, template_ref, parent);
-                }
-                (Some(seg), None) => {
-                    if current_node_id.is_none() {
-                        let id = diff_state.scopes.reserve_phantom_node();
-                        diff_state.mutations.store_with_id(id.as_u64());
-                        temp_id = true;
-                        current_node_id = Some(id);
-                    }
-                    let id = current_node_id.unwrap();
-                    diff_state.mutations.first_child();
-                    traverse_seg(seg, nodes, diff_state, template_ref, id);
-                }
-                (None, Some(seg)) => {
-                    diff_state.mutations.next_sibling();
-                    traverse_seg(seg, nodes, diff_state, template_ref, parent);
-                }
-                (None, None) => {}
-            }
-            if temp_id {
-                if let Some(id) = current_node_id {
-                    // remove the temporary node
-                    diff_state.scopes.collect_garbage(id);
-                }
-            }
-        }
-        fn hydrate_inner<'b, Nodes, Attributes, V, Children, Listeners, TextSegments, Text>(
-            nodes: &Nodes,
-            ctx: (
-                &mut DiffState<'b>,
-                &'b VTemplateRef<'b>,
-                &Template,
-                ElementId,
-            ),
-        ) where
-            Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (diff_state, template_ref, template, parent) = ctx;
-
-            match template {
-                Template::Static(s) => {
-                    if let Some(seg) = &s.dynamic_path {
-                        diff_state.mutations.set_last_node(
-                            template_ref.get_node_id(template.root_nodes()[0]).as_u64(),
-                        );
-                        traverse_seg(seg, nodes, diff_state, template_ref, parent);
-                    }
-                }
-                Template::Owned(o) => {
-                    if let Some(seg) = &o.dynamic_path {
-                        diff_state.mutations.set_last_node(
-                            template_ref.get_node_id(template.root_nodes()[0]).as_u64(),
-                        );
-                        traverse_seg(seg, nodes, diff_state, template_ref, parent);
-                    }
-                }
-            }
-        }
-
-        template.with_nodes(
-            hydrate_inner,
-            hydrate_inner,
-            (diff_state, self, template, parent),
-        );
-    }
-
-    pub(crate) fn get_node_id(&self, id: TemplateNodeId) -> ElementId {
-        self.try_get_node_id(id).unwrap()
-    }
-
-    pub(crate) fn try_get_node_id(&self, id: TemplateNodeId) -> Option<ElementId> {
-        let node_ids = self.node_ids.borrow();
-        node_ids.get(id.0).and_then(|cell| cell.get().copied())
-    }
-
-    pub(crate) fn set_node_id(&self, id: TemplateNodeId, real_id: ElementId) {
-        let mut ids = self.node_ids.borrow_mut();
-        if ids.len() <= id.0 {
-            ids.resize(id.0 + 1, OnceCell::new());
-        }
-        ids[id.0].set(real_id).unwrap();
-    }
-}
-
-/// A template that is created at compile time
-#[derive(Debug, PartialEq)]
-pub struct StaticTemplate {
-    /// The nodes in the template
-    pub nodes: StaticTemplateNodes,
-    /// The ids of the root nodes in the template
-    pub root_nodes: StaticRootNodes,
-    /// Any nodes that contain dynamic components. This is stored in the tmeplate to avoid traversing the tree every time a template is refrenced.
-    pub dynamic_mapping: StaticDynamicNodeMapping,
-    /// The path to take to update the template with dynamic content (starts from the first root node)
-    pub dynamic_path: Option<StaticPathSeg>,
-}
-
-/// A template that is created at runtime
-#[derive(Debug, Clone, PartialEq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-pub struct OwnedTemplate {
-    /// The nodes in the template
-    pub nodes: OwnedTemplateNodes,
-    /// The ids of the root nodes in the template
-    pub root_nodes: OwnedRootNodes,
-    /// Any nodes that contain dynamic components. This is stored in the tmeplate to avoid traversing the tree every time a template is refrenced.
-    pub dynamic_mapping: OwnedDynamicNodeMapping,
-    /// The path to take to update the template with dynamic content
-    pub dynamic_path: Option<OwnedPathSeg>,
-}
-
-/// A template used to skip diffing on some static parts of the rsx
-#[derive(Debug, Clone, PartialEq)]
-pub enum Template {
-    /// A template that is createded at compile time
-    Static(&'static StaticTemplate),
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    /// A template that is created at runtime
-    Owned(OwnedTemplate),
-}
-
-impl Template {
-    pub(crate) fn create<'b>(&self, mutations: &mut Mutations<'b>, bump: &'b Bump, id: ElementId) {
-        let children = match self {
-            Template::Static(s) => self.count_real_nodes(s.root_nodes),
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => self.count_real_nodes(&o.root_nodes),
-        };
-        mutations.create_element("template", None, Some(id.into()), children as u32);
-        let empty = match self {
-            Template::Static(s) => s.nodes.is_empty(),
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => o.nodes.is_empty(),
-        };
-        if !empty {
-            let roots = match self {
-                Template::Static(s) => s.root_nodes,
-                #[cfg(any(feature = "hot-reload", debug_assertions))]
-                Template::Owned(o) => &o.root_nodes,
-            };
-            for root in roots {
-                self.create_node(mutations, bump, *root);
-            }
-        }
-    }
-
-    fn create_node<'b>(&self, mutations: &mut Mutations<'b>, bump: &'b Bump, id: TemplateNodeId) {
-        fn crate_node_inner<'b, Attributes, V, Children, Listeners, TextSegments, Text>(
-            node: &TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>,
-            ctx: (&mut Mutations<'b>, &'b Bump, &Template),
-        ) where
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            let (mutations, bump, template) = ctx;
-            match &node.node_type {
-                TemplateNodeType::Element(el) => {
-                    let TemplateElement {
-                        tag,
-                        namespace,
-                        attributes,
-                        children,
-                        ..
-                    } = el;
-                    mutations.create_element(
-                        tag,
-                        *namespace,
-                        None,
-                        template.count_real_nodes(children.as_ref()) as u32,
-                    );
-                    for attr in attributes.as_ref() {
-                        if let TemplateAttributeValue::Static(val) = &attr.value {
-                            let val: AttributeValue<'b> = val.allocate(bump);
-                            let attribute = Attribute {
-                                attribute: attr.attribute,
-                                is_static: true,
-                                value: val,
-                            };
-                            mutations.set_attribute(bump.alloc(attribute), None);
-                        }
-                    }
-                    for child in children.as_ref() {
-                        template.create_node(mutations, bump, *child);
-                    }
-                }
-                TemplateNodeType::Text(text) => {
-                    let mut text_iter = text.segments.as_ref().iter();
-                    if let (Some(TextTemplateSegment::Static(txt)), None) =
-                        (text_iter.next(), text_iter.next())
-                    {
-                        mutations.create_text_node(bump.alloc_str(txt.as_ref()), None);
-                    } else {
-                        mutations.create_text_node("", None);
-                    }
-                }
-                TemplateNodeType::DynamicNode(_) => {}
-            }
-        }
-        self.with_node(
-            id,
-            crate_node_inner,
-            crate_node_inner,
-            (mutations, bump, self),
-        );
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub(crate) fn with_node<F1, F2, Ctx, R>(
-        &self,
-        id: TemplateNodeId,
-        mut f1: F1,
-        mut f2: F2,
-        ctx: Ctx,
-    ) -> R
-    where
-        F1: FnMut(&StaticTemplateNode, Ctx) -> R,
-        F2: FnMut(&OwnedTemplateNode, Ctx) -> R,
-    {
-        match self {
-            Template::Static(s) => f1(&s.nodes[id.0], ctx),
-            Template::Owned(o) => f2(&o.nodes[id.0], ctx),
-        }
-    }
-
-    #[cfg(not(any(feature = "hot-reload", debug_assertions)))]
-    pub(crate) fn with_node<F1, F2, Ctx, R>(
-        &self,
-        id: TemplateNodeId,
-        mut f1: F1,
-        _f2: F2,
-        ctx: Ctx,
-    ) -> R
-    where
-        F1: FnMut(&StaticTemplateNode, Ctx) -> R,
-        F2: FnMut(&StaticTemplateNode, Ctx) -> R,
-    {
-        match self {
-            Template::Static(s) => f1(&s.nodes[id.0], ctx),
-        }
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub(crate) fn with_nodes<'a, F1, F2, Ctx, R>(&'a self, mut f1: F1, mut f2: F2, ctx: Ctx) -> R
-    where
-        F1: FnMut(&'a &'static [StaticTemplateNode], Ctx) -> R,
-        F2: FnMut(&'a Vec<OwnedTemplateNode>, Ctx) -> R,
-    {
-        match self {
-            Template::Static(s) => f1(&s.nodes, ctx),
-            Template::Owned(o) => f2(&o.nodes, ctx),
-        }
-    }
-
-    #[cfg(not(any(feature = "hot-reload", debug_assertions)))]
-    pub(crate) fn with_nodes<'a, F1, F2, Ctx, R>(&'a self, mut f1: F1, _f2: F2, ctx: Ctx) -> R
-    where
-        F1: FnMut(&'a &'static [StaticTemplateNode], Ctx) -> R,
-        F2: FnMut(&'a &'static [StaticTemplateNode], Ctx) -> R,
-    {
-        match self {
-            Template::Static(s) => f1(&s.nodes, ctx),
-        }
-    }
-
-    fn count_real_nodes(&self, ids: &[TemplateNodeId]) -> usize {
-        fn count_real_nodes_inner<Nodes, Attributes, V, Children, Listeners, TextSegments, Text>(
-            nodes: &Nodes,
-            id: TemplateNodeId,
-        ) -> usize
-        where
-            Nodes: AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-            Attributes: AsRef<[TemplateAttribute<V>]>,
-            V: TemplateValue,
-            Children: AsRef<[TemplateNodeId]>,
-            Listeners: AsRef<[usize]>,
-            TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-            Text: AsRef<str>,
-        {
-            match &nodes.as_ref()[id.0].node_type {
-                TemplateNodeType::DynamicNode(_) => 0,
-                TemplateNodeType::Element(_) => 1,
-                TemplateNodeType::Text(_) => 1,
-            }
-        }
-        ids.iter()
-            .map(|id| self.with_nodes(count_real_nodes_inner, count_real_nodes_inner, *id))
-            .sum()
-    }
-
-    pub(crate) fn volatile_attributes<'a>(
-        &'a self,
-    ) -> Box<dyn Iterator<Item = (TemplateNodeId, usize)> + 'a> {
-        match self {
-            Template::Static(s) => Box::new(
-                s.dynamic_mapping
-                    .volatile_attributes
-                    .as_ref()
-                    .iter()
-                    .copied(),
-            ),
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => Box::new(o.dynamic_mapping.volatile_attributes.iter().copied()),
-        }
-    }
-
-    pub(crate) fn get_dynamic_nodes_for_text_index(&self, idx: usize) -> &[TemplateNodeId] {
-        match self {
-            Template::Static(s) => s.dynamic_mapping.text[idx],
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => o.dynamic_mapping.text[idx].as_ref(),
-        }
-    }
-
-    pub(crate) fn get_dynamic_nodes_for_attribute_index(
-        &self,
-        idx: usize,
-    ) -> &[(TemplateNodeId, usize)] {
-        match self {
-            Template::Static(s) => s.dynamic_mapping.attributes[idx],
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => o.dynamic_mapping.attributes[idx].as_ref(),
-        }
-    }
-
-    pub(crate) fn root_nodes(&self) -> &[TemplateNodeId] {
-        match self {
-            Template::Static(s) => s.root_nodes,
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            Template::Owned(o) => &o.root_nodes,
-        }
-    }
-}
-
-/// A array of stack allocated Template nodes
-pub type StaticTemplateNodes = &'static [StaticTemplateNode];
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A vec of heep allocated Template nodes
-pub type OwnedTemplateNodes = Vec<OwnedTemplateNode>;
-
-/// A stack allocated Template node
-pub type StaticTemplateNode = TemplateNode<
-    &'static [TemplateAttribute<StaticAttributeValue>],
-    StaticAttributeValue,
-    &'static [TemplateNodeId],
-    &'static [usize],
-    &'static [TextTemplateSegment<&'static str>],
-    &'static str,
->;
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A heap allocated Template node
-pub type OwnedTemplateNode = TemplateNode<
-    Vec<TemplateAttribute<OwnedAttributeValue>>,
-    OwnedAttributeValue,
-    Vec<TemplateNodeId>,
-    Vec<usize>,
-    Vec<TextTemplateSegment<String>>,
-    String,
->;
-
-/// A stack allocated list of root Template nodes
-pub type StaticRootNodes = &'static [TemplateNodeId];
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A heap allocated list of root Template nodes
-pub type OwnedRootNodes = Vec<TemplateNodeId>;
-
-/// Templates can only contain a limited subset of VNodes and keys are not needed, as diffing will be skipped.
-/// Dynamic parts of the Template are inserted into the VNode using the `TemplateContext` by traversing the tree in order and filling in dynamic parts
-/// This template node is generic over the storage of the nodes to allow for owned and &'static versions.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>
-where
-    Attributes: AsRef<[TemplateAttribute<V>]>,
-    V: TemplateValue,
-    Children: AsRef<[TemplateNodeId]>,
-    Listeners: AsRef<[usize]>,
-    TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-    Text: AsRef<str>,
-{
-    /// The ID of the [`TemplateNode`]. Note that this is not an elenemt id, and should be allocated seperately from VNodes on the frontend.
-    pub id: TemplateNodeId,
-    /// The depth of the node in the template node tree
-    /// Root nodes have a depth of 0
-    pub depth: usize,
-    /// The type of the [`TemplateNode`].
-    pub node_type: TemplateNodeType<Attributes, V, Children, Listeners, TextSegments, Text>,
-    /// The parent of this node.
-    pub parent: Option<TemplateNodeId>,
-}
-
-impl<Attributes, V, Children, Listeners, TextSegments, Text>
-    TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>
-where
-    Attributes: AsRef<[TemplateAttribute<V>]>,
-    V: TemplateValue,
-    Children: AsRef<[TemplateNodeId]>,
-    Listeners: AsRef<[usize]>,
-    TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-    Text: AsRef<str>,
-{
-    fn hydrate<'b>(
-        &self,
-        real_node_id: ElementId,
-        diff_state: &mut DiffState<'b>,
-        template_ref: &'b VTemplateRef<'b>,
-    ) {
-        match &self.node_type {
-            TemplateNodeType::Element(el) => {
-                let TemplateElement {
-                    attributes,
-                    listeners,
-                    ..
-                } = el;
-                for attr in attributes.as_ref() {
-                    if let TemplateAttributeValue::Dynamic(idx) = attr.value {
-                        let attribute = Attribute {
-                            attribute: attr.attribute,
-                            value: template_ref
-                                .dynamic_context
-                                .resolve_attribute(idx)
-                                .to_owned(),
-                            is_static: false,
-                        };
-                        let scope_bump = diff_state.current_scope_bump();
-                        diff_state.mutations.set_attribute(
-                            scope_bump.alloc(attribute),
-                            Some(real_node_id.as_u64()),
-                        );
-                    }
-                }
-                for listener_idx in listeners.as_ref() {
-                    let listener = template_ref.dynamic_context.resolve_listener(*listener_idx);
-                    listener.mounted_node.set(Some(real_node_id));
-                    diff_state
-                        .mutations
-                        .new_event_listener(listener, diff_state.current_scope());
-                }
-            }
-            TemplateNodeType::Text(text) => {
-                let scope_bump = diff_state.current_scope_bump();
-                let mut bump_str =
-                    bumpalo::collections::String::with_capacity_in(text.min_size, scope_bump);
-                template_ref
-                    .dynamic_context
-                    .resolve_text_into(text, &mut bump_str);
-
-                diff_state
-                    .mutations
-                    .set_text(bump_str.into_bump_str(), Some(real_node_id.as_u64()));
-            }
-            TemplateNodeType::DynamicNode(_) => {}
-        }
-    }
-}
-
-/// A template for an attribute
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct TemplateAttribute<V: TemplateValue> {
-    /// The discription of the attribute
-    pub attribute: AttributeDiscription,
-    /// The value of the attribute
-    pub value: TemplateAttributeValue<V>,
-}
-
-/// A template attribute value that is either dynamic or static
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub enum TemplateAttributeValue<V: TemplateValue> {
-    /// A static attribute
-    Static(V),
-    /// A dynamic attribute
-    Dynamic(usize),
-}
-
-/// The value for an attribute in a template
-pub trait TemplateValue {
-    /// Allocates the attribute in a bump allocator
-    fn allocate<'b>(&self, bump: &'b Bump) -> AttributeValue<'b>;
-}
-
-impl TemplateValue for StaticAttributeValue {
-    fn allocate<'b>(&self, bump: &'b Bump) -> AttributeValue<'b> {
-        match self.clone() {
-            StaticAttributeValue::Text(txt) => AttributeValue::Text(bump.alloc_str(txt)),
-            StaticAttributeValue::Bytes(bytes) => {
-                AttributeValue::Bytes(bump.alloc_slice_copy(bytes))
-            }
-            StaticAttributeValue::Float32(f) => AttributeValue::Float32(f),
-            StaticAttributeValue::Float64(f) => AttributeValue::Float64(f),
-            StaticAttributeValue::Int32(i) => AttributeValue::Int32(i),
-            StaticAttributeValue::Int64(i) => AttributeValue::Int64(i),
-            StaticAttributeValue::Uint32(u) => AttributeValue::Uint32(u),
-            StaticAttributeValue::Uint64(u) => AttributeValue::Uint64(u),
-            StaticAttributeValue::Bool(b) => AttributeValue::Bool(b),
-            StaticAttributeValue::Vec3Float(f1, f2, f3) => AttributeValue::Vec3Float(f1, f2, f3),
-            StaticAttributeValue::Vec3Int(i1, i2, i3) => AttributeValue::Vec3Int(i1, i2, i3),
-            StaticAttributeValue::Vec3Uint(u1, u2, u3) => AttributeValue::Vec3Uint(u1, u2, u3),
-            StaticAttributeValue::Vec4Float(f1, f2, f3, f4) => {
-                AttributeValue::Vec4Float(f1, f2, f3, f4)
-            }
-            StaticAttributeValue::Vec4Int(i1, i2, i3, i4) => {
-                AttributeValue::Vec4Int(i1, i2, i3, i4)
-            }
-            StaticAttributeValue::Vec4Uint(u1, u2, u3, u4) => {
-                AttributeValue::Vec4Uint(u1, u2, u3, u4)
-            }
-        }
-    }
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-impl TemplateValue for OwnedAttributeValue {
-    fn allocate<'b>(&self, bump: &'b Bump) -> AttributeValue<'b> {
-        match self.clone() {
-            OwnedAttributeValue::Text(txt) => AttributeValue::Text(bump.alloc(txt)),
-            OwnedAttributeValue::Bytes(bytes) => AttributeValue::Bytes(bump.alloc(bytes)),
-            OwnedAttributeValue::Float32(f) => AttributeValue::Float32(f),
-            OwnedAttributeValue::Float64(f) => AttributeValue::Float64(f),
-            OwnedAttributeValue::Int32(i) => AttributeValue::Int32(i),
-            OwnedAttributeValue::Int64(i) => AttributeValue::Int64(i),
-            OwnedAttributeValue::Uint32(u) => AttributeValue::Uint32(u),
-            OwnedAttributeValue::Uint64(u) => AttributeValue::Uint64(u),
-            OwnedAttributeValue::Bool(b) => AttributeValue::Bool(b),
-            OwnedAttributeValue::Vec3Float(f1, f2, f3) => AttributeValue::Vec3Float(f1, f2, f3),
-            OwnedAttributeValue::Vec3Int(i1, i2, i3) => AttributeValue::Vec3Int(i1, i2, i3),
-            OwnedAttributeValue::Vec3Uint(u1, u2, u3) => AttributeValue::Vec3Uint(u1, u2, u3),
-            OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => {
-                AttributeValue::Vec4Float(f1, f2, f3, f4)
-            }
-            OwnedAttributeValue::Vec4Int(i1, i2, i3, i4) => AttributeValue::Vec4Int(i1, i2, i3, i4),
-            OwnedAttributeValue::Vec4Uint(u1, u2, u3, u4) => {
-                AttributeValue::Vec4Uint(u1, u2, u3, u4)
-            }
-            OwnedAttributeValue::Any(owned) => {
-                AttributeValue::Any(crate::ArbitraryAttributeValue {
-                    value: bump.alloc(owned.value),
-                    cmp: owned.cmp,
-                })
-            }
-        }
-    }
-}
-
-/// The kind of node the template is.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub enum TemplateNodeType<Attributes, V, Children, Listeners, TextSegments, Text>
-where
-    Attributes: AsRef<[TemplateAttribute<V>]>,
-    Children: AsRef<[TemplateNodeId]>,
-    Listeners: AsRef<[usize]>,
-    V: TemplateValue,
-    TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-    Text: AsRef<str>,
-{
-    /// A element node (e.g. div{}).
-    Element(TemplateElement<Attributes, V, Children, Listeners>),
-    /// A text node (e.g. "Hello World").
-    Text(TextTemplate<TextSegments, Text>),
-    /// A dynamic node (e.g. (0..10).map(|i| cx.render(rsx!{div{}})))
-    /// The index in the dynamic node array this node should be replaced with
-    DynamicNode(usize),
-}
-
-type StaticStr = &'static str;
-
-/// A element template
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct TemplateElement<Attributes, V, Children, Listeners>
-where
-    Attributes: AsRef<[TemplateAttribute<V>]>,
-    Children: AsRef<[TemplateNodeId]>,
-    Listeners: AsRef<[usize]>,
-    V: TemplateValue,
-{
-    /// The tag name of the element
-    #[cfg_attr(
-        all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-        serde(deserialize_with = "crate::util::deserialize_static_leaky")
-    )]
-    pub tag: StaticStr,
-    /// The namespace of the element
-    #[cfg_attr(
-        all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-        serde(deserialize_with = "crate::util::deserialize_static_leaky_ns")
-    )]
-    pub namespace: Option<StaticStr>,
-    /// The attributes that modify the element
-    pub attributes: Attributes,
-    /// The ids of the children of the element
-    pub children: Children,
-    /// The ids of the listeners of the element
-    pub listeners: Listeners,
-    value: PhantomData<V>,
-}
-
-impl<Attributes, V, Children, Listeners> TemplateElement<Attributes, V, Children, Listeners>
-where
-    Attributes: AsRef<[TemplateAttribute<V>]>,
-    Children: AsRef<[TemplateNodeId]>,
-    Listeners: AsRef<[usize]>,
-    V: TemplateValue,
-{
-    /// create a new element template
-    pub const fn new(
-        tag: &'static str,
-        namespace: Option<&'static str>,
-        attributes: Attributes,
-        children: Children,
-        listeners: Listeners,
-    ) -> Self {
-        TemplateElement {
-            tag,
-            namespace,
-            attributes,
-            children,
-            listeners,
-            value: PhantomData,
-        }
-    }
-}
-
-/// A template for some text that may contain dynamic segments for example "Hello {name}" contains the static segment "Hello " and the dynamic segment "{name}".
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub struct TextTemplate<Segments, Text>
-where
-    Segments: AsRef<[TextTemplateSegment<Text>]>,
-    Text: AsRef<str>,
-{
-    /// The segments of the template.
-    pub segments: Segments,
-    /// The minimum size of the output text.
-    pub min_size: usize,
-    text: PhantomData<Text>,
-}
-
-impl<Segments, Text> TextTemplate<Segments, Text>
-where
-    Segments: AsRef<[TextTemplateSegment<Text>]>,
-    Text: AsRef<str>,
-{
-    /// create a new template from the segments it is composed of.
-    pub const fn new(segments: Segments, min_size: usize) -> Self {
-        TextTemplate {
-            segments,
-            min_size,
-            text: PhantomData,
-        }
-    }
-}
-
-/// A segment of a text template that may be dynamic or static.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-pub enum TextTemplateSegment<Text>
-where
-    Text: AsRef<str>,
-{
-    /// A constant text segment
-    Static(Text),
-    /// A dynamic text segment
-    Dynamic(usize),
-}
-
-/// A template value that is created at compile time that is sync.
-#[derive(Debug, Clone, PartialEq)]
-#[allow(missing_docs)]
-pub enum StaticAttributeValue {
-    Text(&'static str),
-    Float32(f32),
-    Float64(f64),
-    Int32(i32),
-    Int64(i64),
-    Uint32(u32),
-    Uint64(u64),
-    Bool(bool),
-
-    Vec3Float(f32, f32, f32),
-    Vec3Int(i32, i32, i32),
-    Vec3Uint(u32, u32, u32),
-
-    Vec4Float(f32, f32, f32, f32),
-    Vec4Int(i32, i32, i32, i32),
-    Vec4Uint(u32, u32, u32, u32),
-
-    Bytes(&'static [u8]),
-}
-
-#[derive(Default)]
-pub(crate) struct TemplateResolver {
-    // maps a id to the rendererid and if that template needs to be re-created
-    pub template_id_mapping: FxHashMap<TemplateId, (ElementId, bool)>,
-}
-
-impl TemplateResolver {
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub fn mark_dirty(&mut self, id: &TemplateId) {
-        if let Some((_, dirty)) = self.template_id_mapping.get_mut(id) {
-            println!("marking dirty {:?}", id);
-            *dirty = true;
-        } else {
-            println!("failed {:?}", id);
-        }
-    }
-
-    pub fn is_dirty(&self, id: &TemplateId) -> bool {
-        matches!(self.template_id_mapping.get(id), Some((_, true)))
-    }
-
-    // returns (id, if the id was created)
-    pub fn get_or_create_client_id(
-        &mut self,
-        template_id: &TemplateId,
-        scopes: &ScopeArena,
-    ) -> (ElementId, bool) {
-        if let Some(id) = self.template_id_mapping.get(template_id) {
-            *id
-        } else {
-            let renderer_id = scopes.reserve_phantom_node();
-            self.template_id_mapping
-                .insert(template_id.clone(), (renderer_id, false));
-            (renderer_id, true)
-        }
-    }
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A message telling the virtual dom to set a template
-#[derive(Debug, Clone)]
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-pub struct SetTemplateMsg(pub TemplateId, pub OwnedTemplate);
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-/// A path segment that lives on the heap.
-pub type OwnedPathSeg = PathSeg<OwnedTraverse, Vec<UpdateOp>>;
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-#[derive(Debug, Clone, PartialEq)]
-/// A traverse message that lives on the heap.
-pub enum OwnedTraverse {
-    /// Halt traversal
-    Halt,
-    /// Traverse to the first child of the current node.
-    FirstChild(Box<OwnedPathSeg>),
-    /// Traverse to the next sibling of the current node.
-    NextSibling(Box<OwnedPathSeg>),
-    /// Traverse to the both the first child and next sibling of the current node.
-    Both(Box<(OwnedPathSeg, OwnedPathSeg)>),
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-impl Traversable<Vec<UpdateOp>> for OwnedTraverse {
-    fn first_child(&self) -> Option<&OwnedPathSeg> {
-        match self {
-            OwnedTraverse::FirstChild(p) => Some(p),
-            OwnedTraverse::Both(ps) => Some(&ps.0),
-            _ => None,
-        }
-    }
-
-    fn next_sibling(&self) -> Option<&OwnedPathSeg> {
-        match self {
-            OwnedTraverse::NextSibling(p) => Some(p),
-            OwnedTraverse::Both(ps) => Some(&ps.1),
-            _ => None,
-        }
-    }
-}
-
-/// A path segment that lives on the stack.
-pub type StaticPathSeg = PathSeg<StaticTraverse, &'static [UpdateOp]>;
-
-#[derive(Debug, Clone, PartialEq)]
-/// A traverse message that lives on the stack.
-pub enum StaticTraverse {
-    /// Halt traversal
-    Halt,
-    /// Traverse to the first child of the current node.
-    FirstChild(&'static StaticPathSeg),
-    /// Traverse to the next sibling of the current node.
-    NextSibling(&'static StaticPathSeg),
-    /// Traverse to the both the first child and next sibling of the current node.
-    Both(&'static (StaticPathSeg, StaticPathSeg)),
-}
-
-impl Traversable<&'static [UpdateOp]> for StaticTraverse {
-    fn first_child(&self) -> Option<&StaticPathSeg> {
-        match self {
-            StaticTraverse::FirstChild(p) => Some(p),
-            StaticTraverse::Both((p, _)) => Some(p),
-            _ => None,
-        }
-    }
-
-    fn next_sibling(&self) -> Option<&StaticPathSeg> {
-        match self {
-            StaticTraverse::NextSibling(p) => Some(p),
-            StaticTraverse::Both((_, p)) => Some(p),
-            _ => None,
-        }
-    }
-}
-
-pub trait Traversable<O: AsRef<[UpdateOp]>>
-where
-    Self: Sized,
-{
-    fn first_child(&self) -> Option<&PathSeg<Self, O>>;
-    fn next_sibling(&self) -> Option<&PathSeg<Self, O>>;
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-/// A path segment that defines a way to traverse a template node and resolve dynamic sections.
-pub struct PathSeg<T: Traversable<O>, O: AsRef<[UpdateOp]>> {
-    /// The operation to perform on the current node.
-    pub ops: O,
-    /// The next traversal step.
-    pub traverse: T,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[cfg_attr(
-    all(feature = "serialize", any(feature = "hot-reload", debug_assertions)),
-    derive(serde::Serialize, serde::Deserialize)
-)]
-/// A operation that can be applied to a template node when intially updating it.
-pub enum UpdateOp {
-    /// Store a dynamic node on the renderer
-    StoreNode(TemplateNodeId),
-    /// Insert a dynamic node before the current node
-    InsertBefore(TemplateNodeId),
-    /// Insert a dynamic node after the current node
-    InsertAfter(TemplateNodeId),
-    /// Append a dynamic node to the current node
-    AppendChild(TemplateNodeId),
-}

+ 3 - 46
packages/core/src/util.rs

@@ -34,13 +34,9 @@ impl<'a> Iterator for ElementIdIterator<'a> {
             if let Some((count, node)) = self.stack.last_mut() {
                 match node {
                     // We can only exit our looping when we get "real" nodes
-                    VNode::Element(_)
-                    | VNode::Text(_)
-                    | VNode::Placeholder(_)
-                    | VNode::TemplateRef(_) => {
+                    VNode::Element(_) | VNode::Text(_) | VNode::Placeholder(_) => {
                         // We've recursed INTO an element/text
                         // We need to recurse *out* of it and move forward to the next
-                        // println!("Found element! Returning it!");
                         should_pop = true;
                         returned_node = Some(&**node);
                     }
@@ -61,6 +57,8 @@ impl<'a> Iterator for ElementIdIterator<'a> {
                         // Simply swap the current node on the stack with the root of the component
                         *node = scope.root_node();
                     }
+
+                    VNode::Template(_) => todo!(),
                 }
             } else {
                 // If there's no more items on the stack, we're done!
@@ -84,44 +82,3 @@ impl<'a> Iterator for ElementIdIterator<'a> {
         returned_node
     }
 }
-
-/// This intentionally leaks once per element name to allow more flexability when hot reloding templetes
-#[cfg(all(any(feature = "hot-reload", debug_assertions), feature = "serde"))]
-mod leaky {
-    use std::sync::Mutex;
-
-    use once_cell::sync::Lazy;
-    use rustc_hash::FxHashSet;
-    static STATIC_CACHE: Lazy<Mutex<FxHashSet<&'static str>>> =
-        Lazy::new(|| Mutex::new(FxHashSet::default()));
-
-    pub fn deserialize_static_leaky<'de, D>(d: D) -> Result<&'static str, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        use serde::Deserialize;
-        let s = <&str>::deserialize(d)?;
-        Ok(if let Some(stat) = STATIC_CACHE.lock().unwrap().get(s) {
-            *stat
-        } else {
-            Box::leak(s.into())
-        })
-    }
-
-    pub fn deserialize_static_leaky_ns<'de, D>(d: D) -> Result<Option<&'static str>, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        use serde::Deserialize;
-        Ok(<Option<&str>>::deserialize(d)?.map(|s| {
-            if let Some(stat) = STATIC_CACHE.lock().unwrap().get(s) {
-                *stat
-            } else {
-                Box::leak(s.into())
-            }
-        }))
-    }
-}
-
-#[cfg(all(any(feature = "hot-reload", debug_assertions), feature = "serde"))]
-pub use leaky::*;

+ 126 - 169
packages/core/src/virtual_dom.rs

@@ -6,8 +6,8 @@ use crate::diff::DiffState;
 use crate::innerlude::*;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_util::{future::poll_fn, StreamExt};
+use fxhash::FxHashSet;
 use indexmap::IndexSet;
-use rustc_hash::FxHashSet;
 use std::{collections::VecDeque, iter::FromIterator, task::Poll};
 
 /// A virtual node system that progresses user events and diffs UI trees.
@@ -103,7 +103,6 @@ use std::{collections::VecDeque, iter::FromIterator, task::Poll};
 /// }
 /// ```
 pub struct VirtualDom {
-    root: ElementId,
     scopes: ScopeArena,
 
     pending_messages: VecDeque<SchedulerMsg>,
@@ -129,10 +128,6 @@ pub enum SchedulerMsg {
     /// Mark all components as dirty and update them
     DirtyAll,
 
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    /// Mark a template as dirty, used for hot reloading
-    SetTemplate(Box<SetTemplateMsg>),
-
     /// New tasks from components that should be polled when the next poll is ready
     NewTask(ScopeId),
 }
@@ -232,10 +227,10 @@ impl VirtualDom {
             }),
             None,
             ElementId(0),
+            0,
         );
 
         Self {
-            root: ElementId(0),
             scopes,
             channel,
             dirty_scopes: IndexSet::from_iter([ScopeId(0)]),
@@ -403,25 +398,6 @@ impl VirtualDom {
                     self.dirty_scopes.insert(*id);
                 }
             }
-            #[cfg(any(feature = "hot-reload", debug_assertions))]
-            SchedulerMsg::SetTemplate(msg) => {
-                let SetTemplateMsg(id, tmpl) = *msg;
-                if self
-                    .scopes
-                    .templates
-                    .borrow_mut()
-                    .insert(
-                        id.clone(),
-                        std::rc::Rc::new(std::cell::RefCell::new(Template::Owned(tmpl))),
-                    )
-                    .is_some()
-                {
-                    self.scopes.template_resolver.borrow_mut().mark_dirty(&id)
-                }
-
-                // mark any scopes that used the template as dirty
-                self.process_message(SchedulerMsg::DirtyAll);
-            }
         }
     }
 
@@ -470,14 +446,14 @@ impl VirtualDom {
     ///     apply_mutations(mutations);
     /// }
     /// ```
-    #[allow(unused)]
-    pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
-        let mut committed_mutations = vec![];
-        self.scopes.template_bump.reset();
-
+    pub fn work_with_deadline<'a>(
+        &'a mut self,
+        renderer: &mut impl Renderer<'a>,
+        mut deadline: impl FnMut() -> bool,
+    ) {
         while !self.dirty_scopes.is_empty() {
             let scopes = &self.scopes;
-            let mut diff_state = DiffState::new(scopes);
+            let mut diff_state = DiffState::new(scopes, renderer);
 
             let mut ran_scopes = FxHashSet::default();
 
@@ -497,17 +473,18 @@ impl VirtualDom {
 
                     self.scopes.run_scope(scopeid);
 
-                    diff_state.diff_scope(self.root, scopeid);
+                    diff_state.diff_scope(scopeid);
 
                     let DiffState { mutations, .. } = diff_state;
 
-                    for scope in &mutations.dirty_scopes {
-                        self.dirty_scopes.remove(scope);
-                    }
+                    todo!()
+                    // for scope in &mutations.dirty_scopes {
+                    //     self.dirty_scopes.remove(scope);
+                    // }
 
-                    if !mutations.edits.is_empty() {
-                        committed_mutations.push(mutations);
-                    }
+                    // if !mutations.edits.is_empty() {
+                    //     committed_mutations.push(mutations);
+                    // }
 
                     // todo: pause the diff machine
                     // if diff_state.work(&mut deadline) {
@@ -526,8 +503,6 @@ impl VirtualDom {
                 }
             }
         }
-
-        committed_mutations
     }
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
@@ -547,26 +522,22 @@ impl VirtualDom {
     ///
     /// apply_edits(edits);
     /// ```
-    pub fn rebuild(&mut self) -> Mutations {
+    pub fn rebuild<'a>(&'a mut self, dom: &mut impl Renderer<'a>) {
         let scope_id = ScopeId(0);
+        let mut diff_state = DiffState::new(&self.scopes, dom);
 
-        let mut diff_state = DiffState::new(&self.scopes);
         self.scopes.run_scope(scope_id);
 
+        diff_state.element_stack.push(ElementId(0));
         diff_state.scope_stack.push(scope_id);
 
         let node = self.scopes.fin_head(scope_id);
-        let mut created = Vec::new();
-        diff_state.create_node(self.root, node, &mut created);
+        let created = diff_state.create_node(node);
 
-        diff_state
-            .mutations
-            .append_children(Some(self.root.as_u64()), created);
+        diff_state.mutations.append_children(created as u32);
 
         self.dirty_scopes.clear();
         assert!(self.dirty_scopes.is_empty());
-
-        diff_state.mutations
     }
 
     /// Compute a manual diff of the VirtualDom between states.
@@ -599,8 +570,8 @@ impl VirtualDom {
     ///
     /// let edits = dom.hard_diff(ScopeId(0));
     /// ```
-    pub fn hard_diff(&mut self, scope_id: ScopeId) -> Mutations {
-        let mut diff_machine = DiffState::new(&self.scopes);
+    pub fn hard_diff<'a>(&'a mut self, scope_id: ScopeId, dom: &mut impl Renderer<'a>) {
+        let mut diff_machine = DiffState::new(&self.scopes, dom);
         self.scopes.run_scope(scope_id);
 
         let (old, new) = (
@@ -611,124 +582,108 @@ impl VirtualDom {
         diff_machine.force_diff = true;
         diff_machine.scope_stack.push(scope_id);
         let scope = diff_machine.scopes.get_scope(scope_id).unwrap();
+        diff_machine.element_stack.push(scope.container);
 
-        diff_machine.diff_node(scope.container, old, new);
-
-        diff_machine.mutations
+        diff_machine.diff_node(old, new);
     }
 
-    /// Renders an `rsx` call into the Base Scope's allocator.
-    ///
-    /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
-    ///
-    /// ```rust, ignore
-    /// fn Base(cx: Scope) -> Element {
-    ///     render!(div {})
-    /// }
-    ///
-    /// let dom = VirtualDom::new(Base);
-    /// let nodes = dom.render_nodes(rsx!("div"));
-    /// ```
-    pub fn render_vnodes<'a>(&'a self, lazy_nodes: LazyNodes<'a, '_>) -> &'a VNode<'a> {
-        let scope = self.scopes.get_scope(ScopeId(0)).unwrap();
-        let frame = scope.wip_frame();
-        let factory = NodeFactory::new(scope);
-        let node = lazy_nodes.call(factory);
-        frame.bump.alloc(node)
-    }
-
-    /// Renders an `rsx` call into the Base Scope's allocator.
-    ///
-    /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
-    ///
-    /// ```rust, ignore
-    /// fn Base(cx: Scope) -> Element {
-    ///     render!(div {})
-    /// }
-    ///
-    /// let dom = VirtualDom::new(Base);
-    /// let nodes = dom.render_nodes(rsx!("div"));
-    /// ```
-    pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
-        let mut machine = DiffState::new(&self.scopes);
-        machine.scope_stack.push(ScopeId(0));
-        machine.diff_node(self.root, old, new);
-
-        machine.mutations
-    }
-
-    /// Renders an `rsx` call into the Base Scope's allocator.
-    ///
-    /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
-    ///
-    ///
-    /// ```rust, ignore
-    /// fn Base(cx: Scope) -> Element {
-    ///     render!(div {})
-    /// }
-    ///
-    /// let dom = VirtualDom::new(Base);
-    /// let nodes = dom.render_nodes(rsx!("div"));
-    /// ```
-    pub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a> {
-        let mut machine = DiffState::new(&self.scopes);
-        machine.scope_stack.push(ScopeId(0));
-        let node = self.render_vnodes(nodes);
-        let mut created = Vec::new();
-        machine.create_node(self.root, node, &mut created);
-        machine
-            .mutations
-            .append_children(Some(self.root.as_u64()), created);
-        machine.mutations
-    }
-
-    /// Renders an `rsx` call into the Base Scopes's arena.
-    ///
-    /// Useful when needing to diff two rsx! calls from outside the VirtualDom, such as in a test.
-    ///
-    ///
-    /// ```rust, ignore
-    /// fn Base(cx: Scope) -> Element {
-    ///     render!(div {})
-    /// }
-    ///
-    /// let dom = VirtualDom::new(Base);
-    /// let nodes = dom.render_nodes(rsx!("div"));
-    /// ```
-    pub fn diff_lazynodes<'a>(
-        &'a self,
-        left: LazyNodes<'a, '_>,
-        right: LazyNodes<'a, '_>,
-    ) -> (Mutations<'a>, Mutations<'a>) {
-        let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
-
-        let mut create = DiffState::new(&self.scopes);
-        create.scope_stack.push(ScopeId(0));
-        let mut created = Vec::new();
-        create.create_node(self.root, old, &mut created);
-        create
-            .mutations
-            .append_children(Some(self.root.as_u64()), created);
-
-        let mut edit = DiffState::new(&self.scopes);
-        edit.scope_stack.push(ScopeId(0));
-        edit.diff_node(self.root, old, new);
-
-        (create.mutations, edit.mutations)
-    }
-
-    /// Runs a function with the template associated with a given id.
-    pub fn with_template<R>(&self, id: &TemplateId, mut f: impl FnMut(&Template) -> R) -> R {
-        self.scopes
-            .templates
-            .borrow()
-            .get(id)
-            .map(|inner| {
-                let borrow = inner;
-                f(&borrow.borrow())
-            })
-            .unwrap()
-    }
+    // /// Renders an `rsx` call into the Base Scope's allocator.
+    // ///
+    // /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
+    // ///
+    // /// ```rust, ignore
+    // /// fn Base(cx: Scope) -> Element {
+    // ///     render!(div {})
+    // /// }
+    // ///
+    // /// let dom = VirtualDom::new(Base);
+    // /// let nodes = dom.render_nodes(rsx!("div"));
+    // /// ```
+    // pub fn render_vnodes<'a>(&'a self, lazy_nodes: LazyNodes<'a, '_>) -> &'a VNode<'a> {
+    //     let scope = self.scopes.get_scope(ScopeId(0)).unwrap();
+    //     let frame = scope.wip_frame();
+    //     let factory = NodeFactory::new(scope);
+    //     let node = lazy_nodes.call(factory);
+    //     frame.bump.alloc(node)
+    // }
+
+    // /// Renders an `rsx` call into the Base Scope's allocator.
+    // ///
+    // /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
+    // ///
+    // /// ```rust, ignore
+    // /// fn Base(cx: Scope) -> Element {
+    // ///     render!(div {})
+    // /// }
+    // ///
+    // /// let dom = VirtualDom::new(Base);
+    // /// let nodes = dom.render_nodes(rsx!("div"));
+    // /// ```
+    // pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
+    //     let mut machine = DiffState::new(&self.scopes);
+    //     machine.element_stack.push(ElementId(0));
+    //     machine.scope_stack.push(ScopeId(0));
+    //     machine.diff_node(old, new);
+
+    //     machine.mutations
+    // }
+
+    // /// Renders an `rsx` call into the Base Scope's allocator.
+    // ///
+    // /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
+    // ///
+    // ///
+    // /// ```rust, ignore
+    // /// fn Base(cx: Scope) -> Element {
+    // ///     render!(div {})
+    // /// }
+    // ///
+    // /// let dom = VirtualDom::new(Base);
+    // /// let nodes = dom.render_nodes(rsx!("div"));
+    // /// ```
+    // pub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a> {
+    //     let mut machine = DiffState::new(&self.scopes);
+    //     machine.scope_stack.push(ScopeId(0));
+    //     machine.element_stack.push(ElementId(0));
+    //     let node = self.render_vnodes(nodes);
+    //     let created = machine.create_node(node);
+    //     machine.mutations.append_children(created as u32);
+    //     machine.mutations
+    // }
+
+    // /// Renders an `rsx` call into the Base Scopes's arena.
+    // ///
+    // /// Useful when needing to diff two rsx! calls from outside the VirtualDom, such as in a test.
+    // ///
+    // ///
+    // /// ```rust, ignore
+    // /// fn Base(cx: Scope) -> Element {
+    // ///     render!(div {})
+    // /// }
+    // ///
+    // /// let dom = VirtualDom::new(Base);
+    // /// let nodes = dom.render_nodes(rsx!("div"));
+    // /// ```
+    // pub fn diff_lazynodes<'a>(
+    //     &'a self,
+    //     left: LazyNodes<'a, '_>,
+    //     right: LazyNodes<'a, '_>,
+    // ) -> (Mutations<'a>, Mutations<'a>) {
+    //     let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
+
+    //     let mut create = DiffState::new(&self.scopes);
+    //     create.scope_stack.push(ScopeId(0));
+    //     create.element_stack.push(ElementId(0));
+    //     let created = create.create_node(old);
+    //     create.mutations.append_children(created as u32);
+
+    //     let mut edit = DiffState::new(&self.scopes);
+    //     edit.scope_stack.push(ScopeId(0));
+    //     edit.element_stack.push(ElementId(0));
+    //     edit.diff_node(old, new);
+
+    //     (create.mutations, edit.mutations)
+    // }
 }
 
 /*
@@ -762,8 +717,10 @@ impl Drop for VirtualDom {
 
         // todo: move the remove nodes method onto scopearena
         // this will clear *all* scopes *except* the root scope
-        let mut machine = DiffState::new(&self.scopes);
-        machine.remove_nodes([scope.root_node()], false);
+        // let mut machine = DiffState::new(&self.scopes);
+        // machine.remove_nodes([scope.root_node()], false);
+
+        todo!("drop the root scope without leaking anything");
 
         // Now, clean up the root scope
         // safety: there are no more references to the root scope

+ 114 - 0
packages/core/tests/interpreter.rs

@@ -0,0 +1,114 @@
+use dioxus_core::{prelude::*, TemplateNode, VTemplate, VText};
+
+// #[test]
+// fn simple_static() {
+//     fn app(cx: Scope) -> Element {
+//         static MyTemplate: TemplateDef = TemplateDef {
+//             id: "my-template",
+//             static_nodes: &[TemplateNode::Element {
+//                 attributes: &[],
+//                 nodes: &[TemplateNode::StaticText("Hello, world!")],
+//                 tag: "div",
+//             }],
+//             dynamic_nodes: &[],
+//         };
+
+//         Some(VNode::Template(NodeFactory::new(&cx).bump().alloc(
+//             VTemplate {
+//                 def: &MyTemplate,
+//                 dynamic_nodes: &[],
+//                 rendered_nodes: &[],
+//             },
+//         )))
+//     }
+
+//     let mut dom = VirtualDom::new(app);
+//     let edits = dom.rebuild();
+//     dbg!(edits);
+// }
+
+// #[test]
+// fn mixed_dynamic() {
+//     fn app(cx: Scope) -> Element {
+//         static MyTemplate: TemplateDef = TemplateDef {
+//             id: "my-template",
+//             static_nodes: &[TemplateNode::Element {
+//                 tag: "div",
+//                 attributes: &[],
+//                 nodes: &[
+//                     TemplateNode::StaticText("Hello, world!"),
+//                     TemplateNode::DynamicText,
+//                 ],
+//             }],
+//             dynamic_nodes: &[],
+//         };
+
+//         let val = cx.use_hook(|| 0);
+//         *val += 1;
+
+//         let fact = NodeFactory::new(&cx);
+
+//         Some(VNode::Template(fact.bump().alloc(VTemplateRef {
+//             def: &MyTemplate,
+//             dynamic_nodes: fact.bump().alloc([fact.text(format_args!("{val}"))]),
+//         })))
+//     }
+
+//     let mut dom = VirtualDom::new(app);
+//     let edits = dom.rebuild();
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+// }
+
+// #[test]
+// fn mixes() {
+//     fn app(cx: Scope) -> Element {
+//         static MyTemplate: TemplateDef = TemplateDef {
+//             id: "my-template",
+//             static_nodes: &[TemplateNode::Element {
+//                 tag: "div",
+//                 attributes: &[],
+//                 nodes: &[
+//                     TemplateNode::StaticText("Hello, world!"),
+//                     TemplateNode::DynamicText,
+//                 ],
+//             }],
+//             dynamic_nodes: &[],
+//         };
+
+//         let val = cx.use_hook(|| 1);
+//         *val += 1;
+
+//         let fact = NodeFactory::new(&cx);
+
+//         if *val % 2 == 0 {
+//             Some(VNode::Template(fact.bump().alloc(VTemplateRef {
+//                 def: &MyTemplate,
+//                 dynamic_nodes: fact.bump().alloc([fact.text(format_args!("{val}"))]),
+//             })))
+//         } else {
+//             Some(fact.text(format_args!("Hello, world! {val}")))
+//         }
+//     }
+
+//     let mut dom = VirtualDom::new(app);
+//     let edits = dom.rebuild();
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+
+//     let edits = dom.hard_diff(ScopeId(0));
+//     dbg!(edits);
+// }

+ 0 - 1
packages/desktop/Cargo.toml

@@ -43,7 +43,6 @@ tokio_runtime = ["tokio"]
 fullscreen = ["wry/fullscreen"]
 transparent = ["wry/transparent"]
 tray = ["wry/tray"]
-hot-reload = ["dioxus-core/hot-reload"]
 
 [dev-dependencies]
 dioxus-core-macro = { path = "../core-macro" }

+ 23 - 21
packages/desktop/src/controller.rs

@@ -50,34 +50,36 @@ impl DesktopController {
 
                 dom.base_scope().provide_context(window_context);
 
-                // allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel
-                #[cfg(any(feature = "hot-reload", debug_assertions))]
-                crate::hot_reload::init(&dom);
+                todo!()
 
-                let edits = dom.rebuild();
+                // // allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel
+                // #[cfg(any(feature = "hot-reload", debug_assertions))]
+                // crate::hot_reload::init(&dom);
 
-                edit_queue
-                    .lock()
-                    .unwrap()
-                    .push(serde_json::to_string(&edits.edits).unwrap());
+                // let edits = dom.rebuild();
 
-                // Make sure the window is ready for any new updates
-                proxy.send_event(UserWindowEvent::Update).unwrap();
+                // edit_queue
+                //     .lock()
+                //     .unwrap()
+                //     .push(serde_json::to_string(&edits.edits).unwrap());
 
-                loop {
-                    dom.wait_for_work().await;
+                // // Make sure the window is ready for any new updates
+                // proxy.send_event(UserWindowEvent::Update).unwrap();
 
-                    let muts = dom.work_with_deadline(|| false);
+                // loop {
+                //     dom.wait_for_work().await;
 
-                    for edit in muts {
-                        edit_queue
-                            .lock()
-                            .unwrap()
-                            .push(serde_json::to_string(&edit.edits).unwrap());
-                    }
+                //     let muts = dom.work_with_deadline(|| false);
 
-                    let _ = proxy.send_event(UserWindowEvent::Update);
-                }
+                //     for edit in muts {
+                //         edit_queue
+                //             .lock()
+                //             .unwrap()
+                //             .push(serde_json::to_string(&edit.edits).unwrap());
+                //     }
+
+                //     let _ = proxy.send_event(UserWindowEvent::Update);
+                // }
             })
         });
 

+ 6 - 5
packages/desktop/src/hot_reload.rs

@@ -1,4 +1,4 @@
-use dioxus_core::{SchedulerMsg, SetTemplateMsg, VirtualDom};
+use dioxus_core::{SchedulerMsg, VirtualDom};
 use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
 use std::io::{BufRead, BufReader};
 use std::time::Duration;
@@ -32,10 +32,11 @@ pub(crate) fn init(dom: &VirtualDom) {
                 let mut buf = String::new();
                 match conn.read_line(&mut buf) {
                     Ok(_) => {
-                        let msg: SetTemplateMsg = serde_json::from_str(&buf).unwrap();
-                        channel
-                            .start_send(SchedulerMsg::SetTemplate(Box::new(msg)))
-                            .unwrap();
+                        todo!()
+                        // let msg: SetTemplateMsg = serde_json::from_str(&buf).unwrap();
+                        // channel
+                        //     .start_send(SchedulerMsg::SetTemplate(Box::new(msg)))
+                        //     .unwrap();
                     }
                     Err(err) => {
                         if err.kind() != std::io::ErrorKind::WouldBlock {

+ 2 - 3
packages/dioxus/Cargo.toml

@@ -23,9 +23,7 @@ default = ["macro", "hooks", "html"]
 macro = ["dioxus-core-macro", "dioxus-rsx"]
 html = ["dioxus-html"]
 hooks = ["dioxus-hooks"]
-hot-reload = [
-    "dioxus-core/hot-reload",
-]
+
 
 [dev-dependencies]
 futures-util = "0.3.21"
@@ -34,6 +32,7 @@ rand = { version = "0.8.4", features = ["small_rng"] }
 criterion = "0.3.5"
 thiserror = "1.0.30"
 env_logger = "0.9.0"
+dioxus-edit-stream = { path = "../edit-stream" }
 
 [[bench]]
 name = "create"

+ 1 - 1
packages/dioxus/tests/create_dom.rs

@@ -7,7 +7,7 @@
 
 use dioxus::prelude::*;
 
-use dioxus_core::DomEdit::*;
+use dioxus_edit_stream::DomEdit::*;
 
 fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
     VirtualDom::new_with_props(app, props)

+ 17 - 0
packages/dioxus/tests/rsx_syntax.rs

@@ -0,0 +1,17 @@
+use dioxus::prelude::*;
+
+#[test]
+fn basic_syntax_is_a_template() {
+    //
+    let var = 123;
+    let asd = 123;
+
+    let g = rsx! {
+        div {
+            class: "asd",
+            class: "{asd}",
+            onclick: move |_| {},
+            div { "{var}" }
+        }
+    };
+}

+ 9 - 0
packages/edit-stream/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "dioxus-edit-stream"
+version = "0.0.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dioxus-core = { path = "../core" }

+ 378 - 0
packages/edit-stream/src/lib.rs

@@ -0,0 +1,378 @@
+use dioxus_core::*;
+
+// /// ## Mutations
+// ///
+// /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
+// /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
+// /// applied the edits.
+// ///
+// /// Mutations are the only link between the RealDOM and the VirtualDOM.
+// pub struct Mutations<'a> {
+//     /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
+//     pub edits: Vec<DomEdit<'a>>,
+
+//     /// The list of Scopes that were diffed, created, and removed during the Diff process.
+//     pub dirty_scopes: FxHashSet<ScopeId>,
+
+//     /// The list of nodes to connect to the RealDOM.
+//     pub refs: Vec<NodeRefMutation<'a>>,
+// }
+
+// impl Debug for Mutations<'_> {
+//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+//         f.debug_struct("Mutations")
+//             .field("edits", &self.edits)
+//             .field("noderefs", &self.refs)
+//             .finish()
+//     }
+// }
+
+// /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
+// /// network or through FFI boundaries.
+// #[derive(Debug, PartialEq)]
+// #[cfg_attr(
+//     feature = "serialize",
+//     derive(serde::Serialize, serde::Deserialize),
+//     serde(tag = "type")
+// )]
+// pub enum DomEdit<'bump> {
+//     /// Push the given root node onto our stack.
+//     PushRoot {
+//         /// The ID of the root node to push.
+//         root: ElementId,
+//     },
+
+//     /// Pop the topmost node from our stack and append them to the node
+//     /// at the top of the stack.
+//     AppendChildren {
+//         /// How many nodes should be popped from the stack.
+//         /// The node remaining on the stack will be the target for the append.
+//         many: u32,
+//     },
+
+//     /// Replace a given (single) node with a handful of nodes currently on the stack.
+//     ReplaceWith {
+//         /// The ID of the node to be replaced.
+//         root: ElementId,
+
+//         /// How many nodes should be popped from the stack to replace the target node.
+//         m: u32,
+//     },
+
+//     /// Insert a number of nodes after a given node.
+//     InsertAfter {
+//         /// The ID of the node to insert after.
+//         root: ElementId,
+
+//         /// How many nodes should be popped from the stack to insert after the target node.
+//         n: u32,
+//     },
+
+//     /// Insert a number of nodes before a given node.
+//     InsertBefore {
+//         /// The ID of the node to insert before.
+//         root: ElementId,
+
+//         /// How many nodes should be popped from the stack to insert before the target node.
+//         n: u32,
+//     },
+
+//     /// Remove a particular node from the DOM
+//     Remove {
+//         /// The ID of the node to remove.
+//         root: ElementId,
+//     },
+
+//     /// Create a new purely-text node
+//     CreateTextNode {
+//         /// The ID the new node should have.
+//         root: ElementId,
+
+//         /// The textcontent of the node
+//         text: &'bump str,
+//     },
+
+//     /// Create a new purely-element node
+//     CreateElement {
+//         /// The ID the new node should have.
+//         root: ElementId,
+
+//         /// The tagname of the node
+//         tag: &'bump str,
+//     },
+
+//     /// Create a new purely-comment node with a given namespace
+//     CreateElementNs {
+//         /// The ID the new node should have.
+//         root: ElementId,
+
+//         /// The namespace of the node
+//         tag: &'bump str,
+
+//         /// The namespace of the node (like `SVG`)
+//         ns: &'static str,
+//     },
+
+//     /// Create a new placeholder node.
+//     /// In most implementations, this will either be a hidden div or a comment node.
+//     CreatePlaceholder {
+//         /// The ID the new node should have.
+//         root: ElementId,
+//     },
+
+//     /// Create a new Event Listener.
+//     NewEventListener {
+//         /// The name of the event to listen for.
+//         event_name: &'static str,
+
+//         /// The ID of the node to attach the listener to.
+//         scope: ScopeId,
+
+//         /// The ID of the node to attach the listener to.
+//         root: ElementId,
+//     },
+
+//     /// Remove an existing Event Listener.
+//     RemoveEventListener {
+//         /// The ID of the node to remove.
+//         root: ElementId,
+
+//         /// The name of the event to remove.
+//         event: &'static str,
+//     },
+
+//     /// Set the textcontent of a node.
+//     SetText {
+//         /// The ID of the node to set the textcontent of.
+//         root: ElementId,
+
+//         /// The textcontent of the node
+//         text: &'bump str,
+//     },
+
+//     /// Set the value of a node's attribute.
+//     SetAttribute {
+//         /// The ID of the node to set the attribute of.
+//         root: ElementId,
+
+//         /// The name of the attribute to set.
+//         field: &'static str,
+
+//         /// The value of the attribute.
+//         value: AttributeValue<'bump>,
+
+//         // value: &'bump str,
+//         /// The (optional) namespace of the attribute.
+//         /// For instance, "style" is in the "style" namespace.
+//         ns: Option<&'bump str>,
+//     },
+
+//     /// Remove an attribute from a node.
+//     RemoveAttribute {
+//         /// The ID of the node to remove.
+//         root: ElementId,
+
+//         /// The name of the attribute to remove.
+//         name: &'static str,
+
+//         /// The namespace of the attribute.
+//         ns: Option<&'bump str>,
+//     },
+
+//     /// Manually pop a root node from the stack.
+//     PopRoot {
+//         /// The amount of nodes to pop
+//         count: u32,
+//     },
+
+//     /// Remove all the children of an element
+//     RemoveChildren {
+//         /// The root
+//         root: ElementId,
+//     },
+
+//     /// Create a template using the nodes on the stack
+//     StoreTemplate {
+//         /// The ID of the template
+//         name: &'static str,
+
+//         /// The amount of nodes to pop from the stack into the template
+//         num_children: u32,
+
+//         /// Indicies for the nodes to pop from the stack into the template
+//         dynamic_nodes: &'static [&'static [u32]],
+//     },
+
+//     /// Load the template onto the stack
+//     LoadTemplate {
+//         /// The ID of the template
+//         name: &'static str,
+
+//         /// The index of the template body
+//         index: u32,
+
+//         /// Give the node a new ID to remove it later
+//         root: ElementId,
+//     },
+
+//     /// Load n nodes into the kth dynamic node of the template
+//     ///
+//     /// Assumes that the template is already on the stack
+//     MergeTemplate {
+//         /// The index of the dynamic node to merge into
+//         index: u32,
+
+//         /// The amount of nodes to pop from the stack into the template
+//         num_children: u32,
+//     },
+// }
+
+// use DomEdit::*;
+
+// impl<'a> Mutations<'a> {
+//     pub(crate) fn new() -> Self {
+//         Self {
+//             edits: Vec::new(),
+//             refs: Vec::new(),
+//             dirty_scopes: Default::default(),
+//         }
+//     }
+
+//     // Navigation
+//     pub(crate) fn push_root(&mut self, root: ElementId) {
+//         self.edits.push(PushRoot { root });
+//     }
+
+//     // Navigation
+//     pub(crate) fn pop_root(&mut self) {
+//         self.edits.push(PopRoot { count: 1 });
+//     }
+
+//     pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) {
+//         self.edits.push(ReplaceWith { m, root });
+//     }
+
+//     pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) {
+//         self.edits.push(InsertAfter { n, root });
+//     }
+
+//     pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) {
+//         self.edits.push(InsertBefore { n, root });
+//     }
+
+//     pub(crate) fn append_children(&mut self, n: u32) {
+//         self.edits.push(AppendChildren { many: n });
+//     }
+
+//     // Remove Nodes from the dom
+//     pub(crate) fn remove(&mut self, root: ElementId) {
+//         self.edits.push(Remove { root });
+//     }
+
+//     // Create
+//     pub(crate) fn create_text_node(&mut self, text: &'a str, root: ElementId) {
+//         self.edits.push(CreateTextNode { text, root });
+//     }
+
+//     pub(crate) fn create_element(
+//         &mut self,
+//         tag: &'static str,
+//         ns: Option<&'static str>,
+//         id: ElementId,
+//     ) {
+//         match ns {
+//             Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
+//             None => self.edits.push(CreateElement { root: id, tag }),
+//         }
+//     }
+//     // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
+//     pub(crate) fn create_placeholder(&mut self, id: ElementId) {
+//         self.edits.push(CreatePlaceholder { root: id });
+//     }
+
+//     // events
+//     pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
+//         let Listener {
+//             event,
+//             mounted_node,
+//             ..
+//         } = listener;
+
+//         let element_id = mounted_node.get().unwrap();
+
+//         self.edits.push(NewEventListener {
+//             scope,
+//             event_name: event,
+//             root: element_id,
+//         });
+//     }
+//     pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: ElementId) {
+//         self.edits.push(RemoveEventListener { event, root });
+//     }
+
+//     // modify
+//     pub(crate) fn set_text(&mut self, text: &'a str, root: ElementId) {
+//         self.edits.push(SetText { text, root });
+//     }
+
+//     pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: ElementId) {
+//         let Attribute {
+//             name,
+//             value,
+//             namespace,
+//             ..
+//         } = attribute;
+
+//         self.edits.push(SetAttribute {
+//             field: name,
+//             value: value.clone(),
+//             ns: *namespace,
+//             root,
+//         });
+//     }
+
+//     pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId) {
+//         let Attribute {
+//             name, namespace, ..
+//         } = attribute;
+
+//         self.edits.push(RemoveAttribute {
+//             name,
+//             ns: *namespace,
+//             root,
+//         });
+//     }
+
+//     pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) {
+//         self.dirty_scopes.insert(scope);
+//     }
+// }
+
+// // refs are only assigned once
+// pub struct NodeRefMutation<'a> {
+//     pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
+//     pub element_id: ElementId,
+// }
+
+// impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
+//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+//         f.debug_struct("NodeRefMutation")
+//             .field("element_id", &self.element_id)
+//             .finish()
+//     }
+// }
+
+// impl<'a> NodeRefMutation<'a> {
+//     pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
+//         self.element
+//             .as_ref()
+//             .and_then(|f| f.get())
+//             .and_then(|f| f.downcast_ref::<T>())
+//     }
+//     pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
+//         self.element
+//             .as_mut()
+//             .and_then(|f| f.get_mut())
+//             .and_then(|f| f.downcast_mut::<T>())
+//     }
+// }

+ 25 - 79
packages/html/src/elements.rs

@@ -1,5 +1,8 @@
+#![allow(non_upper_case_globals)]
+
 use crate::{GlobalAttributes, SvgAttributes};
-use dioxus_core::*;
+
+pub type AttributeDiscription = (&'static str, Option<&'static str>, bool);
 
 macro_rules! builder_constructors {
     (
@@ -18,23 +21,17 @@ macro_rules! builder_constructors {
             $(#[$attr])*
             pub struct $name;
 
-            impl DioxusElement for $name {
-                const TAG_NAME: &'static str = stringify!($name);
-                const NAME_SPACE: Option<&'static str> = None;
-            }
-
-            impl GlobalAttributes for $name {}
 
             impl $name {
+                pub const TAG_NAME: &'static str = stringify!($name);
+                pub const NAME_SPACE: Option<&'static str> = None;
+
                 $(
-                    #[allow(non_upper_case_globals)]
-                    pub const $fil: AttributeDiscription = AttributeDiscription{
-                        name: stringify!($fil),
-                        namespace: None,
-                        volatile: false
-                    };
+                    pub const $fil: (&'static str, &'static str) = (stringify!($fil), stringify!($vil));
                 )*
             }
+
+            impl GlobalAttributes for $name {}
         )*
     };
 
@@ -49,22 +46,14 @@ macro_rules! builder_constructors {
             $(#[$attr])*
             pub struct $name;
 
-            impl DioxusElement for $name {
-                const TAG_NAME: &'static str = stringify!($name);
-                const NAME_SPACE: Option<&'static str> = Some($namespace);
-            }
-
             impl SvgAttributes for $name {}
 
             impl $name {
-                $(
+                pub const TAG_NAME: &'static str = stringify!($name);
+                pub const NAME_SPACE: Option<&'static str> = Some($namespace);
 
-                    #[allow(non_upper_case_globals)]
-                    pub const $fil: AttributeDiscription = AttributeDiscription{
-                        name: stringify!($fil),
-                        namespace: Some(stringify!($namespace)),
-                        volatile: false
-                    };
+                $(
+                    pub const $fil: AttributeDiscription = (stringify!($fil), Some(stringify!($namespace)), false)
                 )*
             }
         )*
@@ -1068,19 +1057,10 @@ impl input {
     /// - `time`
     /// - `url`
     /// - `week`
-    #[allow(non_upper_case_globals)]
-    pub const r#type: AttributeDiscription = AttributeDiscription {
-        name: "type",
-        namespace: None,
-        volatile: false,
-    };
 
-    #[allow(non_upper_case_globals)]
-    pub const value: AttributeDiscription = AttributeDiscription {
-        name: "value",
-        namespace: None,
-        volatile: true,
-    };
+    pub const r#type: AttributeDiscription = ("type", None, false);
+
+    pub const value: AttributeDiscription = ("value", None, true);
 }
 
 /*
@@ -1090,63 +1070,29 @@ volatile attributes
 impl script {
     // r#async: Bool,
     // r#type: String, // TODO could be an enum
-    #[allow(non_upper_case_globals)]
-    pub const r#type: AttributeDiscription = AttributeDiscription {
-        name: "type",
-        namespace: None,
-        volatile: false,
-    };
 
-    #[allow(non_upper_case_globals)]
-    pub const r#script: AttributeDiscription = AttributeDiscription {
-        name: "script",
-        namespace: None,
-        volatile: false,
-    };
+    pub const r#type: AttributeDiscription = ("type", None, false);
+
+    pub const r#script: AttributeDiscription = ("script", None, false);
 }
 
 impl button {
-    #[allow(non_upper_case_globals)]
-    pub const r#type: AttributeDiscription = AttributeDiscription {
-        name: "type",
-        namespace: None,
-        volatile: false,
-    };
+    pub const r#type: AttributeDiscription = ("type", None, false);
 }
 
 impl select {
-    #[allow(non_upper_case_globals)]
-    pub const value: AttributeDiscription = AttributeDiscription {
-        name: "value",
-        namespace: None,
-        volatile: true,
-    };
+    pub const value: AttributeDiscription = ("value", None, true);
 }
 
 impl option {
-    #[allow(non_upper_case_globals)]
-    pub const selected: AttributeDiscription = AttributeDiscription {
-        name: "selected",
-        namespace: None,
-        volatile: true,
-    };
+    pub const selected: AttributeDiscription = ("selected", None, true);
 }
 
 impl textarea {
-    #[allow(non_upper_case_globals)]
-    pub const value: AttributeDiscription = AttributeDiscription {
-        name: "value",
-        namespace: None,
-        volatile: true,
-    };
+    pub const value: AttributeDiscription = ("value", None, true);
 }
 impl label {
-    #[allow(non_upper_case_globals)]
-    pub const r#for: AttributeDiscription = AttributeDiscription {
-        name: "for",
-        namespace: None,
-        volatile: false,
-    };
+    pub const r#for: AttributeDiscription = ("for", None, false);
 }
 
 builder_constructors! {

+ 22 - 31
packages/html/src/global_attributes.rs

@@ -1,4 +1,6 @@
-use dioxus_core::*;
+#![allow(non_upper_case_globals)]
+
+use crate::AttributeDiscription;
 
 macro_rules! no_namespace_trait_methods {
     (
@@ -8,12 +10,11 @@ macro_rules! no_namespace_trait_methods {
         )*
     ) => {
         $(
-            #[allow(non_upper_case_globals)]
-            const $name: AttributeDiscription = AttributeDiscription{
-                name: stringify!($name),
-                namespace: None,
-                volatile: false
-            };
+            const $name: AttributeDiscription = (
+                stringify!($name),
+                None,
+                false
+            );
         )*
     };
 }
@@ -25,12 +26,11 @@ macro_rules! style_trait_methods {
         )*
     ) => {
         $(
-            #[allow(non_upper_case_globals)]
-            const $name: AttributeDiscription = AttributeDiscription{
-                name: $lit,
-                namespace: Some("style"),
-                volatile: false
-            };
+            const $name: AttributeDiscription = (
+                $lit,
+                Some("style"),
+                false
+            );
         )*
     };
 }
@@ -42,12 +42,11 @@ macro_rules! aria_trait_methods {
         )*
     ) => {
         $(
-            #[allow(non_upper_case_globals)]
-            const $name: AttributeDiscription = AttributeDiscription{
-                name: $lit,
-                namespace: None,
-                volatile: false
-            };
+            const $name: AttributeDiscription = (
+                $lit,
+                None,
+                false
+            );
         )*
     };
 }
@@ -57,12 +56,8 @@ pub trait GlobalAttributes {
     ///
     /// For more information, see the MDN docs:
     /// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
-    #[allow(non_upper_case_globals)]
-    const prevent_default: AttributeDiscription = AttributeDiscription {
-        name: "dioxus-prevent-default",
-        namespace: None,
-        volatile: false,
-    };
+
+    const prevent_default: AttributeDiscription = ("dioxus-prevent-default", None, false);
 
     no_namespace_trait_methods! {
         accesskey;
@@ -604,12 +599,8 @@ pub trait SvgAttributes {
     ///
     /// For more information, see the MDN docs:
     /// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
-    #[allow(non_upper_case_globals)]
-    const prevent_default: AttributeDiscription = AttributeDiscription {
-        name: "dioxus-prevent-default",
-        namespace: None,
-        volatile: false,
-    };
+    const prevent_default: AttributeDiscription = ("dioxus-prevent-default", None, false);
+
     aria_trait_methods! {
         accent_height: "accent-height",
         accumulate: "accumulate",

+ 0 - 1
packages/router/Cargo.toml

@@ -42,7 +42,6 @@ default = ["query"]
 web = ["web-sys", "gloo-events", "js-sys", "wasm-bindgen"]
 query = ["serde", "serde_urlencoded"]
 wasm_test = []
-hot-reload = ["dioxus/hot-reload"]
 
 [dev-dependencies]
 console_error_panic_hook = "0.1.7"

+ 1 - 4
packages/rsx/Cargo.toml

@@ -10,8 +10,5 @@ license = "MIT/Apache-2.0"
 proc-macro2 = { version = "1.0", features = ["span-locations"] }
 syn = { version = "1.0", features = ["full", "extra-traits"] }
 quote = { version = "1.0" }
-dioxus-core = { path = "../core", features = ["serialize", "hot-reload"] }
+dioxus-core = { path = "../core", features = ["serialize"] }
 serde = { version = "1.0", features = ["derive"] }
-
-[features]
-hot-reload = []

+ 9 - 3
packages/rsx/src/error.rs

@@ -1,6 +1,5 @@
 use std::fmt::Display;
 
-use dioxus_core::OwnedCodeLocation;
 use serde::{Deserialize, Serialize};
 
 /// An error produced when interperting the rsx
@@ -19,14 +18,21 @@ pub enum RecompileReason {
     Attribute(String),
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+pub struct CodeLocation {
+    pub line: u32,
+    pub column: u32,
+    pub file_path: &'static str,
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 pub struct ParseError {
     pub message: String,
-    pub location: OwnedCodeLocation,
+    pub location: CodeLocation,
 }
 
 impl ParseError {
-    pub fn new(error: syn::Error, mut location: OwnedCodeLocation) -> Self {
+    pub fn new(error: syn::Error, mut location: CodeLocation) -> Self {
         let message = error.to_string();
         let syn_call_site = error.span().start();
         location.line += syn_call_site.line as u32;

+ 9 - 0
packages/rsx/src/ifmt.rs

@@ -19,6 +19,15 @@ pub struct IfmtInput {
     pub segments: Vec<Segment>,
 }
 
+impl IfmtInput {
+    pub fn is_static(&self) -> bool {
+        match self.segments.as_slice() {
+            &[Segment::Literal(_)] => true,
+            _ => false,
+        }
+    }
+}
+
 impl IfmtInput {
     pub fn to_static(&self) -> Option<String> {
         self.segments

+ 144 - 40
packages/rsx/src/lib.rs

@@ -13,14 +13,14 @@
 
 #[macro_use]
 mod errors;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-mod attributes;
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// mod attributes;
 mod component;
 mod element;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-mod elements;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-mod error;
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// mod elements;
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// mod error;
 mod ifmt;
 mod node;
 mod template;
@@ -30,8 +30,6 @@ pub use component::*;
 pub use element::*;
 pub use ifmt::*;
 pub use node::*;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-pub use template::{try_parse_template, DynamicTemplateContextBuilder};
 
 // imports
 use proc_macro2::TokenStream as TokenStream2;
@@ -40,8 +38,9 @@ use syn::{
     parse::{Parse, ParseStream},
     Result, Token,
 };
-use template::TemplateBuilder;
 
+/// Fundametnally, every CallBody is a template
+#[derive(Default)]
 pub struct CallBody {
     pub roots: Vec<BodyNode>,
 }
@@ -67,13 +66,107 @@ impl Parse for CallBody {
 /// Serialize the same way, regardless of flavor
 impl ToTokens for CallBody {
     fn to_tokens(&self, out_tokens: &mut TokenStream2) {
-        let mut inner = TokenStream2::new();
-        self.to_tokens_without_lazynodes(&mut inner);
+        // As we print out the dynamic nodes, we want to keep track of them in a linear fashion
+        // We'll use the size of the vecs to determine the index of the dynamic node in the final output
+        struct DynamicContext<'a> {
+            dynamic_nodes: Vec<&'a BodyNode>,
+            dynamic_attributes: Vec<&'a ElementAttrNamed>,
+            dynamic_listeners: Vec<&'a ElementAttrNamed>,
+        }
+
+        let mut context = DynamicContext {
+            dynamic_nodes: vec![],
+            dynamic_attributes: vec![],
+            dynamic_listeners: vec![],
+        };
+
+        fn render_static_node<'a>(root: &'a BodyNode, cx: &mut DynamicContext<'a>) -> TokenStream2 {
+            match root {
+                BodyNode::Element(el) => {
+                    let name = &el.name;
+
+                    let children = {
+                        let children = el.children.iter().map(|root| render_static_node(root, cx));
+                        quote! { #(#children),* }
+                    };
+
+                    let attrs = el.attributes.iter().map(|attr| {
+                        //
+                        match &attr.attr {
+                            ElementAttr::AttrText { name, value } if value.is_static() => {
+                                let value = value.source.as_ref().unwrap();
+                                quote! { ::dioxus::core::TemplateAttribute::Static { name: stringify!(#name), value: #value } }
+                            }
+
+                            ElementAttr::CustomAttrText { name, value } if value.is_static() => {
+                                quote! { ::dioxus::core::TemplateAttribute::Static { name: #name, value: #value } }
+                            },
+
+                            ElementAttr::AttrExpression { .. }
+                            | ElementAttr::AttrText { .. }
+                            | ElementAttr::CustomAttrText { .. }
+                            | ElementAttr::CustomAttrExpression { .. } => {
+                                let ct = cx.dynamic_attributes.len();
+                                cx.dynamic_attributes.push(attr);
+                                quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) }
+                            }
+
+                            ElementAttr::EventTokens { .. } => {
+                                let ct = cx.dynamic_listeners.len();
+                                cx.dynamic_listeners.push(attr);
+                                quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) }
+                            }
+                        }
+                    });
+
+                    quote! {
+                        ::dioxus::core::TemplateNode::Element {
+                            tag: dioxus_elements::#name::TAG_NAME,
+                            attrs: &[ #(#attrs),* ],
+                            children: &[ #children ],
+                        }
+                    }
+                }
+
+                BodyNode::Text(text) if text.is_static() => {
+                    let text = text.source.as_ref().unwrap();
+                    quote! { ::dioxus::core::TemplateNode::Text(#text) }
+                }
+
+                BodyNode::RawExpr(_) | BodyNode::Component(_) | BodyNode::Text(_) => {
+                    let ct = cx.dynamic_nodes.len();
+                    cx.dynamic_nodes.push(root);
+                    quote! { ::dioxus::core::TemplateNode::Dynamic(#ct) }
+                }
+            }
+        }
+
+        let root_printer = self
+            .roots
+            .iter()
+            .map(|root| render_static_node(root, &mut context));
+
+        // Render and release the mutable borrow on context
+        let roots = quote! { #( #root_printer ),* };
+
+        let dyn_printer = &context.dynamic_nodes;
+        let attr_printer = context.dynamic_attributes.iter();
+        let listener_printer = context.dynamic_listeners.iter();
 
         out_tokens.append_all(quote! {
-            LazyNodes::new(move |__cx: NodeFactory| -> VNode {
-                use dioxus_elements::{GlobalAttributes, SvgAttributes};
-                #inner
+            LazyNodes::new(move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
+                static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
+                    id: ::dioxus::core::get_line_num!(),
+                    roots: &[ #roots ]
+                };
+
+                __cx.template_ref(
+                    TEMPLATE,
+                    &[ #( #dyn_printer ),* ],
+                    &[ #( #attr_printer ),* ],
+                    &[ #( #listener_printer ),* ],
+                    None
+                )
             })
         })
     }
@@ -81,35 +174,46 @@ impl ToTokens for CallBody {
 
 impl CallBody {
     pub fn to_tokens_without_template(&self, out_tokens: &mut TokenStream2) {
-        let children = &self.roots;
-        let inner = if children.len() == 1 {
-            let inner = &self.roots[0];
-            quote! { #inner }
-        } else {
-            quote! { __cx.fragment_root([ #(#children),* ]) }
-        };
 
-        out_tokens.append_all(quote! {
-            LazyNodes::new(move |__cx: NodeFactory| -> VNode {
-                use dioxus_elements::{GlobalAttributes, SvgAttributes};
-                #inner
-            })
-        })
+        // let children = &self.roots;
+        // let inner = if children.len() == 1 {
+        //     let inner = &self.roots[0];
+        //     quote! { #inner }
+        // } else {
+        //     quote! { __cx.fragment_root([ #(#children),* ]) }
+        // };
+
+        // out_tokens.append_all(quote! {
+        //     LazyNodes::new(move |__cx: NodeFactory| -> VNode {
+        //         use dioxus_elements::{GlobalAttributes, SvgAttributes};
+        //         #inner
+        //     })
+        // })
     }
 
     pub fn to_tokens_without_lazynodes(&self, out_tokens: &mut TokenStream2) {
-        let template = TemplateBuilder::from_roots(self.roots.clone());
-        let inner = if let Some(template) = template {
-            quote! { #template }
-        } else {
-            let children = &self.roots;
-            if children.len() == 1 {
-                let inner = &self.roots[0];
-                quote! { #inner }
-            } else {
-                quote! { __cx.fragment_root([ #(#children),* ]) }
-            }
-        };
-        out_tokens.append_all(inner);
+        out_tokens.append_all(quote! {
+            static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
+                id: ::dioxus::core::get_line_num!(),
+                roots: &[]
+            };
+
+            LazyNodes::new(TEMPLATE, move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
+                todo!()
+            })
+        })
+        // let template = TemplateBuilder::from_roots(self.roots.clone());
+        // let inner = if let Some(template) = template {
+        //     quote! { #template }
+        // } else {
+        //     let children = &self.roots;
+        //     if children.len() == 1 {
+        //         let inner = &self.roots[0];
+        //         quote! { #inner }
+        //     } else {
+        //         quote! { __cx.fragment_root([ #(#children),* ]) }
+        //     }
+        // };
+        // out_tokens.append_all(inner);
     }
 }

+ 1059 - 1080
packages/rsx/src/template.rs

@@ -1,1085 +1,1064 @@
-use dioxus_core::{
-    OwnedAttributeValue, OwnedPathSeg, OwnedTraverse, TemplateAttributeValue, TemplateNodeId,
-    TextTemplate, TextTemplateSegment, UpdateOp,
-};
 use proc_macro2::TokenStream;
 use quote::TokenStreamExt;
 use quote::{quote, ToTokens};
 use syn::{Expr, Ident, LitStr};
 
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-pub fn try_parse_template(
-    rsx: &str,
-    location: OwnedCodeLocation,
-    previous_template: Option<DynamicTemplateContextBuilder>,
-) -> Result<(OwnedTemplate, DynamicTemplateContextBuilder), Error> {
-    use crate::CallBody;
-
-    let call_body: CallBody =
-        syn::parse_str(rsx).map_err(|e| Error::ParseError(ParseError::new(e, location.clone())))?;
-    let mut template_builder = TemplateBuilder::from_roots_always(call_body.roots);
-    if let Some(prev) = previous_template {
-        template_builder = template_builder
-            .try_switch_dynamic_context(prev)
-            .ok_or_else(|| {
-                Error::RecompileRequiredError(RecompileReason::Variable(
-                    "dynamic context updated".to_string(),
-                ))
-            })?;
-    }
-    let dyn_ctx = template_builder.dynamic_context.clone();
-    Ok((template_builder.try_into_owned(&location)?, dyn_ctx))
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-use hot_reload_imports::*;
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-mod hot_reload_imports {
-    pub use crate::{
-        attributes::attrbute_to_static_str,
-        elements::element_to_static_str,
-        error::{Error, ParseError, RecompileReason},
-    };
-    pub use dioxus_core::prelude::OwnedTemplate;
-    pub use dioxus_core::{
-        AttributeDiscription, OwnedAttributeValue, OwnedCodeLocation, OwnedDynamicNodeMapping,
-        OwnedTemplateNode, Template, TemplateAttribute, TemplateAttributeValue, TemplateElement,
-        TemplateNodeId, TemplateNodeType, TextTemplate, TextTemplateSegment,
-    };
-    pub use std::collections::HashMap;
-}
-use crate::{BodyNode, ElementAttr, FormattedSegment, Segment};
-
-struct TemplateElementBuilder {
-    tag: Ident,
-    attributes: Vec<TemplateAttributeBuilder>,
-    children: Vec<TemplateNodeId>,
-    listeners: Vec<usize>,
-    locally_static: bool,
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-type OwndedTemplateElement = TemplateElement<
-    Vec<TemplateAttribute<OwnedAttributeValue>>,
-    OwnedAttributeValue,
-    Vec<TemplateNodeId>,
-    Vec<usize>,
->;
-
-impl TemplateElementBuilder {
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwndedTemplateElement, Error> {
-        let Self {
-            tag,
-            attributes,
-            children,
-            listeners,
-            ..
-        } = self;
-        let (element_tag, element_ns) =
-            element_to_static_str(&tag.to_string()).ok_or_else(|| {
-                Error::ParseError(ParseError::new(
-                    syn::Error::new(tag.span(), format!("unknown element: {}", tag)),
-                    location.clone(),
-                ))
-            })?;
-
-        let mut owned_attributes = Vec::new();
-        for a in attributes {
-            owned_attributes.push(a.try_into_owned(location, element_tag, element_ns)?);
-        }
-
-        Ok(TemplateElement::new(
-            element_tag,
-            element_ns,
-            owned_attributes,
-            children,
-            listeners,
-        ))
-    }
-}
-
-impl ToTokens for TemplateElementBuilder {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Self {
-            tag,
-            attributes,
-            children,
-            listeners,
-            ..
-        } = self;
-        let children = children.iter().map(|id| {
-            let raw = id.0;
-            quote! {TemplateNodeId(#raw)}
-        });
-        tokens.append_all(quote! {
-            TemplateElement::new(
-                dioxus_elements::#tag::TAG_NAME,
-                dioxus_elements::#tag::NAME_SPACE,
-                &[#(#attributes),*],
-                &[#(#children),*],
-                &[#(#listeners),*],
-            )
-        })
-    }
-}
-
-enum AttributeName {
-    Ident(Ident),
-    Str(LitStr),
-}
-
-struct TemplateAttributeBuilder {
-    element_tag: Ident,
-    name: AttributeName,
-    value: TemplateAttributeValue<OwnedAttributeValue>,
-}
-
-impl TemplateAttributeBuilder {
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    fn try_into_owned(
-        self,
-        location: &OwnedCodeLocation,
-        element_tag: &'static str,
-        element_ns: Option<&'static str>,
-    ) -> Result<TemplateAttribute<OwnedAttributeValue>, Error> {
-        let Self { name, value, .. } = self;
-        let (name, span, literal) = match name {
-            AttributeName::Ident(name) => (name.to_string(), name.span(), false),
-            AttributeName::Str(name) => (name.value(), name.span(), true),
-        };
-        let (name, namespace, volatile) = attrbute_to_static_str(&name, element_tag, element_ns)
-            .ok_or_else(|| {
-                if literal {
-                    // literals will be captured when a full recompile is triggered
-                    Error::RecompileRequiredError(RecompileReason::Attribute(name.to_string()))
-                } else {
-                    Error::ParseError(ParseError::new(
-                        syn::Error::new(span, format!("unknown attribute: {}", name)),
-                        location.clone(),
-                    ))
-                }
-            })?;
-        let attribute = AttributeDiscription {
-            name,
-            namespace,
-            volatile,
-        };
-        Ok(TemplateAttribute { value, attribute })
-    }
-}
-
-impl ToTokens for TemplateAttributeBuilder {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Self {
-            element_tag,
-            name,
-            value,
-        } = self;
-        let value = match value {
-            TemplateAttributeValue::Static(val) => {
-                let val = match val {
-                    OwnedAttributeValue::Text(txt) => quote! {StaticAttributeValue::Text(#txt)},
-                    OwnedAttributeValue::Float32(f) => quote! {StaticAttributeValue::Float32(#f)},
-                    OwnedAttributeValue::Float64(f) => quote! {StaticAttributeValue::Float64(#f)},
-                    OwnedAttributeValue::Int32(i) => quote! {StaticAttributeValue::Int32(#i)},
-                    OwnedAttributeValue::Int64(i) => quote! {StaticAttributeValue::Int64(#i)},
-                    OwnedAttributeValue::Uint32(u) => quote! {StaticAttributeValue::Uint32(#u)},
-                    OwnedAttributeValue::Uint64(u) => quote! {StaticAttributeValue::Uint64(#u)},
-                    OwnedAttributeValue::Bool(b) => quote! {StaticAttributeValue::Bool(#b)},
-                    OwnedAttributeValue::Vec3Float(f1, f2, f3) => {
-                        quote! {StaticAttributeValue::Vec3Float(#f1, #f2, #f3)}
-                    }
-                    OwnedAttributeValue::Vec3Int(f1, f2, f3) => {
-                        quote! {StaticAttributeValue::Vec3Int(#f1, #f2, #f3)}
-                    }
-                    OwnedAttributeValue::Vec3Uint(f1, f2, f3) => {
-                        quote! {StaticAttributeValue::Vec3Uint(#f1, #f2, #f3)}
-                    }
-                    OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => {
-                        quote! {StaticAttributeValue::Vec4Float(#f1, #f2, #f3, #f4)}
-                    }
-                    OwnedAttributeValue::Vec4Int(f1, f2, f3, f4) => {
-                        quote! {StaticAttributeValue::Vec4Int(#f1, #f2, #f3, #f4)}
-                    }
-                    OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4) => {
-                        quote! {StaticAttributeValue::Vec4Uint(#f1, #f2, #f3, #f4)}
-                    }
-                    OwnedAttributeValue::Bytes(b) => {
-                        quote! {StaticAttributeValue::Bytes(&[#(#b),*])}
-                    }
-                    OwnedAttributeValue::Any(_) => todo!(),
-                };
-                quote! {TemplateAttributeValue::Static(#val)}
-            }
-            TemplateAttributeValue::Dynamic(idx) => quote! {TemplateAttributeValue::Dynamic(#idx)},
-        };
-        match name {
-            AttributeName::Ident(name) => tokens.append_all(quote! {
-                TemplateAttribute{
-                    attribute: dioxus_elements::#element_tag::#name,
-                    value: #value,
-                }
-            }),
-            AttributeName::Str(lit) => tokens.append_all(quote! {
-                TemplateAttribute{
-                    attribute: dioxus::prelude::AttributeDiscription{
-                        name: #lit,
-                        namespace: None,
-                        volatile: false
-                    },
-                    value: #value,
-                }
-            }),
-        }
-    }
-}
-
-enum TemplateNodeTypeBuilder {
-    Element(TemplateElementBuilder),
-    Text(TextTemplate<Vec<TextTemplateSegment<String>>, String>),
-    DynamicNode(usize),
-}
-
-#[cfg(any(feature = "hot-reload", debug_assertions))]
-type OwnedTemplateNodeType = TemplateNodeType<
-    Vec<TemplateAttribute<OwnedAttributeValue>>,
-    OwnedAttributeValue,
-    Vec<TemplateNodeId>,
-    Vec<usize>,
-    Vec<TextTemplateSegment<String>>,
-    String,
->;
-
-impl TemplateNodeTypeBuilder {
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNodeType, Error> {
-        match self {
-            TemplateNodeTypeBuilder::Element(el) => {
-                Ok(TemplateNodeType::Element(el.try_into_owned(location)?))
-            }
-            TemplateNodeTypeBuilder::Text(txt) => Ok(TemplateNodeType::Text(txt)),
-            TemplateNodeTypeBuilder::DynamicNode(idx) => Ok(TemplateNodeType::DynamicNode(idx)),
-        }
-    }
-}
-
-impl ToTokens for TemplateNodeTypeBuilder {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        match self {
-            TemplateNodeTypeBuilder::Element(el) => tokens.append_all(quote! {
-                TemplateNodeType::Element(#el)
-            }),
-            TemplateNodeTypeBuilder::Text(txt) => {
-                let mut length = 0;
-
-                let segments = txt.segments.iter().map(|seg| match seg {
-                    TextTemplateSegment::Static(s) => {
-                        length += s.len();
-                        quote!(TextTemplateSegment::Static(#s))
-                    }
-                    TextTemplateSegment::Dynamic(idx) => quote!(TextTemplateSegment::Dynamic(#idx)),
-                });
-
-                tokens.append_all(quote! {
-                    TemplateNodeType::Text(TextTemplate::new(&[#(#segments),*], #length))
-                });
-            }
-            TemplateNodeTypeBuilder::DynamicNode(idx) => tokens.append_all(quote! {
-                TemplateNodeType::DynamicNode(#idx)
-            }),
-        }
-    }
-}
-
-struct TemplateNodeBuilder {
-    id: TemplateNodeId,
-    depth: usize,
-    parent: Option<TemplateNodeId>,
-    node_type: TemplateNodeTypeBuilder,
-    fully_static: bool,
-}
-
-impl TemplateNodeBuilder {
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNode, Error> {
-        let TemplateNodeBuilder {
-            id,
-            node_type,
-            parent,
-            depth,
-            ..
-        } = self;
-        let node_type = node_type.try_into_owned(location)?;
-        Ok(OwnedTemplateNode {
-            id,
-            node_type,
-            parent,
-            depth,
-        })
-    }
-
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Self {
-            id,
-            node_type,
-            parent,
-            depth,
-            ..
-        } = self;
-        let raw_id = id.0;
-        let parent = match parent {
-            Some(id) => {
-                let id = id.0;
-                quote! {Some(TemplateNodeId(#id))}
-            }
-            None => quote! {None},
-        };
-
-        tokens.append_all(quote! {
-            TemplateNode {
-                id: TemplateNodeId(#raw_id),
-                node_type: #node_type,
-                parent: #parent,
-                depth: #depth,
-            }
-        })
-    }
-}
-
-#[derive(Default)]
-pub struct TemplateBuilder {
-    nodes: Vec<TemplateNodeBuilder>,
-    root_nodes: Vec<TemplateNodeId>,
-    dynamic_context: DynamicTemplateContextBuilder,
-}
-
-impl TemplateBuilder {
-    /// Create a template builder from nodes if it would improve performance to do so.
-    pub fn from_roots(roots: Vec<BodyNode>) -> Option<Self> {
-        let mut builder = Self::default();
-
-        for (i, root) in roots.into_iter().enumerate() {
-            let id = builder.build_node(root, None, vec![i], 0);
-            builder.root_nodes.push(id);
-        }
-
-        // only build a template if there is at least one static node
-        if builder
-            .nodes
-            .iter()
-            .all(|r| matches!(&r.node_type, TemplateNodeTypeBuilder::DynamicNode(_)))
-        {
-            None
-        } else {
-            Some(builder)
-        }
-    }
-
-    /// Create a template builder from nodes regardless of performance.
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    fn from_roots_always(roots: Vec<BodyNode>) -> Self {
-        let mut builder = Self::default();
-
-        for (i, root) in roots.into_iter().enumerate() {
-            let id = builder.build_node(root, None, vec![i], 0);
-            builder.root_nodes.push(id);
-        }
-
-        builder
-    }
-
-    fn build_node(
-        &mut self,
-        node: BodyNode,
-        parent: Option<TemplateNodeId>,
-        path: Vec<usize>,
-        depth: usize,
-    ) -> TemplateNodeId {
-        let id = TemplateNodeId(self.nodes.len());
-        match node {
-            BodyNode::Element(el) => {
-                let mut locally_static = true;
-                let mut attributes = Vec::new();
-                let mut listeners = Vec::new();
-                for attr in el.attributes {
-                    match attr.attr {
-                        ElementAttr::AttrText { name, value } => {
-                            if let Some(static_value) = value.to_static() {
-                                attributes.push(TemplateAttributeBuilder {
-                                    element_tag: el.name.clone(),
-                                    name: AttributeName::Ident(name),
-                                    value: TemplateAttributeValue::Static(
-                                        OwnedAttributeValue::Text(static_value),
-                                    ),
-                                })
-                            } else {
-                                locally_static = false;
-                                attributes.push(TemplateAttributeBuilder {
-                                    element_tag: el.name.clone(),
-                                    name: AttributeName::Ident(name),
-                                    value: TemplateAttributeValue::Dynamic(
-                                        self.dynamic_context.add_attr(quote!(#value)),
-                                    ),
-                                })
-                            }
-                        }
-                        ElementAttr::CustomAttrText { name, value } => {
-                            if let Some(static_value) = value.to_static() {
-                                attributes.push(TemplateAttributeBuilder {
-                                    element_tag: el.name.clone(),
-                                    name: AttributeName::Str(name),
-                                    value: TemplateAttributeValue::Static(
-                                        OwnedAttributeValue::Text(static_value),
-                                    ),
-                                })
-                            } else {
-                                locally_static = false;
-                                attributes.push(TemplateAttributeBuilder {
-                                    element_tag: el.name.clone(),
-                                    name: AttributeName::Str(name),
-                                    value: TemplateAttributeValue::Dynamic(
-                                        self.dynamic_context.add_attr(quote!(#value)),
-                                    ),
-                                })
-                            }
-                        }
-                        ElementAttr::AttrExpression { name, value } => {
-                            locally_static = false;
-                            attributes.push(TemplateAttributeBuilder {
-                                element_tag: el.name.clone(),
-                                name: AttributeName::Ident(name),
-                                value: TemplateAttributeValue::Dynamic(
-                                    self.dynamic_context.add_attr(quote!(#value)),
-                                ),
-                            })
-                        }
-                        ElementAttr::CustomAttrExpression { name, value } => {
-                            locally_static = false;
-                            attributes.push(TemplateAttributeBuilder {
-                                element_tag: el.name.clone(),
-                                name: AttributeName::Str(name),
-                                value: TemplateAttributeValue::Dynamic(
-                                    self.dynamic_context.add_attr(quote!(#value)),
-                                ),
-                            })
-                        }
-                        ElementAttr::EventTokens { name, tokens } => {
-                            locally_static = false;
-                            listeners.push(self.dynamic_context.add_listener(name, tokens))
-                        }
-                    }
-                }
-                if let Some(key) = el.key {
-                    self.dynamic_context.add_key(quote!(
-                        dioxus::core::exports::bumpalo::format!(in __bump, "{}", #key)
-                            .into_bump_str()
-                    ));
-                }
-                self.nodes.push(TemplateNodeBuilder {
-                    id,
-                    node_type: TemplateNodeTypeBuilder::Element(TemplateElementBuilder {
-                        tag: el.name,
-                        attributes,
-                        children: Vec::new(),
-                        listeners,
-                        locally_static,
-                    }),
-                    parent,
-                    depth,
-                    fully_static: false,
-                });
-                let children: Vec<_> = el
-                    .children
-                    .into_iter()
-                    .enumerate()
-                    .map(|(i, child)| {
-                        let mut new_path = path.clone();
-                        new_path.push(i);
-                        self.build_node(child, Some(id), new_path, depth + 1)
-                    })
-                    .collect();
-                let children_fully_static = children.iter().all(|c| self.nodes[c.0].fully_static);
-                let parent = &mut self.nodes[id.0];
-                parent.fully_static = locally_static && children_fully_static;
-                if let TemplateNodeTypeBuilder::Element(element) = &mut parent.node_type {
-                    element.children = children;
-                }
-            }
-
-            BodyNode::Component(comp) => {
-                self.nodes.push(TemplateNodeBuilder {
-                    id,
-                    node_type: TemplateNodeTypeBuilder::DynamicNode(
-                        self.dynamic_context.add_node(BodyNode::Component(comp)),
-                    ),
-                    parent,
-                    depth,
-                    fully_static: false,
-                });
-            }
-
-            BodyNode::Text(txt) => {
-                let mut segments = Vec::new();
-                let mut length = 0;
-                let mut fully_static = true;
-
-                for segment in txt.segments {
-                    segments.push(match segment {
-                        Segment::Literal(lit) => {
-                            length += lit.len();
-                            TextTemplateSegment::Static(lit)
-                        }
-                        Segment::Formatted(fmted) => {
-                            fully_static = false;
-                            TextTemplateSegment::Dynamic(self.dynamic_context.add_text(fmted))
-                        }
-                    })
-                }
-
-                self.nodes.push(TemplateNodeBuilder {
-                    id,
-                    node_type: TemplateNodeTypeBuilder::Text(TextTemplate::new(segments, length)),
-                    parent,
-                    depth,
-                    fully_static,
-                });
-            }
-
-            BodyNode::RawExpr(expr) => {
-                self.nodes.push(TemplateNodeBuilder {
-                    id,
-                    node_type: TemplateNodeTypeBuilder::DynamicNode(
-                        self.dynamic_context.add_node(BodyNode::RawExpr(expr)),
-                    ),
-                    parent,
-                    depth,
-                    fully_static: false,
-                });
-            }
-        }
-        id
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub fn try_switch_dynamic_context(
-        mut self,
-        dynamic_context: DynamicTemplateContextBuilder,
-    ) -> Option<Self> {
-        let attribute_mapping: HashMap<String, usize> = dynamic_context
-            .attributes
-            .iter()
-            .enumerate()
-            .map(|(i, ts)| (ts.to_string(), i))
-            .collect();
-        let text_mapping: HashMap<String, usize> = dynamic_context
-            .text
-            .iter()
-            .enumerate()
-            .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
-            .collect();
-        let listener_mapping: HashMap<(String, Expr), usize> = dynamic_context
-            .listeners
-            .iter()
-            .enumerate()
-            .map(|(i, ts)| (ts.clone(), i))
-            .collect();
-        let node_mapping: HashMap<String, usize> = dynamic_context
-            .nodes
-            .iter()
-            .enumerate()
-            .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
-            .collect();
-
-        for node in &mut self.nodes {
-            match &mut node.node_type {
-                TemplateNodeTypeBuilder::Element(element) => {
-                    for listener in &mut element.listeners {
-                        *listener =
-                            *listener_mapping.get(&self.dynamic_context.listeners[*listener])?;
-                    }
-                    for attribute in &mut element.attributes {
-                        if let TemplateAttributeValue::Dynamic(idx) = &mut attribute.value {
-                            *idx = *attribute_mapping
-                                .get(&self.dynamic_context.attributes[*idx].to_string())?;
-                        }
-                    }
-                }
-                TemplateNodeTypeBuilder::Text(txt) => {
-                    for seg in &mut txt.segments {
-                        if let TextTemplateSegment::Dynamic(idx) = seg {
-                            *idx = *text_mapping.get(
-                                &self.dynamic_context.text[*idx]
-                                    .to_token_stream()
-                                    .to_string(),
-                            )?;
-                        }
-                    }
-                }
-                TemplateNodeTypeBuilder::DynamicNode(idx) => {
-                    *idx = *node_mapping.get(
-                        &self.dynamic_context.nodes[*idx]
-                            .to_token_stream()
-                            .to_string(),
-                    )?;
-                }
-            }
-        }
-        self.dynamic_context = dynamic_context;
-
-        Some(self)
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplate, Error> {
-        let mut nodes = Vec::new();
-        let dynamic_mapping = self.dynamic_mapping(&nodes);
-        let dynamic_path = self.dynamic_path();
-        for node in self.nodes {
-            nodes.push(node.try_into_owned(location)?);
-        }
-
-        Ok(OwnedTemplate {
-            nodes,
-            root_nodes: self.root_nodes,
-            dynamic_mapping,
-            dynamic_path,
-        })
-    }
-
-    #[cfg(any(feature = "hot-reload", debug_assertions))]
-    pub fn dynamic_mapping(
-        &self,
-        resolved_nodes: &Vec<OwnedTemplateNode>,
-    ) -> OwnedDynamicNodeMapping {
-        let dynamic_context = &self.dynamic_context;
-        let mut node_mapping = vec![None; dynamic_context.nodes.len()];
-        let nodes = &self.nodes;
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
-                node_mapping[*idx] = Some(n.id)
-            }
-        }
-        let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
-                for seg in &txt.segments {
-                    match seg {
-                        TextTemplateSegment::Static(_) => (),
-                        TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
-                    }
-                }
-            }
-        }
-        let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
-                for (i, attr) in el.attributes.iter().enumerate() {
-                    match attr.value {
-                        TemplateAttributeValue::Static(_) => (),
-                        TemplateAttributeValue::Dynamic(idx) => {
-                            attribute_mapping[idx].push((n.id, i));
-                        }
-                    }
-                }
-            }
-        }
-        let mut listener_mapping = Vec::new();
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
-                if !el.listeners.is_empty() {
-                    listener_mapping.push(n.id);
-                }
-            }
-        }
-
-        let mut volatile_mapping = Vec::new();
-        for n in resolved_nodes {
-            if let TemplateNodeType::Element(el) = &n.node_type {
-                for (i, attr) in el.attributes.iter().enumerate() {
-                    if attr.attribute.volatile {
-                        volatile_mapping.push((n.id, i));
-                    }
-                }
-            }
-        }
-
-        OwnedDynamicNodeMapping::new(
-            node_mapping,
-            text_mapping,
-            attribute_mapping,
-            volatile_mapping,
-            listener_mapping,
-        )
-    }
-
-    fn dynamic_path(&self) -> Option<OwnedPathSeg> {
-        let mut last_seg: Option<OwnedPathSeg> = None;
-        let mut nodes_to_insert_after = Vec::new();
-        // iterating from the last root to the first
-        for root in self.root_nodes.iter().rev() {
-            let root_node = &self.nodes[root.0];
-            if let TemplateNodeTypeBuilder::DynamicNode(_) = root_node.node_type {
-                match &mut last_seg {
-                    // if there has been no static nodes, we can append the child to the parent node
-                    None => nodes_to_insert_after.push(*root),
-                    // otherwise we insert the child before the last static node
-                    Some(seg) => {
-                        seg.ops.push(UpdateOp::InsertBefore(*root));
-                    }
-                }
-            } else if let Some(mut new) = self.construct_path_segment(*root) {
-                if let Some(last) = last_seg.take() {
-                    match new.traverse {
-                        OwnedTraverse::Halt => {
-                            new.traverse = OwnedTraverse::NextSibling(Box::new(last));
-                        }
-                        OwnedTraverse::FirstChild(b) => {
-                            new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
-                        }
-                        _ => unreachable!(),
-                    }
-                } else {
-                    for node in nodes_to_insert_after.drain(..) {
-                        new.ops.push(UpdateOp::InsertAfter(node));
-                    }
-                }
-                last_seg = Some(new);
-            } else if let Some(last) = last_seg.take() {
-                last_seg = Some(OwnedPathSeg {
-                    ops: Vec::new(),
-                    traverse: OwnedTraverse::NextSibling(Box::new(last)),
-                });
-            }
-        }
-        last_seg
-    }
-
-    fn construct_path_segment(&self, node_id: TemplateNodeId) -> Option<OwnedPathSeg> {
-        let n = &self.nodes[node_id.0];
-        if n.fully_static {
-            return None;
-        }
-        match &n.node_type {
-            TemplateNodeTypeBuilder::Element(el) => {
-                let mut last_seg: Option<OwnedPathSeg> = None;
-                let mut children_to_append = Vec::new();
-                // iterating from the last child to the first
-                for child in el.children.iter().rev() {
-                    let child_node = &self.nodes[child.0];
-                    if let TemplateNodeTypeBuilder::DynamicNode(_) = child_node.node_type {
-                        match &mut last_seg {
-                            // if there has been no static nodes, we can append the child to the parent node
-                            None => children_to_append.push(*child),
-                            // otherwise we insert the child before the last static node
-                            Some(seg) => {
-                                seg.ops.push(UpdateOp::InsertBefore(*child));
-                            }
-                        }
-                    } else if let Some(mut new) = self.construct_path_segment(*child) {
-                        if let Some(last) = last_seg.take() {
-                            match new.traverse {
-                                OwnedTraverse::Halt => {
-                                    new.traverse = OwnedTraverse::NextSibling(Box::new(last));
-                                }
-                                OwnedTraverse::FirstChild(b) => {
-                                    new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
-                                }
-                                _ => unreachable!(),
-                            }
-                        }
-                        last_seg = Some(new);
-                    } else if let Some(last) = last_seg.take() {
-                        last_seg = Some(OwnedPathSeg {
-                            ops: Vec::new(),
-                            traverse: OwnedTraverse::NextSibling(Box::new(last)),
-                        });
-                    }
-                }
-                let mut ops = Vec::new();
-                if !el.locally_static || n.parent.is_none() {
-                    ops.push(UpdateOp::StoreNode(node_id));
-                }
-                for child in children_to_append.into_iter().rev() {
-                    ops.push(UpdateOp::AppendChild(child));
-                }
-                Some(OwnedPathSeg {
-                    ops,
-                    traverse: match last_seg {
-                        Some(last) => OwnedTraverse::FirstChild(Box::new(last)),
-                        None => OwnedTraverse::Halt,
-                    },
-                })
-            }
-            TemplateNodeTypeBuilder::Text(_) => Some(OwnedPathSeg {
-                ops: vec![UpdateOp::StoreNode(n.id)],
-                traverse: dioxus_core::OwnedTraverse::Halt,
-            }),
-            TemplateNodeTypeBuilder::DynamicNode(_) => unreachable!(
-                "constructing path segment for dynamic nodes is handled in the parent node"
-            ),
-        }
-    }
-}
-
-impl ToTokens for TemplateBuilder {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Self {
-            nodes,
-            root_nodes,
-            dynamic_context,
-        } = self;
-
-        let mut node_mapping = vec![None; dynamic_context.nodes.len()];
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
-                node_mapping[*idx] = Some(n.id);
-            }
-        }
-        let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
-                for seg in &txt.segments {
-                    match seg {
-                        TextTemplateSegment::Static(_) => (),
-                        TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
-                    }
-                }
-            }
-        }
-        let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
-                for (i, attr) in el.attributes.iter().enumerate() {
-                    match attr.value {
-                        TemplateAttributeValue::Static(_) => (),
-                        TemplateAttributeValue::Dynamic(idx) => {
-                            attribute_mapping[idx].push((n.id, i));
-                        }
-                    }
-                }
-            }
-        }
-        let mut listener_mapping = Vec::new();
-        for n in nodes {
-            if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
-                if !el.listeners.is_empty() {
-                    listener_mapping.push(n.id);
-                }
-            }
-        }
-
-        let root_nodes = root_nodes.iter().map(|id| {
-            let raw = id.0;
-            quote! { TemplateNodeId(#raw) }
-        });
-        let node_mapping_quoted = node_mapping.iter().map(|op| match op {
-            Some(id) => {
-                let raw_id = id.0;
-                quote! {Some(TemplateNodeId(#raw_id))}
-            }
-            None => quote! {None},
-        });
-        let text_mapping_quoted = text_mapping.iter().map(|inner| {
-            let raw = inner.iter().map(|id| id.0);
-            quote! {&[#(TemplateNodeId(#raw)),*]}
-        });
-        let attribute_mapping_quoted = attribute_mapping.iter().map(|inner| {
-            let raw = inner.iter().map(|(id, _)| id.0);
-            let indecies = inner.iter().map(|(_, idx)| idx);
-            quote! {&[#((TemplateNodeId(#raw), #indecies)),*]}
-        });
-        let listener_mapping_quoted = listener_mapping.iter().map(|id| {
-            let raw = id.0;
-            quote! {TemplateNodeId(#raw)}
-        });
-        let mut nodes_quoted = TokenStream::new();
-        for n in nodes {
-            n.to_tokens(&mut nodes_quoted);
-            quote! {,}.to_tokens(&mut nodes_quoted);
-        }
-
-        let dynamic_path = match self.dynamic_path() {
-            Some(seg) => {
-                let seg = quote_owned_segment(seg);
-                quote! {Some(#seg)}
-            }
-            None => quote! {None},
-        };
-
-        let quoted = quote! {
-            {
-                const __NODES: dioxus::prelude::StaticTemplateNodes = &[#nodes_quoted];
-                const __TEXT_MAPPING: &'static [&'static [dioxus::prelude::TemplateNodeId]] = &[#(#text_mapping_quoted),*];
-                const __ATTRIBUTE_MAPPING: &'static [&'static [(dioxus::prelude::TemplateNodeId, usize)]] = &[#(#attribute_mapping_quoted),*];
-                const __ROOT_NODES: &'static [dioxus::prelude::TemplateNodeId] = &[#(#root_nodes),*];
-                const __NODE_MAPPING: &'static [Option<dioxus::prelude::TemplateNodeId>] = &[#(#node_mapping_quoted),*];
-                const __NODES_WITH_LISTENERS: &'static [dioxus::prelude::TemplateNodeId] = &[#(#listener_mapping_quoted),*];
-                static __VOLITALE_MAPPING_INNER: dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = dioxus::core::exports::once_cell::sync::Lazy::new(||{
-                    // check each property to see if it is volatile
-                    let mut volatile = Vec::new();
-                    for n in __NODES {
-                        if let TemplateNodeType::Element(el) = &n.node_type {
-                            for (i, attr) in el.attributes.iter().enumerate() {
-                                if attr.attribute.volatile {
-                                    volatile.push((n.id, i));
-                                }
-                            }
-                        }
-                    }
-                    volatile
-                });
-                static __VOLITALE_MAPPING: &'static dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = &__VOLITALE_MAPPING_INNER;
-                static __STATIC_VOLITALE_MAPPING: dioxus::prelude::LazyStaticVec<(dioxus::prelude::TemplateNodeId, usize)> = LazyStaticVec(__VOLITALE_MAPPING);
-                static __TEMPLATE: dioxus::prelude::Template = Template::Static(&StaticTemplate {
-                    nodes: __NODES,
-                    root_nodes: __ROOT_NODES,
-                    dynamic_mapping: StaticDynamicNodeMapping::new(__NODE_MAPPING, __TEXT_MAPPING, __ATTRIBUTE_MAPPING, __STATIC_VOLITALE_MAPPING, __NODES_WITH_LISTENERS),
-                    dynamic_path: #dynamic_path,
-                });
-
-                let __bump = __cx.bump();
-                __cx.template_ref(dioxus::prelude::TemplateId(get_line_num!()), __TEMPLATE.clone(), #dynamic_context)
-            }
-        };
-
-        tokens.append_all(quoted)
-    }
-}
-
-#[derive(Default, Clone, Debug)]
-pub struct DynamicTemplateContextBuilder {
-    nodes: Vec<BodyNode>,
-    text: Vec<FormattedSegment>,
-    attributes: Vec<TokenStream>,
-    listeners: Vec<(String, Expr)>,
-    key: Option<TokenStream>,
-}
-
-impl DynamicTemplateContextBuilder {
-    fn add_node(&mut self, node: BodyNode) -> usize {
-        let node_id = self.nodes.len();
-
-        self.nodes.push(node);
-
-        node_id
-    }
-
-    fn add_text(&mut self, text: FormattedSegment) -> usize {
-        let text_id = self.text.len();
-
-        self.text.push(text);
-
-        text_id
-    }
-
-    fn add_attr(&mut self, attr: TokenStream) -> usize {
-        let attr_id = self.attributes.len();
-
-        self.attributes.push(attr);
-
-        attr_id
-    }
-
-    fn add_listener(&mut self, name: Ident, listener: Expr) -> usize {
-        let listener_id = self.listeners.len();
-
-        self.listeners.push((name.to_string(), listener));
-
-        listener_id
-    }
-
-    fn add_key(&mut self, key: TokenStream) {
-        self.key = Some(key);
-    }
-}
-
-impl ToTokens for DynamicTemplateContextBuilder {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let nodes = &self.nodes;
-        let text = &self.text;
-        let attributes = &self.attributes;
-        let listeners_names = self
-            .listeners
-            .iter()
-            .map(|(n, _)| syn::parse_str::<Ident>(n).expect(n));
-        let listeners_exprs = self.listeners.iter().map(|(_, e)| e);
-        let key = match &self.key {
-            Some(k) => quote!(Some(#k)),
-            None => quote!(None),
-        };
-        tokens.append_all(quote! {
-            TemplateContext {
-                nodes: __cx.bump().alloc([#(#nodes),*]),
-                text_segments: __cx.bump().alloc([#(&*dioxus::core::exports::bumpalo::format!(in __bump, "{}", #text).into_bump_str()),*]),
-                attributes: __cx.bump().alloc([#({#attributes}.into_value(__cx.bump())),*]),
-                listeners: __cx.bump().alloc([#(dioxus_elements::on::#listeners_names(__cx, #listeners_exprs)),*]),
-                key: #key,
-            }
-        })
-    }
-}
-
-fn quote_owned_segment(seg: OwnedPathSeg) -> proc_macro2::TokenStream {
-    let OwnedPathSeg { ops, traverse } = seg;
-
-    let ops = ops
-        .into_iter()
-        .map(|op| match op {
-            UpdateOp::StoreNode(id) => {
-                let id = quote_template_node_id(id);
-                quote!(UpdateOp::StoreNode(#id))
-            }
-            UpdateOp::InsertBefore(id) => {
-                let id = quote_template_node_id(id);
-                quote!(UpdateOp::InsertBefore(#id))
-            }
-            UpdateOp::InsertAfter(id) => {
-                let id = quote_template_node_id(id);
-                quote!(UpdateOp::InsertAfter(#id))
-            }
-            UpdateOp::AppendChild(id) => {
-                let id = quote_template_node_id(id);
-                quote!(UpdateOp::AppendChild(#id))
-            }
-        })
-        .collect::<Vec<_>>();
-
-    let traverse = quote_owned_traverse(traverse);
-
-    quote! {
-        StaticPathSeg {
-            ops: &[#(#ops),*],
-            traverse: #traverse,
-        }
-    }
-}
-
-fn quote_owned_traverse(traverse: OwnedTraverse) -> proc_macro2::TokenStream {
-    match traverse {
-        OwnedTraverse::Halt => {
-            quote! {StaticTraverse::Halt}
-        }
-        OwnedTraverse::FirstChild(seg) => {
-            let seg = quote_owned_segment(*seg);
-            quote! {StaticTraverse::FirstChild(&#seg)}
-        }
-        OwnedTraverse::NextSibling(seg) => {
-            let seg = quote_owned_segment(*seg);
-            quote! {StaticTraverse::NextSibling(&#seg)}
-        }
-        OwnedTraverse::Both(b) => {
-            let (child, sibling) = *b;
-            let child = quote_owned_segment(child);
-            let sibling = quote_owned_segment(sibling);
-            quote! {StaticTraverse::Both(&(#child, #sibling))}
-        }
-    }
-}
-
-fn quote_template_node_id(id: TemplateNodeId) -> proc_macro2::TokenStream {
-    let raw = id.0;
-    quote! {
-        TemplateNodeId(#raw)
-    }
-}
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// pub fn try_parse_template(
+//     rsx: &str,
+//     location: OwnedCodeLocation,
+//     previous_template: Option<DynamicTemplateContextBuilder>,
+// ) -> Result<(OwnedTemplate, DynamicTemplateContextBuilder), Error> {
+//     use crate::CallBody;
+
+//     let call_body: CallBody =
+//         syn::parse_str(rsx).map_err(|e| Error::ParseError(ParseError::new(e, location.clone())))?;
+//     let mut template_builder = TemplateBuilder::from_roots_always(call_body.roots);
+//     if let Some(prev) = previous_template {
+//         template_builder = template_builder
+//             .try_switch_dynamic_context(prev)
+//             .ok_or_else(|| {
+//                 Error::RecompileRequiredError(RecompileReason::Variable(
+//                     "dynamic context updated".to_string(),
+//                 ))
+//             })?;
+//     }
+//     let dyn_ctx = template_builder.dynamic_context.clone();
+//     Ok((template_builder.try_into_owned(&location)?, dyn_ctx))
+// }
+
+// use crate::{BodyNode, ElementAttr, FormattedSegment, Segment};
+
+// struct TemplateElementBuilder {
+//     tag: Ident,
+//     attributes: Vec<TemplateAttributeBuilder>,
+//     children: Vec<TemplateNodeId>,
+//     listeners: Vec<usize>,
+//     locally_static: bool,
+// }
+
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// type OwndedTemplateElement = TemplateElement<
+//     Vec<TemplateAttribute<OwnedAttributeValue>>,
+//     OwnedAttributeValue,
+//     Vec<TemplateNodeId>,
+//     Vec<usize>,
+// >;
+
+// impl TemplateElementBuilder {
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwndedTemplateElement, Error> {
+//         let Self {
+//             tag,
+//             attributes,
+//             children,
+//             listeners,
+//             ..
+//         } = self;
+//         let (element_tag, element_ns) =
+//             element_to_static_str(&tag.to_string()).ok_or_else(|| {
+//                 Error::ParseError(ParseError::new(
+//                     syn::Error::new(tag.span(), format!("unknown element: {}", tag)),
+//                     location.clone(),
+//                 ))
+//             })?;
+
+//         let mut owned_attributes = Vec::new();
+//         for a in attributes {
+//             owned_attributes.push(a.try_into_owned(location, element_tag, element_ns)?);
+//         }
+
+//         Ok(TemplateElement::new(
+//             element_tag,
+//             element_ns,
+//             owned_attributes,
+//             children,
+//             listeners,
+//         ))
+//     }
+// }
+
+// impl ToTokens for TemplateElementBuilder {
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         let Self {
+//             tag,
+//             attributes,
+//             children,
+//             listeners,
+//             ..
+//         } = self;
+//         let children = children.iter().map(|id| {
+//             let raw = id.0;
+//             quote! {TemplateNodeId(#raw)}
+//         });
+//         tokens.append_all(quote! {
+//             TemplateElement::new(
+//                 dioxus_elements::#tag::TAG_NAME,
+//                 dioxus_elements::#tag::NAME_SPACE,
+//                 &[#(#attributes),*],
+//                 &[#(#children),*],
+//                 &[#(#listeners),*],
+//             )
+//         })
+//     }
+// }
+
+// enum AttributeName {
+//     Ident(Ident),
+//     Str(LitStr),
+// }
+
+// struct TemplateAttributeBuilder {
+//     element_tag: Ident,
+//     name: AttributeName,
+//     value: TemplateAttributeValue<OwnedAttributeValue>,
+// }
+
+// impl TemplateAttributeBuilder {
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     fn try_into_owned(
+//         self,
+//         location: &OwnedCodeLocation,
+//         element_tag: &'static str,
+//         element_ns: Option<&'static str>,
+//     ) -> Result<TemplateAttribute<OwnedAttributeValue>, Error> {
+//         let Self { name, value, .. } = self;
+//         let (name, span, literal) = match name {
+//             AttributeName::Ident(name) => (name.to_string(), name.span(), false),
+//             AttributeName::Str(name) => (name.value(), name.span(), true),
+//         };
+//         let (name, namespace, volatile) = attrbute_to_static_str(&name, element_tag, element_ns)
+//             .ok_or_else(|| {
+//                 if literal {
+//                     // literals will be captured when a full recompile is triggered
+//                     Error::RecompileRequiredError(RecompileReason::Attribute(name.to_string()))
+//                 } else {
+//                     Error::ParseError(ParseError::new(
+//                         syn::Error::new(span, format!("unknown attribute: {}", name)),
+//                         location.clone(),
+//                     ))
+//                 }
+//             })?;
+//         let attribute = AttributeDiscription {
+//             name,
+//             namespace,
+//             volatile,
+//         };
+//         Ok(TemplateAttribute { value, attribute })
+//     }
+// }
+
+// impl ToTokens for TemplateAttributeBuilder {
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         let Self {
+//             element_tag,
+//             name,
+//             value,
+//         } = self;
+//         let value = match value {
+//             TemplateAttributeValue::Static(val) => {
+//                 let val = match val {
+//                     OwnedAttributeValue::Text(txt) => quote! {StaticAttributeValue::Text(#txt)},
+//                     OwnedAttributeValue::Float32(f) => quote! {StaticAttributeValue::Float32(#f)},
+//                     OwnedAttributeValue::Float64(f) => quote! {StaticAttributeValue::Float64(#f)},
+//                     OwnedAttributeValue::Int32(i) => quote! {StaticAttributeValue::Int32(#i)},
+//                     OwnedAttributeValue::Int64(i) => quote! {StaticAttributeValue::Int64(#i)},
+//                     OwnedAttributeValue::Uint32(u) => quote! {StaticAttributeValue::Uint32(#u)},
+//                     OwnedAttributeValue::Uint64(u) => quote! {StaticAttributeValue::Uint64(#u)},
+//                     OwnedAttributeValue::Bool(b) => quote! {StaticAttributeValue::Bool(#b)},
+//                     OwnedAttributeValue::Vec3Float(f1, f2, f3) => {
+//                         quote! {StaticAttributeValue::Vec3Float(#f1, #f2, #f3)}
+//                     }
+//                     OwnedAttributeValue::Vec3Int(f1, f2, f3) => {
+//                         quote! {StaticAttributeValue::Vec3Int(#f1, #f2, #f3)}
+//                     }
+//                     OwnedAttributeValue::Vec3Uint(f1, f2, f3) => {
+//                         quote! {StaticAttributeValue::Vec3Uint(#f1, #f2, #f3)}
+//                     }
+//                     OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => {
+//                         quote! {StaticAttributeValue::Vec4Float(#f1, #f2, #f3, #f4)}
+//                     }
+//                     OwnedAttributeValue::Vec4Int(f1, f2, f3, f4) => {
+//                         quote! {StaticAttributeValue::Vec4Int(#f1, #f2, #f3, #f4)}
+//                     }
+//                     OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4) => {
+//                         quote! {StaticAttributeValue::Vec4Uint(#f1, #f2, #f3, #f4)}
+//                     }
+//                     OwnedAttributeValue::Bytes(b) => {
+//                         quote! {StaticAttributeValue::Bytes(&[#(#b),*])}
+//                     }
+//                     OwnedAttributeValue::Any(_) => todo!(),
+//                 };
+//                 quote! {TemplateAttributeValue::Static(#val)}
+//             }
+//             TemplateAttributeValue::Dynamic(idx) => quote! {TemplateAttributeValue::Dynamic(#idx)},
+//         };
+//         match name {
+//             AttributeName::Ident(name) => tokens.append_all(quote! {
+//                 TemplateAttribute{
+//                     attribute: dioxus_elements::#element_tag::#name,
+//                     value: #value,
+//                 }
+//             }),
+//             AttributeName::Str(lit) => tokens.append_all(quote! {
+//                 TemplateAttribute{
+//                     attribute: dioxus::prelude::AttributeDiscription{
+//                         name: #lit,
+//                         namespace: None,
+//                         volatile: false
+//                     },
+//                     value: #value,
+//                 }
+//             }),
+//         }
+//     }
+// }
+
+// enum TemplateNodeTypeBuilder {
+//     Element(TemplateElementBuilder),
+//     Text(TextTemplate<Vec<TextTemplateSegment<String>>, String>),
+//     DynamicNode(usize),
+// }
+
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
+// type OwnedTemplateNodeType = TemplateNodeType<
+//     Vec<TemplateAttribute<OwnedAttributeValue>>,
+//     OwnedAttributeValue,
+//     Vec<TemplateNodeId>,
+//     Vec<usize>,
+//     Vec<TextTemplateSegment<String>>,
+//     String,
+// >;
+
+// impl TemplateNodeTypeBuilder {
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNodeType, Error> {
+//         match self {
+//             TemplateNodeTypeBuilder::Element(el) => {
+//                 Ok(TemplateNodeType::Element(el.try_into_owned(location)?))
+//             }
+//             TemplateNodeTypeBuilder::Text(txt) => Ok(TemplateNodeType::Text(txt)),
+//             TemplateNodeTypeBuilder::DynamicNode(idx) => Ok(TemplateNodeType::DynamicNode(idx)),
+//         }
+//     }
+// }
+
+// impl ToTokens for TemplateNodeTypeBuilder {
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         match self {
+//             TemplateNodeTypeBuilder::Element(el) => tokens.append_all(quote! {
+//                 TemplateNodeType::Element(#el)
+//             }),
+//             TemplateNodeTypeBuilder::Text(txt) => {
+//                 let mut length = 0;
+
+//                 let segments = txt.segments.iter().map(|seg| match seg {
+//                     TextTemplateSegment::Static(s) => {
+//                         length += s.len();
+//                         quote!(TextTemplateSegment::Static(#s))
+//                     }
+//                     TextTemplateSegment::Dynamic(idx) => quote!(TextTemplateSegment::Dynamic(#idx)),
+//                 });
+
+//                 tokens.append_all(quote! {
+//                     TemplateNodeType::Text(TextTemplate::new(&[#(#segments),*], #length))
+//                 });
+//             }
+//             TemplateNodeTypeBuilder::DynamicNode(idx) => tokens.append_all(quote! {
+//                 TemplateNodeType::DynamicNode(#idx)
+//             }),
+//         }
+//     }
+// }
+
+// struct TemplateNodeBuilder {
+//     id: TemplateNodeId,
+//     depth: usize,
+//     parent: Option<TemplateNodeId>,
+//     node_type: TemplateNodeTypeBuilder,
+//     fully_static: bool,
+// }
+
+// impl TemplateNodeBuilder {
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNode, Error> {
+//         let TemplateNodeBuilder {
+//             id,
+//             node_type,
+//             parent,
+//             depth,
+//             ..
+//         } = self;
+//         let node_type = node_type.try_into_owned(location)?;
+//         Ok(OwnedTemplateNode {
+//             id,
+//             node_type,
+//             parent,
+//             depth,
+//         })
+//     }
+
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         let Self {
+//             id,
+//             node_type,
+//             parent,
+//             depth,
+//             ..
+//         } = self;
+//         let raw_id = id.0;
+//         let parent = match parent {
+//             Some(id) => {
+//                 let id = id.0;
+//                 quote! {Some(TemplateNodeId(#id))}
+//             }
+//             None => quote! {None},
+//         };
+
+//         tokens.append_all(quote! {
+//             TemplateNode {
+//                 id: TemplateNodeId(#raw_id),
+//                 node_type: #node_type,
+//                 parent: #parent,
+//                 depth: #depth,
+//             }
+//         })
+//     }
+// }
+
+// #[derive(Default)]
+// pub struct TemplateBuilder {
+//     nodes: Vec<TemplateNodeBuilder>,
+//     root_nodes: Vec<TemplateNodeId>,
+//     dynamic_context: DynamicTemplateContextBuilder,
+// }
+
+// impl TemplateBuilder {
+//     /// Create a template builder from nodes if it would improve performance to do so.
+//     pub fn from_roots(roots: Vec<BodyNode>) -> Option<Self> {
+//         let mut builder = Self::default();
+
+//         for (i, root) in roots.into_iter().enumerate() {
+//             let id = builder.build_node(root, None, vec![i], 0);
+//             builder.root_nodes.push(id);
+//         }
+
+//         // only build a template if there is at least one static node
+//         if builder
+//             .nodes
+//             .iter()
+//             .all(|r| matches!(&r.node_type, TemplateNodeTypeBuilder::DynamicNode(_)))
+//         {
+//             None
+//         } else {
+//             Some(builder)
+//         }
+//     }
+
+//     /// Create a template builder from nodes regardless of performance.
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     fn from_roots_always(roots: Vec<BodyNode>) -> Self {
+//         let mut builder = Self::default();
+
+//         for (i, root) in roots.into_iter().enumerate() {
+//             let id = builder.build_node(root, None, vec![i], 0);
+//             builder.root_nodes.push(id);
+//         }
+
+//         builder
+//     }
+
+//     fn build_node(
+//         &mut self,
+//         node: BodyNode,
+//         parent: Option<TemplateNodeId>,
+//         path: Vec<usize>,
+//         depth: usize,
+//     ) -> TemplateNodeId {
+//         let id = TemplateNodeId(self.nodes.len());
+//         match node {
+//             BodyNode::Element(el) => {
+//                 let mut locally_static = true;
+//                 let mut attributes = Vec::new();
+//                 let mut listeners = Vec::new();
+//                 for attr in el.attributes {
+//                     match attr.attr {
+//                         ElementAttr::AttrText { name, value } => {
+//                             if let Some(static_value) = value.to_static() {
+//                                 attributes.push(TemplateAttributeBuilder {
+//                                     element_tag: el.name.clone(),
+//                                     name: AttributeName::Ident(name),
+//                                     value: TemplateAttributeValue::Static(
+//                                         OwnedAttributeValue::Text(static_value),
+//                                     ),
+//                                 })
+//                             } else {
+//                                 locally_static = false;
+//                                 attributes.push(TemplateAttributeBuilder {
+//                                     element_tag: el.name.clone(),
+//                                     name: AttributeName::Ident(name),
+//                                     value: TemplateAttributeValue::Dynamic(
+//                                         self.dynamic_context.add_attr(quote!(#value)),
+//                                     ),
+//                                 })
+//                             }
+//                         }
+//                         ElementAttr::CustomAttrText { name, value } => {
+//                             if let Some(static_value) = value.to_static() {
+//                                 attributes.push(TemplateAttributeBuilder {
+//                                     element_tag: el.name.clone(),
+//                                     name: AttributeName::Str(name),
+//                                     value: TemplateAttributeValue::Static(
+//                                         OwnedAttributeValue::Text(static_value),
+//                                     ),
+//                                 })
+//                             } else {
+//                                 locally_static = false;
+//                                 attributes.push(TemplateAttributeBuilder {
+//                                     element_tag: el.name.clone(),
+//                                     name: AttributeName::Str(name),
+//                                     value: TemplateAttributeValue::Dynamic(
+//                                         self.dynamic_context.add_attr(quote!(#value)),
+//                                     ),
+//                                 })
+//                             }
+//                         }
+//                         ElementAttr::AttrExpression { name, value } => {
+//                             locally_static = false;
+//                             attributes.push(TemplateAttributeBuilder {
+//                                 element_tag: el.name.clone(),
+//                                 name: AttributeName::Ident(name),
+//                                 value: TemplateAttributeValue::Dynamic(
+//                                     self.dynamic_context.add_attr(quote!(#value)),
+//                                 ),
+//                             })
+//                         }
+//                         ElementAttr::CustomAttrExpression { name, value } => {
+//                             locally_static = false;
+//                             attributes.push(TemplateAttributeBuilder {
+//                                 element_tag: el.name.clone(),
+//                                 name: AttributeName::Str(name),
+//                                 value: TemplateAttributeValue::Dynamic(
+//                                     self.dynamic_context.add_attr(quote!(#value)),
+//                                 ),
+//                             })
+//                         }
+//                         ElementAttr::EventTokens { name, tokens } => {
+//                             locally_static = false;
+//                             listeners.push(self.dynamic_context.add_listener(name, tokens))
+//                         }
+//                     }
+//                 }
+//                 if let Some(key) = el.key {
+//                     self.dynamic_context.add_key(quote!(
+//                         dioxus::core::exports::bumpalo::format!(in __bump, "{}", #key)
+//                             .into_bump_str()
+//                     ));
+//                 }
+//                 self.nodes.push(TemplateNodeBuilder {
+//                     id,
+//                     node_type: TemplateNodeTypeBuilder::Element(TemplateElementBuilder {
+//                         tag: el.name,
+//                         attributes,
+//                         children: Vec::new(),
+//                         listeners,
+//                         locally_static,
+//                     }),
+//                     parent,
+//                     depth,
+//                     fully_static: false,
+//                 });
+//                 let children: Vec<_> = el
+//                     .children
+//                     .into_iter()
+//                     .enumerate()
+//                     .map(|(i, child)| {
+//                         let mut new_path = path.clone();
+//                         new_path.push(i);
+//                         self.build_node(child, Some(id), new_path, depth + 1)
+//                     })
+//                     .collect();
+//                 let children_fully_static = children.iter().all(|c| self.nodes[c.0].fully_static);
+//                 let parent = &mut self.nodes[id.0];
+//                 parent.fully_static = locally_static && children_fully_static;
+//                 if let TemplateNodeTypeBuilder::Element(element) = &mut parent.node_type {
+//                     element.children = children;
+//                 }
+//             }
+
+//             BodyNode::Component(comp) => {
+//                 self.nodes.push(TemplateNodeBuilder {
+//                     id,
+//                     node_type: TemplateNodeTypeBuilder::DynamicNode(
+//                         self.dynamic_context.add_node(BodyNode::Component(comp)),
+//                     ),
+//                     parent,
+//                     depth,
+//                     fully_static: false,
+//                 });
+//             }
+
+//             BodyNode::Text(txt) => {
+//                 let mut segments = Vec::new();
+//                 let mut length = 0;
+//                 let mut fully_static = true;
+
+//                 for segment in txt.segments {
+//                     segments.push(match segment {
+//                         Segment::Literal(lit) => {
+//                             length += lit.len();
+//                             TextTemplateSegment::Static(lit)
+//                         }
+//                         Segment::Formatted(fmted) => {
+//                             fully_static = false;
+//                             TextTemplateSegment::Dynamic(self.dynamic_context.add_text(fmted))
+//                         }
+//                     })
+//                 }
+
+//                 self.nodes.push(TemplateNodeBuilder {
+//                     id,
+//                     node_type: TemplateNodeTypeBuilder::Text(TextTemplate::new(segments, length)),
+//                     parent,
+//                     depth,
+//                     fully_static,
+//                 });
+//             }
+
+//             BodyNode::RawExpr(expr) => {
+//                 self.nodes.push(TemplateNodeBuilder {
+//                     id,
+//                     node_type: TemplateNodeTypeBuilder::DynamicNode(
+//                         self.dynamic_context.add_node(BodyNode::RawExpr(expr)),
+//                     ),
+//                     parent,
+//                     depth,
+//                     fully_static: false,
+//                 });
+//             }
+//         }
+//         id
+//     }
+
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     pub fn try_switch_dynamic_context(
+//         mut self,
+//         dynamic_context: DynamicTemplateContextBuilder,
+//     ) -> Option<Self> {
+//         let attribute_mapping: HashMap<String, usize> = dynamic_context
+//             .attributes
+//             .iter()
+//             .enumerate()
+//             .map(|(i, ts)| (ts.to_string(), i))
+//             .collect();
+//         let text_mapping: HashMap<String, usize> = dynamic_context
+//             .text
+//             .iter()
+//             .enumerate()
+//             .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
+//             .collect();
+//         let listener_mapping: HashMap<(String, Expr), usize> = dynamic_context
+//             .listeners
+//             .iter()
+//             .enumerate()
+//             .map(|(i, ts)| (ts.clone(), i))
+//             .collect();
+//         let node_mapping: HashMap<String, usize> = dynamic_context
+//             .nodes
+//             .iter()
+//             .enumerate()
+//             .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
+//             .collect();
+
+//         for node in &mut self.nodes {
+//             match &mut node.node_type {
+//                 TemplateNodeTypeBuilder::Element(element) => {
+//                     for listener in &mut element.listeners {
+//                         *listener =
+//                             *listener_mapping.get(&self.dynamic_context.listeners[*listener])?;
+//                     }
+//                     for attribute in &mut element.attributes {
+//                         if let TemplateAttributeValue::Dynamic(idx) = &mut attribute.value {
+//                             *idx = *attribute_mapping
+//                                 .get(&self.dynamic_context.attributes[*idx].to_string())?;
+//                         }
+//                     }
+//                 }
+//                 TemplateNodeTypeBuilder::Text(txt) => {
+//                     for seg in &mut txt.segments {
+//                         if let TextTemplateSegment::Dynamic(idx) = seg {
+//                             *idx = *text_mapping.get(
+//                                 &self.dynamic_context.text[*idx]
+//                                     .to_token_stream()
+//                                     .to_string(),
+//                             )?;
+//                         }
+//                     }
+//                 }
+//                 TemplateNodeTypeBuilder::DynamicNode(idx) => {
+//                     *idx = *node_mapping.get(
+//                         &self.dynamic_context.nodes[*idx]
+//                             .to_token_stream()
+//                             .to_string(),
+//                     )?;
+//                 }
+//             }
+//         }
+//         self.dynamic_context = dynamic_context;
+
+//         Some(self)
+//     }
+
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     pub fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplate, Error> {
+//         let mut nodes = Vec::new();
+//         let dynamic_mapping = self.dynamic_mapping(&nodes);
+//         let dynamic_path = self.dynamic_path();
+//         for node in self.nodes {
+//             nodes.push(node.try_into_owned(location)?);
+//         }
+
+//         Ok(OwnedTemplate {
+//             nodes,
+//             root_nodes: self.root_nodes,
+//             dynamic_mapping,
+//             dynamic_path,
+//         })
+//     }
+
+//     #[cfg(any(feature = "hot-reload", debug_assertions))]
+//     pub fn dynamic_mapping(
+//         &self,
+//         resolved_nodes: &Vec<OwnedTemplateNode>,
+//     ) -> OwnedDynamicNodeMapping {
+//         let dynamic_context = &self.dynamic_context;
+//         let mut node_mapping = vec![None; dynamic_context.nodes.len()];
+//         let nodes = &self.nodes;
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
+//                 node_mapping[*idx] = Some(n.id)
+//             }
+//         }
+//         let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
+//                 for seg in &txt.segments {
+//                     match seg {
+//                         TextTemplateSegment::Static(_) => (),
+//                         TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
+//                     }
+//                 }
+//             }
+//         }
+//         let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
+//                 for (i, attr) in el.attributes.iter().enumerate() {
+//                     match attr.value {
+//                         TemplateAttributeValue::Static(_) => (),
+//                         TemplateAttributeValue::Dynamic(idx) => {
+//                             attribute_mapping[idx].push((n.id, i));
+//                         }
+//                     }
+//                 }
+//             }
+//         }
+//         let mut listener_mapping = Vec::new();
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
+//                 if !el.listeners.is_empty() {
+//                     listener_mapping.push(n.id);
+//                 }
+//             }
+//         }
+
+//         let mut volatile_mapping = Vec::new();
+//         for n in resolved_nodes {
+//             if let TemplateNodeType::Element(el) = &n.node_type {
+//                 for (i, attr) in el.attributes.iter().enumerate() {
+//                     if attr.attribute.volatile {
+//                         volatile_mapping.push((n.id, i));
+//                     }
+//                 }
+//             }
+//         }
+
+//         OwnedDynamicNodeMapping::new(
+//             node_mapping,
+//             text_mapping,
+//             attribute_mapping,
+//             volatile_mapping,
+//             listener_mapping,
+//         )
+//     }
+
+//     fn dynamic_path(&self) -> Option<OwnedPathSeg> {
+//         let mut last_seg: Option<OwnedPathSeg> = None;
+//         let mut nodes_to_insert_after = Vec::new();
+//         // iterating from the last root to the first
+//         for root in self.root_nodes.iter().rev() {
+//             let root_node = &self.nodes[root.0];
+//             if let TemplateNodeTypeBuilder::DynamicNode(_) = root_node.node_type {
+//                 match &mut last_seg {
+//                     // if there has been no static nodes, we can append the child to the parent node
+//                     None => nodes_to_insert_after.push(*root),
+//                     // otherwise we insert the child before the last static node
+//                     Some(seg) => {
+//                         seg.ops.push(UpdateOp::InsertBefore(*root));
+//                     }
+//                 }
+//             } else if let Some(mut new) = self.construct_path_segment(*root) {
+//                 if let Some(last) = last_seg.take() {
+//                     match new.traverse {
+//                         OwnedTraverse::Halt => {
+//                             new.traverse = OwnedTraverse::NextSibling(Box::new(last));
+//                         }
+//                         OwnedTraverse::FirstChild(b) => {
+//                             new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
+//                         }
+//                         _ => unreachable!(),
+//                     }
+//                 } else {
+//                     for node in nodes_to_insert_after.drain(..) {
+//                         new.ops.push(UpdateOp::InsertAfter(node));
+//                     }
+//                 }
+//                 last_seg = Some(new);
+//             } else if let Some(last) = last_seg.take() {
+//                 last_seg = Some(OwnedPathSeg {
+//                     ops: Vec::new(),
+//                     traverse: OwnedTraverse::NextSibling(Box::new(last)),
+//                 });
+//             }
+//         }
+//         last_seg
+//     }
+
+//     fn construct_path_segment(&self, node_id: TemplateNodeId) -> Option<OwnedPathSeg> {
+//         let n = &self.nodes[node_id.0];
+//         if n.fully_static {
+//             return None;
+//         }
+//         match &n.node_type {
+//             TemplateNodeTypeBuilder::Element(el) => {
+//                 let mut last_seg: Option<OwnedPathSeg> = None;
+//                 let mut children_to_append = Vec::new();
+//                 // iterating from the last child to the first
+//                 for child in el.children.iter().rev() {
+//                     let child_node = &self.nodes[child.0];
+//                     if let TemplateNodeTypeBuilder::DynamicNode(_) = child_node.node_type {
+//                         match &mut last_seg {
+//                             // if there has been no static nodes, we can append the child to the parent node
+//                             None => children_to_append.push(*child),
+//                             // otherwise we insert the child before the last static node
+//                             Some(seg) => {
+//                                 seg.ops.push(UpdateOp::InsertBefore(*child));
+//                             }
+//                         }
+//                     } else if let Some(mut new) = self.construct_path_segment(*child) {
+//                         if let Some(last) = last_seg.take() {
+//                             match new.traverse {
+//                                 OwnedTraverse::Halt => {
+//                                     new.traverse = OwnedTraverse::NextSibling(Box::new(last));
+//                                 }
+//                                 OwnedTraverse::FirstChild(b) => {
+//                                     new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
+//                                 }
+//                                 _ => unreachable!(),
+//                             }
+//                         }
+//                         last_seg = Some(new);
+//                     } else if let Some(last) = last_seg.take() {
+//                         last_seg = Some(OwnedPathSeg {
+//                             ops: Vec::new(),
+//                             traverse: OwnedTraverse::NextSibling(Box::new(last)),
+//                         });
+//                     }
+//                 }
+//                 let mut ops = Vec::new();
+//                 if !el.locally_static || n.parent.is_none() {
+//                     ops.push(UpdateOp::StoreNode(node_id));
+//                 }
+//                 for child in children_to_append.into_iter().rev() {
+//                     ops.push(UpdateOp::AppendChild(child));
+//                 }
+//                 Some(OwnedPathSeg {
+//                     ops,
+//                     traverse: match last_seg {
+//                         Some(last) => OwnedTraverse::FirstChild(Box::new(last)),
+//                         None => OwnedTraverse::Halt,
+//                     },
+//                 })
+//             }
+//             TemplateNodeTypeBuilder::Text(_) => Some(OwnedPathSeg {
+//                 ops: vec![UpdateOp::StoreNode(n.id)],
+//                 traverse: dioxus_core::OwnedTraverse::Halt,
+//             }),
+//             TemplateNodeTypeBuilder::DynamicNode(_) => unreachable!(
+//                 "constructing path segment for dynamic nodes is handled in the parent node"
+//             ),
+//         }
+//     }
+// }
+
+// impl ToTokens for TemplateBuilder {
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         let Self {
+//             nodes,
+//             root_nodes,
+//             dynamic_context,
+//         } = self;
+
+//         let mut node_mapping = vec![None; dynamic_context.nodes.len()];
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
+//                 node_mapping[*idx] = Some(n.id);
+//             }
+//         }
+//         let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
+//                 for seg in &txt.segments {
+//                     match seg {
+//                         TextTemplateSegment::Static(_) => (),
+//                         TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
+//                     }
+//                 }
+//             }
+//         }
+//         let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
+//                 for (i, attr) in el.attributes.iter().enumerate() {
+//                     match attr.value {
+//                         TemplateAttributeValue::Static(_) => (),
+//                         TemplateAttributeValue::Dynamic(idx) => {
+//                             attribute_mapping[idx].push((n.id, i));
+//                         }
+//                     }
+//                 }
+//             }
+//         }
+//         let mut listener_mapping = Vec::new();
+//         for n in nodes {
+//             if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
+//                 if !el.listeners.is_empty() {
+//                     listener_mapping.push(n.id);
+//                 }
+//             }
+//         }
+
+//         let root_nodes = root_nodes.iter().map(|id| {
+//             let raw = id.0;
+//             quote! { TemplateNodeId(#raw) }
+//         });
+//         let node_mapping_quoted = node_mapping.iter().map(|op| match op {
+//             Some(id) => {
+//                 let raw_id = id.0;
+//                 quote! {Some(TemplateNodeId(#raw_id))}
+//             }
+//             None => quote! {None},
+//         });
+//         let text_mapping_quoted = text_mapping.iter().map(|inner| {
+//             let raw = inner.iter().map(|id| id.0);
+//             quote! {&[#(TemplateNodeId(#raw)),*]}
+//         });
+//         let attribute_mapping_quoted = attribute_mapping.iter().map(|inner| {
+//             let raw = inner.iter().map(|(id, _)| id.0);
+//             let indecies = inner.iter().map(|(_, idx)| idx);
+//             quote! {&[#((TemplateNodeId(#raw), #indecies)),*]}
+//         });
+//         let listener_mapping_quoted = listener_mapping.iter().map(|id| {
+//             let raw = id.0;
+//             quote! {TemplateNodeId(#raw)}
+//         });
+//         let mut nodes_quoted = TokenStream::new();
+//         for n in nodes {
+//             n.to_tokens(&mut nodes_quoted);
+//             quote! {,}.to_tokens(&mut nodes_quoted);
+//         }
+
+//         let dynamic_path = match self.dynamic_path() {
+//             Some(seg) => {
+//                 let seg = quote_owned_segment(seg);
+//                 quote! {Some(#seg)}
+//             }
+//             None => quote! {None},
+//         };
+
+//         let quoted = quote! {
+//             {
+//                 const __NODES: dioxus::prelude::StaticTemplateNodes = &[#nodes_quoted];
+//                 const __TEXT_MAPPING: &'static [&'static [dioxus::prelude::TemplateNodeId]] = &[#(#text_mapping_quoted),*];
+//                 const __ATTRIBUTE_MAPPING: &'static [&'static [(dioxus::prelude::TemplateNodeId, usize)]] = &[#(#attribute_mapping_quoted),*];
+//                 const __ROOT_NODES: &'static [dioxus::prelude::TemplateNodeId] = &[#(#root_nodes),*];
+//                 const __NODE_MAPPING: &'static [Option<dioxus::prelude::TemplateNodeId>] = &[#(#node_mapping_quoted),*];
+//                 const __NODES_WITH_LISTENERS: &'static [dioxus::prelude::TemplateNodeId] = &[#(#listener_mapping_quoted),*];
+//                 static __VOLITALE_MAPPING_INNER: dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = dioxus::core::exports::once_cell::sync::Lazy::new(||{
+//                     // check each property to see if it is volatile
+//                     let mut volatile = Vec::new();
+//                     for n in __NODES {
+//                         if let TemplateNodeType::Element(el) = &n.node_type {
+//                             for (i, attr) in el.attributes.iter().enumerate() {
+//                                 if attr.attribute.volatile {
+//                                     volatile.push((n.id, i));
+//                                 }
+//                             }
+//                         }
+//                     }
+//                     volatile
+//                 });
+//                 static __VOLITALE_MAPPING: &'static dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = &__VOLITALE_MAPPING_INNER;
+//                 static __STATIC_VOLITALE_MAPPING: dioxus::prelude::LazyStaticVec<(dioxus::prelude::TemplateNodeId, usize)> = LazyStaticVec(__VOLITALE_MAPPING);
+//                 static __TEMPLATE: dioxus::prelude::Template = Template::Static(&StaticTemplate {
+//                     nodes: __NODES,
+//                     root_nodes: __ROOT_NODES,
+//                     dynamic_mapping: StaticDynamicNodeMapping::new(__NODE_MAPPING, __TEXT_MAPPING, __ATTRIBUTE_MAPPING, __STATIC_VOLITALE_MAPPING, __NODES_WITH_LISTENERS),
+//                     dynamic_path: #dynamic_path,
+//                 });
+
+//                 let __bump = __cx.bump();
+//                 __cx.template_ref(dioxus::prelude::TemplateId(get_line_num!()), __TEMPLATE.clone(), #dynamic_context)
+//             }
+//         };
+
+//         tokens.append_all(quoted)
+//     }
+// }
+
+// #[derive(Default, Clone, Debug)]
+// pub struct DynamicTemplateContextBuilder {
+//     nodes: Vec<BodyNode>,
+//     text: Vec<FormattedSegment>,
+//     attributes: Vec<TokenStream>,
+//     listeners: Vec<(String, Expr)>,
+//     key: Option<TokenStream>,
+// }
+
+// impl DynamicTemplateContextBuilder {
+//     fn add_node(&mut self, node: BodyNode) -> usize {
+//         let node_id = self.nodes.len();
+
+//         self.nodes.push(node);
+
+//         node_id
+//     }
+
+//     fn add_text(&mut self, text: FormattedSegment) -> usize {
+//         let text_id = self.text.len();
+
+//         self.text.push(text);
+
+//         text_id
+//     }
+
+//     fn add_attr(&mut self, attr: TokenStream) -> usize {
+//         let attr_id = self.attributes.len();
+
+//         self.attributes.push(attr);
+
+//         attr_id
+//     }
+
+//     fn add_listener(&mut self, name: Ident, listener: Expr) -> usize {
+//         let listener_id = self.listeners.len();
+
+//         self.listeners.push((name.to_string(), listener));
+
+//         listener_id
+//     }
+
+//     fn add_key(&mut self, key: TokenStream) {
+//         self.key = Some(key);
+//     }
+// }
+
+// impl ToTokens for DynamicTemplateContextBuilder {
+//     fn to_tokens(&self, tokens: &mut TokenStream) {
+//         let nodes = &self.nodes;
+//         let text = &self.text;
+//         let attributes = &self.attributes;
+//         let listeners_names = self
+//             .listeners
+//             .iter()
+//             .map(|(n, _)| syn::parse_str::<Ident>(n).expect(n));
+//         let listeners_exprs = self.listeners.iter().map(|(_, e)| e);
+//         let key = match &self.key {
+//             Some(k) => quote!(Some(#k)),
+//             None => quote!(None),
+//         };
+//         tokens.append_all(quote! {
+//             TemplateContext {
+//                 nodes: __cx.bump().alloc([#(#nodes),*]),
+//                 text_segments: __cx.bump().alloc([#(&*dioxus::core::exports::bumpalo::format!(in __bump, "{}", #text).into_bump_str()),*]),
+//                 attributes: __cx.bump().alloc([#({#attributes}.into_value(__cx.bump())),*]),
+//                 listeners: __cx.bump().alloc([#(dioxus_elements::on::#listeners_names(__cx, #listeners_exprs)),*]),
+//                 key: #key,
+//             }
+//         })
+//     }
+// }
+
+// fn quote_owned_segment(seg: OwnedPathSeg) -> proc_macro2::TokenStream {
+//     let OwnedPathSeg { ops, traverse } = seg;
+
+//     let ops = ops
+//         .into_iter()
+//         .map(|op| match op {
+//             UpdateOp::StoreNode(id) => {
+//                 let id = quote_template_node_id(id);
+//                 quote!(UpdateOp::StoreNode(#id))
+//             }
+//             UpdateOp::InsertBefore(id) => {
+//                 let id = quote_template_node_id(id);
+//                 quote!(UpdateOp::InsertBefore(#id))
+//             }
+//             UpdateOp::InsertAfter(id) => {
+//                 let id = quote_template_node_id(id);
+//                 quote!(UpdateOp::InsertAfter(#id))
+//             }
+//             UpdateOp::AppendChild(id) => {
+//                 let id = quote_template_node_id(id);
+//                 quote!(UpdateOp::AppendChild(#id))
+//             }
+//         })
+//         .collect::<Vec<_>>();
+
+//     let traverse = quote_owned_traverse(traverse);
+
+//     quote! {
+//         StaticPathSeg {
+//             ops: &[#(#ops),*],
+//             traverse: #traverse,
+//         }
+//     }
+// }
+
+// fn quote_owned_traverse(traverse: OwnedTraverse) -> proc_macro2::TokenStream {
+//     match traverse {
+//         OwnedTraverse::Halt => {
+//             quote! {StaticTraverse::Halt}
+//         }
+//         OwnedTraverse::FirstChild(seg) => {
+//             let seg = quote_owned_segment(*seg);
+//             quote! {StaticTraverse::FirstChild(&#seg)}
+//         }
+//         OwnedTraverse::NextSibling(seg) => {
+//             let seg = quote_owned_segment(*seg);
+//             quote! {StaticTraverse::NextSibling(&#seg)}
+//         }
+//         OwnedTraverse::Both(b) => {
+//             let (child, sibling) = *b;
+//             let child = quote_owned_segment(child);
+//             let sibling = quote_owned_segment(sibling);
+//             quote! {StaticTraverse::Both(&(#child, #sibling))}
+//         }
+//     }
+// }
+
+// fn quote_template_node_id(id: TemplateNodeId) -> proc_macro2::TokenStream {
+//     let raw = id.0;
+//     quote! {
+//         TemplateNodeId(#raw)
+//     }
+// }

+ 183 - 232
packages/ssr/src/lib.rs

@@ -34,7 +34,6 @@ impl SsrRenderer {
                 cfg: self.cfg.clone(),
                 root: &root,
                 vdom: Some(&self.vdom),
-                bump: bumpalo::Bump::new(),
             }
         )
     }
@@ -65,7 +64,6 @@ pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
         cfg: SsrConfig::default(),
         root: &root,
         vdom,
-        bump: bumpalo::Bump::new(),
     };
     let r = ssr_renderer.to_string();
     drop(ssr_renderer);
@@ -97,7 +95,6 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
             cfg: SsrConfig::default(),
             root: vdom.get_scope(scope).unwrap().root_node(),
             vdom: Some(vdom),
-            bump: bumpalo::Bump::new()
         }
     ))
 }
@@ -124,7 +121,6 @@ pub struct TextRenderer<'a, 'b, 'c> {
     vdom: Option<&'c VirtualDom>,
     root: &'b VNode<'a>,
     cfg: SsrConfig,
-    bump: bumpalo::Bump,
 }
 
 impl<'a: 'c, 'c> Display for TextRenderer<'a, '_, 'c> {
@@ -140,7 +136,6 @@ impl<'a> TextRenderer<'a, '_, 'a> {
             cfg,
             root: vdom.base_scope().root_node(),
             vdom: Some(vdom),
-            bump: bumpalo::Bump::new(),
         }
     }
 }
@@ -169,16 +164,6 @@ impl<'a: 'c, 'c> TextRenderer<'a, '_, 'c> {
 
                 write!(f, "{}", text.text)?
             }
-            VNode::Placeholder(_anchor) => {
-                *last_node_was_text = false;
-
-                if self.cfg.indent {
-                    for _ in 0..il {
-                        write!(f, "    ")?;
-                    }
-                }
-                write!(f, "<!--placeholder-->")?;
-            }
             VNode::Element(el) => {
                 *last_node_was_text = false;
 
@@ -220,11 +205,22 @@ impl<'a: 'c, 'c> TextRenderer<'a, '_, 'c> {
                     writeln!(f)?;
                 }
             }
-            VNode::Fragment(frag) => {
-                for child in frag.children {
-                    self.html_render(child, f, il + 1, last_node_was_text)?;
+            VNode::Fragment(frag) => match frag.children.len() {
+                0 => {
+                    *last_node_was_text = false;
+                    if self.cfg.indent {
+                        for _ in 0..il {
+                            write!(f, "    ")?;
+                        }
+                    }
+                    write!(f, "<!--placeholder-->")?;
                 }
-            }
+                _ => {
+                    for child in frag.children {
+                        self.html_render(child, f, il + 1, last_node_was_text)?;
+                    }
+                }
+            },
             VNode::Component(vcomp) => {
                 let idx = vcomp.scope.get().unwrap();
 
@@ -234,39 +230,9 @@ impl<'a: 'c, 'c> TextRenderer<'a, '_, 'c> {
                 } else {
                 }
             }
-            VNode::TemplateRef(tmpl) => {
+            VNode::Template(t) => {
                 if let Some(vdom) = self.vdom {
-                    let template_id = &tmpl.template_id;
-                    let dynamic_context = &tmpl.dynamic_context;
-                    vdom.with_template(template_id, move |tmpl| {
-                        match tmpl {
-                            Template::Static(s) => {
-                                for r in s.root_nodes {
-                                    self.render_template_node(
-                                        &s.nodes,
-                                        &s.nodes[r.0],
-                                        dynamic_context,
-                                        f,
-                                        last_node_was_text,
-                                        il,
-                                    )?;
-                                }
-                            }
-                            Template::Owned(o) => {
-                                for r in &o.root_nodes {
-                                    self.render_template_node(
-                                        &o.nodes,
-                                        &o.nodes[r.0],
-                                        dynamic_context,
-                                        f,
-                                        last_node_was_text,
-                                        il,
-                                    )?;
-                                }
-                            }
-                        };
-                        Ok(())
-                    })?
+                    todo!()
                 } else {
                     panic!("Cannot render template without vdom");
                 }
@@ -275,197 +241,182 @@ impl<'a: 'c, 'c> TextRenderer<'a, '_, 'c> {
         Ok(())
     }
 
-    fn render_template_node<TemplateNodes, Attributes, V, Children, Listeners, TextSegments, Text>(
-        &self,
-        template_nodes: &TemplateNodes,
-        node: &TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>,
-        dynamic_context: &TemplateContext,
-        f: &mut impl Write,
-        last_node_was_text: &mut bool,
-        il: u16,
-    ) -> std::fmt::Result
-    where
-        TemplateNodes:
-            AsRef<[TemplateNode<Attributes, V, Children, Listeners, TextSegments, Text>]>,
-        Attributes: AsRef<[TemplateAttribute<V>]>,
-        Children: AsRef<[TemplateNodeId]>,
-        Listeners: AsRef<[usize]>,
-        Text: AsRef<str>,
-        TextSegments: AsRef<[TextTemplateSegment<Text>]>,
-        V: TemplateValue,
-    {
-        match &node.node_type {
-            TemplateNodeType::Element(el) => {
-                *last_node_was_text = false;
-
-                if self.cfg.indent {
-                    for _ in 0..il {
-                        write!(f, "    ")?;
-                    }
-                }
-
-                write!(f, "<{}", el.tag)?;
-
-                let mut inner_html = None;
-
-                let mut attr_iter = el.attributes.as_ref().iter().peekable();
-
-                while let Some(attr) = attr_iter.next() {
-                    match attr.attribute.namespace {
-                        None => {
-                            if attr.attribute.name == "dangerous_inner_html" {
-                                inner_html = {
-                                    let text = match &attr.value {
-                                        TemplateAttributeValue::Static(val) => {
-                                            val.allocate(&self.bump).as_text().unwrap()
-                                        }
-                                        TemplateAttributeValue::Dynamic(idx) => dynamic_context
-                                            .resolve_attribute(*idx)
-                                            .as_text()
-                                            .unwrap(),
-                                    };
-                                    Some(text)
-                                }
-                            } else if is_boolean_attribute(attr.attribute.name) {
-                                match &attr.value {
-                                    TemplateAttributeValue::Static(val) => {
-                                        let val = val.allocate(&self.bump);
-                                        if val.is_truthy() {
-                                            write!(f, " {}=\"{}\"", attr.attribute.name, val)?
-                                        }
-                                    }
-                                    TemplateAttributeValue::Dynamic(idx) => {
-                                        let val = dynamic_context.resolve_attribute(*idx);
-                                        if val.is_truthy() {
-                                            write!(f, " {}=\"{}\"", attr.attribute.name, val)?
-                                        }
-                                    }
-                                }
-                            } else {
-                                match &attr.value {
-                                    TemplateAttributeValue::Static(val) => {
-                                        let val = val.allocate(&self.bump);
-                                        write!(f, " {}=\"{}\"", attr.attribute.name, val)?
-                                    }
-                                    TemplateAttributeValue::Dynamic(idx) => {
-                                        let val = dynamic_context.resolve_attribute(*idx);
-                                        write!(f, " {}=\"{}\"", attr.attribute.name, val)?
-                                    }
-                                }
-                            }
-                        }
-
-                        Some(ns) => {
-                            // write the opening tag
-                            write!(f, " {}=\"", ns)?;
-                            let mut cur_ns_el = attr;
-                            loop {
-                                match &attr.value {
-                                    TemplateAttributeValue::Static(val) => {
-                                        let val = val.allocate(&self.bump);
-                                        write!(f, "{}:{};", cur_ns_el.attribute.name, val)?;
-                                    }
-                                    TemplateAttributeValue::Dynamic(idx) => {
-                                        let val = dynamic_context.resolve_attribute(*idx);
-                                        write!(f, "{}:{};", cur_ns_el.attribute.name, val)?;
-                                    }
-                                }
-                                match attr_iter.peek() {
-                                    Some(next_attr)
-                                        if next_attr.attribute.namespace == Some(ns) =>
-                                    {
-                                        cur_ns_el = attr_iter.next().unwrap();
-                                    }
-                                    _ => break,
-                                }
-                            }
-                            // write the closing tag
-                            write!(f, "\"")?;
-                        }
-                    }
-                }
-
-                match self.cfg.newline {
-                    true => writeln!(f, ">")?,
-                    false => write!(f, ">")?,
-                }
-
-                if let Some(inner_html) = inner_html {
-                    write!(f, "{}", inner_html)?;
-                } else {
-                    let mut last_node_was_text = false;
-                    for child in el.children.as_ref() {
-                        self.render_template_node(
-                            template_nodes,
-                            &template_nodes.as_ref()[child.0],
-                            dynamic_context,
-                            f,
-                            &mut last_node_was_text,
-                            il + 1,
-                        )?;
-                    }
-                }
-
-                if self.cfg.newline {
-                    writeln!(f)?;
-                }
-                if self.cfg.indent {
-                    for _ in 0..il {
-                        write!(f, "    ")?;
-                    }
-                }
-
-                write!(f, "</{}>", el.tag)?;
-                if self.cfg.newline {
-                    writeln!(f)?;
-                }
-            }
-            TemplateNodeType::Text(txt) => {
-                if *last_node_was_text {
-                    write!(f, "<!--spacer-->")?;
-                }
-
-                if self.cfg.indent {
-                    for _ in 0..il {
-                        write!(f, "    ")?;
-                    }
-                }
-
-                *last_node_was_text = true;
-
-                let text = dynamic_context.resolve_text(txt);
-
-                write!(f, "{}", text)?
-            }
-            TemplateNodeType::DynamicNode(idx) => {
-                let node = dynamic_context.resolve_node(*idx);
-                self.html_render(node, f, il, last_node_was_text)?;
-            }
-        }
-        Ok(())
-    }
+    // fn render_template_node(
+    //     &self,
+    //     node: &VTemplate,
+    //     f: &mut impl Write,
+    //     last_node_was_text: &mut bool,
+    //     il: u16,
+    // ) -> std::fmt::Result {
+    //     match &node.node_type {
+    //         TemplateNodeType::Element(el) => {
+    //             *last_node_was_text = false;
+
+    //             if self.cfg.indent {
+    //                 for _ in 0..il {
+    //                     write!(f, "    ")?;
+    //                 }
+    //             }
+
+    //             write!(f, "<{}", el.tag)?;
+
+    //             let mut inner_html = None;
+
+    //             let mut attr_iter = el.attributes.as_ref().iter().peekable();
+
+    //             while let Some(attr) = attr_iter.next() {
+    //                 match attr.attribute.namespace {
+    //                     None => {
+    //                         if attr.attribute.name == "dangerous_inner_html" {
+    //                             inner_html = {
+    //                                 let text = match &attr.value {
+    //                                     TemplateAttributeValue::Static(val) => {
+    //                                         val.allocate(&self.bump).as_text().unwrap()
+    //                                     }
+    //                                     TemplateAttributeValue::Dynamic(idx) => dynamic_context
+    //                                         .resolve_attribute(*idx)
+    //                                         .as_text()
+    //                                         .unwrap(),
+    //                                 };
+    //                                 Some(text)
+    //                             }
+    //                         } else if is_boolean_attribute(attr.attribute.name) {
+    //                             match &attr.value {
+    //                                 TemplateAttributeValue::Static(val) => {
+    //                                     let val = val.allocate(&self.bump);
+    //                                     if val.is_truthy() {
+    //                                         write!(f, " {}=\"{}\"", attr.attribute.name, val)?
+    //                                     }
+    //                                 }
+    //                                 TemplateAttributeValue::Dynamic(idx) => {
+    //                                     let val = dynamic_context.resolve_attribute(*idx);
+    //                                     if val.is_truthy() {
+    //                                         write!(f, " {}=\"{}\"", attr.attribute.name, val)?
+    //                                     }
+    //                                 }
+    //                             }
+    //                         } else {
+    //                             match &attr.value {
+    //                                 TemplateAttributeValue::Static(val) => {
+    //                                     let val = val.allocate(&self.bump);
+    //                                     write!(f, " {}=\"{}\"", attr.attribute.name, val)?
+    //                                 }
+    //                                 TemplateAttributeValue::Dynamic(idx) => {
+    //                                     let val = dynamic_context.resolve_attribute(*idx);
+    //                                     write!(f, " {}=\"{}\"", attr.attribute.name, val)?
+    //                                 }
+    //                             }
+    //                         }
+    //                     }
+
+    //                     Some(ns) => {
+    //                         // write the opening tag
+    //                         write!(f, " {}=\"", ns)?;
+    //                         let mut cur_ns_el = attr;
+    //                         loop {
+    //                             match &attr.value {
+    //                                 TemplateAttributeValue::Static(val) => {
+    //                                     let val = val.allocate(&self.bump);
+    //                                     write!(f, "{}:{};", cur_ns_el.attribute.name, val)?;
+    //                                 }
+    //                                 TemplateAttributeValue::Dynamic(idx) => {
+    //                                     let val = dynamic_context.resolve_attribute(*idx);
+    //                                     write!(f, "{}:{};", cur_ns_el.attribute.name, val)?;
+    //                                 }
+    //                             }
+    //                             match attr_iter.peek() {
+    //                                 Some(next_attr)
+    //                                     if next_attr.attribute.namespace == Some(ns) =>
+    //                                 {
+    //                                     cur_ns_el = attr_iter.next().unwrap();
+    //                                 }
+    //                                 _ => break,
+    //                             }
+    //                         }
+    //                         // write the closing tag
+    //                         write!(f, "\"")?;
+    //                     }
+    //                 }
+    //             }
+
+    //             match self.cfg.newline {
+    //                 true => writeln!(f, ">")?,
+    //                 false => write!(f, ">")?,
+    //             }
+
+    //             if let Some(inner_html) = inner_html {
+    //                 write!(f, "{}", inner_html)?;
+    //             } else {
+    //                 let mut last_node_was_text = false;
+    //                 for child in el.children.as_ref() {
+    //                     self.render_template_node(
+    //                         template_nodes,
+    //                         &template_nodes.as_ref()[child.0],
+    //                         dynamic_context,
+    //                         f,
+    //                         &mut last_node_was_text,
+    //                         il + 1,
+    //                     )?;
+    //                 }
+    //             }
+
+    //             if self.cfg.newline {
+    //                 writeln!(f)?;
+    //             }
+    //             if self.cfg.indent {
+    //                 for _ in 0..il {
+    //                     write!(f, "    ")?;
+    //                 }
+    //             }
+
+    //             write!(f, "</{}>", el.tag)?;
+    //             if self.cfg.newline {
+    //                 writeln!(f)?;
+    //             }
+    //         }
+    //         TemplateNodeType::Text(txt) => {
+    //             if *last_node_was_text {
+    //                 write!(f, "<!--spacer-->")?;
+    //             }
+
+    //             if self.cfg.indent {
+    //                 for _ in 0..il {
+    //                     write!(f, "    ")?;
+    //                 }
+    //             }
+
+    //             *last_node_was_text = true;
+
+    //             let text = dynamic_context.resolve_text(txt);
+
+    //             write!(f, "{}", text)?
+    //         }
+    //         TemplateNodeType::DynamicNode(idx) => {
+    //             let node = dynamic_context.resolve_node(*idx);
+    //             self.html_render(node, f, il, last_node_was_text)?;
+    //         }
+    //     }
+    //     Ok(())
+    // }
 }
 
-fn render_attributes<'a, 'b: 'a, I>(
-    attrs: I,
+fn render_attributes<'a, 'b: 'a>(
+    attrs: impl Iterator<Item = &'a Attribute<'b>>,
     f: &mut impl Write,
-) -> Result<Option<&'b str>, std::fmt::Error>
-where
-    I: Iterator<Item = &'a Attribute<'b>>,
-{
+) -> Result<Option<&'b str>, std::fmt::Error> {
     let mut inner_html = None;
     let mut attr_iter = attrs.peekable();
 
     while let Some(attr) = attr_iter.next() {
-        match attr.attribute.namespace {
+        match attr.namespace {
             None => {
-                if attr.attribute.name == "dangerous_inner_html" {
+                if attr.name == "dangerous_inner_html" {
                     inner_html = Some(attr.value.as_text().unwrap())
                 } else {
-                    if is_boolean_attribute(attr.attribute.name) && !attr.value.is_truthy() {
+                    if is_boolean_attribute(attr.name) && !attr.value.is_truthy() {
                         continue;
                     }
-                    write!(f, " {}=\"{}\"", attr.attribute.name, attr.value)?
+                    write!(f, " {}=\"{}\"", attr.name, attr.value)?
                 }
             }
             Some(ns) => {
@@ -473,9 +424,9 @@ where
                 write!(f, " {}=\"", ns)?;
                 let mut cur_ns_el = attr;
                 loop {
-                    write!(f, "{}:{};", cur_ns_el.attribute.name, cur_ns_el.value)?;
+                    write!(f, "{}:{};", cur_ns_el.name, cur_ns_el.value)?;
                     match attr_iter.peek() {
-                        Some(next_attr) if next_attr.attribute.namespace == Some(ns) => {
+                        Some(next_attr) if next_attr.namespace == Some(ns) => {
                             cur_ns_el = attr_iter.next().unwrap();
                         }
                         _ => break,

+ 0 - 1
packages/web/Cargo.toml

@@ -80,7 +80,6 @@ features = [
 [features]
 default = ["panic_hook"]
 panic_hook = ["console_error_panic_hook"]
-hot-reload = ["dioxus/hot-reload"]
 
 
 [dev-dependencies]