فهرست منبع

WIP: remove FC

Jonathan Kelley 4 سال پیش
والد
کامیت
92d9521

+ 1 - 1
Cargo.toml

@@ -4,7 +4,7 @@ members = [
     "packages/dioxus",
     "packages/core-macro",
     "packages/core",
-    "packages/web",
+    # "packages/web",
     # "packages/router",
     # "packages/ssr",
     # "packages/webview",

+ 1 - 0
README.md

@@ -11,6 +11,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-pla
 
 
 ```rust
+#[fc]
 static Example: FC<()> = |ctx, props| {
     let (selection, set_selection) = use_state(&ctx, || "...?");
 

+ 1 - 0
packages/core-macro/Cargo.toml

@@ -12,6 +12,7 @@ description = "Core macro for Dioxus Virtual DOM"
 proc-macro = true
 
 [dependencies]
+once_cell = "1.7.2"
 proc-macro-hack = "0.5.19"
 proc-macro2 = "1.0.6"
 quote = "1.0"

+ 3 - 0
packages/core-macro/examples/fc.rs

@@ -0,0 +1,3 @@
+use dioxus_core_macro::fc;
+
+fn main() {}

+ 122 - 105
packages/core-macro/src/fc.rs

@@ -9,80 +9,13 @@ use syn::{
     parse_macro_input, Attribute, Block, FnArg, Ident, Item, ItemFn, ReturnType, Type, Visibility,
 };
 
-pub fn function_component_impl(
-    // name: FunctionComponentName,
-    component: FunctionComponent,
-) -> syn::Result<TokenStream> {
-    // let FunctionComponentName { component_name } = name;
-
-    let FunctionComponent {
-        block,
-        props_type,
-        arg,
-        vis,
-        attrs,
-        name: function_name,
-        return_type,
-    } = component;
-
-    // if function_name == component_name {
-    //     return Err(syn::Error::new_spanned(
-    //         component_name,
-    //         "the component must not have the same name as the function",
-    //     ));
-    // }
-
-    let quoted = quote! {
-        #[doc(hidden)]
-        #[allow(non_camel_case_types)]
-        mod __component_blah {
-            use super::*;
-
-            #[derive(PartialEq)]
-            pub struct Props<'a> {
-                name: &'a str
-            }
-
-            pub fn component<'a>(ctx: &'a Context<'a, Props>) -> VNode<'a> {
-                // Destructure the props into the parent scope
-                // todo: handle expansion of lifetimes
-                let Props {
-                    name
-                } = ctx.props;
-
-                #block
-            }
-        }
-        #[allow(non_snake_case)]
-        pub use __component_blah::component as #function_name;
-    };
-    // let quoted = quote! {
-    //     #[doc(hidden)]
-    //     #[allow(non_camel_case_types)]
-    //     #vis struct #function_name;
-
-    //     impl ::yew_functional::FunctionProvider for #function_name {
-    //         type TProps = #props_type;
-
-    //         fn run(#arg) -> #ret_type {
-    //             #block
-    //         }
-    //     }
-
-    //     #(#attrs)*
-    //     #vis type #component_name = ::yew_functional::FunctionComponent<#function_name>;
-    // };
-    Ok(quoted)
-}
-
 /// A parsed version of the user's input
 pub struct FunctionComponent {
     // The actual contents of the function
     block: Box<Block>,
 
-    // The user's props type
-    props_type: Box<Type>,
-
+    // // The user's props type
+    // props_type: Box<Type>,
     arg: FnArg,
     vis: Visibility,
     attrs: Vec<Attribute>,
@@ -117,7 +50,7 @@ impl Parse for FunctionComponent {
             .unwrap_or_else(|| syn::parse_quote! { _: &() });
 
         // Extract the "context" object
-        let props_type = validate_context_arg(&first_arg)?;
+        // let props_type = validate_context_arg(&first_arg)?;
 
         /*
         Extract the rest of the function arguments into a struct body
@@ -159,7 +92,7 @@ impl Parse for FunctionComponent {
         let name = sig.ident;
 
         Ok(Self {
-            props_type,
+            // props_type,
             block,
             arg: first_arg,
             vis,
@@ -169,6 +102,89 @@ impl Parse for FunctionComponent {
         })
     }
 }
+impl ToTokens for FunctionComponent {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        // let FunctionComponentName { component_name } = name;
+
+        let FunctionComponent {
+            block,
+            // props_type,
+            arg,
+            vis,
+            attrs,
+            name: function_name,
+            return_type,
+        } = self;
+
+        // if function_name == component_name {
+        //     return Err(syn::Error::new_spanned(
+        //         component_name,
+        //         "the component must not have the same name as the function",
+        //     ));
+        // }
+
+        let quoted = quote! {
+
+
+            #[doc(hidden)]
+            #[allow(non_camel_case_types)]
+            #[derive(PartialEq)]
+            pub struct #function_name<'a> {
+                // and some other attrs
+                ___p: std::marker::PhantomData<&'a ()>
+            }
+
+            impl<'a> FC for #function_name<'a> {
+                fn render(ctx: Context<'_>, props: &#function_name<'a>) -> DomTree {
+                    let #function_name {
+                        ..
+                    } = props;
+
+                    #block
+                }
+            }
+
+            // mod __component_blah {
+            // use super::*;
+
+            // #[derive(PartialEq)]
+            // pub struct Props<'a> {
+            //     name: &'a str
+            // }
+
+            // pub fn component<'a>(ctx: &'a Context<'a, Props>) -> VNode<'a> {
+            //     // Destructure the props into the parent scope
+            //     // todo: handle expansion of lifetimes
+            //     let Props {
+            //         name
+            //     } = ctx.props;
+
+            //     #block
+            // }
+            // }
+            // #[allow(non_snake_case)]
+            // pub use __component_blah::component as #function_name;
+        };
+
+        quoted.to_tokens(tokens);
+        // let quoted = quote! {
+        //     #[doc(hidden)]
+        //     #[allow(non_camel_case_types)]
+        //     #vis struct #function_name;
+
+        //     impl ::yew_functional::FunctionProvider for #function_name {
+        //         type TProps = #props_type;
+
+        //         fn run(#arg) -> #ret_type {
+        //             #block
+        //         }
+        //     }
+
+        //     #(#attrs)*
+        //     #vis type #component_name = ::yew_functional::FunctionComponent<#function_name>;
+        // };
+    }
+}
 
 /// Ensure the user's input is actually a functional component
 pub fn ensure_fn_block(item: Item) -> syn::Result<ItemFn> {
@@ -228,7 +244,7 @@ pub fn ensure_return_type(output: ReturnType) -> syn::Result<Box<Type>> {
     match output {
         ReturnType::Default => Err(syn::Error::new_spanned(
             output,
-            "function components must return `dioxus::VNode`",
+            "function components must return a `DomTree`",
         )),
         ReturnType::Type(_, ty) => Ok(ty),
     }
@@ -268,39 +284,40 @@ pub fn validate_signature(sig: Signature) -> syn::Result<Signature> {
     Ok(sig)
 }
 
-pub fn validate_context_arg(first_arg: &FnArg) -> syn::Result<Box<Type>> {
-    if let FnArg::Typed(arg) = first_arg {
-        // Input arg is a reference to an &mut Context
-        if let Type::Reference(ty) = &*arg.ty {
-            if ty.lifetime.is_some() {
-                return Err(syn::Error::new_spanned(
-                    &ty.lifetime,
-                    "reference must not have a lifetime",
-                ));
-            }
-
-            if ty.mutability.is_some() {
-                return Err(syn::Error::new_spanned(
-                    &ty.mutability,
-                    "reference must not be mutable",
-                ));
-            }
-
-            Ok(ty.elem.clone())
-        } else {
-            let msg = format!(
-                "expected a reference to a `Context` object (try: `&mut {}`)",
-                arg.ty.to_token_stream()
-            );
-            return Err(syn::Error::new_spanned(arg.ty.clone(), msg));
-        }
-    } else {
-        return Err(syn::Error::new_spanned(
-            first_arg,
-            "function components can't accept a receiver",
-        ));
-    }
-}
+// pub fn validate_context_arg(first_arg: &FnArg) -> syn::Result<Box<Type>> {
+//     if let FnArg::Typed(arg) = first_arg {
+//         // if let Type::R
+//         // Input arg is a reference to an &mut Context
+//         // if let Type::Reference(ty) = &*arg.ty {
+//         //     if ty.lifetime.is_some() {
+//         //         return Err(syn::Error::new_spanned(
+//         //             &ty.lifetime,
+//         //             "reference must not have a lifetime",
+//         //         ));
+//         //     }
+
+//         //     if ty.mutability.is_some() {
+//         //         return Err(syn::Error::new_spanned(
+//         //             &ty.mutability,
+//         //             "reference must not be mutable",
+//         //         ));
+//         //     }
+
+//         //     Ok(ty.elem.clone())
+//         // } else {
+//         //     let msg = format!(
+//         //         "expected a reference to a `Context` object (try: `&mut {}`)",
+//         //         arg.ty.to_token_stream()
+//         //     );
+//         //     return Err(syn::Error::new_spanned(arg.ty.clone(), msg));
+//         // }
+//     } else {
+//         return Err(syn::Error::new_spanned(
+//             first_arg,
+//             "function components can't accept a receiver",
+//         ));
+//     }
+// }
 
 pub fn collect_inline_args() {}
 

+ 27 - 32
packages/core-macro/src/ifmt.rs

@@ -1,4 +1,3 @@
-use ::proc_macro::TokenStream;
 use ::quote::{quote, ToTokens};
 use ::std::ops::Not;
 use ::syn::{
@@ -6,10 +5,7 @@ use ::syn::{
     punctuated::Punctuated,
     *,
 };
-use proc_macro2::TokenStream as TokenStream2;
-
-// #[macro_use]
-// mod macros {
+use proc_macro2::TokenStream;
 
 // #[cfg(not(feature = "verbose-expansions"))]
 macro_rules! debug_input {
@@ -18,18 +14,6 @@ macro_rules! debug_input {
     };
 }
 
-// #[cfg(feature = "verbose-expansions")]
-macro_rules! debug_input {
-    ($expr:expr) => {
-        match $expr {
-            expr => {
-                eprintln!("-------------------\n{} ! ( {} )", FUNCTION_NAME, expr);
-                expr
-            }
-        }
-    };
-}
-
 // #[cfg(not(feature = "verbose-expansions"))]
 macro_rules! debug_output {
     ($expr:expr) => {
@@ -38,19 +22,30 @@ macro_rules! debug_output {
 }
 
 // #[cfg(feature = "verbose-expansions")]
-macro_rules! debug_output {
-    ($expr:expr) => {
-        match $expr {
-            expr => {
-                eprintln!("=>\n{}\n-------------------\n", expr);
-                expr
-            }
-        }
-    };
-}
+// macro_rules! debug_input {
+//     ($expr:expr) => {
+//         match $expr {
+//             expr => {
+//                 eprintln!("-------------------\n{} ! ( {} )", FUNCTION_NAME, expr);
+//                 expr
+//             }
+//         }
+//     };
+// }
+
+// #[cfg(feature = "verbose-expansions")]
+// macro_rules! debug_output {
+//     ($expr:expr) => {
+//         match $expr {
+//             expr => {
+//                 eprintln!("=>\n{}\n-------------------\n", expr);
+//                 expr
+//             }
+//         }
+//     };
 // }
 
-pub fn format_args_f_impl(input: IfmtInput) -> TokenStream {
+pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
     let IfmtInput {
         mut format_literal,
         mut positional_args,
@@ -121,7 +116,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> TokenStream {
                 arg,
             ) {
                 Ok(segments) => segments.into_iter().collect(),
-                Err(err) => return err.to_compile_error().into(),
+                Err(err) => return Err(err),
             }
         };
         match segments.len() {
@@ -151,7 +146,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> TokenStream {
                     format_args!("{}", positional_args.len()),
                 )
                 .expect("`usize` or `char` Display impl cannot panic");
-                let segments: Punctuated<TokenStream2, Token![.]> = segments
+                let segments: Punctuated<TokenStream, Token![.]> = segments
                     .into_iter()
                     .map(|it| match it {
                         Segment::Ident(ident) => ident.into_token_stream(),
@@ -172,13 +167,13 @@ pub fn format_args_f_impl(input: IfmtInput) -> TokenStream {
     });
     format_literal = LitStr::new(out_format_literal, format_literal.span());
 
-    TokenStream::from(debug_output!(quote! {
+    Ok(TokenStream::from(debug_output!(quote! {
         format_args!(
             #format_literal
             #(, #positional_args)*
             #(, #named_args)*
         )
-    }))
+    })))
 }
 
 #[allow(dead_code)] // dumb compiler does not see the struct being used...

+ 21 - 17
packages/core-macro/src/lib.rs

@@ -6,6 +6,7 @@ mod fc;
 mod htm;
 mod ifmt;
 mod rsxt;
+mod util;
 
 /// The html! macro makes it easy for developers to write jsx-style markup in their components.
 /// We aim to keep functional parity with html templates.
@@ -27,31 +28,34 @@ pub fn rsx(s: TokenStream) -> TokenStream {
     }
 }
 
+// #[proc_macro_attribute]
+// pub fn fc(attr: TokenStream, item: TokenStream) -> TokenStream {
+
 /// Label a function or static closure as a functional component.
 /// This macro reduces the need to create a separate properties struct.
+///
+/// Using this macro is fun and simple
+///
+/// ```ignore
+///
+/// #[fc]
+/// fn Example(ctx: Context, name: &str) -> DomTree {
+///     ctx.render(rsx! { h1 {"hello {name}"} })
+/// }
+/// ```
 #[proc_macro_attribute]
 pub fn fc(attr: TokenStream, item: TokenStream) -> TokenStream {
-    use fc::{function_component_impl, FunctionComponent};
-
-    let item = parse_macro_input!(item as FunctionComponent);
-
-    function_component_impl(item)
-        .unwrap_or_else(|err| err.to_compile_error())
-        .into()
+    match syn::parse::<fc::FunctionComponent>(item) {
+        Err(e) => e.to_compile_error().into(),
+        Ok(s) => s.to_token_stream().into(),
+    }
 }
 
 #[proc_macro]
 pub fn format_args_f(input: TokenStream) -> TokenStream {
     use ifmt::*;
-
     let item = parse_macro_input!(input as IfmtInput);
-
-    // #[allow(unused)]
-    // const FUNCTION_NAME: &str = "format_args_f";
-
-    // debug_input!(&input);
-
-    ifmt::format_args_f_impl(item)
-    // .unwrap_or_else(|err| err.to_compile_error())
-    // .into()
+    format_args_f_impl(item)
+        .unwrap_or_else(|err| err.to_compile_error())
+        .into()
 }

+ 184 - 88
packages/core-macro/src/rsxt.rs

@@ -88,6 +88,7 @@ impl ToTokens for RsxRender {
 enum Node {
     Element(Element),
     Text(TextNode),
+    Component(Component),
 }
 
 impl ToTokens for ToToksCtx<&Node> {
@@ -95,28 +96,29 @@ impl ToTokens for ToToksCtx<&Node> {
         match &self.inner {
             Node::Element(el) => self.recurse(el).to_tokens(tokens),
             Node::Text(txt) => self.recurse(txt).to_tokens(tokens),
+            Node::Component(c) => self.recurse(c).to_tokens(tokens),
         }
     }
 }
 
-// impl Node {
-//     fn peek(s: ParseStream) -> bool {
-//         (s.peek(Token![<]) && !s.peek2(Token![/])) || s.peek(token::Brace) || s.peek(LitStr)
-//     }
-// }
-
 impl Parse for Node {
     fn parse(s: ParseStream) -> Result<Self> {
-        let fork = s.fork();
-
-        let ret = if let Ok(text) = fork.parse::<TextNode>() {
-            s.advance_to(&fork);
-            Ok(Self::Text(text))
-        } else if let Ok(el) = s.parse::<Element>() {
-            Ok(Self::Element(el))
+        let fork1 = s.fork();
+        let fork2 = s.fork();
+
+        // todo: map issues onto the second fork if any arise
+        // it's unlikely that textnodes or components would fail?
+
+        let ret = if let Ok(text) = fork1.parse::<TextNode>() {
+            s.advance_to(&fork1);
+            Self::Text(text)
+        } else if let Ok(element) = fork2.parse::<Element>() {
+            s.advance_to(&fork2);
+            Self::Element(element)
+        } else if let Ok(comp) = s.parse::<Component>() {
+            Self::Component(comp)
         } else {
-            // TODO: Span information
-            panic!("Not a valid child node");
+            return Err(Error::new(s.span(), "Failed to parse as a valid child"));
         };
 
         // consume comma if it exists
@@ -124,7 +126,120 @@ impl Parse for Node {
         if s.peek(Token![,]) {
             let _ = s.parse::<Token![,]>();
         }
-        ret
+        Ok(ret)
+    }
+}
+
+struct Component {
+    name: Ident,
+    body: Vec<Field>,
+    // attrs: Vec<Attr>,
+    children: MaybeExpr<Vec<Node>>,
+}
+
+impl ToTokens for ToToksCtx<&Component> {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        let name = &self.inner.name;
+
+        // let mut toks = quote! {};
+
+        // for attr in self.inner.attrs.iter() {
+        //     self.recurse(attr).to_tokens(&mut toks);
+        // }
+
+        let body = &self.inner.body;
+        // panic!("tokens are {:#?}", toks);
+
+        // tokens.append_all(quote! {
+        //     dioxus::builder::virtual_child(ctx, #name{ #body, ___p: PhantomData{} })
+        //     // dioxus::builder::ElementBuilder::new(ctx, #name)
+        // });
+
+        // no children right now
+
+        // match &self.inner.children {
+        //     MaybeExpr::Expr(expr) => tokens.append_all(quote! {
+        //         .children(#expr)
+        //     }),
+        //     MaybeExpr::Literal(nodes) => {
+        //         let mut children = nodes.iter();
+        //         if let Some(child) = children.next() {
+        //             let mut inner_toks = TokenStream2::new();
+        //             self.recurse(child).to_tokens(&mut inner_toks);
+        //             while let Some(child) = children.next() {
+        //                 quote!(,).to_tokens(&mut inner_toks);
+        //                 self.recurse(child).to_tokens(&mut inner_toks);
+        //             }
+        //             tokens.append_all(quote! {
+        //                 .children([#inner_toks])
+        //             });
+        //         }
+        //     }
+        // }
+        // tokens.append_all(quote! {
+        //     .finish()
+        // });
+    }
+}
+
+impl Parse for Component {
+    fn parse(s: ParseStream) -> Result<Self> {
+        // TODO: reject anything weird/nonstandard
+        // we want names *only*
+        let name = Ident::parse_any(s)?;
+
+        if crate::util::is_valid_tag(&name.to_string()) {
+            return Err(Error::new(
+                name.span(),
+                "Components cannot share names with valid HTML tags",
+            ));
+        }
+
+        // parse the guts
+        let content: ParseBuffer;
+        syn::braced!(content in s);
+        let mut fields: Vec<Field> = Vec::new();
+
+        'parsing: loop {
+            // [1] Break if empty
+            if content.is_empty() {
+                break 'parsing;
+            }
+        }
+
+        // let body = content.parse()?;
+
+        // eventually we'll parse the attrs
+        // let mut attrs: Vec<Attr> = vec![];
+        let mut children: Vec<Node> = vec![];
+        // parse_element_content(content, &mut attrs, &mut children);
+
+        let children = MaybeExpr::Literal(children);
+
+        Ok(Self {
+            name,
+            body,
+            // attrs,
+            children,
+        })
+    }
+}
+
+// the struct's fields info
+pub struct Field {
+    name: Ident,
+    content: Ident,
+}
+
+impl Parse for Field {
+    fn parse(input: ParseStream) -> Result<Self> {
+        todo!()
+    }
+}
+
+impl ToTokens for ToToksCtx<&Field> {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        todo!()
     }
 }
 
@@ -142,12 +257,12 @@ struct Element {
 
 impl ToTokens for ToToksCtx<&Element> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
-        // todo!()
-        // // let ctx = self.ctx;
         let name = &self.inner.name.to_string();
+
         tokens.append_all(quote! {
             dioxus::builder::ElementBuilder::new(ctx, #name)
         });
+
         for attr in self.inner.attrs.iter() {
             self.recurse(attr).to_tokens(tokens);
         }
@@ -182,89 +297,70 @@ impl Parse for Element {
         // we want names *only*
         let name = Ident::parse_any(s)?;
 
+        if !crate::util::is_valid_tag(&name.to_string()) {
+            return Err(Error::new(name.span(), "Not a valid Html tag"));
+        }
+
         // parse the guts
         let content: ParseBuffer;
         syn::braced!(content in s);
 
-        let mut attrs = vec![];
+        let mut attrs: Vec<Attr> = vec![];
         let mut children: Vec<Node> = vec![];
+        parse_element_content(content, &mut attrs, &mut children);
 
-        'parsing: loop {
-            // todo move this around into a more functional style
-            // [1] Break if empty
-            // [2] Try to consume an attr (with comma)
-            // [3] Try to consume a child node (with comma)
-            // [4] Try to consume brackets as anything thats Into<Node>
-            // [last] Fail if none worked
-
-            // [1] Break if empty
-            if content.is_empty() {
-                break 'parsing;
-            }
+        let children = MaybeExpr::Literal(children);
 
-            // [2] Try to consume an attr
-            let fork = content.fork();
-            if let Ok(attr) = fork.parse::<Attr>() {
-                // make sure to advance or your computer will become a spaceheater :)
-                content.advance_to(&fork);
-                attrs.push(attr);
-                continue 'parsing;
-            }
+        Ok(Self {
+            name,
+            attrs,
+            children,
+        })
+    }
+}
 
-            // [3] Try to consume a child node
-            let fork = content.fork();
-            if let Ok(node) = fork.parse::<Node>() {
-                // make sure to advance or your computer will become a spaceheater :)
-                content.advance_to(&fork);
-                children.push(node);
-                continue 'parsing;
-            }
+// used by both vcomponet and velement to parse the contents of the elements into attras and children
+fn parse_element_content(content: ParseBuffer, attrs: &mut Vec<Attr>, children: &mut Vec<Node>) {
+    'parsing: loop {
+        // todo move this around into a more functional style
+        // [1] Break if empty
+        // [2] Try to consume an attr (with comma)
+        // [3] Try to consume a child node (with comma)
+        // [4] Try to consume brackets as anything thats Into<Node>
+        // [last] Fail if none worked
+
+        // [1] Break if empty
+        if content.is_empty() {
+            break 'parsing;
+        }
 
-            // [4] TODO: Parsing brackets
-            // let fork = content.fork();
-            // if let Ok(el) = fork.parse() {
-            //     children.push(el);
-            //     continue 'parsing;
-            // }
+        // [2] Try to consume an attr
+        let fork = content.fork();
+        if let Ok(attr) = fork.parse::<Attr>() {
+            // make sure to advance or your computer will become a spaceheater :)
+            content.advance_to(&fork);
+            attrs.push(attr);
+            continue 'parsing;
+        }
 
-            // todo: pass a descriptive error onto the offending tokens
-            panic!("Entry is not an attr or element\n {}", content)
+        // [3] Try to consume a child node
+        let fork = content.fork();
+        if let Ok(node) = fork.parse::<Node>() {
+            // make sure to advance or your computer will become a spaceheater :)
+            content.advance_to(&fork);
+            children.push(node);
+            continue 'parsing;
         }
 
-        let children = MaybeExpr::Literal(children);
-        // let children = MaybeExpr::Literal(Vec::new());
-        // // Contents of an element can either be a brace (in which case we just copy verbatim), or a
-        // // sequence of nodes.
-        // let children = if s.peek(token::Brace) {
-        //     // expr
-        //     let content;
-        //     syn::braced!(content in s);
-        //     MaybeExpr::Expr(content.parse()?)
-        // } else {
-        //     // nodes
-        //     let mut children = vec![];
-        //     while !(s.peek(Token![<]) && s.peek2(Token![/])) {
-        //         children.push(s.parse()?);
-        //     }
-        //     MaybeExpr::Literal(children)
-        // };
-
-        // // closing element
-        // s.parse::<Token![<]>()?;
-        // s.parse::<Token![/]>()?;
-        // let close = Ident::parse_any(s)?;
-        // if close.to_string() != name.to_string() {
-        //     return Err(Error::new_spanned(
-        //         close,
-        //         "closing element does not match opening",
-        //     ));
+        // [4] TODO: Parsing brackets
+        // let fork = content.fork();
+        // if let Ok(el) = fork.parse() {
+        //     children.push(el);
+        //     continue 'parsing;
         // }
-        // s.parse::<Token![>]>()?;
-        Ok(Self {
-            name,
-            attrs,
-            children,
-        })
+
+        // todo: pass a descriptive error onto the offending tokens
+        panic!("Entry is not an attr or element\n {}", content)
     }
 }
 

+ 137 - 0
packages/core-macro/src/util.rs

@@ -0,0 +1,137 @@
+// use lazy_static::lazy_static;
+use once_cell::sync::Lazy;
+use std::collections::hash_set::HashSet;
+
+static VALID_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
+    [
+        "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)
+}

+ 41 - 0
packages/core/examples/fc.rs

@@ -0,0 +1,41 @@
+use dioxus_core::prelude::*;
+use dioxus_core::scope::FC;
+use dioxus_core_macro::fc;
+
+use std::marker::PhantomData;
+// #[derive(PartialEq)]
+// pub struct Example<'a> {
+//     b: &'a str,
+//     ___p: std::marker::PhantomData<&'a ()>,
+// }
+
+// impl<'a> FC for Example<'a> {
+//     fn render(ctx: Context<'_>, props: &Example<'a>) -> DomTree {
+//         let Example { b, .. } = props;
+//         {
+//             ctx.render(rsx! {
+//                 div { "abcd {b}" }
+//             })
+//         }
+//     }
+// }
+
+// always try to fill in with Default
+
+#[fc]
+fn Example(ctx: Context, a: &str, b: &str, c: &str) -> DomTree {
+    ctx.render(rsx! {
+        div {
+            SomeComponent {
+                a: "123"
+            }
+        }
+    })
+}
+
+#[fc]
+fn SomeComponent(ctx: Context, a: &str, b: &str) -> DomTree {
+    todo!()
+}
+
+fn main() {}

+ 41 - 21
packages/core/examples/rsx_usage.rs

@@ -3,33 +3,53 @@ use dioxus_core::prelude::*;
 
 fn main() {}
 
+trait SProps {}
+
+trait Comp {
+    type Props;
+}
+
+impl<T> Comp for FC<T> {
+    type Props = T;
+}
+
+fn test() {
+    // let g: FC<ButtonProps> = CustomButton;
+}
+
+trait Render {
+    fn render(ctx: Context, props: &Self) -> DomTree;
+}
 // include as much as you might accept
-struct ButtonProps<'a> {
+struct Button<'a> {
     onhover: Option<&'a dyn Fn()>,
-    // // A list of any attrs
-    // attrs: AttrList<'a>,
 }
 
