浏览代码

Feat: remove old macro

Jonathan Kelley 4 年之前
父节点
当前提交
9d0727e
共有 43 个文件被更改,包括 207 次插入2305 次删除
  1. 1 1
      Cargo.toml
  2. 7 7
      notes/CHANGELOG.md
  3. 0 3
      packages/core/Cargo.toml
  4. 2 5
      packages/core/examples/step.rs
  5. 5 1
      packages/core/src/debug_renderer.rs
  6. 50 49
      packages/core/src/diff.rs
  7. 1 1
      packages/core/src/events.rs
  8. 14 42
      packages/core/src/nodes.rs
  9. 4 20
      packages/core/src/patch.rs
  10. 10 7
      packages/core/src/scope.rs
  11. 46 30
      packages/core/src/virtual_dom.rs
  12. 0 11
      packages/html-macro-test/Cargo.toml
  13. 0 3
      packages/html-macro-test/README.md
  14. 0 13
      packages/html-macro-test/src/lib.rs
  15. 0 3
      packages/html-macro-test/src/tests.rs
  16. 0 377
      packages/html-macro-test/src/tests/all_tests.rs
  17. 0 171
      packages/html-macro-test/src/tests/text.rs
  18. 0 7
      packages/html-macro-test/src/tests/ui.rs
  19. 0 11
      packages/html-macro-test/src/tests/ui/invalid_html_tag.rs
  20. 0 11
      packages/html-macro-test/src/tests/ui/invalid_html_tag.stderr
  21. 0 12
      packages/html-macro-test/src/tests/ui/should_be_self_closing_tag.rs
  22. 0 5
      packages/html-macro-test/src/tests/ui/should_be_self_closing_tag.stderr
  23. 0 11
      packages/html-macro-test/src/tests/ui/wrong_closing_tag.rs
  24. 0 5
      packages/html-macro-test/src/tests/ui/wrong_closing_tag.stderr
  25. 0 22
      packages/html-macro/Cargo.toml
  26. 0 3
      packages/html-macro/README.md
  27. 0 56
      packages/html-macro/src/lib.rs
  28. 0 102
      packages/html-macro/src/parser/braced.rs
  29. 0 49
      packages/html-macro/src/parser/close_tag.rs
  30. 0 273
      packages/html-macro/src/parser/mod.rs
  31. 0 153
      packages/html-macro/src/parser/open_tag.rs
  32. 0 73
      packages/html-macro/src/parser/statement.rs
  33. 0 96
      packages/html-macro/src/parser/text.rs
  34. 0 310
      packages/html-macro/src/tag.rs
  35. 0 36
      packages/html-macro/src/validation/mod.rs
  36. 0 31
      packages/html-macro/src/validation/self_closing.rs
  37. 0 114
      packages/html-macro/src/validation/svg_namespace.rs
  38. 0 138
      packages/html-macro/src/validation/valid_tags.rs
  39. 4 1
      packages/web/Cargo.toml
  40. 38 30
      packages/web/examples/deep.rs
  41. 9 6
      packages/web/examples/rsxt.rs
  42. 13 3
      packages/web/src/interpreter.rs
  43. 3 3
      packages/web/src/lib.rs

+ 1 - 1
Cargo.toml

