Explorar el Código

wip: use the new structure

Jonathan Kelley hace 4 años
padre
commit
a05047d01e

+ 5 - 18
packages/core/src/bumpframe.rs

@@ -1,20 +1,7 @@
-use crate::hooklist::HookList;
-use crate::{arena::SharedArena, innerlude::*};
-use appendlist::AppendList;
+use crate::innerlude::*;
 use bumpalo::Bump;
-use slotmap::DefaultKey;
-use slotmap::SlotMap;
-use std::marker::PhantomData;
-use std::{
-    any::{Any, TypeId},
-    cell::{Cell, RefCell},
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
-    future::Future,
-    ops::Deref,
-    pin::Pin,
-    rc::{Rc, Weak},
-};
+
+use std::cell::RefCell;
 pub struct ActiveFrame {
     // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
     pub generation: RefCell<usize>,
@@ -33,11 +20,11 @@ impl ActiveFrame {
         Self::from_frames(
             BumpFrame {
                 bump: Bump::new(),
-                head_node: VNode::static_text(""),
+                head_node: NodeFactory::static_text(""),
             },
             BumpFrame {
                 bump: Bump::new(),
-                head_node: VNode::static_text(""),
+                head_node: NodeFactory::static_text(""),
             },
         )
     }

+ 2 - 11
packages/core/src/context.rs

@@ -270,17 +270,8 @@ Any function prefixed with "use" should not be called conditionally.
     ) -> VNode<'src> {
         use futures_util::FutureExt;
         match fut.now_or_never() {
-            Some(out) => {
-                let suspended_cx = SuspendedContext {};
-                let nodes = callback(suspended_cx, out);
-                return nodes;
-            }
-            None => {
-                // we need to register this task
-                VNode::Suspended {
-                    real: Cell::new(RealDomNode::empty()),
-                }
-            }
+            Some(out) => callback(SuspendedContext {}, out),
+            None => NodeFactory::suspended(),
         }
     }
 

+ 64 - 62
packages/core/src/diff.rs

@@ -158,50 +158,46 @@ where
     //
     // each function call assumes the stack is fresh (empty).
     pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