-fn CustomButton(ctx: Context, props: ButtonProps) -> DomTree {
-    let onfocus = move |evt: ()| log::debug!("Focused");
-
-    // todo!()
-    ctx.render(rsx! {
-        button {
-            // ..props.attrs,
-            class: "abc123",
-            // style: { a: 2, b: 3, c: 4 },
-            onclick: move |evt| {
-                // log::info("hello world");
-            },
-            // Custom1 { a: 123 }
-            // Custom2 { a: 456, "abc", h1 {"1"}, h2 {"2"} }
-            // Custom3 { a: "sometext goes here" }
-            // Custom4 { onclick: |evt| log::info("click") }
-        }
-    })
+impl Render for Button<'_> {
+    fn render(ctx: Context, props: &Self) -> DomTree {
+        let onfocus = move |evt: ()| log::debug!("Focused");
+
+        // todo!()
+        ctx.render(rsx! {
+            button {
+                // ..props.attrs,
+                class: "abc123",
+                // style: { a: 2, b: 3, c: 4 },
+                onclick: move |evt| {
+                    // log::info("hello world");
+                },
+                // Custom1 { a: 123 }
+                // Custom2 { a: 456, "abc", h1 {"1"}, h2 {"2"} }
+                // Custom3 { a: "sometext goes here" }
+                // Custom4 { onclick: |evt| log::info("click") }
+            }
+        })
+    }
 }
 