@@ -16,7 +16,7 @@ members = [
     # TODO @Jon, share the validation code
     # "packages/web",
     # "packages/hooks",
-    "packages/cli",
+    # "packages/cli",
     # "examples",
     # "packages/html-macro",
     # "packages/html-macro-2",

+ 7 - 7
notes/CHANGELOG.md

@@ -28,12 +28,12 @@
 
 # Project: Web_sys renderer (TBD)
 - [x] WebSys edit interpreter
-- [ ] Event system using async channels
-
+- [x] Event system using async channels
+- [ ] Implement conversion of all event types into synthetic events
 # Project: String Render (TBD)
 > Implement a light-weight string renderer with basic caching 
-- [ ] (SSR) Implement stateful 3rd party string renderer
 - [x] (Macro) Make VText nodes automatically capture and format IE allow "Text is {blah}"
+- [ ] (SSR) Implement stateful 3rd party string renderer
 
 # Project: Hooks + Context + Subscriptions (TBD)
 > Implement the foundations for state management
@@ -41,12 +41,12 @@
 - [x] Implement use_state (rewrite to use the use_reducer api like rei)
 - [x] Implement use_ref
 - [x] Implement use_context (only the API, not the state management solution)
-- [ ] Implement use_reducer
+- [ ] Implement use_reducer (WIP)
 
 # Project: QOL 
 > Make it easier to write components
-- [x] (Macro) Tweak event syntax to not be dependent on wasm32 target (just return regular closures which get boxed)
-- [ ] (Macro) Tweak component syntax to accept a new custom element 
+- [x] (Macro) Tweak event syntax to not be dependent on wasm32 target (just return regular closures which get boxed/alloced)
+- [x] (Macro) Tweak component syntax to accept a new custom element 
 - [ ] (Macro) Allow components to specify their props as function args
 
 # Project: Initial VDOM support (TBD)
@@ -58,6 +58,6 @@
 - [x] (Core) Introduce the VDOM and patch API for 3rd party renderers
 - [x] (Core) Implement lifecycle
 - [x] (Core) Implement an event system 
-- [ ] (Core) Implement child nodes, scope creation
+- [x] (Core) Implement child nodes, scope creation
 - [ ] (Core) Implement dirty tagging and compression
 

+ 0 - 3
packages/core/Cargo.toml

@@ -36,8 +36,5 @@ log = "0.4.14"
 
 serde = { version = "1.0.123", features = ["derive"], optional=true }
 
-# todo, remove 
-uuid = {version = "0.8.2", features=["serde", "v4"]}
-
 [features]
 default = []

+ 2 - 5
packages/core/examples/step.rs

@@ -1,10 +1,10 @@
-use dioxus_core::virtual_dom::VirtualDom;
+use dioxus_core::debug_renderer::DebugRenderer;
 use dioxus_core::{component::Properties, prelude::*};
 
 fn main() -> Result<(), ()> {
     let p1 = SomeProps { name: "bob".into() };
 
-    let _vdom = VirtualDom::new_with_props(Example, p1);
+    let _vdom = DebugRenderer::new_with_props(Example, p1);
 
     Ok(())
 }
@@ -18,9 +18,6 @@ static Example: FC<SomeProps> = |ctx, _props| {
     ctx.render(html! {
         <div>
             <h1> "hello world!" </h1>
-            <h1> "hello world!" </h1>
-            <h1> "hello world!" </h1>
-            <h1> "hello world!" </h1>
         </div>
     })
 };

+ 5 - 1
packages/core/src/debug_renderer.rs

@@ -3,7 +3,7 @@
 //!
 //! Renderers don't actually need to own the virtual dom (it's up to the implementer).
 
-use crate::virtual_dom::VirtualDom;
+use crate::{events::EventTrigger, virtual_dom::VirtualDom};
 use crate::{innerlude::Result, prelude::*};
 
 pub struct DebugRenderer {
@@ -33,6 +33,10 @@ impl DebugRenderer {
         Self { internal_dom: dom }
     }
 
+    pub fn handle_event(&mut self, trigger: EventTrigger) -> Result<()> {
+        Ok(())
+    }
+
     pub fn step(&mut self, machine: &mut DiffMachine) -> Result<()> {
         Ok(())
     }

+ 50 - 49
packages/core/src/diff.rs

@@ -36,13 +36,13 @@ use crate::{innerlude::*, scope::Scope};
 use bumpalo::Bump;
 use fxhash::{FxHashMap, FxHashSet};
 use generational_arena::Arena;
-use uuid::Uuid;
 
 use std::{
     cell::{RefCell, RefMut},
     cmp::Ordering,
     collections::VecDeque,
     rc::{Rc, Weak},
+    sync::atomic::AtomicU32,
 };
 
 /// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
@@ -66,13 +66,33 @@ pub struct DiffMachine<'a> {
 pub enum LifeCycleEvent<'a> {
     Mount {
         caller: Weak<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
-        id: Uuid,
+        scope: Weak<VCompAssociatedScope>,
+        id: u32,
+    },
+    PropsChanged {
+        caller: Weak<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
+        scope: Weak<VCompAssociatedScope>,
+        id: u32,
+    },
+    SameProps {
+        caller: Weak<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
+        scope: Weak<VCompAssociatedScope>,
+        id: u32,
+    },
+    Replace {
+        caller: Weak<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
+        old_scope: Weak<VCompAssociatedScope>,
+        new_scope: Weak<VCompAssociatedScope>,
+        id: u32,
     },
-    PropsChanged,
-    SameProps,
     Remove,
 }
 
+static COUNTER: AtomicU32 = AtomicU32::new(1);
+fn get_id() -> u32 {
+    COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
+}
+
 impl<'a> DiffMachine<'a> {
     pub fn new() -> Self {
         Self {
@@ -128,12 +148,27 @@ impl<'a> DiffMachine<'a> {
             }
 
             (VNode::Component(cold), VNode::Component(cnew)) => {
-                todo!("should not happen")
-                // if cold.caller_ref != cnew.caller_ref {
-                //     // todo: create a stable addr
-                //     self.lifecycle_events.push_back(LifeCycleEvent::Mount);
-                //     return;
-                // }
+                // todo!("should not happen")
+                if cold.user_fc == cnew.user_fc {
+                    // todo: create a stable addr
+                    let caller = Rc::downgrade(&cnew.caller);
+                    let id = cold.stable_addr.borrow().unwrap();
+                    let scope = Rc::downgrade(&cold.ass_scope);
+                    self.lifecycle_events
+                        .push_back(LifeCycleEvent::PropsChanged { caller, id, scope });
+                } else {
+                    let caller = Rc::downgrade(&cnew.caller);
+                    let id = cold.stable_addr.borrow().unwrap();
+                    let old_scope = Rc::downgrade(&cold.ass_scope);
+                    let new_scope = Rc::downgrade(&cnew.ass_scope);
+
+                    self.lifecycle_events.push_back(LifeCycleEvent::Replace {
+                        caller,
+                        id,
+                        old_scope,
+                        new_scope,
+                    });
+                }
 
                 // let comparator = &cnew.comparator.0;
                 // let old_props = cold.raw_props.as_ref();
@@ -151,23 +186,7 @@ impl<'a> DiffMachine<'a> {
 
             // todo: knock out any listeners
             (_, VNode::Component(_new)) => {
-                // self.lifecycle_events.push_back(LifeCycleEvent::Mount);
-                // we have no stable reference to work from
-                // push the lifecycle event onto the queue
-                // self.lifecycle_events
-                //     .borrow_mut()
-                //     .push_back(LifecycleEvent {
-                //         event_type: LifecycleType::Mount {
-                //             props: new.props,
-                //             to: self.cur_idx,
-                //         },
-                //     });
-                // we need to associaote this new component with a scope...
-
-                // self.change_list.save_known_root(id)
                 self.change_list.commit_traversal();
-
-                // push the current
             }
 
             (VNode::Component(_old), _) => {
@@ -242,36 +261,18 @@ impl<'a> DiffMachine<'a> {
             /*
             todo: integrate re-entrace
             */
-            // NodeKind::Cached(ref c) => {
-            //     cached_roots.insert(c.id);
-            //     let (node, template) = cached_set.get(c.id);
-            //     if let Some(template) = template {
-            //         create_with_template(
-            //             cached_set,
-            //             self.change_list,
-            //             registry,
-            //             template,
-            //             node,
-            //             cached_roots,
-            //         );
-            //     } else {
-            //         create(cached_set, change_list, registry, node, cached_roots);
-            //     }
-            // }
             VNode::Component(component) => {
                 self.change_list
                     .create_text_node("placeholder for vcomponent");
-                let id = uuid::Uuid::new_v4();
+
+                let id = get_id();
                 *component.stable_addr.as_ref().borrow_mut() = Some(id);
                 self.change_list.save_known_root(id);
-
-                let caller = Rc::downgrade(&component.caller);
-                // let broken_caller: Weak<dyn Fn(Context) -> DomTree + 'static> =
-                //     unsafe { std::mem::transmute(caller) };
+                let scope = Rc::downgrade(&component.ass_scope);    
                 self.lifecycle_events.push_back(LifeCycleEvent::Mount {
-                    caller,
-                    // caller: broken_caller,
+                    caller: Rc::downgrade(&component.caller),
                     id,
+                    scope
                 });
             }
             VNode::Suspended => {

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

@@ -3,7 +3,7 @@
 //! 3rd party renderers are responsible for forming this virtual events from events.
 //! The goal here is to provide a consistent event interface across all renderer types.
 //!
-//! also... websys is kinda bad for rust-analyzer so we coerce for you automatically :)
+//! also... websys integerates poorly with rust analyzer, so we handle that for you automatically.
 
 use crate::innerlude::ScopeIdx;
 

+ 14 - 42
packages/core/src/nodes.rs

@@ -88,8 +88,8 @@ impl<'a> VNode<'a> {
             VNode::Suspended => {
                 todo!()
             }
-            VNode::Component(_) => {
-                todo!()
+            VNode::Component(c) => {
+                c.key
             }
         }
     }
@@ -107,27 +107,6 @@ pub struct VElement<'a> {
     pub attributes: &'a [Attribute<'a>],
     pub children: &'a [VNode<'a>],
     pub namespace: Option<&'a str>,
-    // The HTML tag, such as "div"
-    // pub tag: &'a str,
-
-    // pub tag_name: &'a str,
-    // pub attributes: &'a [Attribute<'a>],
-    // todo: hook up listeners
-    // pub listeners: &'a [Listener<'a>],
-    // / HTML attributes such as id, class, style, etc
-    // pub attrs: HashMap<String, String>,
-    // TODO: @JON Get this to not heap allocate, but rather borrow
-    // pub attrs: HashMap<&'static str, &'static str>,
-
-    // TODO @Jon, re-enable "events"
-    //
-    // /// Events that will get added to your real DOM element via `.addEventListener`
-    // pub events: Events,
-    // pub events: HashMap<String, ()>,
-
-    // /// The children of this `VNode`. So a <div> <em></em> </div> structure would
-    // /// have a parent div and one child, em.
-    // pub children: Vec<VNode>,
 }
 
 impl<'a> VElement<'a> {
@@ -264,10 +243,14 @@ impl<'a> VText<'a> {
 
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
-pub type StableScopeAddres = Rc<RefCell<Option<uuid::Uuid>>>;
+pub type StableScopeAddres = RefCell<Option<u32>>;
+pub type VCompAssociatedScope = RefCell<Option<ScopeIdx>>;
 
 pub struct VComponent<'src> {
-    pub stable_addr: StableScopeAddres,
+    pub key: NodeKey,
+
+    pub stable_addr: Rc<StableScopeAddres>,
+    pub ass_scope: Rc<VCompAssociatedScope>,
 
     pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
     pub caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'src>,
@@ -276,28 +259,15 @@ pub struct VComponent<'src> {
     raw_props: *const (),
 
     // a pointer to the raw fn typ
-    caller_ref: *const (),
+    pub user_fc: *const (),
     _p: PhantomData<&'src ()>,
 }
 
 impl std::fmt::Debug for VComponent<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        todo!()
+        Ok(())
     }
 }
-// pub struct Comparator(pub Rc<dyn Fn(&VComponent) -> bool>);
-// impl std::fmt::Debug for Comparator {
-//     fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-//         Ok(())
-//     }
-// }
-
-// pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>);
-// impl std::fmt::Debug for Caller {
-//     fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-//         Ok(())
-//     }
-// }
 
 impl<'a> VComponent<'a> {
     // use the type parameter on props creation and move it into a portable context
@@ -318,7 +288,7 @@ impl<'a> VComponent<'a> {
             // 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
-            if caller_ref == other.caller_ref {
+            if caller_ref == other.user_fc {
                 let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
                 real_other == props
             } else {
@@ -333,7 +303,9 @@ impl<'a> VComponent<'a> {
         };
 
         Self {
-            caller_ref,
+            key: NodeKey::NONE,
+            ass_scope: Rc::new(RefCell::new(None)),
+            user_fc: caller_ref,
             raw_props: props as *const P as *const _,
             _p: PhantomData,
             caller: Rc::new(caller),

+ 4 - 20
packages/core/src/patch.rs

@@ -94,12 +94,12 @@ pub enum Edit<'src_bump> {
     // ========================================================
     // push a known node on to the stack
     TraverseToKnown {
-        node: uuid::Uuid,
+        node: u32,
         // node: ScopeIdx,
     },
     // Add the current top of the stack to the known nodes
     MakeKnown {
-        node: uuid::Uuid,
+        node: u32,
         // node: ScopeIdx,
     },
     // Remove the current top of the stack from the known nodes
@@ -383,32 +383,16 @@ impl<'a> EditMachine<'a> {
         // debug!("emit: remove_event_listener({:?})", event);
     }
 
-    pub fn save_known_root(&mut self, id: uuid::Uuid) {
-        // pub fn save_known_root(&mut self, id: ScopeIdx) {
-        // pub fn save_known_root(&mut self, id: ScopeIdx) {
+    pub fn save_known_root(&mut self, id: u32) {
         log::debug!("emit: save_known_root({:?})", id);
         self.emitter.push(Edit::MakeKnown { node: id })
     }
 
-    pub fn load_known_root(&mut self, id: uuid::Uuid) {
+    pub fn load_known_root(&mut self, id: u32) {
         log::debug!("emit: TraverseToKnown({:?})", id);
         self.emitter.push(Edit::TraverseToKnown { node: id })
     }
 
-    // pub fn save_template(&mut self, id: CacheId) {
-    //     debug_assert!(self.traversal_is_committed());
-    //     debug_assert!(!self.has_template(id));
-    //     // debug!("emit: save_template({:?})", id);
-    //     self.templates.insert(id);
-    //     self.emitter.save_template(id.into());
-    // }
-
-    // pub fn push_template(&mut self, id: CacheId) {
-    //     debug_assert!(self.traversal_is_committed());
-    //     debug_assert!(self.has_template(id));
-    //     // debug!("emit: push_template({:?})", id);
-    //     self.emitter.push_template(id.into());
-    // }
 }
 
 // Keeps track of where we are moving in a DOM tree, and shortens traversal

+ 10 - 7
packages/core/src/scope.rs

@@ -96,10 +96,11 @@ impl Scope {
             scope: self.myidx,
             listeners: &self.listeners,
         };
+        let caller = self.caller.upgrade().expect("Failed to get caller");
 
-        todo!()
+        // todo!()
         // Note that the actual modification of the vnode head element occurs during this call
-        // let _: DomTree = (self.caller.0.as_ref())(ctx);
+        let _: DomTree = (caller.as_ref())(ctx);
         // let _: DomTree = (self.raw_caller)(ctx, &self.props);
 
         /*
@@ -114,11 +115,13 @@ impl Scope {
         - Public API cannot drop or destructure VNode
         */
 
-        // frame.head_node = node_slot
-        //     .deref()
-        //     .borrow_mut()
-        //     .take()
-        //     .expect("Viewing did not happen");
+        frame.head_node = node_slot.as_ref()
+            // .deref()
+            .borrow_mut()
+            .take()
+            .expect("Viewing did not happen");
+
+            Ok(())
     }
 
     // A safe wrapper around calling listeners

+ 46 - 30
packages/core/src/virtual_dom.rs

@@ -3,10 +3,7 @@
 use crate::{error::Error, innerlude::*};
 use crate::{patch::Edit, scope::Scope};
 use generational_arena::Arena;
-use std::{
-    any::TypeId,
-    rc::{Rc, Weak},
-};
+use std::{any::TypeId, borrow::Borrow, rc::{Rc, Weak}};
 use thiserror::private::AsDynError;
 
 // We actually allocate the properties for components in their parent's properties
@@ -82,19 +79,19 @@ impl VirtualDom {
             .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?;
 
         component.run_scope()?;
-        let component = &*component;
+        // let component = &*component;
 
-        // // get raw pointer to the arena
-        // let very_unsafe_components = &mut self.components as *mut generational_arena::Arena<Scope>;
+        // get raw pointer to the arena
+        let very_unsafe_components = &mut self.components as *mut generational_arena::Arena<Scope>;
 
-        // {
-        //         let component = self
-        //             .components
-        //             .get(self.base_scope)
-        // .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?
+        {
+            let component = self
+                .components
+                .get(self.base_scope)
+                .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?;
 
-        //     diff_machine.diff_node(component.old_frame(), component.new_frame());
-        // }
+            diff_machine.diff_node(component.old_frame(), component.new_frame());
+        }
         // let p = &mut self.components;
         // chew down the the lifecycle events until all dirty nodes are computed
         while let Some(event) = diff_machine.lifecycle_events.pop_front() {
@@ -102,31 +99,48 @@ impl VirtualDom {
                 // A new component has been computed from the diffing algorithm
                 // create a new component in the arena, run it, move the diffing machine to this new spot, and then diff it
                 // this will flood the lifecycle queue with new updates
-                LifeCycleEvent::Mount { caller, id } => {
+                LifeCycleEvent::Mount { caller, id, scope } => {
                     log::debug!("Mounting a new component");
-                    diff_machine.change_list.load_known_root(id);
 
                     // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
                     // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
                     // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
                     unsafe {
-                        // let p = &mut *(very_unsafe_components);
-                        // let p = &mut self.components;
+                        let p = &mut *(very_unsafe_components);
 
                         // todo, hook up the parent/child indexes properly
-                        let idx = self.components.insert_with(|f| Scope::new(caller, f, None));
-                        let c = self.components.get_mut(idx).unwrap();
-                        // let idx = p.insert_with(|f| Scope::new(caller, f, None));
-                        // let c = p.get_mut(idx).unwrap();
-                        c.run_scope();
-                        // diff_machine.diff_node(c.old_frame(), c.new_frame());
+                        let idx = p.insert_with(|f| Scope::new(caller, f, None));
+                        let c = p.get_mut(idx).unwrap();
+
+                        let real_scope = scope.upgrade().unwrap();
+                        *real_scope.as_ref().borrow_mut() = Some(idx);
+                        c.run_scope()?;
+
+                        diff_machine.change_list.load_known_root(id);
+
+                        diff_machine.diff_node(c.old_frame(), c.new_frame());
                     }
                 }
-                LifeCycleEvent::PropsChanged => {
-                    //
+                LifeCycleEvent::PropsChanged { caller, id, scope } => {
+                    let idx = scope.upgrade().unwrap().as_ref().borrow().unwrap();
+
+                    // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
+                    // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
+                    // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
+                    unsafe {
+                        let p = &mut *(very_unsafe_components);
+                        // todo, hook up the parent/child indexes properly
+                        // let idx = p.insert_with(|f| Scope::new(caller, f, None));
+                        let c = p.get_mut(idx).unwrap();
+                        c.run_scope()?;
+
+                        diff_machine.change_list.load_known_root(id);
+
+                        diff_machine.diff_node(c.old_frame(), c.new_frame());
+                    }
                     // break 'render;
                 }
-                LifeCycleEvent::SameProps => {
+                LifeCycleEvent::SameProps { caller, id, scope } => {
                     //
                     // break 'render;
                 }
@@ -134,6 +148,7 @@ impl VirtualDom {
                     //
                     // break 'render;
                 }
+                LifeCycleEvent::Replace { caller, id, .. } => {}
             }
 
             // } else {
@@ -176,13 +191,14 @@ impl VirtualDom {
             .expect("Borrowing should not fail");
 
         component.call_listener(event);
-        component.run_scope();
+        component.run_scope()?;
 
         let mut diff_machine = DiffMachine::new();
         // let mut diff_machine = DiffMachine::new(&self.diff_bump);
 
-        diff_machine.diff_node(component.old_frame(), component.new_frame());
+        // diff_machine.diff_node(component.old_frame(), component.new_frame());
 
-        Ok(diff_machine.consume())
+        // Ok(diff_machine.consume())
+        self.rebuild()
     }
 }

+ 0 - 11
packages/html-macro-test/Cargo.toml

@@ -1,11 +0,0 @@
-[package]
-name = "html-macro-test"
-version = "0.1.0"
-authors = ["Chinedu Francis Nwafili <frankie.nwafili@gmail.com>"]
-edition = "2018"
-
-[dependencies]
-html-macro = {path = "../html-macro"}
-virtual-dom-rs = { path = "../virtual-dom-rs" }
-virtual-node = {path = "../virtual-node"}
-trybuild = "1.0"

+ 0 - 3
packages/html-macro-test/README.md

@@ -1,3 +0,0 @@
-# html-macro-test
-
-Unit tests for the `html!` macro

+ 0 - 13
packages/html-macro-test/src/lib.rs

@@ -1,13 +0,0 @@
-//! Tests for our html! procedural macro
-//!
-//! To run all tests in this library:
-//!
-//! cargo test --color=always --package html-macro-test --lib "" -- --nocapture
-
-// #![feature(proc_macro_hygiene)]
-
-// TODO: Deny warnings to ensure that the macro isn't creating any warnings.
-// #![deny(warnings)]
-
-#[cfg(test)]
-mod tests;

+ 0 - 3
packages/html-macro-test/src/tests.rs

@@ -1,3 +0,0 @@
-mod all_tests;
-mod text;
-mod ui;

+ 0 - 377
packages/html-macro-test/src/tests/all_tests.rs

@@ -1,377 +0,0 @@
-//! This is a catch-all module to place new tests as we go.
-//!
-//! Over time we'll pull tests out of here and organize them.
-//!
-//! For example - there is a `text_tests.rs` module where all of our text node related
-//! tests live.
-
-use html_macro::html;
-use std::collections::HashMap;
-use virtual_node::{IterableNodes, VElement, VText, View, VirtualNode};
-
-struct HtmlMacroTest {
-    generated: VirtualNode,
-    expected: VirtualNode,
-}
-
-impl HtmlMacroTest {
-    /// Ensure that the generated and the expected virtual node are equal.
-    fn test(self) {
-        assert_eq!(self.expected, self.generated);
-    }
-}
-
-#[test]
-fn empty_div() {
-    HtmlMacroTest {
-        generated: html! { <div></div> },
-        expected: VirtualNode::element("div"),
-    }
-    .test();
-}
-
-#[test]
-fn one_attr() {
-    let mut attrs = HashMap::new();
-    attrs.insert("id".to_string(), "hello-world".to_string());
-    let mut expected = VElement::new("div");
-    expected.attrs = attrs;
-
-    HtmlMacroTest {
-        generated: html! { <div id="hello-world"></div> },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// Events are ignored in non wasm-32 targets
-#[test]
-fn ignore_events_on_non_wasm32_targets() {
-    HtmlMacroTest {
-        generated: html! {
-            <div onclick=|_: u8|{}></div>
-        },
-        expected: html! {<div></div>},
-    }
-    .test();
-}
-
-#[test]
-fn child_node() {
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("span")];
-
-    HtmlMacroTest {
-        generated: html! { <div><span></span></div> },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn sibling_child_nodes() {
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("span"), VirtualNode::element("b")];
-
-    HtmlMacroTest {
-        generated: html! { <div><span></span><b></b></div> },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// Nested 3 nodes deep
-#[test]
-fn three_nodes_deep() {
-    let mut child = VElement::new("span");
-    child.children = vec![VirtualNode::element("b")];
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![child.into()];
-
-    HtmlMacroTest {
-        generated: html! { <div><span><b></b></span></div> },
-        expected: expected.into(),
-    }
-    .test()
-}
-
-#[test]
-fn sibling_text_nodes() {
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::text("This is a text node")];
-
-    HtmlMacroTest {
-        generated: html! { <div>This is a text node</div> },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn nested_macro() {
-    let child_2 = html! { <b></b> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("span"), VirtualNode::element("b")];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>
-            { html! { <span></span> } }
-            { child_2 }
-          </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// If the first thing we see is a block then we grab whatever is inside it.
-#[test]
-fn block_root() {
-    let em = html! { <em></em> };
-
-    let expected = VirtualNode::element("em");
-
-    HtmlMacroTest {
-        generated: html! {
-            { em }
-        },
-        expected,
-    }
-    .test();
-}
-
-/// Text followed by a block
-#[test]
-fn text_next_to_block() {
-    let child = html! { <ul></ul> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![
-        VirtualNode::text(" A bit of text "),
-        VirtualNode::element("ul"),
-    ];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>
-            A bit of text
-            { child }
-          </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// Ensure that we maintain the correct spacing around punctuation tokens, since
-/// they resolve into a separate TokenStream during parsing.
-#[test]
-fn punctuation_token() {
-    let text = "Hello, World";
-
-    HtmlMacroTest {
-        generated: html! { Hello, World },
-        expected: VirtualNode::text(text),
-    }
-    .test()
-}
-
-#[test]
-fn vec_of_nodes() {
-    let children = vec![html! { <div> </div>}, html! { <strong> </strong>}];
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("div"), VirtualNode::element("strong")];
-
-    HtmlMacroTest {
-        generated: html! { <div> { children } </div> },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// Just make sure that this compiles since async, for, loop, and type are keywords
-#[test]
-fn keyword_attribute() {
-    html! { <script src="/app.js" async="async" /> };
-    html! { <label for="username">Username:</label> };
-    html! { <audio loop="loop"><source src="/beep.mp3" type="audio/mpeg" /></audio> };
-    html! { <link rel="stylesheet" type="text/css" href="/app.css" /> };
-}
-
-/// For unquoted text apostrophes should be parsed correctly
-#[test]
-fn apostrophe() {
-    assert_eq!(html! { Aren't }, VText::new("Aren't").into());
-    assert_eq!(html! { Aren'ttt }, VText::new("Aren'ttt").into());
-}
-
-/// Verify that all of our self closing tags work without backslashes.
-#[test]
-fn self_closing_tag_without_backslash() {
-    let mut expected = VElement::new("div");
-    let children = vec![
-        "area", "base", "br", "col", "hr", "img", "input", "link", "meta", "param", "command",
-        "keygen", "source",
-    ]
-    .into_iter()
-    .map(|tag| VirtualNode::element(tag))
-    .collect();
-    expected.children = children;
-
-    HtmlMacroTest {
-        generated: html! {
-            <div>
-                <area> <base> <br> <col> <hr> <img> <input> <link> <meta> <param> <command>
-                <keygen> <source>
-            </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-/// Verify that our self closing tags work with backslashes
-#[test]
-fn self_closing_tag_with_backslace() {
-    HtmlMacroTest {
-        generated: html! {
-            <br />
-        },
-        expected: VirtualNode::element("br"),
-    }
-    .test();
-}
-
-#[test]
-fn if_true_block() {
-    let child_valid = html! { <b></b> };
-    let child_invalid = html! { <i></i> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("b")];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>
-            {if true {child_valid} else {child_invalid}}
-          </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn if_false_block() {
-    let child_valid = html! { <b></b> };
-    let child_invalid = html! { <i></i> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("i")];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>
-            {if false {
-                child_valid
-            } else {
-                child_invalid
-            }}
-          </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn single_branch_if_true_block() {
-    let child_valid = html! { <b></b> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::element("b")];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>{if true {child_valid}}</div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn single_branch_if_false_block() {
-    let child_valid = html! { <b></b> };
-
-    let mut expected = VElement::new("div");
-    expected.children = vec![VirtualNode::text("")];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>{if false {child_valid}}</div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn custom_component_props() {
-    struct Counter {
-        count: u8,
-    }
-
-    impl View for Counter {
-        fn render(&self) -> VirtualNode {
-            html! {
-                <span>Counter = {format!("{}", self.count)}</span>
-            }
-        }
-    }
-
-    let mut expected = VElement::new("div");
-    let mut child = VElement::new("span");
-    child.children = vec![VirtualNode::text("Counter = "), VirtualNode::text("1")];
-    expected.children = vec![child.into()];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div><Counter count={1}/></div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}
-
-#[test]
-fn custom_component_children() {
-    struct Child;
-
-    impl View for Child {
-        fn render(&self) -> VirtualNode {
-            html! {
-                <span></span>
-            }
-        }
-    }
-
-    let mut expected = VElement::new("div");
-    let mut child = VElement::new("span");
-    child.children = vec![VirtualNode::text("Hello World")];
-    expected.children = vec![child.into()];
-
-    HtmlMacroTest {
-        generated: html! {
-          <div>
-            <Child>Hello World</Child>
-          </div>
-        },
-        expected: expected.into(),
-    }
-    .test();
-}

+ 0 - 171
packages/html-macro-test/src/tests/text.rs

@@ -1,171 +0,0 @@
-use html_macro::html;
-use virtual_node::{IterableNodes, VirtualNode};
-
-#[test]
-fn text_root_node() {
-    assert_eq!(&html! { some text }.to_string(), "some text");
-}
-
-#[test]
-fn text_variable_root() {
-    let text = "hello world";
-
-    assert_eq!(&html! { { text } }.to_string(), "hello world");
-}
-
-#[test]
-fn raw_string_literal() {
-    assert_eq!(
-        &html! { <div>{ r#"Hello World"# }</div> }.to_string(),
-        "<div>Hello World</div>"
-    );
-}
-
-#[test]
-fn text_variable_child() {
-    let text = "world";
-
-    assert_eq!(
-        &html! { <div>{ text }</div> }.to_string(),
-        "<div>world</div>"
-    )
-}
-
-#[test]
-fn text_space_after_start_tag() {
-    assert_eq!(
-        &html! { <div> After Start Tag</div> }.to_string(),
-        "<div> After Start Tag</div>"
-    )
-}
-
-#[test]
-fn text_space_before_end_tag() {
-    assert_eq!(
-        &html! { <div>Before End Tag </div> }.to_string(),
-        "<div>Before End Tag </div>"
-    )
-}
-
-#[test]
-fn text_space_before_block() {
-    let text = "Before Block";
-
-    assert_eq!(
-        &html! { <div> {text}</div> }.to_string(),
-        "<div> Before Block</div>"
-    )
-}
-
-#[test]
-fn text_space_after_block() {
-    let text = "Hello";
-
-    assert_eq!(
-        &html! { <div>{text} </div> }.to_string(),
-        "<div>Hello </div>"
-    )
-}
-
-#[test]
-fn text_space_in_block_ignored() {
-    let text = "Hello";
-
-    assert_eq!(
-        &html! { <div>{ text }</div> }.to_string(),
-        "<div>Hello</div>"
-    )
-}
-
-#[test]
-fn text_multiple_text_no_space_between() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div>{ hello }{ world }</div> }.to_string(),
-        "<div>HelloWorld</div>"
-    )
-}
-
-#[test]
-fn text_multiple_text_space_between() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div>{ hello } { world }</div> }.to_string(),
-        "<div>Hello World</div>"
-    )
-}
-
-#[test]
-fn text_multiple_text_space_around() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div> { hello }{ world } </div> }.to_string(),
-        "<div> HelloWorld </div>"
-    )
-}
-
-#[test]
-fn text_multiple_text_space_between_around() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div> { hello } { world } </div> }.to_string(),
-        "<div> Hello World </div>"
-    )
-}
-
-#[test]
-fn text_tokens_in_between_vars_without_space() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div>{ hello }NoSpace{ world }</div> }.to_string(),
-        "<div>HelloNoSpaceWorld</div>"
-    )
-}
-
-#[test]
-fn text_tokens_in_between_vars_with_space() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div>{ hello } Space { world }</div> }.to_string(),
-        "<div>Hello Space World</div>"
-    )
-}
-
-#[test]
-fn text_tokens_in_between_vars_space_around_between() {
-    let hello = "Hello";
-    let world = "World";
-
-    assert_eq!(
-        &html! { <div> { hello } Space { world } </div> }.to_string(),
-        "<div> Hello Space World </div>"
-    )
-}
-
-#[test]
-fn text_space_before_next_open_tag() {
-    assert_eq!(
-        &html! { <div>Hello <img /> world</div> }.to_string(),
-        "<div>Hello <img> world</div>"
-    )
-}
-
-#[test]
-fn text_no_space_before_open_tag() {
-    assert_eq!(
-        &html! { <div>Hello<img /> world</div> }.to_string(),
-        "<div>Hello<img> world</div>"
-    )
-}

+ 0 - 7
packages/html-macro-test/src/tests/ui.rs

@@ -1,7 +0,0 @@
-#[test]
-fn ui() {
-    let t = trybuild::TestCases::new();
-
-    let ui_tests = concat!(env!("CARGO_MANIFEST_DIR"), "/src/tests/ui/*.rs");
-    t.compile_fail(ui_tests);
-}

+ 0 - 11
packages/html-macro-test/src/tests/ui/invalid_html_tag.rs

@@ -1,11 +0,0 @@
-#![feature(proc_macro_hygiene)]
-
-extern crate virtual_dom_rs;
-use virtual_dom_rs::prelude::*;
-
-// Used a tag name that does not exist in the HTML spec
-fn main() {
-    html! {
-        <invalidtagname></invalidtagname>
-    };
-}

+ 0 - 11
packages/html-macro-test/src/tests/ui/invalid_html_tag.stderr

@@ -1,11 +0,0 @@
-error: invalidtagname is not a valid HTML tag.
-
-If you are trying to use a valid HTML tag, perhaps there's a typo?
-
-If you are trying to use a custom component, please capitalize the component name.
-
-custom components: https://chinedufn.github.io/percy/html-macro/custom-components/index.html
- --> $DIR/invalid_html_tag.rs:9:10
-  |
-9 |         <invalidtagname></invalidtagname>
-  |          ^^^^^^^^^^^^^^

+ 0 - 12
packages/html-macro-test/src/tests/ui/should_be_self_closing_tag.rs

@@ -1,12 +0,0 @@
-#![feature(proc_macro_hygiene)]
-
-extern crate virtual_dom_rs;
-use virtual_dom_rs::prelude::*;
-
-// We are using open and close tags for a tag that should
-// actually be a self closing tag
-fn main () {
-    html! {
-        <br></br>
-    };
-}

+ 0 - 5
packages/html-macro-test/src/tests/ui/should_be_self_closing_tag.stderr

@@ -1,5 +0,0 @@
-error: br is a self closing tag. Try "<br>" or "<br />"
-  --> $DIR/should_be_self_closing_tag.rs:10:15
-   |
-10 |         <br></br>
-   |               ^^

+ 0 - 11
packages/html-macro-test/src/tests/ui/wrong_closing_tag.rs

@@ -1,11 +0,0 @@
-#![feature(proc_macro_hygiene)]
-
-extern crate virtual_dom_rs;
-use virtual_dom_rs::prelude::*;
-
-// Expected a closing div tag, found a closing strong tag
-fn main () {
-    html! {
-        <div> </strong>
-    };
-}

+ 0 - 5
packages/html-macro-test/src/tests/ui/wrong_closing_tag.stderr

@@ -1,5 +0,0 @@
-error: Wrong closing tag. Try changing "strong" into "div"
- --> $DIR/wrong_closing_tag.rs:9:17
-  |
-9 |         <div> </strong>
-  |                 ^^^^^^

+ 0 - 22
packages/html-macro/Cargo.toml

@@ -1,22 +0,0 @@
-[package]
-name = "dioxus-html-macro"
-version = "0.1.0"
-description = "HTML-compliant macro for creating Dioxus VNodes"
-authors = [
-    "Jonathan Kelley",
-    # Originally pulled from Percy - check it out!
-    "Chinedu Francis Nwafili <frankie.nwafili@gmail.com>",
-]
-keywords = ["virtual", "dom", "wasm", "assembly", "webassembly"]
-license = "MIT/Apache-2.0"
-edition = "2018"
-
-[lib]
-proc-macro = true
-
-[dependencies]
-proc-macro2 = { version = "0.4", features = ["span-locations"] }
-quote = "0.6.11"
-syn = { version = "0.15", features = ["full", "extra-traits"] }
-lazy_static = "1.4.0"
-# html-validation = { path = "../html-validation", version = "0.1.2" }

+ 0 - 3
packages/html-macro/README.md

@@ -1,3 +0,0 @@
-# html-macro
-
-Disclaimer: much of this macro's source comes from Percy - another vdom implementation for Rust.

+ 0 - 56
packages/html-macro/src/lib.rs

@@ -1,56 +0,0 @@
-extern crate proc_macro;
-
-use crate::parser::HtmlParser;
-use crate::tag::Tag;
-use syn::parse::{Parse, ParseStream, Result};
-use syn::parse_macro_input;
-
-mod parser;
-mod tag;
-pub(crate) mod validation;
-
-/// Used to generate VirtualNode's from a TokenStream.
-///
-/// html! { <div> Welcome to the html! procedural macro! </div> }
-#[proc_macro]
-pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let parsed = parse_macro_input!(input as Html);
-
-    let mut html_parser = HtmlParser::new();
-
-    let parsed_tags_len = parsed.tags.len();
-
-    // Iterate over all of our parsed tags and push them into our HtmlParser one by one.
-    //
-    // As we go out HtmlParser will maintain some heuristics about what we've done so far
-    // since that will sometimes inform how to parse the next token.
-    for (idx, tag) in parsed.tags.iter().enumerate() {
-        let mut next_tag = None;
-
-        if parsed_tags_len - 1 > idx {
-            next_tag = Some(&parsed.tags[idx + 1])
-        }
-
-        html_parser.push_tag(tag, next_tag);
-    }
-
-    html_parser.finish().into()
-}
-
-#[derive(Debug)]
-struct Html {
-    tags: Vec<Tag>,
-}
-
-impl Parse for Html {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let mut tags = Vec::new();
-
-        while !input.is_empty() {
-            let tag: Tag = input.parse()?;
-            tags.push(tag);
-        }
-
-        Ok(Html { tags })
-    }
-}

+ 0 - 102
packages/html-macro/src/parser/braced.rs

@@ -1,102 +0,0 @@
-use crate::parser::HtmlParser;
-use crate::tag::{Tag, TagKind};
-use proc_macro2::Span;
-use quote::quote;
-use syn::spanned::Spanned;
-use syn::Block;
-
-impl HtmlParser {
-    /// Parse an incoming Tag::Braced text node
-    pub(crate) fn parse_braced(
-        &mut self,
-        block: &Box<Block>,
-        brace_span: &Span,
-        next_tag: Option<&Tag>,
-    ) {
-        // We'll check to see if there is a space between this block and the previous open
-        // tag's closing brace.
-        //
-        // If so we'll then check if the node in this block is a text node. If it is we'll
-        // insert a single white space before it.
-        //
-        // let some_var = "hello"
-        // let another_var = "world";
-        //
-        // html! { <span>{some_var}</span> }  -> would not get a " " inserted
-        //
-        // html! { <span> {some_var}</span> } -> would get a " " inserted
-        let mut insert_whitespace_before_text = false;
-        if let Some(open_tag_end) = self.recent_span_locations.most_recent_open_tag_end.as_ref() {
-            if self.last_tag_kind == Some(TagKind::Open)
-                && self.separated_by_whitespace(open_tag_end, brace_span)
-            {
-                insert_whitespace_before_text = true;
-            }
-        }
-
-        // If
-        //   1. The next tag is a closing tag or another braced block
-        //   2. There is space between this brace and that next tag / braced block
-        //
-        // Then
-        //   We'll insert some spacing after this brace.
-        //
-        // This ensures that we properly maintain spacing between two neighboring braced
-        // text nodes
-        //
-        // html! { <div>{ This Brace } { Space WILL be inserted }</div>
-        //   -> <div>This Brace Space WILL be inserted</div>
-        //
-        // html! { <div>{ This Brace }{ Space WILL NOT be inserted }</div>
-        //   -> <div>This BraceSpace WILL NOT be inserted</div>
-        let insert_whitespace_after_text = match next_tag {
-            Some(Tag::Close {
-                first_angle_bracket_span,
-                ..
-            }) => self.separated_by_whitespace(brace_span, &first_angle_bracket_span),
-            Some(Tag::Braced {
-                brace_span: next_brace_span,
-                ..
-            }) => self.separated_by_whitespace(brace_span, &next_brace_span),
-            _ => false,
-        };
-
-        // TODO: Only allow one statement per block. Put a quote_spanned! compiler error if
-        // there is more than 1 statement. Add a UI test for this.
-        block.stmts.iter().for_each(|stmt| {
-            if self.current_node_idx == 0 {
-                // Here we handle a block being the root node of an `html!` call
-                //
-                // html { { some_node }  }
-                let node = quote! {
-                    let node_0: VirtualNode = #stmt.into();
-                };
-                self.push_tokens(node);
-            } else {
-                self.parse_statement(stmt);
-
-                if insert_whitespace_before_text {
-                    let node = self.current_virtual_node_ident(stmt.span());
-
-                    let insert_whitespace = quote! {
-                        #node.first().insert_space_before_text();
-                    };
-
-                    self.push_tokens(insert_whitespace);
-                }
-
-                if insert_whitespace_after_text {
-                    let node = self.current_virtual_node_ident(stmt.span());
-
-                    let insert_whitespace = quote! {
-                        #node.last().insert_space_after_text();
-                    };
-
-                    self.push_tokens(insert_whitespace);
-                }
-            }
-        });
-
-        self.set_most_recent_block_start(brace_span.clone());
-    }
-}

+ 0 - 49
packages/html-macro/src/parser/close_tag.rs

@@ -1,49 +0,0 @@
-use crate::parser::{is_self_closing, HtmlParser};
-use proc_macro2::Ident;
-use quote::quote_spanned;
-
-impl HtmlParser {
-    /// Parse an incoming Tag::Close
-    pub(crate) fn parse_close_tag(&mut self, name: &Ident) {
-        let parent_stack = &mut self.parent_stack;
-
-        let close_span = name.span();
-        let close_tag = name.to_string();
-
-        // For example, this should have been <br /> instead of </br>
-        if is_self_closing(&close_tag) {
-            let error = format!(
-                r#"{} is a self closing tag. Try "<{}>" or "<{} />""#,
-                close_tag, close_tag, close_tag
-            );
-            let error = quote_spanned! {close_span=> {
-                compile_error!(#error);
-            }};
-
-            self.push_tokens(error);
-            return;
-        }
-
-        let last_open_tag = parent_stack.pop().expect("Last open tag");
-
-        let last_open_tag = last_open_tag.1.to_string();
-
-        // TODO: 2 compile_error!'s one pointing to the open tag and one pointing to the
-        // closing tag. Update the ui test accordingly
-        //
-        // ex: if div != strong
-        if last_open_tag != close_tag {
-            let error = format!(
-                r#"Wrong closing tag. Try changing "{}" into "{}""#,
-                close_tag, last_open_tag
-            );
-
-            let error = quote_spanned! {close_span=> {
-                compile_error!(#error);
-            }};
-            // TODO: Abort early if we find an error. So we should be returning
-            // a Result.
-            self.push_tokens(error);
-        }
-    }
-}

+ 0 - 273
packages/html-macro/src/parser/mod.rs

@@ -1,273 +0,0 @@
-use crate::tag::TagKind;
-use crate::Tag;
-use quote::{quote, quote_spanned};
-use std::collections::HashMap;
-use syn::export::Span;
-use syn::spanned::Spanned;
-use syn::{Ident, Stmt};
-
-mod braced;
-mod close_tag;
-mod open_tag;
-mod statement;
-mod text;
-
-pub enum NodesToPush<'a> {
-    Stmt(&'a Stmt),
-    TokenStream(&'a Stmt, proc_macro2::TokenStream),
-}
-
-/// Used to parse [`Tag`]s that we've parsed and build a tree of `VirtualNode`s
-///
-/// [`Tag`]: enum.Tag.html
-pub struct HtmlParser {
-    /// As we parse our macro tokens we'll generate new tokens to return back into the compiler
-    /// when we're done.
-    tokens: Vec<proc_macro2::TokenStream>,
-    /// Everytime we encounter a new node we'll use the current_node_idx to name it.
-    /// Then we'll increment the current_idx by one.
-    /// This gives every node that we encounter a unique name that we can use to find
-    /// it later when we want to push child nodes into parent nodes
-    current_node_idx: usize,
-    /// The order that we encountered nodes while parsing.
-    node_order: Vec<usize>,
-    /// Each time we encounter a new node that could possible be a parent node
-    /// we push it's node index onto the stack.
-    ///
-    /// Text nodes cannot be parent nodes.
-    parent_stack: Vec<(usize, Ident)>,
-    /// Key -> index of the parent node within the HTML tree
-    /// Value -> vector of child node indices
-    parent_to_children: HashMap<usize, Vec<usize>>,
-    /// The locations of the most recent spans that we parsed.
-    /// Used to determine whether or not to put space around text nodes.
-    recent_span_locations: RecentSpanLocations,
-    /// The last kind of tag that we parsed.
-    /// Used to determine whether or not to put space around text nodes.
-    last_tag_kind: Option<TagKind>,
-}
-
-/// TODO: I've hit a good stopping point... but we can clean these methods up / split them up
-/// a bit...
-impl HtmlParser {
-    /// Create a new HtmlParser
-    pub fn new() -> HtmlParser {
-        let mut parent_to_children: HashMap<usize, Vec<usize>> = HashMap::new();
-        parent_to_children.insert(0, vec![]);
-
-        HtmlParser {
-            tokens: vec![],
-            current_node_idx: 0,
-            node_order: vec![],
-            parent_stack: vec![],
-            parent_to_children,
-            recent_span_locations: RecentSpanLocations::default(),
-            last_tag_kind: None,
-        }
-    }
-
-    /// Generate the tokens for the incoming Tag and update our parser's heuristics that keep
-    /// track of information about what we've parsed.
-    pub fn push_tag(&mut self, tag: &Tag, next_tag: Option<&Tag>) {
-        match tag {
-            Tag::Open {
-                name,
-                attrs,
-                closing_bracket_span,
-                is_self_closing,
-                ..
-            } => {
-                self.parse_open_tag(name, closing_bracket_span, attrs, *is_self_closing);
-                self.last_tag_kind = Some(TagKind::Open);
-            }
-            Tag::Close { name, .. } => {
-                self.parse_close_tag(name);
-                self.last_tag_kind = Some(TagKind::Close);
-            }
-            Tag::Text {
-                text,
-                start_span,
-                end_span,
-            } => {
-                self.parse_text(text, start_span.unwrap(), end_span.unwrap(), next_tag);
-                self.last_tag_kind = Some(TagKind::Text);
-            }
-            Tag::Braced { block, brace_span } => {
-                self.parse_braced(block, brace_span, next_tag);
-                self.last_tag_kind = Some(TagKind::Braced);
-            }
-        };
-    }
-
-    ///  1. Pop a node off the stack
-    ///  2. Look up all of it's children in parent_to_children
-    ///  3. Append the children to this node
-    ///  4. Move on to the next node (as in, go back to step 1)
-    pub fn finish(&mut self) -> proc_macro2::TokenStream {
-        let node_order = &mut self.node_order;
-        let parent_to_children = &mut self.parent_to_children;
-        let tokens = &mut self.tokens;
-
-        if node_order.len() > 1 {
-            for _ in 0..(node_order.len()) {
-                let parent_idx = node_order.pop().unwrap();
-
-                // TODO: Figure out how to really use spans
-                let parent_name =
-                    Ident::new(format!("node_{}", parent_idx).as_str(), Span::call_site());
-
-                let parent_to_children_indices = match parent_to_children.get(&parent_idx) {
-                    Some(children) => children,
-                    None => continue,
-                };
-
-                if parent_to_children_indices.len() > 0 {
-                    for child_idx in parent_to_children_indices.iter() {
-                        let children =
-                            Ident::new(format!("node_{}", child_idx).as_str(), Span::call_site());
-
-                        let unreachable = quote_spanned!(Span::call_site() => {
-                            unreachable!("Non-elements cannot have children");
-                        });
-
-                        let push_children = quote! {
-                            if let Some(ref mut element_node) = #parent_name.as_velement_mut() {
-                                element_node.children.extend(#children.into_iter());
-                            } else {
-                                #unreachable;
-                            }
-                        };
-
-                        tokens.push(push_children);
-                    }
-                }
-            }
-        }
-
-        // Create a virtual node tree
-        let node = quote! {
-            {
-                move |bump| {
-                // __domtree_helper(move |bump| {
-                    #(#tokens)*
-                    // Root node is always named node_0
-                    node_0
-                }
-            }
-        };
-        node
-    }
-
-    /// Add more tokens to our tokens that we'll eventually return to the compiler.
-    fn push_tokens(&mut self, tokens: proc_macro2::TokenStream) {
-        self.tokens.push(tokens);
-    }
-
-    /// Set the location of the most recent start tag's ending LineColumn
-    fn set_most_recent_open_tag_end(&mut self, span: Span) {
-        self.recent_span_locations.most_recent_open_tag_end = Some(span);
-    }
-
-    /// Set the location of the most recent start tag's ending LineColumn
-    fn set_most_recent_block_start(&mut self, span: Span) {
-        self.recent_span_locations.most_recent_block_start = Some(span);
-    }
-
-    /// Determine whether or not there is any space between the end of the first
-    /// span and the beginning of the second span.
-    ///
-    /// There is space if they are on separate lines or if they have different columns.
-    ///
-    /// html! { <div>Hello</div> } <--- no space between end of div and Hello
-    ///
-    /// html! { <div> Hello</div> } <--- space between end of div and Hello
-    fn separated_by_whitespace(&self, first_span: &Span, second_span: &Span) -> bool {
-        if first_span.end().line != second_span.end().line {
-            return true;
-        }
-
-        second_span.start().column - first_span.end().column > 0
-    }
-
-    /// Create a new identifier for a VirtualNode and increment our node_idx so that next
-    /// time we call this our node will get a different name.
-    fn new_virtual_node_ident(&mut self, span: Span) -> Ident {
-        let node_name = format!("node_{}", self.current_node_idx);
-
-        let node_ident = Ident::new(node_name.as_str(), span);
-
-        // TODO: Increment before creating the new node, not after.
-        // This way the current virtual node ident won't need to do strange subtraction
-        self.current_node_idx += 1;
-
-        node_ident
-    }
-
-    /// Get the Ident for the current (last created) virtual node, without incrementing
-    /// the node index.
-    fn current_virtual_node_ident(&self, span: Span) -> Ident {
-        // TODO: Increment before creating the new node, not after.
-        // This way the current virtual node ident won't need to do strange subtraction
-        let node_name = format!("node_{}", self.current_node_idx - 1);
-
-        Ident::new(node_name.as_str(), span)
-    }
-
-    /// Generate virtual node tokens for a statement that came from in between braces
-    ///
-    /// examples:
-    ///
-    /// html! { <div> { some_var_in_braces } </div>
-    /// html! { <div> { some_other_variable } </div>
-    fn push_iterable_nodes(&mut self, nodes: NodesToPush) {
-        let node_idx = self.current_node_idx;
-
-        match nodes {
-            NodesToPush::Stmt(stmt) => {
-                let node_ident = self.new_virtual_node_ident(stmt.span());
-
-                self.push_tokens(quote! {
-                    let mut #node_ident: IterableNodes = (#stmt).into();
-                });
-            }
-            NodesToPush::TokenStream(stmt, tokens) => {
-                let node_ident = self.new_virtual_node_ident(stmt.span());
-
-                self.push_tokens(quote! {
-                    let mut #node_ident: IterableNodes = #tokens.into();
-                });
-            }
-        }
-
-        let parent_idx = *&self.parent_stack[self.parent_stack.len() - 1].0;
-
-        self.parent_to_children
-            .get_mut(&parent_idx)
-            .expect("Parent of these iterable nodes")
-            .push(node_idx);
-        self.node_order.push(node_idx);
-    }
-}
-
-/// Keep track of the locations of different kinds of tokens that we encounter.
-///
-/// This helps us determine whether or not to insert space before or after text tokens
-/// in cases such as:
-///
-/// ```ignore
-/// html! { <div> { Hello World } </div>
-/// html! { <div>{Hello World}</div>
-/// ```
-#[derive(Default)]
-struct RecentSpanLocations {
-    most_recent_open_tag_end: Option<Span>,
-    most_recent_block_start: Option<Span>,
-}
-
-fn is_self_closing(tag: &str) -> bool {
-    crate::validation::self_closing::is_self_closing(tag)
-}
-
-fn is_valid_tag(tag: &str) -> bool {
-    crate::validation::valid_tags::is_valid_tag(tag)
-}

+ 0 - 153
packages/html-macro/src/parser/open_tag.rs

@@ -1,153 +0,0 @@
-use crate::parser::{is_self_closing, is_valid_tag, HtmlParser};
-use crate::tag::Attr;
-use proc_macro2::{Ident, Span};
-use quote::{quote, quote_spanned};
-use syn::Expr;
-
-impl HtmlParser {
-    /// Parse an incoming Tag::Open
-    pub(crate) fn parse_open_tag(
-        &mut self,
-        name: &Ident,
-        closing_span: &Span,
-        attrs: &Vec<Attr>,
-        is_self_closing_tag: bool,
-    ) {
-        self.set_most_recent_open_tag_end(closing_span.clone());
-
-        let idx = &mut self.current_node_idx;
-        let parent_to_children = &mut self.parent_to_children;
-        let parent_stack = &mut self.parent_stack;
-        let tokens = &mut self.tokens;
-        let node_order = &mut self.node_order;
-
-        // The root node is named `node_0`. All of it's descendants are node_1.. node_2.. etc.
-        // This just comes from the `idx` variable
-        // TODO: Not sure what the span is supposed to be so I just picked something..
-        let var_name_node = Ident::new(format!("node_{}", idx).as_str(), name.span());
-        let html_tag = format!("{}", name);
-        let is_html_tag = is_valid_tag(&html_tag);
-
-        // TODO: Maybe this could be split up into two functions at some point, would have to pass
-        // a lot of vars around though, which isn't very nice.
-        if is_html_tag {
-            let node = quote! {
-                let mut #var_name_node = VirtualNode::element(#html_tag);
-            };
-
-            tokens.push(node);
-
-            for attr in attrs.iter() {
-                let key = format!("{}", attr.key);
-                let value = &attr.value;
-
-                match value {
-                    Expr::Closure(closure) => {
-                        // TODO: Use this to decide Box<FnMut(_, _, _, ...)
-                        // After we merge the DomUpdater
-                        let _arg_count = closure.inputs.len();
-
-                        // NOTE: Closures don't work on non wasm32 targets so we only add
-                        // events on wasm32 targets.
-                        let add_closure = quote! {
-                            #[cfg(target_arch = "wasm32")]
-                            {
-                                let closure = Closure::wrap(
-                                    Box::new(#value) as Box<FnMut(_)>
-                                );
-                                let closure_rc = std::rc::Rc::new(closure);
-                                #var_name_node.as_velement_mut().expect("Not an element")
-                                    .events.0.insert(#key.to_string(), closure_rc);
-                            }
-                        };
-
-                        tokens.push(add_closure);
-                    }
-                    _ => {
-                        let insert_attribute = quote! {
-                            #var_name_node.as_velement_mut().expect("Not an element")
-                                .attrs.insert(#key.to_string(), #value.to_string());
-                        };
-
-                        tokens.push(insert_attribute);
-                    }
-                };
-            }
-        } else if !html_tag.chars().next().unwrap().is_uppercase() {
-            let error = format!(
-                r#"{} is not a valid HTML tag.
-                
-If you are trying to use a valid HTML tag, perhaps there's a typo?
-                
-If you are trying to use a custom component, please capitalize the component name.
-                
-custom components: https://chinedufn.github.io/percy/html-macro/custom-components/index.html"#,
-                html_tag,
-            );
-            let span = name.span();
-            let invalid_tag_name_error = quote_spanned! {span=> {
-                compile_error!(#error);
-            }};
-            tokens.push(invalid_tag_name_error);
-
-            let node = quote! {
-                let mut #var_name_node = VirtualNode::text("error");
-            };
-
-            tokens.push(node);
-        } else {
-            let var_name_component = Ident::new(format!("component_{}", idx).as_str(), name.span());
-            let component_ident = Ident::new(format!("{}", html_tag).as_str(), name.span());
-
-            let component_props: Vec<proc_macro2::TokenStream> = attrs
-                .into_iter()
-                .map(|attr| {
-                    let key = Ident::new(format!("{}", attr.key).as_str(), name.span());
-                    let value = &attr.value;
-
-                    quote! {
-                        #key: #value,
-                    }
-                })
-                .collect();
-
-            // TODO @Jon - this is where we need to start injecting new logic about props/children etc
-            let node = quote! {
-                let mut #var_name_component = #component_ident { #(#component_props),* };
-                let mut #var_name_node = #var_name_component.render();
-            };
-
-            tokens.push(node);
-        }
-
-        // The first open tag that we see is our root node so we won't worry about
-        // giving it a parent
-        if *idx == 0 {
-            node_order.push(0);
-
-            if !is_self_closing(&html_tag) && !is_self_closing_tag {
-                parent_stack.push((0, name.clone()));
-            }
-
-            *idx += 1;
-            return;
-        }
-
-        let parent_idx = *&parent_stack[parent_stack.len() - 1].0;
-
-        if !is_self_closing(&html_tag) && !is_self_closing_tag {
-            parent_stack.push((*idx, name.clone()));
-        }
-
-        node_order.push(*idx);
-
-        parent_to_children
-            .get_mut(&parent_idx)
-            .expect("Parent of this node")
-            .push(*idx);
-
-        parent_to_children.insert(*idx, vec![]);
-
-        *idx += 1;
-    }
-}

+ 0 - 73
packages/html-macro/src/parser/statement.rs

@@ -1,73 +0,0 @@
-use crate::parser::{HtmlParser, NodesToPush};
-use quote::quote;
-use syn::{Expr, ExprIf, Stmt};
-
-impl HtmlParser {
-    /// Parse an incoming syn::Stmt node inside a block
-    pub(crate) fn parse_statement(&mut self, stmt: &Stmt) {
-        // Here we handle a block being a descendant within some html! call.
-        //
-        // The descendant should implement Into<IterableNodes>
-        //
-        // html { <div> { some_node } </div> }
-        match stmt {
-            Stmt::Expr(expr) => {
-                self.parse_expr(stmt, expr);
-            }
-            _ => {
-                self.push_iterable_nodes(NodesToPush::Stmt(stmt));
-            }
-        };
-    }
-
-    /// Parse an incoming syn::Expr node inside a block
-    pub(crate) fn parse_expr(&mut self, stmt: &Stmt, expr: &Expr) {
-        match expr {
-            Expr::If(expr_if) => {
-                self.expand_if(stmt, expr_if);
-            }
-            _ => {
-                self.push_iterable_nodes(NodesToPush::Stmt(stmt));
-            }
-        }
-    }
-
-    /// Expand an incoming Expr::If block
-    /// This enables us to use JSX-style conditions inside of blocks such as
-    /// the following example.
-    ///
-    /// # Examples
-    ///
-    /// ```rust,ignore
-    /// html! {
-    ///     <div>
-    ///         {if condition_is_true {
-    ///	            html! { <span>Hello World</span> }
-    ///         }}
-    ///     </div>
-    /// }
-    /// ```
-    ///
-    /// Traditionally this would be possible as an if statement in rust is an
-    /// expression, so the then, and the else block have to return matching types.
-    /// Here we identify whether the block is missing the else and fill it in with
-    /// a blank VirtualNode::text("")
-    pub(crate) fn expand_if(&mut self, stmt: &Stmt, expr_if: &ExprIf) {
-        // Has else branch, we can parse the expression as normal.
-        if let Some(_else_branch) = &expr_if.else_branch {
-            self.push_iterable_nodes(NodesToPush::Stmt(stmt));
-        } else {
-            let condition = &expr_if.cond;
-            let block = &expr_if.then_branch;
-            let tokens = quote! {
-                if #condition {
-                    #block.into()
-                } else {
-                    VirtualNode::text("")
-                }
-            };
-
-            self.push_iterable_nodes(NodesToPush::TokenStream(stmt, tokens));
-        }
-    }
-}

+ 0 - 96
packages/html-macro/src/parser/text.rs

@@ -1,96 +0,0 @@
-use crate::parser::HtmlParser;
-use crate::tag::{Tag, TagKind};
-use proc_macro2::{Ident, Span};
-use quote::quote;
-
-impl HtmlParser {
-    /// Parse an incoming Tag::Text text node
-    pub(crate) fn parse_text(
-        &mut self,
-        text: &str,
-        text_start: Span,
-        text_end: Span,
-        next_tag: Option<&Tag>,
-    ) {
-        let mut text = text.to_string();
-
-        if self.should_insert_space_before_text(&text_start) {
-            text = " ".to_string() + &text;
-        }
-
-        let should_insert_space_after_text = match next_tag {
-            Some(Tag::Close {
-                first_angle_bracket_span,
-                ..
-            }) => self.separated_by_whitespace(&text_end, first_angle_bracket_span),
-            Some(Tag::Braced { brace_span, .. }) => {
-                self.separated_by_whitespace(&text_end, brace_span)
-            }
-            Some(Tag::Open {
-                open_bracket_span, ..
-            }) => self.separated_by_whitespace(&text_end, open_bracket_span),
-            _ => false,
-        };
-        if should_insert_space_after_text {
-            text += " ";
-        }
-
-        let idx = &mut self.current_node_idx;
-        let parent_to_children = &mut self.parent_to_children;
-        let parent_stack = &mut self.parent_stack;
-        let tokens = &mut self.tokens;
-        let node_order = &mut self.node_order;
-
-        if *idx == 0 {
-            node_order.push(0);
-            // TODO: This is just a consequence of bad code. We're pushing this to make
-            // things work but in reality a text node isn't a parent ever.
-            // Just need to make the code DRY / refactor so that we can make things make
-            // sense vs. just bolting things together.
-            parent_stack.push((0, Ident::new("unused", Span::call_site())));
-        }
-
-        let var_name = Ident::new(format!("node_{}", idx).as_str(), Span::call_site());
-
-        let text_node = quote! {
-            let mut #var_name = VirtualNode::text(#text);
-        };
-
-        tokens.push(text_node);
-
-        if *idx == 0 {
-            *idx += 1;
-            return;
-        }
-
-        let parent_idx = &parent_stack[parent_stack.len() - 1];
-
-        node_order.push(*idx);
-
-        parent_to_children
-            .get_mut(&parent_idx.0)
-            .expect("Parent of this text node")
-            .push(*idx);
-
-        *idx += 1;
-    }
-
-    /// If the last TagKind was a block or an open tag we check to see if there is space
-    /// between this text and that tag. If so we insert some space before this text.
-    fn should_insert_space_before_text(&self, text_start: &Span) -> bool {
-        if self.last_tag_kind == Some(TagKind::Braced) {
-            let most_recent_block_start = self.recent_span_locations.most_recent_block_start;
-            let most_recent_block_start = most_recent_block_start.as_ref().unwrap();
-
-            self.separated_by_whitespace(most_recent_block_start, text_start)
-        } else if self.last_tag_kind == Some(TagKind::Open) {
-            let most_recent_open_tag_end =
-                self.recent_span_locations.most_recent_open_tag_end.as_ref();
-            let most_recent_open_tag_end = most_recent_open_tag_end.unwrap();
-
-            self.separated_by_whitespace(most_recent_open_tag_end, text_start)
-        } else {
-            false
-        }
-    }
-}

+ 0 - 310
packages/html-macro/src/tag.rs

@@ -1,310 +0,0 @@
-use proc_macro2::{Span, TokenStream, TokenTree};
-use syn::parse::{Parse, ParseStream, Result};
-use syn::spanned::Spanned;
-use syn::token::Brace;
-use syn::{braced, Block, Expr, Ident, Token};
-
-/// The different kinds of tokens that we parse.
-///
-/// TODO: A better name than tag since not all of these are tags
-#[derive(Debug)]
-pub enum Tag {
-    /// <div id="app" class=*CSS>
-    /// <br />
-    Open {
-        name: Ident,
-        attrs: Vec<Attr>,
-        open_bracket_span: Span,
-        closing_bracket_span: Span,
-        is_self_closing: bool,
-    },
-    /// </div>
-    Close {
-        name: Ident,
-        first_angle_bracket_span: Span,
-    },
-    /// html! { <div> Hello World </div> }
-    ///
-    ///  -> Hello world
-    ///
-    /// start_span -> the span for the first token within the text
-    /// end_span -> the span for the last token within the text
-    Text {
-        text: String,
-        start_span: Option<Span>,
-        end_span: Option<Span>,
-    },
-    /// let text_var = VirtualNode::text("3");
-    ///
-    /// let iter_nodes =
-    ///   vec![
-    ///     html!{ <div></div> },
-    ///     html! {<span> </span>}
-    ///   ];
-    ///
-    /// html! {
-    ///   <div>
-    ///     Here are some examples of blocks
-    ///     { text_var }
-    ///     { iter_nodes }
-    ///     { html! { <div> </div> }
-    ///   </div>
-    /// }
-    Braced { block: Box<Block>, brace_span: Span },
-}
-
-/// The different kinds of tokens that we parse.
-///
-/// TODO: A better name than tag since not all of these are tags
-#[derive(Debug, Eq, PartialEq)]
-pub enum TagKind {
-    Open,
-    Close,
-    Text,
-    Braced,
-}
-
-/// id="my-id"
-/// class="some classes"
-/// etc...
-#[derive(Debug)]
-pub struct Attr {
-    pub key: Ident,
-    pub value: Expr,
-}
-
-impl Parse for Tag {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let mut input = input;
-
-        // If it starts with a `<` it's either an open or close tag.
-        //   ex: <div>
-        //   ex: </em>
-        if input.peek(Token![<]) {
-            let first_angle_bracket_span = input.parse::<Token![<]>()?;
-            let first_angle_bracket_span = first_angle_bracket_span.span();
-
-            let optional_close: Option<Token![/]> = input.parse()?;
-            let is_open_tag = optional_close.is_none();
-
-            if is_open_tag {
-                return parse_open_tag(&mut input, first_angle_bracket_span);
-            } else {
-                return parse_close_tag(&mut input, first_angle_bracket_span);
-            }
-        }
-
-        // { node_inside_block }
-        if input.peek(Brace) {
-            return parse_block(&mut input);
-        }
-
-        return parse_text_node(&mut input);
-    }
-}
-
-/// `<div id="app" class=*CSS>`
-fn parse_open_tag(input: &mut ParseStream, open_bracket_span: Span) -> Result<Tag> {
-    let name: Ident = input.parse()?;
-
-    let attrs = parse_attributes(input)?;
-
-    let is_self_closing: Option<Token![/]> = input.parse()?;
-    let is_self_closing = is_self_closing.is_some();
-
-    let closing_bracket = input.parse::<Token![>]>()?;
-    let closing_bracket_span = closing_bracket.span();
-
-    Ok(Tag::Open {
-        name,
-        attrs,
-        open_bracket_span,
-        closing_bracket_span,
-        is_self_closing,
-    })
-}
-
-/// Parse the attributes starting from something like:
-///     id="app" class=*CSS>
-///
-/// As soon as we see
-///     >
-/// We know that the element has no more attributes and our loop will end
-fn parse_attributes(input: &mut ParseStream) -> Result<Vec<Attr>> {
-    let mut attrs = Vec::new();
-
-    // Do we see an identifier such as `id`? If so proceed
-    while input.peek(Ident)
-        || input.peek(Token![async])
-        || input.peek(Token![for])
-        || input.peek(Token![loop])
-        || input.peek(Token![type])
-    {
-        // <link rel="stylesheet" type="text/css"
-        //   .. async, for, loop, type need to be handled specially since they are keywords
-        let maybe_async_key: Option<Token![async]> = input.parse()?;
-        let maybe_for_key: Option<Token![for]> = input.parse()?;
-        let maybe_loop_key: Option<Token![loop]> = input.parse()?;
-        let maybe_type_key: Option<Token![type]> = input.parse()?;
-
-        let key = if maybe_async_key.is_some() {
-            Ident::new("async", maybe_async_key.unwrap().span())
-        } else if maybe_for_key.is_some() {
-            Ident::new("for", maybe_for_key.unwrap().span())
-        } else if maybe_loop_key.is_some() {
-            Ident::new("loop", maybe_loop_key.unwrap().span())
-        } else if maybe_type_key.is_some() {
-            Ident::new("type", maybe_type_key.unwrap().span())
-        } else {
-            input.parse()?
-        };
-
-        // =
-        input.parse::<Token![=]>()?;
-
-        // Continue parsing tokens until we see the next attribute or a closing > tag
-        let mut value_tokens = TokenStream::new();
-
-        loop {
-            let tt: TokenTree = input.parse()?;
-            value_tokens.extend(Some(tt));
-
-            let has_attrib_key = input.peek(Ident)
-                || input.peek(Token![async])
-                || input.peek(Token![for])
-                || input.peek(Token![loop])
-                || input.peek(Token![type]);
-            let peek_start_of_next_attr = has_attrib_key && input.peek2(Token![=]);
-
-            let peek_end_of_tag = input.peek(Token![>]);
-
-            let peek_self_closing = input.peek(Token![/]);
-
-            if peek_end_of_tag || peek_start_of_next_attr || peek_self_closing {
-                break;
-            }
-        }
-
-        let value: Expr = syn::parse2(value_tokens)?;
-
-        attrs.push(Attr { key, value });
-    }
-
-    Ok(attrs)
-}
-
-/// </div>
-fn parse_close_tag(input: &mut ParseStream, first_angle_bracket_span: Span) -> Result<Tag> {
-    let name: Ident = input.parse()?;
-
-    input.parse::<Token![>]>()?;
-
-    Ok(Tag::Close {
-        name,
-        first_angle_bracket_span,
-    })
-}
-
-fn parse_block(input: &mut ParseStream) -> Result<Tag> {
-    let content;
-    let brace_token = braced!(content in input);
-
-    let brace_span = brace_token.span;
-
-    let block_expr = content.call(Block::parse_within)?;
-
-    let block = Box::new(Block {
-        brace_token,
-        stmts: block_expr,
-    });
-
-    Ok(Tag::Braced { block, brace_span })
-}
-
-/// Parse a sequence of tokens until we run into a closing tag
-///   html! { <div> Hello World </div> }
-/// or a brace
-///   html! { <div> Hello World { Braced } </div>
-///
-/// So, in the second case, there would be two VText nodes created. "Hello World" and "Braced".
-///
-/// Later in parser/text.rs we'll look at how close the VText nodes are to their neighboring tags
-/// to determine whether or not to insert spacing.
-///
-/// So, in the examples above, since the opening "<div>" has a space after it we'll later transform
-/// "Hello World" into " Hello World" in parser/tag.rs
-fn parse_text_node(input: &mut ParseStream) -> Result<Tag> {
-    // Continue parsing tokens until we see a closing tag <
-    let _text_tokens = TokenStream::new();
-
-    let mut text = "".to_string();
-
-    let mut idx = 0;
-
-    let mut start_span = None;
-
-    let mut most_recent_span: Option<Span> = None;
-
-    loop {
-        if input.is_empty() {
-            break;
-        }
-
-        let tt: TokenTree = input.parse()?;
-
-        if idx == 0 {
-            start_span = Some(tt.span());
-            most_recent_span = Some(tt.span());
-        }
-
-        // TODO: Properly handle whitespace and new lines
-        // https://github.com/chinedufn/percy/pull/97#discussion_r263039215
-        if idx != 0 {
-            if let Some(most_recent_span) = most_recent_span {
-                let current_span_start = tt.span().start();
-                let most_recent_span_end = most_recent_span.end();
-
-                let spans_on_different_lines = current_span_start.line != most_recent_span_end.line;
-
-                // Contraptions such as "Aren't" give the "'" and the "t" the
-                // same span, even though they get parsed separately when calling
-                // input.parse::<TokenTree>().
-                // As in - it takes two input.parse calls to get the "'" and "t",
-                // even though they have the same span.
-                // This might be a bug in syn - but regardless we address this by
-                // not inserting a space in this case.
-                let span_comes_before_previous_span = current_span_start.column
-                    < most_recent_span_end.column
-                    && !spans_on_different_lines;
-
-                // Spans are on different lines, insert space
-                if spans_on_different_lines {
-                    text += " ";
-                } else if !span_comes_before_previous_span
-                    && current_span_start.column - most_recent_span_end.column > 0
-                {
-                    text += " ";
-                }
-            }
-        }
-
-        text += &tt.to_string();
-
-        most_recent_span = Some(tt.span());
-
-        let peek_closing_tag = input.peek(Token![<]);
-        let peek_start_block = input.peek(Brace);
-
-        if peek_closing_tag || peek_start_block {
-            break;
-        }
-
-        idx += 1;
-    }
-
-    Ok(Tag::Text {
-        text,
-        start_span,
-        end_span: most_recent_span,
-    })
-}

+ 0 - 36
packages/html-macro/src/validation/mod.rs

@@ -1,36 +0,0 @@
-//! The html-validation crate provides method that can be used when validating html elements
-//! and attributes.
-//!
-//! The original goal of this crate was to be used as a dependency in procedural macros that
-//! validate html at compile time, but it is general purpose and can be used in other problem
-//! spaces.
-//!
-//! ## Potential Strategy - Pessimistic Validation
-//!
-//! We might make the html-validation crate is pessimistic by nature.
-//!
-//! This means that as we develop the create we'll blacklist more and more things - but in general
-//! we default to not saying that something is invalid until we've specifically encoded that it is
-//! not allowed.
-//!
-//! This means that you'll see methods with names like `is_definitely_invalid_child` - hinting
-//! that we're telling you that we're certain that the relationship is not allowed.
-//!
-//! Over time we'll cover more and more cases and this should become a non issue, but at the
-//! beginning it will mean that our validation is less strict than it should really be.
-//!
-//! The reason behind this strategy is that it lets people get up and running from day one without
-//! needing to wait until our validation is perfect.
-//! A downside is that as we become more and more strict there might be situations where you have
-//! to go back and tweak your html if you had something that we are now calling invalid.
-//!
-//! ## Potential Strategy - Optimistic Validation
-//!
-//! In this case we'd make html! generate a compile time error for anything that isn't certainly valid.
-//! Then there would be a second macro such as html_unstrict! that would be a bit more permissive.
-//!
-//! Over time as our validation permitted more cases people could use html! more and more instead of html_loose!
-
-pub mod self_closing;
-pub mod svg_namespace;
-pub mod valid_tags;

+ 0 - 31
packages/html-macro/src/validation/self_closing.rs

@@ -1,31 +0,0 @@
-use lazy_static::lazy_static;
-use std::collections::hash_set::HashSet;
-
-use super::svg_namespace::is_self_closing_svg_tag;
-
-// Used to uniquely identify elements that contain closures so that the DomUpdater can
-// look them up by their unique id.
-// When the DomUpdater sees that the element no longer exists it will drop all of it's
-// Rc'd Closures for those events.
-lazy_static! {
-    static ref SELF_CLOSING_TAGS: HashSet<&'static str> = [
-        "area", "base", "br", "col", "hr", "img", "input", "link", "meta", "param", "command",
-        "keygen", "source",
-    ]
-    .iter()
-    .cloned()
-    .collect();
-}
-
-/// Whether or not this tag is self closing
-///
-/// ```
-/// use html_validation::is_self_closing;
-///
-/// assert_eq!(is_self_closing("br"), true);
-///
-/// assert_eq!(is_self_closing("div"), false);
-/// ```
-pub fn is_self_closing(tag: &str) -> bool {
-    SELF_CLOSING_TAGS.contains(tag) || is_self_closing_svg_tag(tag)
-}

+ 0 - 114
packages/html-macro/src/validation/svg_namespace.rs

@@ -1,114 +0,0 @@
-use lazy_static::lazy_static;
-use std::collections::HashMap;
-
-lazy_static! {
-    // list of svg elements
-    //  https://developer.mozilla.org/en-US/docs/Web/SVG/Element
-    //  a hashmap of `(tag, is_self_closing)`
-    static ref SVG_NAMESPACED_TAGS: HashMap<&'static str, bool> = [
-        // TODO: can cause conflict with html `a`
-        //("a", true),
-        ("animate", true),
-        ("animateMotion", false),
-        ("animateTransform", true),
-        ("circle", true),
-        ("clipPath",false),
-        // TODO: blocked with [issue](https://github.com/chinedufn/percy/issues/106)
-        //("color-profile",),
-        ("defs", false),
-        ("desc", false),
-        ("discard", true),
-        ("ellipse",true),
-        ("feBlend", true),
-        ("feColorMatrix", true),
-        ("feComponentTransfer", false),
-        ("feComposite", true),
-        ("feConvolveMatrix", true),
-        ("feDiffuseLighting", false),
-        ("feDisplacementMap", true),
-        ("feDistantLight", true),
-        ("feDropShadow", true),
-        ("feFlood", true),
-        ("feFuncA", true),
-        ("feFuncB", true),
-        ("feFuncG", true),
-        ("feFuncR", true),
-        ("feGaussianBlur", true),
-        ("feImage", true),
-        ("feMerge", false),
-        ("feMergeNode", true),
-        ("feMorphology", true),
-        ("feOffset", true),
-        ("fePointLight", true),
-        ("feSpecularLighting", false),
-        ("feSpotLight", true),
-        ("feTile", true),
-        ("feTurbulence", true),
-        ("filter", false),
-        ("foreignObject", false),
-        ("g",false),
-        ("hatch", false),
-        ("hatchpath", true),
-        ("image", true),
-        ("line", true),
-        ("linearGradient", false),
-        ("marker", false),
-        ("mask", false),
-        // TODO: undocumented
-        //("mesh",),
-        // TODO: undocumented
-        //("meshgradient",),
-        // TODO: undocumented
-        //("meshpatch",),
-        // TODO: undocumented
-        //("meshrow",),
-        ("metadata", false),
-        ("mpath", true),
-        ("path", true),
-        ("pattern", false),
-        ("polygon", true),
-        ("polyline", true),
-        ("radialGradient", false),
-        ("rect", true),
-        // TODO: can cause conflict with html `script` tag
-        //("script", false),
-        ("set", true),
-        ("solidcolor", true),
-        ("stop", true),
-        // TODO: can cause conflict with html `style` tag
-        //("style", false),
-        ("svg", false),
-        ("switch", false),
-        ("symbol", false),
-        ("text", false),
-        ("textPath", false),
-        // TODO: can cause conflict with html `title` tag
-        //("title", false),
-        ("tspan", false),
-        // TODO: undocumented
-        //("unknown",),
-        ("use", true),
-        ("view", true),
-    ]
-    .iter()
-    .cloned()
-    .collect();
-}
-/// Whether or not this tag is part svg elements
-/// ```
-/// use html_validation::is_svg_namespace;
-///
-/// assert_eq!(is_svg_namespace("svg"), true);
-///
-/// assert_eq!(is_svg_namespace("circle"), true);
-///
-/// assert_eq!(is_svg_namespace("div"), false);
-/// ```
-pub fn is_svg_namespace(tag: &str) -> bool {
-    SVG_NAMESPACED_TAGS.contains_key(tag)
-}
-
-/// Whether or not this svg tag is self closing
-pub(crate) fn is_self_closing_svg_tag(tag: &str) -> bool {
-    SVG_NAMESPACED_TAGS.get(tag).map(|v| *v).unwrap_or(false)
-}

+ 0 - 138
packages/html-macro/src/validation/valid_tags.rs

@@ -1,138 +0,0 @@
-use lazy_static::lazy_static;
-use std::collections::hash_set::HashSet;
-
-use super::svg_namespace::is_svg_namespace;
-
-lazy_static! {
-    static ref VALID_TAGS: HashSet<&'static str> = [
-        "a",
-        "abbr",
-        "address",
-        "area",
-        "article",
-        "aside",
-        "audio",
-        "b",
-        "base",
-        "bdi",
-        "bdo",
-        "big",
-        "blockquote",
-        "body",
-        "br",
-        "button",
-        "canvas",
-        "caption",
-        "cite",
-        "code",
-        "col",
-        "colgroup",
-        "command",
-        "data",
-        "datalist",
-        "dd",
-        "del",
-        "details",
-        "dfn",
-        "dialog",
-        "div",
-        "dl",
-        "dt",
-        "em",
-        "embed",
-        "fieldset",
-        "figcaption",
-        "figure",
-        "footer",
-        "form",
-        "h1",
-        "h2",
-        "h3",
-        "h4",
-        "h5",
-        "h6",
-        "head",
-        "header",
-        "hr",
-        "html",
-        "i",
-        "iframe",
-        "img",
-        "input",
-        "ins",
-        "kbd",
-        "keygen",
-        "label",
-        "legend",
-        "li",
-        "link",
-        "main",
-        "map",
-        "mark",
-        "menu",
-        "menuitem",
-        "meta",
-        "meter",
-        "nav",
-        "noscript",
-        "object",
-        "ol",
-        "optgroup",
-        "option",
-        "output",
-        "p",
-        "param",
-        "picture",
-        "pre",
-        "progress",
-        "q",
-        "rp",
-        "rt",
-        "ruby",
-        "s",
-        "samp",
-        "script",
-        "section",
-        "select",
-        "small",
-        "source",
-        "span",
-        "strong",
-        "style",
-        "sub",
-        "summary",
-        "sup",
-        "table",
-        "tbody",
-        "td",
-        "textarea",
-        "tfoot",
-        "th",
-        "thead",
-        "time",
-        "title",
-        "tr",
-        "track",
-        "u",
-        "ul",
-        "var",
-        "video",
-        "wbr",
-    ]
-    .iter()
-    .cloned()
-    .collect();
-}
-
-/// Whether or not this tag is valid
-///
-/// ```
-/// use html_validation::is_valid_tag;
-///
-/// assert_eq!(is_valid_tag("br"), true);
-///
-/// assert_eq!(is_valid_tag("random"), false);
-/// ```
-pub fn is_valid_tag(tag: &str) -> bool {
-    VALID_TAGS.contains(tag) || is_svg_namespace(tag)
-}

+ 4 - 1
packages/web/Cargo.toml

@@ -14,7 +14,7 @@ wasm-bindgen = "0.2.71"
 # wasm-bindgen = "0.2.70"
 lazy_static = "1.4.0"
 wasm-bindgen-futures = "0.4.20"
-futures = "0.3.12"
+# futures = "0.3.12"
 wasm-logger = "0.2.0"
 log = "0.4.14"
 fxhash = "0.2.1"
@@ -25,6 +25,9 @@ wasm-bindgen-test = "0.3.21"
 once_cell = "1.7.2"
 # html-validation = { path = "../html-validation", version = "0.1.1" }
 
+async-channel = "1.6.1"
+# futures-lite = "1.11.3"
+
 [dependencies.web-sys]
 version = "0.3"
 features = [

+ 38 - 30
packages/web/examples/deep.rs

@@ -1,17 +1,19 @@
-use dioxus_web::{WebsysRenderer, dioxus::prelude::*};
+use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
 
 fn main() {
     wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
     console_error_panic_hook::set_once();
 
-    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(CustomA))    
+    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(CustomA))
 }
 
+use components::CustomB;
 
 fn CustomA<'a>(ctx: Context<'a>, props: &'a ()) -> DomTree {
     let (val, set_val) = use_state(&ctx, || "abcdef".to_string());
-    ctx.render(rsx!{
+    ctx.render(rsx! {
         div {
+            class: "m-8"
             "CustomA {val}"
             button {
                 "Upper"
@@ -29,35 +31,41 @@ fn CustomA<'a>(ctx: Context<'a>, props: &'a ()) -> DomTree {
 }
 
 
-#[derive(Debug, Props, PartialEq)]
-struct PropsB<'src> {
-    val: &'src str
-}
+mod components {
+    use super::*;
 
-fn CustomB<'a>(ctx: Context<'a>, props: &'a PropsB<'a>) -> DomTree {
-    let (first, last) = props.val.split_at(3);
-    ctx.render(rsx!{
-        div {
-            "CustomB {props.val}"
-            CustomC {
-                val: first
-            }
-            CustomC {
-                val: last
+    #[derive(Debug, Props, PartialEq)]
+    pub struct PropsB<'src> {
+        val: &'src str,
+    }
+
+    pub fn CustomB<'a>(ctx: Context<'a>, props: &'a PropsB<'a>) -> DomTree {
+        let (first, last) = props.val.split_at(3);
+        ctx.render(rsx! {
+            div {
+                class: "m-8"
+                "CustomB {props.val}"
+                CustomC {
+                    val: first
+                }
+                CustomC {
+                    val: last
+                }
             }
-        }
-    })
-}
+        })
+    }
 
-#[derive(Debug, Props, PartialEq)]
-struct PropsC<'src> {
-    val: &'src str
-}
+    #[derive(Debug, Props, PartialEq)]
+    struct PropsC<'src> {
+        val: &'src str,
+    }
 
-fn CustomC<'a>(ctx: Context<'a>, props: &'a PropsC<'a>) -> DomTree {
-    ctx.render(rsx!{
-        div {
-            "CustomC {props.val}"
-        }
-    })
+    fn CustomC<'a>(ctx: Context<'a>, props: &'a PropsC<'a>) -> DomTree {
+        ctx.render(rsx! {
+            div {
+                class: "m-8"
+                "CustomC {props.val}"
+            }
+        })
+    }
 }

+ 9 - 6
packages/web/examples/rsxt.rs

@@ -8,7 +8,8 @@ fn main() {
     console_error_panic_hook::set_once();
     
     wasm_bindgen_futures::spawn_local(async {
-        WebsysRenderer::new_with_props(Example, ExampleProps { initial_name: "..?", blarg: Vec::new()})
+        let props = ExampleProps { initial_name: "..?", blarg: vec!["abc".to_string(), "abc".to_string()]};
+        WebsysRenderer::new_with_props(Example, props)
             .run()
             .await
             .unwrap()
@@ -38,17 +39,19 @@ static Example: FC<ExampleProps> = |ctx, props| {
                 "Hello, {name}"
             }
             
-            CustomButton { name: sub, set_name: Box::new(move || set_name("jack")) }
-            // CustomButton { name: "Jack!", set_name: Box::new(move || set_name("jack")) }
-            // CustomButton { name: "Jill!", set_name: Box::new(move || set_name("jill")) }
-            // CustomButton { name: "Reset!", set_name: Box::new(move || set_name(props.initial_name)) }
+            // CustomButton { name: sub, set_name: Box::new(move || set_name("jack")) }
+            CustomButton { name: "Jack!", set_name: Box::new(move || set_name("jack")) }
+            CustomButton { name: "Jill!", set_name: Box::new(move || set_name("jill")) }
+            CustomButton { name: "Bill!", set_name: Box::new(move || set_name("Bill")) }
+            CustomButton { name: "Reset!", set_name: Box::new(move || set_name(props.initial_name)) }
+
         }
     })
 };
 
 #[derive(Props)]
 struct ButtonProps<'src> {
-    name: &'src String,
+    name: &'src str,
     // name: &'src str,
     set_name: Box< dyn Fn() + 'src>
 }

+ 13 - 3
packages/web/src/interpreter.rs

@@ -1,4 +1,4 @@
-use std::{borrow::Borrow, convert::TryInto, fmt::Debug, sync::Arc};
+use std::{borrow::Borrow, convert::TryInto, default, fmt::Debug, sync::Arc};
 
 use dioxus_core::{
     events::{EventTrigger, VirtualEvent},
@@ -27,6 +27,8 @@ impl std::fmt::Debug for RootCallback {
 pub(crate) struct PatchMachine {
     pub(crate) stack: Stack,
 
+    pub(crate) known_roots: FxHashMap<u32, Node>,
+
     pub(crate) root: Element,
 
     pub(crate) temporaries: FxHashMap<u32, Node>,
@@ -165,6 +167,7 @@ impl PatchMachine {
         let events = EventDelegater::new(root.clone(), event_callback);
 
         Self {
+            known_roots: Default::default(),
             root,
             events,
             stack: Stack::with_capacity(20),
@@ -476,8 +479,15 @@ impl PatchMachine {
                     el.set_class_name(class_name);
                 }
             }
-            Edit::TraverseToKnown { node } => {}
-            Edit::MakeKnown { node } => {}
+            Edit::MakeKnown { node } => {
+                let domnode = self.stack.top();
+                self.known_roots.insert(node, domnode.clone());
+            }
+            Edit::TraverseToKnown { node } => {
+                let domnode = self.known_roots.get(&node).expect("Failed to pop know root");
+                self.stack.push(domnode.clone());
+
+            }
             Edit::RemoveKnown => {}
         }
     }

+ 3 - 3
packages/web/src/lib.rs

@@ -9,7 +9,7 @@ use web_sys::{window, Document, Element, Event, Node};
 use dioxus::virtual_dom::VirtualDom;
 pub use dioxus_core as dioxus;
 use dioxus_core::{events::EventTrigger, prelude::FC};
-use futures::{channel::mpsc, SinkExt, StreamExt};
+// use futures::{channel::mpsc, SinkExt, StreamExt};
 
 pub mod interpreter;
 
@@ -58,7 +58,7 @@ impl WebsysRenderer {
     }
 
     pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
-        let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
+        let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
 
         let body_element = prepare_websys_dom();
 
@@ -85,7 +85,7 @@ impl WebsysRenderer {
         // Event loop waits for the receiver to finish up
         // TODO! Connect the sender to the virtual dom's suspense system
         // Suspense is basically an external event that can force renders to specific nodes
-        while let Some(event) = receiver.next().await {
+        while let Ok(event) = receiver.recv().await {
             // log::debug!("patch stack size before {:#?}", patch_machine.stack);
             // patch_machine.reset();
             // patch_machine.stack.push(root_node.clone());