-        match (old_node, new_node) {
+        match (&old_node.kind, &new_node.kind) {
             // Handle the "sane" cases first.
             // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
             // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
-            (VNode::Text(old), VNode::Text(new)) => {
-                if old.is_static {
-                    log::debug!("encountered static text node: {:?}", old.text);
-                }
-
+            (VNodeKind::Text(old), VNodeKind::Text(new)) => {
+                let root = old_node.dom_id.get();
                 if old.text != new.text {
-                    self.dom.push(old.dom_id.get());
+                    self.dom.push(root);
                     log::debug!("Text has changed {}, {}", old.text, new.text);
                     self.dom.set_text(new.text);
                     self.dom.pop();
                 }
-
-                new.dom_id.set(old.dom_id.get());
+                new_node.dom_id.set(root);
             }
 
-            (VNode::Element(old), VNode::Element(new)) => {
+            (VNodeKind::Element(old), VNodeKind::Element(new)) => {
                 // If the element type is completely different, the element needs to be re-rendered completely
                 // This is an optimization React makes due to how users structure their code
                 //
                 // In Dioxus, this is less likely to occur unless through a fragment
+                let root = old_node.dom_id.get();
                 if new.tag_name != old.tag_name || new.namespace != old.namespace {
-                    self.dom.push(old.dom_id.get());
+                    self.dom.push(root);
                     let meta = self.create(new_node);
                     self.dom.replace_with(meta.added_to_stack);
                     self.dom.pop();
                     return;
                 }
 
-                let oldid = old.dom_id.get();
-                new.dom_id.set(oldid);
+                new_node.dom_id.set(root);
 
                 // push it just in case
-                self.dom.push(oldid);
+                self.dom.push(root);
                 self.diff_listeners(old.listeners, new.listeners);
                 self.diff_attr(old.attributes, new.attributes, new.namespace);
                 self.diff_children(old.children, new.children);
                 self.dom.pop();
             }
 
-            (VNode::Component(old), VNode::Component(new)) => {
+            (VNodeKind::Component(old), VNodeKind::Component(new)) => {
                 log::warn!("diffing components? {:#?}", new.user_fc);
                 if old.user_fc == new.user_fc {
                     // Make sure we're dealing with the same component (by function pointer)
@@ -257,7 +253,7 @@ where
                 }
             }
 
-            (VNode::Fragment(old), VNode::Fragment(new)) => {
+            (VNodeKind::Fragment(old), VNodeKind::Fragment(new)) => {
                 // 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 {
@@ -278,15 +274,19 @@ where
             // We also walk the "real node" list to make sure all latent roots are claened up
             // This covers the case any time a fragment or component shows up with pretty much anything else
             (
-                VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_),
-                VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_),
+                VNodeKind::Component(_)
+                | VNodeKind::Fragment(_)
+                | VNodeKind::Text(_)
+                | VNodeKind::Element(_),
+                VNodeKind::Component(_)
+                | VNodeKind::Fragment(_)
+                | VNodeKind::Text(_)
+                | VNodeKind::Element(_),
             ) => {
                 // Choose the node to use as the placeholder for replacewith
-                let back_node = match old_node {
+                let back_node = match old_node.kind {
                     // We special case these two types to avoid allocating the small-vecs
-                    VNode::Element(_) | VNode::Text(_) => old_node
-                        .get_mounted_id(&self.components)
-                        .expect("Element and text always have a real node"),
+                    VNodeKind::Element(_) | VNodeKind::Text(_) => old_node.dom_id.get(),
 
                     _ => {
                         let mut old_iter = RealChildIterator::new(old_node, &self.components);
@@ -314,8 +314,8 @@ where
             }
 
             // TODO
-            (VNode::Suspended { .. }, _) => todo!(),
-            (_, VNode::Suspended { .. }) => todo!(),
+            (VNodeKind::Suspended { .. }, _) => todo!(),
+            (_, VNodeKind::Suspended { .. }) => todo!(),
         }
     }
 }
@@ -351,13 +351,14 @@ where
     //     [... node]
     fn create(&mut self, node: &'bump VNode<'bump>) -> CreateMeta {
         log::warn!("Creating node! ... {:#?}", node);
-        match node {
-            VNode::Text(text) => {
+        match &node.kind {
+            VNodeKind::Text(text) => {
                 let real_id = self.dom.create_text_node(text.text);
-                text.dom_id.set(real_id);
-                CreateMeta::new(text.is_static, 1)
+                todo!()
+                // text.dom_id.set(real_id);
+                // CreateMeta::new(text.is_static, 1)
             }
-            VNode::Element(el) => {
+            VNodeKind::Element(el) => {
                 // we have the potential to completely eliminate working on this node in the future(!)
                 //
                 // This can only be done if all of the elements properties (attrs, children, listeners, etc) are static
@@ -366,14 +367,14 @@ where
                 let mut is_static: bool = true;
 
                 let VElement {
-                    key,
                     tag_name,
                     listeners,
                     attributes,
                     children,
                     namespace,
-                    dom_id,
-                    is_static: el_is_static,
+                    static_attrs,
+                    static_children,
+                    static_listeners,
                 } = el;
 
                 let real_id = if let Some(namespace) = namespace {
@@ -381,7 +382,7 @@ where
                 } else {
                     self.dom.create_element(tag_name, None)
                 };
-                dom_id.set(real_id);
+                // dom_id.set(real_id);
 
                 listeners.iter().enumerate().for_each(|(idx, listener)| {
                     listener.mounted_node.set(real_id);
@@ -408,7 +409,7 @@ where
                 //
                 // TODO move over
                 // if children.len() == 1 {
-                //     if let VNode::Text(text) = &children[0] {
+                //     if let VNodeKind::Text(text) = &children[0] {
                 //         self.dom.set_text(text.text);
                 //         return;
                 //     }
@@ -428,11 +429,11 @@ where
                     log::debug!("created a dynamic node {:#?}", node);
                 }
 
-                el_is_static.set(is_static);
+                // el_is_static.set(is_static);
                 CreateMeta::new(is_static, 1)
             }
 
-            VNode::Component(vcomponent) => {
+            VNodeKind::Component(vcomponent) => {
                 log::debug!("Mounting a new component");
                 let caller = vcomponent.caller.clone();
 
@@ -493,7 +494,7 @@ where
             // Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators).
             // We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them.
             // Fragments will just put all their nodes onto the stack after creation
-            VNode::Fragment(frag) => {
+            VNodeKind::Fragment(frag) => {
                 let mut nodes_added = 0;
                 for child in frag.children.iter().rev() {
                     // different types of nodes will generate different amounts on the stack
@@ -508,9 +509,10 @@ where
                 CreateMeta::new(false, nodes_added)
             }
 
-            VNode::Suspended { real } => {
-                let id = self.dom.create_placeholder();
-                real.set(id);
+            VNodeKind::Suspended => {
+                todo!();
+                // let id = self.dom.create_placeholder();
+                // real.set(id);
                 CreateMeta::new(false, 1)
             }
         }
@@ -620,7 +622,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         // The "fast" path is when the list of attributes name is identical and in the same order
         // With the Rsx and Html macros, this will almost always be the case
         'outer: for new_attr in new {
-            if new_attr.is_volatile() {
+            if new_attr.is_volatile {
                 // self.dom.commit_traversal();
                 self.dom
                     .set_attribute(new_attr.name, new_attr.value, namespace);
@@ -675,13 +677,13 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
 
         if new.len() == 1 {
             match (&old.first(), &new[0]) {
-                (Some(VNode::Text(old_vtext)), VNode::Text(new_vtext))
-                    if old_vtext.text == new_vtext.text =>
-                {
-                    // Don't take this fast path...
-                }
+                // (Some(VNodeKind::Text(old_vtext)), VNodeKind::Text(new_vtext))
+                //     if old_vtext.text == new_vtext.text =>
+                // {
+                //     // Don't take this fast path...
+                // }
 
-                // (_, VNode::Text(text)) => {
+                // (_, VNodeKind::Text(text)) => {
                 //     // self.dom.commit_traversal();
                 //     log::debug!("using optimized text set");
                 //     self.dom.set_text(text.text);
@@ -701,15 +703,15 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             return;
         }
 
-        let new_is_keyed = new[0].key().is_some();
-        let old_is_keyed = old[0].key().is_some();
+        let new_is_keyed = new[0].key.is_some();
+        let old_is_keyed = old[0].key.is_some();
 
         debug_assert!(
-            new.iter().all(|n| n.key().is_some() == new_is_keyed),
+            new.iter().all(|n| n.key.is_some() == new_is_keyed),
             "all siblings must be keyed or all siblings must be non-keyed"
         );
         debug_assert!(
-            old.iter().all(|o| o.key().is_some() == old_is_keyed),
+            old.iter().all(|o| o.key.is_some() == old_is_keyed),
             "all siblings must be keyed or all siblings must be non-keyed"
         );
 
@@ -754,7 +756,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
                 keys.clear();
                 for child in children {
-                    let key = child.key();
+                    let key = child.key;
                     debug_assert!(
                         key.is_some(),
                         "if any sibling is keyed, all siblings must be keyed"
@@ -797,7 +799,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             .iter()
             .rev()
             .zip(new[shared_prefix_count..].iter().rev())
-            .take_while(|&(old, new)| old.key() == new.key())
+            .take_while(|&(old, new)| old.key == new.key)
             .count();
 
         let old_shared_suffix_start = old.len() - shared_suffix_count;
@@ -1259,7 +1261,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
         // debug_assert!(self.dom.traversal_is_committed());
         for child in old {
-            if let VNode::Component(vcomp) = child {
+            if let VNodeKind::Component(vcomp) = child.kind {
                 // dom
                 //     .create_text_node("placeholder for vcomponent");
 
@@ -1328,23 +1330,23 @@ impl<'a> Iterator for RealChildIterator<'a> {
 
         while returned_node.is_none() {
             if let Some((count, node)) = self.stack.last_mut() {
-                match node {
+                match &node.kind {
                     // We can only exit our looping when we get "real" nodes
                     // This includes fragments and components when they're empty (have a single root)
-                    VNode::Element(_) | VNode::Text(_) => {
+                    VNodeKind::Element(_) | VNodeKind::Text(_) => {
                         // We've recursed INTO an element/text
                         // We need to recurse *out* of it and move forward to the next
                         should_pop = true;
-                        returned_node = node.get_mounted_id(&self.scopes);
+                        returned_node = Some(node.dom_id.get());
                     }
 
                     // If we get a fragment we push the next child
-                    VNode::Fragment(frag) => {
+                    VNodeKind::Fragment(frag) => {
                         let subcount = *count as usize;
 
                         if frag.children.len() == 0 {
                             should_pop = true;
-                            returned_node = node.get_mounted_id(&self.scopes);
+                            returned_node = Some(node.dom_id.get());
                         }
 
                         if subcount >= frag.children.len() {
@@ -1355,11 +1357,11 @@ impl<'a> Iterator for RealChildIterator<'a> {
                     }
 
                     // Immediately abort suspended nodes - can't do anything with them yet
-                    // VNode::Suspended => should_pop = true,
-                    VNode::Suspended { real } => todo!(),
+                    // VNodeKind::Suspended => should_pop = true,
+                    VNodeKind::Suspended => todo!(),
 
                     // For components, we load their root and push them onto the stack
-                    VNode::Component(sc) => {
+                    VNodeKind::Component(sc) => {
                         let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap();
 
                         // Simply swap the current node on the stack with the root of the component

+ 0 - 1
packages/core/src/events.rs

@@ -125,7 +125,6 @@ pub mod on {
     use std::{fmt::Debug, ops::Deref, rc::Rc};
 
     use crate::{
-        innerlude::ElementBuilder,
         innerlude::NodeFactory,
         innerlude::{Attribute, Listener, RealDomNode, VNode},
     };

+ 1 - 6
packages/core/src/lib.rs

@@ -19,11 +19,9 @@ pub mod diff;
 pub mod error;
 pub mod events;
 pub mod hooklist;
-pub mod nodebuilder;
 pub mod nodes;
 pub mod scope;
 pub mod signals;
-pub mod styles;
 pub mod tasks;
 pub mod util;
 pub mod virtual_dom;
@@ -36,7 +34,6 @@ pub(crate) mod innerlude {
     pub use crate::diff::*;
     pub use crate::error::*;
     pub use crate::events::*;
-    pub use crate::nodebuilder::*;
     pub use crate::nodes::*;
     pub use crate::scope::*;
     pub use crate::serialize::*;
@@ -46,8 +43,6 @@ pub(crate) mod innerlude {
 
     pub type FC<P> = fn(Context<P>) -> VNode;
 
-    // Re-export the FC macro
-    pub use crate::nodebuilder as builder;
     pub use dioxus_core_macro::{html, rsx};
 }
 
@@ -61,8 +56,8 @@ pub use crate::{
 pub mod prelude {
     pub use crate::component::{fc_to_builder, Fragment, Properties};
     pub use crate::context::Context;
+    pub use crate::innerlude::DioxusElement;
     pub use crate::innerlude::{LazyNodes, NodeFactory, FC};
-    pub use crate::nodebuilder::DioxusElement;
     pub use crate::nodes::VNode;
     pub use crate::VirtualDom;
     pub use dioxus_core_macro::{format_args_f, html, rsx, Props};

+ 0 - 748
packages/core/src/nodebuilder.rs

@@ -1,749 +1 @@
-//! Helpers for building virtual DOM VNodes.
 
-use std::{
-    any::Any,
-    borrow::BorrowMut,
-    cell::{Cell, RefCell},
-    fmt::Arguments,
-    intrinsics::transmute,
-    marker::PhantomData,
-    u128,
-};
-
-use bumpalo::Bump;
-
-use crate::{
-    events::VirtualEvent,
-    innerlude::{Properties, RealDomNode, Scope, VComponent, VFragment, VText, FC},
-    nodes::{Attribute, Listener, NodeKey, VNode},
-};
-
-/// A virtual DOM element builder.
-///
-/// Typically constructed with element-specific constructors, eg the `div`
-/// function for building `<div>` elements or the `button` function for building
-/// `<button>` elements.
-#[derive(Debug)]
-pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
-where
-    Listeners: 'a + AsRef<[Listener<'a>]>,
-    Attributes: 'a + AsRef<[Attribute<'a>]>,
-    Children: 'a + AsRef<[VNode<'a>]>,
-{
-    cx: &'b NodeFactory<'a>,
-    key: NodeKey<'a>,
-    tag_name: &'static str,
-    listeners: Listeners,
-    attributes: Attributes,
-    children: Children,
-    namespace: Option<&'static str>,
-}
-
-impl<'a, 'b>
-    ElementBuilder<
-        'a,
-        'b,
-        bumpalo::collections::Vec<'a, Listener<'a>>,
-        bumpalo::collections::Vec<'a, Attribute<'a>>,
-        bumpalo::collections::Vec<'a, VNode<'a>>,
-    >
-{
-    /// Create a new `ElementBuilder` for an element with the given tag name.
-    ///
-    /// In general, only use this constructor if the tag is dynamic (i.e. you
-    /// might build a `<div>` or you might build a `<span>` and you don't know
-    /// until runtime). Prefer using the tag-specific constructors instead:
-    /// `div(bump)` or `span(bump)`, etc.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// let tag_name = if flip_coin() {
-    ///     "div"
-    /// } else {
-    ///     "span"
-    /// };
-    ///
-    /// let my_element_builder = ElementBuilder::new(&b, tag_name);
-    /// # fn flip_coin() -> bool { true }
-    /// ```
-    pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
-        let bump = cx.bump();
-        ElementBuilder {
-            cx,
-            key: NodeKey::NONE,
-            tag_name,
-            listeners: bumpalo::collections::Vec::new_in(bump),
-            attributes: bumpalo::collections::Vec::new_in(bump),
-            children: bumpalo::collections::Vec::new_in(bump),
-            namespace: None,
-        }
-    }
-}
-
-impl<'a, 'b, Listeners, Attributes, Children>
-    ElementBuilder<'a, 'b, Listeners, Attributes, Children>
-where
-    Listeners: 'a + AsRef<[Listener<'a>]>,
-    Attributes: 'a + AsRef<[Attribute<'a>]>,
-    Children: 'a + AsRef<[VNode<'a>]>,
-{
-    /// Set the listeners for this element.
-    ///
-    /// You can use this method to customize the backing storage for listeners,
-    /// for example to use a fixed-size array instead of the default
-    /// dynamically-sized `bumpalo::collections::Vec`.
-    ///
-    /// Any listeners already added to the builder will be overridden.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create a `<div>` with a fixed-size array of two listeners.
-    /// let my_div = div(&b)
-    ///     .listeners([
-    ///         on(&b, "click", |root, vdom, event| {
-    ///             // ...
-    ///         }),
-    ///         on(&b, "dblclick", |root, vdom, event| {
-    ///             // ...
-    ///         }),
-    ///     ])
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
-    where
-        L: 'a + AsRef<[Listener<'a>]>,
-    {
-        ElementBuilder {
-            cx: self.cx,
-            key: self.key,
-            tag_name: self.tag_name,
-            listeners,
-            attributes: self.attributes,
-            children: self.children,
-            namespace: self.namespace,
-        }
-    }
-
-    /// Set the attributes for this element.
-    ///
-    /// You can use this method to customize the backing storage for attributes,
-    /// for example to use a fixed-size array instead of the default
-    /// dynamically-sized `bumpalo::collections::Vec`.
-    ///
-    /// Any attributes already added to the builder will be overridden.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump, Attribute};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create a `<div>` with a fixed-size array of two attributes.
-    /// let my_div = div(&b)
-    ///     .attributes([
-    ///         attr("id", "my-div"),
-    ///         attr("class", "notification"),
-    ///     ])
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
-    where
-        A: 'a + AsRef<[Attribute<'a>]>,
-    {
-        ElementBuilder {
-            cx: self.cx,
-            key: self.key,
-            tag_name: self.tag_name,
-            listeners: self.listeners,
-            attributes,
-            children: self.children,
-            namespace: self.namespace,
-        }
-    }
-
-    /// Set the children for this element.
-    ///
-    /// You can use this method to customize the backing storage for children,
-    /// for example to use a fixed-size array instead of the default
-    /// dynamically-sized `bumpalo::collections::Vec`.
-    ///
-    /// Any children already added to the builder will be overridden.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create a `<div>` with a fixed-size array of two `<span>` children.
-    /// let my_div = div(&b)
-    ///     .children([
-    ///         span(&b).finish(),
-    ///         span(&b).finish(),
-    ///     ])
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
-    where
-        C: 'a + AsRef<[VNode<'a>]>,
-    {
-        ElementBuilder {
-            cx: self.cx,
-            key: self.key,
-            tag_name: self.tag_name,
-            listeners: self.listeners,
-            attributes: self.attributes,
-            children,
-            namespace: self.namespace,
-        }
-    }
-
-    /// Set the namespace for this element.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create a `<td>` tag with an xhtml namespace
-    /// let my_td = td(&b)
-    ///     .namespace(Some("http://www.w3.org/1999/xhtml"))
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn namespace(self, namespace: Option<&'static str>) -> Self {
-        ElementBuilder {
-            cx: self.cx,
-            key: self.key,
-            tag_name: self.tag_name,
-            listeners: self.listeners,
-            attributes: self.attributes,
-            children: self.children,
-            namespace,
-        }
-    }
-
-    /// Set this element's key.
-    ///
-    /// When diffing sets of siblings, if an old sibling and new sibling share a
-    /// key, then they will always reuse the same physical DOM VNode. This is
-    /// important when using CSS animations, web components, third party JS, or
-    /// anything else that makes the diffing implementation observable.
-    ///
-    /// Do not use keys if such a scenario does not apply. Keyed diffing is
-    /// generally more expensive than not, since it is putting greater
-    /// constraints on the diffing algorithm.
-    ///
-    /// # Invariants You Must Uphold
-    ///
-    /// The key may not be `u32::MAX`, which is a reserved key value.
-    ///
-    /// Keys must be unique among siblings.
-    ///
-    /// All sibling VNodes must be keyed, or they must all not be keyed. You may
-    /// not mix keyed and unkeyed siblings.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// let my_li = li(&b)
-    ///     .key(1337)
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn key(mut self, key: &'a str) -> Self {
-        self.key = NodeKey(Some(key));
-        self
-    }
-
-    pub fn key2(mut self, args: Arguments) -> Self {
-        let key = match args.as_str() {
-            Some(static_str) => static_str,
-            None => {
-                use bumpalo::core_alloc::fmt::Write;
-                let mut s = bumpalo::collections::String::new_in(self.cx.bump());
-                s.write_fmt(args).unwrap();
-                s.into_bump_str()
-            }
-        };
-        self.key = NodeKey(Some(key));
-        self
-    }
-
-    /// Create the virtual DOM VNode described by this builder.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump, VNode};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Start with a builder...
-    /// let builder: ElementBuilder<_, _, _> = div(&b);
-    ///
-    /// // ...and finish it to create a virtual DOM VNode!
-    /// let my_div: VNode = builder.finish();
-    /// ```
-    #[inline]
-    pub fn finish(mut self) -> VNode<'a> {
-        let bump = self.cx.bump();
-
-        let children: &'a Children = bump.alloc(self.children);
-        let children: &'a [VNode<'a>] = children.as_ref();
-
-        let listeners: &'a Listeners = bump.alloc(self.listeners);
-        let listeners: &'a [Listener<'a>] = listeners.as_ref();
-
-        for listener in listeners {
-            // bump the context id forward
-            let id = self.cx.listener_id.get();
-            self.cx.listener_id.set(id + 1);
-
-            // Add this listener to the context list
-            // This casts the listener to a self-referential pointer
-            // This is okay because the bump arena is stable
-
-            // TODO: maybe not add it into CTX immediately
-            let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(listener) };
-            self.cx.scope_ref.listeners.borrow_mut().push((
-                r.mounted_node as *const _ as *mut _,
-                r.callback as *const _ as *mut _,
-            ));
-        }
-
-        let attributes: &'a Attributes = bump.alloc(self.attributes);
-        let attributes: &'a [Attribute<'a>] = attributes.as_ref();
-
-        VNode::element(
-            bump,
-            self.key,
-            self.tag_name,
-            listeners,
-            attributes,
-            children,
-            self.namespace,
-        )
-    }
-}
-
-impl<'a, 'b, Attributes, Children>
-    ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
-where
-    Attributes: 'a + AsRef<[Attribute<'a>]>,
-    Children: 'a + AsRef<[VNode<'a>]>,
-{
-    // / Add a new event listener to this element.
-    // /
-    // / The `event` string specifies which event will be listened for. The
-    // / `callback` function is the function that will be invoked if the
-    // / specified event occurs.
-    // /
-    // / # Example
-    // /
-    // / ```no_run
-    // / use dioxus::{builder::*, bumpalo::Bump};
-    // /
-    // / let b = Bump::new();
-    // /
-    // / // A button that does something when clicked!
-    // / let my_button = button(&b)
-    // /     .on("click", |event| {
-    // /         // ...
-    // /     })
-    // /     .finish();
-    // / ```
-    // pub fn on(self, event: &'static str, callback: impl FnMut(VirtualEvent) + 'a) -> Self {
-    //     let bump = &self.cx.bump();
-    //     let listener = Listener {
-    //         event,
-    //         callback: bump.alloc(callback),
-    //         scope: self.cx.scope_ref.arena_idx,
-    //         mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
-    //     };
-    //     self.add_listener(listener)
-    // }
-
-    // pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
-    //     self.listeners.push(listener);
-
-    //     // bump the context id forward
-    //     let id = self.cx.listener_id.get();
-    //     self.cx.listener_id.set(id + 1);
-
-    //     // Add this listener to the context list
-    //     // This casts the listener to a self-referential pointer
-    //     // This is okay because the bump arena is stable
-    //     self.listeners.last().map(|g| {
-    //         let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
-    //         self.cx.scope_ref.listeners.borrow_mut().push((
-    //             r.mounted_node as *const _ as *mut _,
-    //             r.callback as *const _ as *mut _,
-    //         ));
-    //     });
-
-    //     self
-    // }
-}
-
-impl<'a, 'b, Listeners, Children>
-    ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
-where
-    Listeners: 'a + AsRef<[Listener<'a>]>,
-    Children: 'a + AsRef<[VNode<'a>]>,
-{
-    /// Add a new attribute to this element.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create the `<div id="my-div"/>` element.
-    /// let my_div = div(&b).attr("id", "my-div").finish();
-    /// ```
-    pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
-        let (value, is_static) = raw_text(self.cx.bump(), args);
-
-        self.attributes.push(Attribute {
-            name,
-            value,
-            is_static,
-            namespace: None,
-        });
-        self
-    }
-}
-
-impl<'a, 'b, Listeners, Attributes>
-    ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
-where
-    Listeners: 'a + AsRef<[Listener<'a>]>,
-    Attributes: 'a + AsRef<[Attribute<'a>]>,
-{
-    /// Add a new child to this element.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    /// use js_sys::Math;
-    ///
-    /// let b = Bump::new();
-    ///
-    /// // Create `<p><span></span></p>`.
-    /// let my_div = p(&b)
-    ///     .child(span(&b).finish())
-    ///     .finish();
-    /// ```
-    #[inline]
-    pub fn child(mut self, child: VNode<'a>) -> Self {
-        self.children.push(child);
-        self
-    }
-
-    /// Add multiple children to this element from an iterator.
-    ///
-    /// # Example
-    ///
-    /// ```no_run
-    /// use dioxus::{builder::*, bumpalo::Bump};
-    ///
-    /// let b = Bump::new();
-    ///
-    /// let my_div = p(&b)
-    ///     .iter_child((0..10).map(|f| span(&b).finish())
-    ///     .finish();
-    /// ```    
-    pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
-        todo!();
-        let len_before = self.children.len();
-        for item in nodes {
-            todo!()
-            // let child = item.into_vnode(&self.cx);
-            // self.children.push(child);
-        }
-        if cfg!(debug_assertions) {
-            if self.children.len() > len_before + 1 {
-                if self.children.last().unwrap().key().is_none() {
-                    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. 
----
-To help you identify where this error is coming from, we've generated a backtrace.
-                        "#,
-                    );
-                }
-            }
-        }
-        self
-    }
-}
-
-impl<'a> IntoIterator for VNode<'a> {
-    type Item = VNode<'a>;
-    type IntoIter = std::iter::Once<Self::Item>;
-    fn into_iter(self) -> Self::IntoIter {
-        std::iter::once(self)
-    }
-}
-impl<'a> IntoVNode<'a> for VNode<'a> {
-    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        self
-    }
-}
-
-impl<'a> IntoVNode<'a> for &VNode<'a> {
-    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        self.clone()
-    }
-}
-
-pub trait IntoVNode<'a> {
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
-}
-
-// Wrap the the node-builder closure in a concrete type.
-// ---
-// This is a bit of a hack to implement the IntoVNode trait for closure types.
-pub struct LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    inner: G,
-    _p: PhantomData<&'a ()>,
-}
-
-impl<'a, G> LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    pub fn new(f: G) -> Self {
-        Self {
-            inner: f,
-            _p: PhantomData {},
-        }
-    }
-}
-
-// Cover the cases where nodes are used by macro.
-// Likely used directly.
-// ---
-//  let nodes = rsx!{ ... };
-//  rsx! { {nodes } }
-impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        (self.inner)(cx)
-    }
-}
-
-// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
-impl<'a, G> IntoIterator for LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    type Item = Self;
-    type IntoIter = std::iter::Once<Self::Item>;
-    fn into_iter(self) -> Self::IntoIter {
-        std::iter::once(self)
-    }
-}
-
-impl IntoVNode<'_> for () {
-    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        cx.fragment_from_iter(None as Option<VNode>)
-    }
-}
-
-impl IntoVNode<'_> for Option<()> {
-    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        cx.fragment_from_iter(None as Option<VNode>)
-    }
-}
-
-pub fn raw_text<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> (&'a str, bool) {
-    match args.as_str() {
-        Some(static_str) => (static_str, true),
-        None => {
-            use bumpalo::core_alloc::fmt::Write;
-            let mut s = bumpalo::collections::String::new_in(bump);
-            s.write_fmt(args).unwrap();
-            (s.into_bump_str(), false)
-        }
-    }
-}
-
-pub fn virtual_child<'a, T: Properties + 'a>(
-    cx: NodeFactory<'a>,
-    f: FC<T>,
-    props: T,
-    key: Option<&'a str>, // key: NodeKey<'a>,
-    children: &'a [VNode<'a>],
-) -> VNode<'a> {
-    // currently concerned about if props have a custom drop implementation
-    // might override it with the props macro
-    // todo!()
-    VNode::Component(
-        cx.bump()
-            .alloc(crate::nodes::VComponent::new(&cx, f, props, key, children)),
-    )
-}
-
-pub fn vfragment<'a>(
-    cx: NodeFactory<'a>,
-    key: Option<&'a str>, // key: NodeKey<'a>,
-    children: &'a [VNode<'a>],
-) -> VNode<'a> {
-    VNode::Fragment(cx.bump().alloc(VFragment::new(key, children)))
-}
-
-/// 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 scope_ref: &'a Scope,
-    pub listener_id: &'a Cell<usize>,
-}
-
-impl<'a> NodeFactory<'a> {
-    #[inline]
-    pub fn bump(&self) -> &'a bumpalo::Bump {
-        &self.scope_ref.cur_frame().bump
-    }
-
-    /// Create some text that's allocated along with the other vnodes
-    pub fn text(&self, args: Arguments) -> VNode<'a> {
-        VNode::text(self.bump(), args)
-    }
-
-    /// Create an element builder
-    pub fn raw_element<'b>(
-        &'b self,
-        tag: &'static str,
-    ) -> ElementBuilder<
-        'a,
-        'b,
-        bumpalo::collections::Vec<'a, Listener<'a>>,
-        bumpalo::collections::Vec<'a, Attribute<'a>>,
-        bumpalo::collections::Vec<'a, VNode<'a>>,
-    > {
-        ElementBuilder::new(self, tag)
-    }
-
-    /// Create an element builder
-    pub fn element<'b>(
-        &'b self,
-        tag: impl DioxusElement,
-    ) -> ElementBuilder<
-        'a,
-        'b,
-        bumpalo::collections::Vec<'a, Listener<'a>>,
-        bumpalo::collections::Vec<'a, Attribute<'a>>,
-        bumpalo::collections::Vec<'a, VNode<'a>>,
-    > {
-        ElementBuilder::new(self, tag.tag_name())
-    }
-
-    pub fn attr(
-        &self,
-        name: &'static str,
-        val: Arguments,
-        namespace: Option<&'static str>,
-    ) -> Attribute<'a> {
-        let (value, is_static) = raw_text(self.bump(), val);
-        Attribute {
-            name,
-            value,
-            is_static,
-            namespace,
-        }
-    }
-
-    pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
-        VNode::Fragment(self.bump().alloc(VFragment {
-            children,
-            key: NodeKey::new_opt(key),
-            void_root: Cell::new(None),
-        }))
-    }
-
-    pub fn virtual_child<T, C>(
-        &self,
-        f: FC<T>,
-        props: T,
-        key: Option<&'a str>, // key: NodeKey<'a>,
-        children: C,
-    ) -> VNode<'a>
-    where
-        C: 'a + AsRef<[VNode<'a>]>,
-        T: Properties + 'a,
-    {
-        let children: &'a C = self.bump().alloc(children);
-        VNode::Component(self.bump().alloc(crate::nodes::VComponent::new(
-            self,
-            f,
-            props,
-            key,
-            children.as_ref(),
-        )))
-    }
-
-    pub fn fragment_from_iter(
-        self,
-        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
-    ) -> VNode<'a> {
-        let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
-        for node in node_iter.into_iter() {
-            nodes.push(node.into_vnode(self));
-        }
-        VNode::Fragment(
-            self.bump()
-                .alloc(VFragment::new(None, nodes.into_bump_slice())),
-        )
-    }
-}
-
-use std::fmt::Debug;
-impl Debug for NodeFactory<'_> {
-    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        Ok(())
-    }
-}
-
-pub trait DioxusElement {
-    const TAG_NAME: &'static str;
-    const NAME_SPACE: Option<&'static str>;
-    fn tag_name(&self) -> &'static str {
-        Self::TAG_NAME
-    }
-}