+// #[fc]
+// fn Button(ctx: Context, onhover: Option<&dyn Fn()>) -> DomTree {}
+
 // h1 {
 //     tag: "type", abc: 123, class: "big small wide short",
 //     "title1"

+ 1 - 1
packages/core/examples/step.rs

@@ -4,7 +4,7 @@ fn main() -> Result<(), ()> {
     let p1 = Props { name: "bob".into() };
 
     let mut vdom = VirtualDom::new_with_props(Example, p1);
-    vdom.update_props(|p: &mut Props| {});
+    // vdom.update_props(|p: &mut Props| {});
 
     Ok(())
 }

+ 21 - 21
packages/core/src/component.rs

@@ -39,31 +39,31 @@ mod tests {
     use crate::prelude::bumpalo::Bump;
     use crate::prelude::*;
 
-    fn test_static_fn<'a, P>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
-        todo!()
-    }
+    // fn test_static_fn<'a, P>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
+    //     todo!()
+    // }
 
-    static TestComponent: FC<()> = |ctx, props| {
-        //
+    // static TestComponent: FC<()> = |ctx, props| {
+    //     //
 
-        ctx.render(html! {
-            <div>
+    //     ctx.render(html! {
+    //         <div>
 
-            </div>
-        })
-    };
+    //         </div>
+    //     })
+    // };
 