+ 305 - 379
packages/core/src/nodes.rs

@@ -4,225 +4,66 @@
 //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
 
 use crate::{
-    arena::SharedArena,
     events::VirtualEvent,
     innerlude::{Context, Properties, RealDom, RealDomNode, Scope, ScopeIdx, FC},
-    nodebuilder::NodeFactory,
 };
-use appendlist::AppendList;
-use bumpalo::Bump;
 use std::{
     cell::{Cell, RefCell},
     fmt::{Arguments, Debug, Formatter},
+    marker::PhantomData,
     rc::Rc,
 };
 
+pub struct VNode<'src> {
+    pub kind: VNodeKind<'src>,
+    pub dom_id: Cell<RealDomNode>,
+    pub key: Option<&'src str>,
+}
+
 /// Tools for the base unit of the virtual dom - the VNode
 /// VNodes are intended to be quickly-allocated, lightweight enum values.
 ///
 /// Components will be generating a lot of these very quickly, so we want to
 /// limit the amount of heap allocations / overly large enum sizes.
-pub enum VNode<'src> {
-    /// An element node (node type `ELEMENT_NODE`).
-    Element(&'src VElement<'src>),
-
-    /// A text node (node type `TEXT_NODE`).
+pub enum VNodeKind<'src> {
     Text(VText<'src>),
-
-    /// A fragment is a list of elements that might have a dynamic order.
-    /// Normally, children will have a fixed order. However, Fragments allow a dynamic order and must be diffed differently.
-    ///
-    /// Fragments don't have a single mount into the dom, so their position is characterized by the head and tail nodes.
-    ///
-    /// Fragments may have children and keys
-    Fragment(&'src VFragment<'src>),
-
-    /// A "suspended component"
-    /// This is a masqeurade over an underlying future that needs to complete
-    /// When the future is completed, the VNode will then trigger a render and the `real` field gets populated
-    Suspended { real: Cell<RealDomNode> },
-
-    /// A User-defined componen node (node type COMPONENT_NODE)
+    Element(&'src VElement<'src>),
+    Fragment(VFragment<'src>),
     Component(&'src VComponent<'src>),
+    Suspended,
 }
 
-// it's okay to clone because vnodes are just references to places into the bump
-impl<'a> Clone for VNode<'a> {
-    fn clone(&self) -> Self {
-        match self {
-            VNode::Element(element) => VNode::Element(element),
-            VNode::Text(old) => VNode::Text(old.clone()),
-            VNode::Fragment(fragment) => VNode::Fragment(fragment),
-            VNode::Component(component) => VNode::Component(component),
-            VNode::Suspended { real } => VNode::Suspended { real: real.clone() },
-        }
-    }
-}
-
-impl<'old, 'new> VNode<'old> {
-    // performs a somewhat costly clone of this vnode into another bump
-    // this is used when you want to drag nodes from an old frame into a new frame
-    // There is no way to safely drag listeners over (no way to clone a closure)
-    //
-    // This method will only be called if a component was once a real node and then becomes suspended
-    fn deep_clone_to_new_bump(&self, new: &'new Bump) -> VNode<'new> {
-        match self {
-            VNode::Element(el) => {
-                let new_el: VElement<'new> = VElement {
-                    key: NodeKey::NONE,
-                    // key: el.key.clone(),
-                    tag_name: el.tag_name,
-                    // wipe listeners on deep clone, there's no way to know what other bump material they might be referencing (nodes, etc)
-                    listeners: &[],
-                    attributes: {
-                        let attr_vec = bumpalo::collections::Vec::new_in(new);
-                        attr_vec.into_bump_slice()
-                    },
-                    children: {
-                        let attr_vec = bumpalo::collections::Vec::new_in(new);
-                        attr_vec.into_bump_slice()
-                    },
-                    namespace: el.namespace.clone(),
-                    dom_id: el.dom_id.clone(),
-                    is_static: el.is_static.clone(),
-                };
-
-                VNode::Element(new.alloc_with(move || new_el))
-            }
-            VNode::Text(_) => todo!(),
-            VNode::Fragment(_) => todo!(),
-            VNode::Suspended { real } => todo!(),
-            VNode::Component(_) => todo!(),
-        }
-    }
-}
-
-impl<'a> VNode<'a> {
-    /// Low-level constructor for making a new `Node` of type element with given
-    /// parts.
-    ///
-    /// This is primarily intended for JSX and templating proc-macros to compile
-    /// down into. If you are building nodes by-hand, prefer using the
-    /// `dodrio::builder::*` APIs.
-    #[inline]
-    pub fn element(
-        bump: &'a Bump,
-        key: NodeKey<'a>,
-        tag_name: &'static str,
-        listeners: &'a [Listener<'a>],
-        attributes: &'a [Attribute<'a>],
-        children: &'a [VNode<'a>],
-        namespace: Option<&'static str>,
-    ) -> VNode<'a> {
-        let element = bump.alloc_with(|| VElement {
-            key,
-            tag_name,
-            listeners,
-            attributes,
-            children,
-            namespace,
-            dom_id: Cell::new(RealDomNode::empty()),
-            is_static: Cell::new(false),
-        });
-        VNode::Element(element)
-    }
-
-    pub fn static_text(text: &'static str) -> VNode {
-        VNode::Text(VText {
-            text,
-            is_static: true,
-            dom_id: Cell::new(RealDomNode::empty()),
-        })
-    }
-    /// Construct a new text node with the given text.
-    pub fn text(bump: &'a Bump, args: Arguments) -> VNode<'a> {
-        match args.as_str() {
-            Some(text) => VNode::static_text(text),
-            None => {
-                use bumpalo::core_alloc::fmt::Write;
-                let mut s = bumpalo::collections::String::new_in(bump);
-                s.write_fmt(args).unwrap();
-                VNode::Text(VText {
-                    text: s.into_bump_str(),
-                    is_static: false,
-                    dom_id: Cell::new(RealDomNode::empty()),
-                })
-            }
-        }
-    }
-
-    #[inline]
-    pub(crate) fn key(&self) -> NodeKey {
-        match &self {
-            VNode::Text { .. } => NodeKey::NONE,
-            VNode::Element(e) => e.key,
-            VNode::Fragment(frag) => frag.key,
-            VNode::Component(c) => c.key,
-
-            // todo suspend should be allowed to have keys
-            VNode::Suspended { .. } => NodeKey::NONE,
-        }
-    }
-
-    fn get_child(&self, id: u32) -> Option<&'a VNode<'a>> {
-        todo!()
-    }
-
-    pub fn is_real(&self) -> bool {
-        match self {
-            VNode::Element(_) => true,
-            VNode::Text(_) => true,
-            VNode::Fragment(_) => false,
-            VNode::Suspended { .. } => false,
-            VNode::Component(_) => false,
-        }
-    }
-
-    pub fn get_mounted_id(&self, components: &SharedArena) -> Option<RealDomNode> {
-        match self {
-            VNode::Element(el) => Some(el.dom_id.get()),
-            VNode::Text(te) => Some(te.dom_id.get()),
-            VNode::Fragment(frag) => frag.void_root.get(),
-            VNode::Suspended { .. } => todo!(),
-            VNode::Component(el) => todo!(),
-        }
-    }
-}
-
-impl Debug for VNode<'_> {
-    fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
-        match self {
-            VNode::Element(el) => write!(s, "element, {}", el.tag_name),
-            VNode::Text(t) => write!(s, "text, {}", t.text),
-            VNode::Fragment(_) => write!(s, "fragment"),
-            VNode::Suspended { .. } => write!(s, "suspended"),
-            VNode::Component(_) => write!(s, "component"),
-        }
-    }
-}
-
-#[derive(Clone)]
 pub struct VText<'src> {
     pub text: &'src str,
     pub is_static: bool,
-    pub dom_id: Cell<RealDomNode>,
 }
 
-// ========================================================
-//   VElement (div, h1, etc), attrs, keys, listener handle
-// ========================================================
+pub struct VFragment<'src> {
+    pub children: &'src [VNode<'src>],
+}
 
-#[derive(Clone)]
+pub trait DioxusElement {
+    const TAG_NAME: &'static str;
+    const NAME_SPACE: Option<&'static str>;
+    fn tag_name(&self) -> &'static str {
+        Self::TAG_NAME
+    }
+}
 pub struct VElement<'a> {
-    /// Elements have a tag name, zero or more attributes, and zero or more
-    pub key: NodeKey<'a>,
+    // tag is always static
     pub tag_name: &'static str,
+
+    pub static_listeners: bool,
     pub listeners: &'a [Listener<'a>],
+
+    pub static_attrs: bool,
     pub attributes: &'a [Attribute<'a>],
+
+    pub static_children: bool,
     pub children: &'a [VNode<'a>],
+
+    // namespace is always static
     pub namespace: Option<&'static str>,
-    pub dom_id: Cell<RealDomNode>,
-    pub is_static: Cell<bool>,
 }
 
 /// An attribute on a DOM node, such as `id="my-thing"` or
@@ -232,175 +73,141 @@ pub struct Attribute<'a> {
     pub name: &'static str,
     pub value: &'a str,
     pub is_static: bool,