-    static TestComponent2: FC<()> = |ctx, props| {
-        //
-        ctx.render(|ctx| VNode::text("blah"))
-    };
+    // static TestComponent2: FC<()> = |ctx, props| {
+    //     //
+    //     ctx.render(|ctx| VNode::text("blah"))
+    // };
 
-    #[test]
-    fn ensure_types_work() {
-        let bump = Bump::new();
+    // #[test]
+    // fn ensure_types_work() {
+    //     let bump = Bump::new();
 
-        // Happiness! The VNodes are now allocated onto the bump vdom
-        let _ = test_static_fn(&bump, TestComponent);
-        let _ = test_static_fn(&bump, TestComponent2);
-    }
+    //     // Happiness! The VNodes are now allocated onto the bump vdom
+    //     let _ = test_static_fn(&bump, TestComponent);
+    //     let _ = test_static_fn(&bump, TestComponent2);
+    // }
 }

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

@@ -25,15 +25,20 @@ impl DebugRenderer {
 mod tests {
     use super::*;
     use crate::prelude::*;
+    use crate::scope::FC;
 
     #[test]
     fn ensure_creation() -> Result<(), ()> {
-        let mut dom = VirtualDom::new(|ctx, props| {
-            //
-            ctx.render(html! { <div>"hello world" </div> })
-        });
+        #[derive(PartialEq)]
+        struct Creation {}
+        impl FC for Creation {
+            fn render(ctx: Context, props: &Self) -> DomTree {
+                ctx.render(html! { <div>"hello world" </div> })
+            }
+        }
+
+        let mut dom = VirtualDom::new_with_props(Creation {});
 
-        // dom.progress()?;
         Ok(())
     }
 }

+ 9 - 27
packages/core/src/diff.rs

@@ -53,23 +53,19 @@ use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
 /// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
 /// that were modified by the eventtrigger. This prevents doubly evaluating components if they wereboth updated via
 /// subscriptions and props changes.
-pub struct DiffMachine<'a, 'b> {
+pub struct DiffMachine<'a> {
     pub change_list: EditMachine<'a>,
 
-    pub vdom: &'b VirtualDom,
-    pub cur_idx: ScopeIdx,
     pub diffed: FxHashSet<ScopeIdx>,
     pub need_to_diff: FxHashSet<ScopeIdx>,
 }
 
-impl<'a, 'b> DiffMachine<'a, 'b> {
-    pub fn new(vdom: &'b VirtualDom, bump: &'a Bump, idx: ScopeIdx) -> Self {
+impl<'a> DiffMachine<'a> {
+    pub fn new(bump: &'a Bump) -> Self {
         Self {
-            cur_idx: idx,
             change_list: EditMachine::new(bump),
             diffed: FxHashSet::default(),
             need_to_diff: FxHashSet::default(),
-            vdom,
         }
     }
 
@@ -118,10 +114,10 @@ impl<'a, 'b> DiffMachine<'a, 'b> {
             }
 
             (VNode::Component(cold), VNode::Component(cnew)) => {
-                if cold.comp != cnew.comp {
-                    // queue an event to mount this new component
-                    return;
-                }
+                // if cold.comp != cnew.comp {
+                //     // queue an event to mount this new component
+                //     return;
+                // }
 
                 // compare props.... somehow.....
 
@@ -843,23 +839,9 @@ impl<'a, 'b> DiffMachine<'a, 'b> {
                 }
 
                 listeners.iter().enumerate().for_each(|(id, listener)| {
-                    // if let Some(index) = self.current_idx {
-                    self.change_list.new_event_listener(
-                        listener.event,
-                        listener.scope,
-                        listener.id,
-                    );
-                    // } else {
-                    // Don't panic
-                    // Used for testing
-                    //     log::trace!("Failed to set listener, create was not called in the context of the virtual dom");
-                    // }
+                    self.change_list
+                        .new_event_listener(listener.event, listener.scope, listener.id)
                 });
-                // for l in listeners {
-                // unsafe {
-                //     registry.add(l);
-                // }
-                // }
 
                 for attr in attributes {
                     self.change_list

+ 25 - 25
packages/core/src/hooks.rs

@@ -183,30 +183,30 @@ mod use_reducer_def {
             Decr,
         }
 
-        #[allow(unused)]
-        static Example: FC<()> = |ctx, props| {
-            let (count, reduce) = use_reducer(
-                &ctx,
-                || 0,
-                |count, action| match action {
-                    Actions::Incr => *count += 1,
-                    Actions::Decr => *count -= 1,
-                },
-            );
-
-            ctx.render(rsx! {
-                div {
-                    h1 {"Count: {count}"}
-                    button {
-                        "Increment"
-                        onclick: move |_| reduce(Actions::Incr)
-                    }
-                    button {
-                        "Decrement"
-                        onclick: move |_| reduce(Actions::Decr)
-                    }
-                }
-            })
-        };
+        // #[allow(unused)]
+        // static Example: FC<()> = |ctx, props| {
+        //     let (count, reduce) = use_reducer(
+        //         &ctx,
+        //         || 0,
+        //         |count, action| match action {
+        //             Actions::Incr => *count += 1,
+        //             Actions::Decr => *count -= 1,
+        //         },
+        //     );
+
+        //     ctx.render(rsx! {
+        //         div {
+        //             h1 {"Count: {count}"}
+        //             button {
+        //                 "Increment"
+        //                 onclick: move |_| reduce(Actions::Incr)
+        //             }
+        //             button {
+        //                 "Decrement"
+        //                 onclick: move |_| reduce(Actions::Decr)
+        //             }
+        //         }
+        //     })
+        // };
     }
 }

+ 5 - 4
packages/core/src/lib.rs

@@ -106,7 +106,7 @@ pub(crate) mod innerlude {
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
 
-    pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
+    // pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
 
     mod fc2 {}
     // pub type FC<'a, P: 'a> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
@@ -122,7 +122,8 @@ pub(crate) mod innerlude {
     // Re-export the FC macro
     pub use crate as dioxus;
     pub use crate::nodebuilder as builder;
-    pub use dioxus_core_macro::{fc, html, rsx};
+    pub use dioxus_core_macro::{html, rsx};
+    // pub use dioxus_core_macro::{fc, html, rsx};
 }
 
 /// Re-export common types for ease of development use.
@@ -136,7 +137,7 @@ pub mod prelude {
 
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
-    pub use crate::innerlude::FC;
+    // pub use crate::innerlude::FC;
 
     // TODO @Jon, fix this
     // hack the VNode type until VirtualNode is fixed in the macro crate
@@ -151,7 +152,7 @@ pub mod prelude {
     pub use crate::nodebuilder as builder;
     // pub use dioxus_core_macro::fc;
     pub use dioxus_core_macro::format_args_f;
-    pub use dioxus_core_macro::{fc, html, rsx};
+    pub use dioxus_core_macro::{html, rsx};
 
     pub use crate::component::ScopeIdx;
     pub use crate::diff::DiffMachine;

+ 3 - 1
packages/core/src/nodebuilder.rs

@@ -524,7 +524,9 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
 //     }
 // }
 
-pub fn virtual_child<'a, T>(_bump: &'a Bump, _props: T, _f: crate::innerlude::FC<T>) -> VNode<'a> {
+// _f: crate::innerlude::FC<T>,
+// _props: T
+pub fn virtual_child<'a, T: crate::scope::FC>(ctx: &'a NodeCtx<'a>, p: T) -> VNode<'a> {
     todo!()
     // VNode::Component()
 }

+ 43 - 10
packages/core/src/nodes.rs

@@ -296,8 +296,16 @@ mod vtext {
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 mod vcomponent {
-    use crate::innerlude::{Context, ScopeIdx, FC};
-    use std::{any::TypeId, cell::RefCell, marker::PhantomData, rc::Rc};
+    use crate::{
+        innerlude::{Context, ScopeIdx},
+        scope::FC,
+    };
+    use std::{
+        any::{Any, TypeId},
+        cell::RefCell,
+        marker::PhantomData,
+        rc::Rc,
+    };
 
     use super::DomTree;
 
@@ -306,16 +314,18 @@ mod vcomponent {
     #[derive(Debug)]
     pub struct VComponent<'src> {
         _p: PhantomData<&'src ()>,
-        pub(crate) props: Box<dyn std::any::Any>,
-        pub(crate) props_type: TypeId,
-        pub(crate) comp: *const (),
-        pub(crate) caller: Caller,
-
+        // pub(crate) props: Box<dyn std::any::Any>,
+        // pub(crate) props_type: TypeId,
+        // pub(crate) comp: *const (),
+        // pub(crate) caller: Caller,
+        pub(crate) comparator: Comparator,
         // once a component gets mounted, its parent gets a stable address.
         // this way we can carry the scope index from between renders
         // genius, really!
-        pub assigned_scope: StableScopeAddres,
+        // pub assigned_scope: StableScopeAddres,
     }
+    pub struct Comparator(Box<dyn Fn(&dyn Any) -> bool>);
+    // pub struct Comparator<'src>(&'src dyn Fn(&dyn Any) -> bool);
 
     pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
     impl std::fmt::Debug for Caller {
@@ -324,12 +334,35 @@ mod vcomponent {
         }
     }
 
+    impl std::fmt::Debug for Comparator {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            todo!()
+        }
+    }
+
     impl<'a> VComponent<'a> {
         // use the type parameter on props creation and move it into a portable context
         // this lets us keep scope generic *and* downcast its props when we need to:
         // - perform comparisons when diffing (memoization)
         // -
-        pub fn new<P>(comp: FC<P>, props: P) -> Self {
+        pub fn new<P: FC + 'static>(comp: P) -> Self {
+            todo!()
+            // let p = Rc::new(props);
+
+            // let props_comparator = move |new_props: &dyn Any| -> bool {
+            //     new_props
+            //         .downcast_ref::<P>()
+            //         .map(|new_p| p.as_ref() == new_p)
+            //         .unwrap_or_else(|| {
+            //             log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
+            //             false
+            //         })
+            // };
+
+            // Self {
+            //     _p: PhantomData,
+            //     comparator: Comparator(Box::new(props_comparator)),
+            // }
             // let caller = move |ctx: Context| {
             //     let t = comp(ctx, &props);
             //     t
@@ -355,7 +388,7 @@ mod vcomponent {
             //     }
             // }
 
-            todo!()
+            // todo!()
             // Self {
             //     _p: PhantomData {},
             //     props,

+ 70 - 74
packages/core/src/scope.rs

@@ -11,9 +11,9 @@ use std::{
     ops::Deref,
 };
 
-pub trait Properties: PartialEq {}
-// just for now
-impl<T: PartialEq> Properties for T {}
+pub trait FC: PartialEq {
+    fn render(ctx: Context, props: &Self) -> DomTree;
+}
 
 pub trait Scoped {
     fn run(&mut self);
@@ -30,15 +30,13 @@ pub trait Scoped {
 ///
 /// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
 /// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
-pub struct Scope<P: Properties> {
+pub struct Scope<P: FC> {
     // Map to the parent
     pub parent: Option<ScopeIdx>,
 
     // our own index
     pub myidx: ScopeIdx,
 
-    pub caller: FC<P>,
-
     pub props: P,
 
     // ==========================
@@ -63,8 +61,7 @@ pub struct Scope<P: Properties> {
 
 // instead of having it as a trait method, we use a single function
 // todo: do the unsafety magic stuff to erase the type of p
-pub fn create_scoped<P: Properties + 'static>(
-    caller: FC<P>,
+pub fn create_scoped<P: FC + 'static>(
     props: P,
     myidx: ScopeIdx,
     parent: Option<ScopeIdx>,
@@ -90,7 +87,6 @@ pub fn create_scoped<P: Properties + 'static>(
         myidx,
         hook_arena,
         hooks,
-        caller,
         frames,
         listeners,
         parent,
@@ -98,7 +94,7 @@ pub fn create_scoped<P: Properties + 'static>(
     })
 }
 
-impl<P: Properties + 'static> Scoped for Scope<P> {
+impl<P: FC + 'static> Scoped for Scope<P> {
     /// Create a new context and run the component with references from the Virtual Dom
     /// This function downcasts the function pointer based on the stored props_type
     ///
@@ -125,7 +121,8 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
 
         // Note that the actual modification of the vnode head element occurs during this call
         // let _: DomTree = caller(ctx, props);
-        let _: DomTree = (self.caller)(ctx, &self.props);
+        let _: DomTree = P::render(ctx, &self.props);
+        // let _: DomTree = (self.caller)(ctx, &self.props);
 
         /*
         SAFETY ALERT
@@ -264,90 +261,89 @@ mod tests {
     use super::*;
     use crate::prelude::*;
 
-    static ListenerTest: FC<()> = |ctx, props| {
-        ctx.render(html! {
-            <div onclick={|_| println!("Hell owlrld")}>
-                "hello"
-            </div>
-        })
-    };
+    // static ListenerTest: FC<()> = |ctx, props| {
+    //     ctx.render(html! {
+    //         <div onclick={|_| println!("Hell owlrld")}>
+    //             "hello"
+    //         </div>
+    //     })
+    // };
 
     #[test]
     fn test_scope() {
-        let example: FC<()> = |ctx, props| {
-            use crate::builder::*;
-            ctx.render(|ctx| {
-                builder::ElementBuilder::new(ctx, "div")
-                    .child(text("a"))
-                    .finish()
-            })
-        };
+        #[derive(PartialEq)]
+        struct Example {}
+        impl FC for Example {
+            fn render(ctx: Context<'_>, _: &Self) -> DomTree {
+                use crate::builder::*;
+                ctx.render(|ctx| {
+                    builder::ElementBuilder::new(ctx, "div")
+                        .child(text("a"))
+                        .finish()
+                })
+            }
+        }
 
-        let props = ();
-        let parent = None;
         let mut nodes = generational_arena::Arena::new();
         nodes.insert_with(|myidx| {
-            let scope = create_scoped(example, props, myidx, parent);
+            let scope = create_scoped(Example {}, myidx, None);
         });
     }
 
-    #[derive(Debug)]
-    struct ExampleProps<'src> {
-        name: &'src String,
-    }
+    use crate::{builder::*, hooks::use_ref};
 
-    #[derive(Debug)]
+    #[derive(Debug, PartialEq)]
     struct EmptyProps<'src> {
         name: &'src String,
     }
 
-    use crate::{builder::*, hooks::use_ref};
-
-    fn example_fc<'a>(ctx: Context<'a>, props: &'a EmptyProps) -> DomTree {
-        let (content, _): (&'a String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
-
-        let childprops: ExampleProps<'a> = ExampleProps { name: content };
-        ctx.render(move |c| {
-            builder::ElementBuilder::new(c, "div")
-                .child(text(props.name))
-                .child(virtual_child::<ExampleProps>(
-                    c.bump,
-                    childprops,
-                    child_example,
-                ))
-                .finish()
-        })
+    impl FC for EmptyProps<'_> {
+        fn render(ctx: Context, props: &Self) -> DomTree {
+            let (content, _): (&String, _) = crate::hooks::use_state(&ctx, || "abcd".to_string());
+
+            let childprops: ExampleProps<'_> = ExampleProps { name: content };
+            todo!()
+            // ctx.render(move |c| {
+            //     builder::ElementBuilder::new(c, "div")
+            //         .child(text(props.name))
+            //         .child(virtual_child(c, childprops))
+            //         .finish()
+            // })
+        }
     }
 
-    fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
-        ctx.render(move |ctx| {
-            builder::ElementBuilder::new(ctx, "div")
-                .child(text(props.name))
-                .finish()
-        })
+    #[derive(Debug, PartialEq)]
+    struct ExampleProps<'src> {
+        name: &'src String,
     }
 
-    static CHILD: FC<ExampleProps> = |ctx, props: &'_ ExampleProps| {
-        ctx.render(move |ctx| {
-            builder::ElementBuilder::new(ctx, "div")
-                .child(text(props.name))
-                .finish()
-        })
-    };
+    impl FC for ExampleProps<'_> {
+        fn render(ctx: Context, props: &Self) -> DomTree {
+            ctx.render(move |ctx| {
+                builder::ElementBuilder::new(ctx, "div")
+                    .child(text(props.name))
+                    .finish()
+            })
+        }
+    }
 
     #[test]
     fn test_borrowed_scope() {
-        let example: FC<EmptyProps> = |ctx, props| {
-            ctx.render(move |b| {
-                builder::ElementBuilder::new(b, "div")
-                    .child(virtual_child(
-                        b.bump,
-                        ExampleProps { name: props.name },
-                        CHILD,
-                    ))
-                    .finish()
-            })
-        };
+        #[derive(Debug, PartialEq)]
+        struct Example {
+            name: String,
+        }
+
+        impl FC for Example {
+            fn render(ctx: Context, props: &Self) -> DomTree {
+                todo!()
+                // ctx.render(move |c| {
+                //     builder::ElementBuilder::new(c, "div")
+                //         .child(virtual_child(c, ExampleProps { name: &props.name }))
+                //         .finish()
+                // })
+            }
+        }
 
         let source_text = "abcd123".to_string();
         let props = ExampleProps { name: &source_text };

+ 19 - 18
packages/core/src/virtual_dom.rs

@@ -1,6 +1,6 @@
 // use crate::{changelist::EditList, nodes::VNode};
 
-use crate::{innerlude::*, scope::Properties};
+use crate::{innerlude::*, scope::FC};
 use crate::{
     patch::Edit,
     scope::{create_scoped, Scoped},
@@ -60,8 +60,9 @@ impl VirtualDom {
     ///
     /// This means that the root component must either consumes its own context, or statics are used to generate the page.
     /// The root component can access things like routing in its context.
-    pub fn new(root: FC<()>) -> Self {
-        Self::new_with_props(root, ())
+    pub fn new(root: impl Fn(Context) -> DomTree) -> Self {
+        todo!()
+        // Self::new_with_props(root)
     }
 
     /// Start a new VirtualDom instance with a dependent props.
@@ -69,13 +70,13 @@ impl VirtualDom {
     ///
     /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
     /// to toss out the entire tree.
-    pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
-        let mut components = Arena::new();
+    pub fn new_with_props<P: FC + 'static>(root: P) -> Self {
+        // let mut components = Arena::new();
 
         // Create a reference to the component in the arena
         // Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
         // This puts the dom in a usable state on creation, rather than being potentially invalid
-        let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
+        // let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
 
         todo!()
         // Self {
@@ -106,19 +107,16 @@ impl VirtualDom {
 
         let b = Bump::new();
 
-        let mut diff_machine = DiffMachine::new(self, &b, self.base_scope);
+        let mut diff_machine = DiffMachine::new(&self.diff_bump);
         // let mut diff_machine = DiffMachine::new(self, &self.diff_bump, self.base_scope);
 
-        todo!()
-        // let component = self.components.get(self.base_scope).unwrap();
-
-        // diff_machine.diff_node(component.old_frame(), component.new_frame());
-
-        // let edits = diff_machine.consume();
+        // todo!()
 
+        let component = self.components.get(self.base_scope).unwrap();
+        diff_machine.diff_node(component.old_frame(), component.new_frame());
+        let edits = diff_machine.consume();
         // self.diff_bump = b;
-
-        // Ok(edits)
+        Ok(edits)
     }
 
     /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
@@ -187,6 +185,9 @@ impl VirtualDom {
     }
 }
 
-// struct LockedEdits<'src> {
-//     edits:
-// }
+enum LifeCycleEvent {
+    // Mount {
+//     props: &dyn Properties,
+// // f: FC<dyn Properties>,
+// },
+}

+ 1 - 1
packages/dioxus/src/lib.rs

@@ -3,7 +3,7 @@ pub mod prelude {
     pub use dioxus_core_macro::fc;
 }
 
-use dioxus_core::prelude::FC;
+// use dioxus_core::prelude::FC;
 
 // Re-export core completely
 pub use dioxus_core as core;

+ 1 - 0
packages/web/Cargo.toml

@@ -22,6 +22,7 @@ pretty_env_logger = "0.4.0"
 console_error_panic_hook = "0.1.6"
 generational-arena = "0.2.8"
 wasm-bindgen-test = "0.3.21"
+once_cell = "1.7.2"
 # html-validation = { path = "../html-validation", version = "0.1.1" }
 
 [dependencies.web-sys]

+ 16 - 2
packages/web/examples/infer.rs

@@ -9,6 +9,7 @@ fn main() {
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
 }
 
+// this is a component
 static Example: FC<()> = |ctx, _props| {
     let (event, set_event) = use_state(&ctx, || None);
     let event = format!("{:#?}", event);
@@ -21,14 +22,12 @@ static Example: FC<()> = |ctx, _props| {
         div {  
             
             class: "py-12 px-4 w-full max-w-2xl mx-auto bg-red-100"
-            // class: "py-12 px-4 text-center w-full max-w-2xl mx-auto bg-red-100"
             span { 
                 class: "text-sm font-semibold"
                 "Dioxus Example: Synthetic Events"
             }            
             button {
                 class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
-                
                 "press me"
             }
             pre {
@@ -36,6 +35,21 @@ static Example: FC<()> = |ctx, _props| {
                 id: "json"
                 "{event}"
             }
+            Example2 { name: "{event}" }
+        }
+    })
+};
+
+
+#[derive(Debug, PartialEq)]
+struct Props {
+    name: String
+}
+
+static Example2: FC<Props> = |ctx, props| {
+    ctx.render(rsx!{
+        div {
+            h1 {"hello {props.name}"}
         }
     })
 };