-
-    /// If an attribute is "namespaced", then it belongs to a group
-    /// The most common namespace is the "style" namespace
-    // pub is_dynamic: bool,
+    pub is_volatile: bool,
+    // Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
     pub namespace: Option<&'static str>,
 }
 
-impl<'a> Attribute<'a> {
-    /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
-    #[inline]
-    pub fn name(&self) -> &'a str {
-        self.name
-    }
-
-    /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
-    #[inline]
-    pub fn value(&self) -> &'a str {
-        self.value
-    }
-
-    /// Certain attributes are considered "volatile" and can change via user
-    /// input that we can't see when diffing against the old virtual DOM. For
-    /// these attributes, we want to always re-set the attribute on the physical
-    /// DOM node, even if the old and new virtual DOM nodes have the same value.
-    #[inline]
-    pub(crate) fn is_volatile(&self) -> bool {
-        match self.name {
-            "value" | "checked" | "selected" => true,
-            _ => false,
-        }
-    }
-}
-
-pub struct ListenerHandle {
-    pub event: &'static str,
-    pub scope: ScopeIdx,
-    pub id: usize,
-}
-
 /// An event listener.
+/// IE onclick, onkeydown, etc
 pub struct Listener<'bump> {
     /// The type of event to listen for.
     pub(crate) event: &'static str,
-
-    /// Which scope?
-    /// This might not actually be relevant
     pub scope: ScopeIdx,
-
     pub mounted_node: &'bump Cell<RealDomNode>,
-
-    /// The callback to invoke when the event happens.
     pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
 }
 
-/// The key for keyed children.
-///
-/// Keys must be unique among siblings.
-///
-/// If any sibling is keyed, then they all must be keyed.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct NodeKey<'a>(pub(crate) Option<&'a str>);
+/// Virtual Components for custom user-defined components
+/// Only supports the functional syntax
+pub struct VComponent<'src> {
+    pub ass_scope: Cell<Option<ScopeIdx>>,
+    pub caller: Rc<dyn Fn(&Scope) -> VNode>,
+    pub children: &'src [VNode<'src>],
+    pub comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
+    // a pointer into the bump arena (given by the 'src lifetime)
+    raw_props: *const (),
+    // a pointer to the raw fn typ
+    pub user_fc: *const (),
+    pub is_static: bool,
+}
 
-impl<'a> Default for NodeKey<'a> {
-    fn default() -> NodeKey<'a> {
-        NodeKey::NONE
-    }
+/// 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 scope_ref: &'a Scope,
+    pub listener_id: &'a Cell<usize>,
 }
-impl<'a> NodeKey<'a> {
-    /// The default, lack of a key.
-    pub const NONE: NodeKey<'a> = NodeKey(None);
 
-    /// Is this key `NodeKey::NONE`?
+impl<'a> NodeFactory<'a> {
     #[inline]
-    pub fn is_none(&self) -> bool {
-        *self == Self::NONE
+    pub fn bump(&self) -> &'a bumpalo::Bump {
+        &self.scope_ref.cur_frame().bump
     }
 
-    /// Is this key not `NodeKey::NONE`?
-    #[inline]
-    pub fn is_some(&self) -> bool {
-        !self.is_none()
+    pub fn static_text(text: &'static str) -> VNode {
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Text(VText {
+                text,
+                is_static: true,
+            }),
+        }
     }
 
-    /// Create a new `NodeKey`.
-    ///
-    /// `key` must not be `u32::MAX`.
-    #[inline]
-    pub fn new(key: &'a str) -> Self {
-        NodeKey(Some(key))
+    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 s = bumpalo::collections::String::new_in(self.bump());
+                s.write_fmt(args).unwrap();
+                (s.into_bump_str(), false)
+            }
+        }
     }
 
-    #[inline]
-    pub fn new_opt(key: Option<&'a str>) -> Self {
-        NodeKey(key)
+    /// 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 {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Text(VText { text, is_static }),
+        }
     }
-}
-
-// ==============================
-//   Custom components
-// ==============================
-
-/// Virtual Components for custom user-defined components
-/// Only supports the functional syntax
-pub type StableScopeAddres = Option<u32>;
-pub type VCompAssociatedScope = Option<ScopeIdx>;
 
-pub struct VComponent<'src> {
-    pub key: NodeKey<'src>,
-
-    // pub void_root: Cell<Option<RealDomNode>>,
-    pub ass_scope: Cell<VCompAssociatedScope>,
-
-    // todo: swap the RC out with
-    pub caller: Rc<dyn Fn(&Scope) -> VNode>,
-
-    pub children: &'src [VNode<'src>],
+    pub const fn const_el(&self) {}
 
-    pub comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
+    pub fn raw_element(
+        &self,
+        tag: &'static str,
+        listeners: &[Listener],
+        attributes: &[Attribute],
+        children: &'a [VNode<'a>],
+    ) {
+    }
 
-    pub is_static: bool,
+    pub fn element() {}
 
-    // a pointer into the bump arena (given by the 'src lifetime)
-    // raw_props: Box<dyn Any>,
-    raw_props: *const (),
+    pub fn suspended() -> VNode<'static> {
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Suspended,
+        }
+    }
 
-    // a pointer to the raw fn typ
-    pub user_fc: *const (),
-}
+    pub fn attr(
+        &self,
+        name: &'static str,
+        val: Arguments,
+        namespace: Option<&'static str>,
+        is_volatile: bool,
+    ) -> Attribute<'a> {
+        let (value, is_static) = self.raw_text(val);
+        Attribute {
+            name,
+            value,
+            is_static,
+            namespace,
+            is_volatile,
+        }
+    }
 
-impl<'a> VComponent<'a> {
-    /// When the rsx! macro is called, it will check if the CanMemo flag is set to true (from the Props impl)
-    /// If it is set to true, then this method will be called which implements automatic memoization.
-    ///
-    /// If the CanMemo is `false`, then the macro will call the backup method which always defaults to "false"
-    pub fn new<P: Properties + 'a>(
-        cx: &NodeFactory<'a>,
+    pub fn virtual_child<P, C>(
+        &self,
         component: FC<P>,
         props: P,
-        key: Option<&'a str>,
+        key: Option<&'a str>, // key: NodeKey<'a>,
         children: &'a [VNode<'a>],
-    ) -> Self {
-        let bump = cx.bump();
-        let user_fc = component as *const ();
-
-        let props = bump.alloc(props);
+    ) -> VNode<'a>
+    where
+        P: Properties + 'a,
+    {
+        // We don't want the fat part of the fat pointer
+        // This function does static dispatch so we don't need any VTable stuff
+        let props = self.bump().alloc(props);
         let raw_props = props as *const P as *const ();
 
-        let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(bump.alloc_with(|| {
+        let user_fc = component as *const ();
+
+        let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(self.bump().alloc_with(|| {
             move |other: &VComponent| {
-                // Safety:
-                // ------
-                //
-                // Invariants:
-                // - Component function pointers are the same
-                // - Generic properties on the same function pointer are the same
-                // - Lifetime of P borrows from its parent
-                // - The parent scope still exists when method is called
-                // - Casting from T to *const () is portable
-                // - Casting raw props to P can only happen when P is static
-                //
-                // Explanation:
-                //   We are guaranteed that the props will be of the same type because
-                //   there is no way to create a VComponent other than this `new` method.
-                //
-                //   Therefore, if the render functions are identical (by address), then so will be
-                //   props type paramter (because it is the same render function). Therefore, we can be
-                //   sure that it is safe to interperet the previous props raw pointer as the same props
-                //   type. From there, we can call the props' "memoize" method to see if we can
-                //   avoid re-rendering the component.
                 if user_fc == other.user_fc {
                     let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
                     let props_memoized = unsafe { props.memoize(&real_other) };
@@ -414,81 +221,200 @@ impl<'a> VComponent<'a> {
             }
         }));
 
-        let key = match key {
-            Some(key) => NodeKey::new(key),
-            None => NodeKey(None),
-        };
+        VNode {
+            key,
+            dom_id: Cell::new(RealDomNode::empty()),
+            kind: VNodeKind::Component(self.bump().alloc_with(|| VComponent {
+                user_fc,
+                comparator,
+                raw_props,
+                children,
+                caller: NodeFactory::create_component_caller(component, raw_props),
+                is_static: children.len() == 0 && P::IS_STATIC && key.is_none(),
+                ass_scope: Cell::new(None),
+            })),
+        }
+    }
 
-        let caller = create_component_caller(component, raw_props);
+    pub fn create_component_caller<'g, P: 'g>(
+        component: FC<P>,
+        raw_props: *const (),
+    ) -> Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r>> {
+        type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r> + 'a>;
+        let caller: Captured = Rc::new(move |scp: &Scope| -> VNode {
+            // cast back into the right lifetime
+            let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
+            let tasks = RefCell::new(Vec::new());
+            let cx: Context<P> = Context {
+                props: safe_props,
+                scope: scp,
+                tasks: &tasks,
+            };
+
+            let res = component(cx);
+
+            // submit any async tasks to the scope
+            for task in tasks.borrow_mut().drain(..) {
+                scp.submit_task(task);
+            }
 
-        // If the component does not have children, has no props (we can't memoize props), and has no no key, then we don't
-        // need to bother diffing it in the future
-        //
-        // This is more of an optimization to prevent unnecessary descending through the tree during diffing, rather than
-        // actually speeding up the diff process itself
-        let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
+            let g2 = unsafe { std::mem::transmute(res) };
 
-        Self {
-            user_fc,
-            comparator,
-            raw_props,
-            children,
-            ass_scope: Cell::new(None),
-            key,
-            caller,
-            is_static,
-            // void_root: Cell::new(None),
+            g2
+        });
+        unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
+    }
+
+    pub fn fragment_from_iter(
+        self,
+        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
+    ) -> VNode<'a> {
+        let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
+        // TODO throw an error if there are nodes without keys
+        for node in node_iter.into_iter() {
+            nodes.push(node.into_vnode(self));
+        }
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Fragment(VFragment {
+                children: nodes.into_bump_slice(),
+            }),
         }
     }
 }
 
-type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r> + 'a>;
+impl<'a> IntoIterator for VNode<'a> {
+    type Item = VNode<'a>;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
+}
+impl<'a> IntoVNode<'a> for VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
+        self
+    }
+}
+
+impl<'a> IntoVNode<'a> for &VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
+        self.clone()
+    }
+}
 
-pub fn create_component_caller<'a, P: 'a>(
-    user_component: FC<P>,
-    raw_props: *const (),
-) -> Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r>> {
-    let g: Captured = Rc::new(move |scp: &Scope| -> VNode {
-        // cast back into the right lifetime
-        let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
-        let tasks = RefCell::new(Vec::new());
-        let cx: Context<P> = Context {
-            props: safe_props,
-            scope: scp,
-            tasks: &tasks,
-        };
+pub trait IntoVNode<'a> {
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
+}
 
-        let g = user_component(cx);
+// Wrap the the node-builder closure in a concrete type.
+// ---
+// This is a bit of a hack to implement the IntoVNode trait for closure types.
+pub struct LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    inner: G,
+    _p: PhantomData<&'a ()>,
+}
 
-        for task in tasks.borrow_mut().drain(..) {
-            scp.submit_task(task);
+impl<'a, G> LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    pub fn new(f: G) -> Self {
+        Self {
+            inner: f,
+            _p: PhantomData {},
         }
+    }
+}
 
-        let g2 = unsafe { std::mem::transmute(g) };
+// Cover the cases where nodes are used by macro.
+// Likely used directly.
+// ---
+//  let nodes = rsx!{ ... };
+//  rsx! { {nodes } }
+impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        (self.inner)(cx)
+    }
+}
 
-        g2
-    });
-    let r: Captured<'static> = unsafe { std::mem::transmute(g) };
-    r
+// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
+impl<'a, G> IntoIterator for LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    type Item = Self;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
 }
 
-pub struct VFragment<'src> {
-    pub key: NodeKey<'src>,
-    pub children: &'src [VNode<'src>],
-    pub void_root: Cell<Option<RealDomNode>>,
+impl IntoVNode<'_> for () {
+    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        cx.fragment_from_iter(None as Option<VNode>)
+    }
 }
 
-impl<'a> VFragment<'a> {
-    pub fn new(key: Option<&'a str>, children: &'a [VNode<'a>]) -> Self {
-        let key = match key {
-            Some(key) => NodeKey::new(key),
-            None => NodeKey(None),
+impl IntoVNode<'_> for Option<()> {
+    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        cx.fragment_from_iter(None as Option<VNode>)
+    }
+}
+
+impl Debug for NodeFactory<'_> {
+    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        Ok(())
+    }
+}
+
+// it's okay to clone because vnodes are just references to places into the bump
+impl<'a> Clone for VNode<'a> {
+    fn clone(&self) -> Self {
+        let kind = match &self.kind {
+            VNodeKind::Element(element) => VNodeKind::Element(element),
+            VNodeKind::Text(old) => VNodeKind::Text(VText {
+                text: old.text,
+                is_static: old.is_static,
+            }),
+            VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
+                children: fragment.children,
+            }),
+            VNodeKind::Component(component) => VNodeKind::Component(component),
+            VNodeKind::Suspended => VNodeKind::Suspended,
         };
 
-        Self {
-            key,
-            children,
-            void_root: Cell::new(None),
+        todo!()
+    }
+}
+
+impl Debug for VNode<'_> {
+    fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+        match &self.kind {
+            VNodeKind::Element(el) => write!(s, "element, {}", el.tag_name),
+            VNodeKind::Text(t) => write!(s, "text, {}", t.text),
+            VNodeKind::Fragment(_) => write!(s, "fragment"),
+            VNodeKind::Suspended { .. } => write!(s, "suspended"),
+            VNodeKind::Component(_) => write!(s, "component"),
         }
     }
 }
+
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test() {}
+
+    #[test]
+    fn sizing() {
+        dbg!(std::mem::size_of::<VElement>());
+        dbg!(std::mem::align_of::<VElement>());
+    }
+}

+ 484 - 0
packages/core/src/oldbuilder.rs

@@ -0,0 +1,484 @@
+/// Typically constructed with element-specific constructors, eg the `div`
+/// function for building `<div>` elements or the `button` function for building
+/// `<button>` elements.
+#[derive(Debug)]
+pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
+where
+    Listeners: 'a + AsRef<[Listener<'a>]>,
+    Attributes: 'a + AsRef<[Attribute<'a>]>,
+    Children: 'a + AsRef<[VNode<'a>]>,
+{
+    cx: &'b NodeFactory<'a>,
+    key: NodeKey<'a>,
+    tag_name: &'static str,
+    listeners: Listeners,
+    attributes: Attributes,
+    children: Children,
+    namespace: Option<&'static str>,
+}
+
+impl<'a, 'b>
+    ElementBuilder<
+        'a,
+        'b,
+        bumpalo::collections::Vec<'a, Listener<'a>>,
+        bumpalo::collections::Vec<'a, Attribute<'a>>,
+        bumpalo::collections::Vec<'a, VNode<'a>>,
+    >
+{
+    /// Create a new `ElementBuilder` for an element with the given tag name.
+    ///
+    /// In general, only use this constructor if the tag is dynamic (i.e. you
+    /// might build a `<div>` or you might build a `<span>` and you don't know
+    /// until runtime). Prefer using the tag-specific constructors instead:
+    /// `div(bump)` or `span(bump)`, etc.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// let tag_name = if flip_coin() {
+    ///     "div"
+    /// } else {
+    ///     "span"
+    /// };
+    ///
+    /// let my_element_builder = ElementBuilder::new(&b, tag_name);
+    /// # fn flip_coin() -> bool { true }
+    /// ```
+    pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
+        let bump = cx.bump();
+        ElementBuilder {
+            cx,
+            key: NodeKey::NONE,
+            tag_name,
+            listeners: bumpalo::collections::Vec::new_in(bump),
+            attributes: bumpalo::collections::Vec::new_in(bump),
+            children: bumpalo::collections::Vec::new_in(bump),
+            namespace: None,
+        }
+    }
+}
+
+impl<'a, 'b, Listeners, Attributes, Children>
+    ElementBuilder<'a, 'b, Listeners, Attributes, Children>
+where
+    Listeners: 'a + AsRef<[Listener<'a>]>,
+    Attributes: 'a + AsRef<[Attribute<'a>]>,
+    Children: 'a + AsRef<[VNode<'a>]>,
+{
+    /// Set the listeners for this element.
+    ///
+    /// You can use this method to customize the backing storage for listeners,
+    /// for example to use a fixed-size array instead of the default
+    /// dynamically-sized `bumpalo::collections::Vec`.
+    ///
+    /// Any listeners already added to the builder will be overridden.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create a `<div>` with a fixed-size array of two listeners.
+    /// let my_div = div(&b)
+    ///     .listeners([
+    ///         on(&b, "click", |root, vdom, event| {
+    ///             // ...
+    ///         }),
+    ///         on(&b, "dblclick", |root, vdom, event| {
+    ///             // ...
+    ///         }),
+    ///     ])
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
+    where
+        L: 'a + AsRef<[Listener<'a>]>,
+    {
+        ElementBuilder {
+            cx: self.cx,
+            key: self.key,
+            tag_name: self.tag_name,
+            listeners,
+            attributes: self.attributes,
+            children: self.children,
+            namespace: self.namespace,
+        }
+    }
+
+    /// Set the attributes for this element.
+    ///
+    /// You can use this method to customize the backing storage for attributes,
+    /// for example to use a fixed-size array instead of the default
+    /// dynamically-sized `bumpalo::collections::Vec`.
+    ///
+    /// Any attributes already added to the builder will be overridden.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump, Attribute};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create a `<div>` with a fixed-size array of two attributes.
+    /// let my_div = div(&b)
+    ///     .attributes([
+    ///         attr("id", "my-div"),
+    ///         attr("class", "notification"),
+    ///     ])
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
+    where
+        A: 'a + AsRef<[Attribute<'a>]>,
+    {
+        ElementBuilder {
+            cx: self.cx,
+            key: self.key,
+            tag_name: self.tag_name,
+            listeners: self.listeners,
+            attributes,
+            children: self.children,
+            namespace: self.namespace,
+        }
+    }
+
+    /// Set the children for this element.
+    ///
+    /// You can use this method to customize the backing storage for children,
+    /// for example to use a fixed-size array instead of the default
+    /// dynamically-sized `bumpalo::collections::Vec`.
+    ///
+    /// Any children already added to the builder will be overridden.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create a `<div>` with a fixed-size array of two `<span>` children.
+    /// let my_div = div(&b)
+    ///     .children([
+    ///         span(&b).finish(),
+    ///         span(&b).finish(),
+    ///     ])
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
+    where
+        C: 'a + AsRef<[VNode<'a>]>,
+    {
+        ElementBuilder {
+            cx: self.cx,
+            key: self.key,
+            tag_name: self.tag_name,
+            listeners: self.listeners,
+            attributes: self.attributes,
+            children,
+            namespace: self.namespace,
+        }
+    }
+
+    /// Set the namespace for this element.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create a `<td>` tag with an xhtml namespace
+    /// let my_td = td(&b)
+    ///     .namespace(Some("http://www.w3.org/1999/xhtml"))
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn namespace(self, namespace: Option<&'static str>) -> Self {
+        ElementBuilder {
+            cx: self.cx,
+            key: self.key,
+            tag_name: self.tag_name,
+            listeners: self.listeners,
+            attributes: self.attributes,
+            children: self.children,
+            namespace,
+        }
+    }
+
+    /// Set this element's key.
+    ///
+    /// When diffing sets of siblings, if an old sibling and new sibling share a
+    /// key, then they will always reuse the same physical DOM VNode. This is
+    /// important when using CSS animations, web components, third party JS, or
+    /// anything else that makes the diffing implementation observable.
+    ///
+    /// Do not use keys if such a scenario does not apply. Keyed diffing is
+    /// generally more expensive than not, since it is putting greater
+    /// constraints on the diffing algorithm.
+    ///
+    /// # Invariants You Must Uphold
+    ///
+    /// The key may not be `u32::MAX`, which is a reserved key value.
+    ///
+    /// Keys must be unique among siblings.
+    ///
+    /// All sibling VNodes must be keyed, or they must all not be keyed. You may
+    /// not mix keyed and unkeyed siblings.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// let my_li = li(&b)
+    ///     .key(1337)
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn key(mut self, key: &'a str) -> Self {
+        self.key = NodeKey(Some(key));
+        self
+    }
+
+    pub fn key2(mut self, args: Arguments) -> Self {
+        let key = match args.as_str() {
+            Some(static_str) => static_str,
+            None => {
+                use bumpalo::core_alloc::fmt::Write;
+                let mut s = bumpalo::collections::String::new_in(self.cx.bump());
+                s.write_fmt(args).unwrap();
+                s.into_bump_str()
+            }
+        };
+        self.key = NodeKey(Some(key));
+        self
+    }
+
+    /// Create the virtual DOM VNode described by this builder.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump, VNode};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Start with a builder...
+    /// let builder: ElementBuilder<_, _, _> = div(&b);
+    ///
+    /// // ...and finish it to create a virtual DOM VNode!
+    /// let my_div: VNode = builder.finish();
+    /// ```
+    #[inline]
+    pub fn finish(mut self) -> VNode<'a> {
+        let bump = self.cx.bump();
+
+        let children: &'a Children = bump.alloc(self.children);
+        let children: &'a [VNode<'a>] = children.as_ref();
+
+        let listeners: &'a Listeners = bump.alloc(self.listeners);
+        let listeners: &'a [Listener<'a>] = listeners.as_ref();
+
+        for listener in listeners {
+            // bump the context id forward
+            let id = self.cx.listener_id.get();
+            self.cx.listener_id.set(id + 1);
+
+            // Add this listener to the context list
+            // This casts the listener to a self-referential pointer
+            // This is okay because the bump arena is stable
+
+            // TODO: maybe not add it into CTX immediately
+            let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(listener) };
+            self.cx.scope_ref.listeners.borrow_mut().push((
+                r.mounted_node as *const _ as *mut _,
+                r.callback as *const _ as *mut _,
+            ));
+        }
+
+        let attributes: &'a Attributes = bump.alloc(self.attributes);
+        let attributes: &'a [Attribute<'a>] = attributes.as_ref();
+
+        VNode::element(
+            bump,
+            self.key,
+            self.tag_name,
+            listeners,
+            attributes,
+            children,
+            self.namespace,
+        )
+    }
+}
+
+impl<'a, 'b, Attributes, Children>
+    ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
+where
+    Attributes: 'a + AsRef<[Attribute<'a>]>,
+    Children: 'a + AsRef<[VNode<'a>]>,
+{
+    // / Add a new event listener to this element.
+    // /
+    // / The `event` string specifies which event will be listened for. The
+    // / `callback` function is the function that will be invoked if the
+    // / specified event occurs.
+    // /
+    // / # Example
+    // /
+    // / ```no_run
+    // / use dioxus::{builder::*, bumpalo::Bump};
+    // /
+    // / let b = Bump::new();
+    // /
+    // / // A button that does something when clicked!
+    // / let my_button = button(&b)
+    // /     .on("click", |event| {
+    // /         // ...
+    // /     })
+    // /     .finish();
+    // / ```
+    // pub fn on(self, event: &'static str, callback: impl FnMut(VirtualEvent) + 'a) -> Self {
+    //     let bump = &self.cx.bump();
+    //     let listener = Listener {
+    //         event,
+    //         callback: bump.alloc(callback),
+    //         scope: self.cx.scope_ref.arena_idx,
+    //         mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
+    //     };
+    //     self.add_listener(listener)
+    // }
+
+    // pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
+    //     self.listeners.push(listener);
+
+    //     // bump the context id forward
+    //     let id = self.cx.listener_id.get();
+    //     self.cx.listener_id.set(id + 1);
+
+    //     // Add this listener to the context list
+    //     // This casts the listener to a self-referential pointer
+    //     // This is okay because the bump arena is stable
+    //     self.listeners.last().map(|g| {
+    //         let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
+    //         self.cx.scope_ref.listeners.borrow_mut().push((
+    //             r.mounted_node as *const _ as *mut _,
+    //             r.callback as *const _ as *mut _,
+    //         ));
+    //     });
+
+    //     self
+    // }
+}
+
+impl<'a, 'b, Listeners, Children>
+    ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
+where
+    Listeners: 'a + AsRef<[Listener<'a>]>,
+    Children: 'a + AsRef<[VNode<'a>]>,
+{
+    /// Add a new attribute to this element.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create the `<div id="my-div"/>` element.
+    /// let my_div = div(&b).attr("id", "my-div").finish();
+    /// ```
+    pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
+        let (value, is_static) = raw_text(self.cx.bump(), args);
+
+        self.attributes.push(Attribute {
+            name,
+            value,
+            is_static,
+            namespace: None,
+        });
+        self
+    }
+}
+
+impl<'a, 'b, Listeners, Attributes>
+    ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
+where
+    Listeners: 'a + AsRef<[Listener<'a>]>,
+    Attributes: 'a + AsRef<[Attribute<'a>]>,
+{
+    /// Add a new child to this element.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    /// use js_sys::Math;
+    ///
+    /// let b = Bump::new();
+    ///
+    /// // Create `<p><span></span></p>`.
+    /// let my_div = p(&b)
+    ///     .child(span(&b).finish())
+    ///     .finish();
+    /// ```
+    #[inline]
+    pub fn child(mut self, child: VNode<'a>) -> Self {
+        self.children.push(child);
+        self
+    }
+
+    /// Add multiple children to this element from an iterator.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// let my_div = p(&b)
+    ///     .iter_child((0..10).map(|f| span(&b).finish())
+    ///     .finish();
+    /// ```    
+    pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
+        todo!();
+        let len_before = self.children.len();
+        for item in nodes {
+            todo!()
+            // let child = item.into_vnode(&self.cx);
+            // self.children.push(child);
+        }
+        if cfg!(debug_assertions) {
+            if self.children.len() > len_before + 1 {
+                if self.children.last().unwrap().key().is_none() {
+                    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. 
+---
+To help you identify where this error is coming from, we've generated a backtrace.
+                        "#,
+                    );
+                }
+            }
+        }
+        self
+    }
+}

+ 4 - 0
packages/core/src/scope.rs

@@ -212,18 +212,22 @@ impl Scope {
         (self.task_submitter)(DTask::new(task, self.arena_idx));
     }
 
+    #[inline]
     pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.current_head_node()
     }
 
+    #[inline]
     pub(crate) fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.prev_head_node()
     }
 
+    #[inline]
     pub(crate) fn cur_frame(&self) -> &BumpFrame {
         self.frames.cur_frame()
     }
 
+    #[inline]
     pub fn root<'a>(&'a self) -> &'a VNode<'a> {
         &self.frames.cur_frame().head_node
     }

+ 4 - 1
packages/core/src/util.rs

@@ -1,5 +1,5 @@
 use std::{
-    cell::{RefCell, RefMut},
+    cell::{Cell, RefCell, RefMut},
     rc::Rc,
     vec::Drain,
 };
@@ -65,6 +65,9 @@ impl RealDomNode {
     pub const fn empty() -> Self {
         Self(u64::MIN)
     }
+    pub const fn empty_cell() -> Cell<Self> {
+        Cell::new(Self::empty())
+    }
 }
 
 pub struct DebugDom {

+ 1 - 1
packages/core/src/virtual_dom.rs

@@ -146,7 +146,7 @@ impl VirtualDom {
             .with(|arena| {
                 arena.insert_with_key(move |myidx| {
                     let event_channel = _event_queue.new_channel(0, myidx);
-                    let caller = crate::nodes::create_component_caller(root, props_ptr as *const _);
+                    let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
                     Scope::new(caller, myidx, None, 0, event_channel, link, &[], submitter)
                 })
             })

+ 1 - 1
packages/core/src/styles.rs → packages/html/src/styles.rs

@@ -12,7 +12,7 @@
 //!
 //!
 
-use crate::{innerlude::Attribute, nodebuilder::NodeFactory};
+use crate::innerlude::{Attribute, NodeFactory};
 
 pub struct StyleBuilder;
 pub trait AsAttr<'a> {