Ver código fonte

wip: serious refactor with const generics

Jonathan Kelley 3 anos atrás
pai
commit
160d86a

+ 0 - 27
packages/core-macro/src/html/mod.rs

@@ -1,27 +0,0 @@
-//! Html body
-//! -------
-//!
-//!
-//! Since both HTML and RSX serialize to the same node structure, the HTML parser uses the same types as RSX,
-//! but has a different Parse implementation.
-
-use crate::rsx::*;
-use quote::ToTokens;
-use syn::parse::Parse;
-
-pub struct HtmlBody(RsxBody);
-
-impl Parse for HtmlBody {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        todo!()
-    }
-}
-impl ToTokens for HtmlBody {
-    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
-        self.0.to_tokens(tokens)
-    }
-}
-
-pub struct HtmlNode(BodyNode);
-pub struct HtmlAmbigiousElement(AmbiguousElement);
-pub struct HtmlComponent(Component);

+ 12 - 57
packages/core-macro/src/lib.rs

@@ -1,69 +1,14 @@
 use proc_macro::TokenStream;
 use quote::ToTokens;
+use rsx::{AS_HTML, AS_RSX};
 use syn::parse_macro_input;
 
-pub(crate) mod fc;
 pub(crate) mod htm;
-pub(crate) mod html;
 pub(crate) mod ifmt;
 pub(crate) mod props;
 pub(crate) mod rsx;
-pub(crate) mod rsxtemplate;
 pub(crate) 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.
-#[proc_macro]
-pub fn html(s: TokenStream) -> TokenStream {
-    match syn::parse::<htm::HtmlRender>(s) {
-        Err(e) => e.to_compile_error().into(),
-        Ok(s) => s.to_token_stream().into(),
-    }
-}
-
-/// 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.
-#[proc_macro]
-pub fn rsx_template(s: TokenStream) -> TokenStream {
-    match syn::parse::<rsxtemplate::RsxTemplate>(s) {
-        Err(e) => e.to_compile_error().into(),
-        Ok(s) => s.to_token_stream().into(),
-    }
-}
-
-/// 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.
-#[proc_macro]
-pub fn html_template(s: TokenStream) -> TokenStream {
-    match syn::parse::<rsxtemplate::RsxTemplate>(s) {
-        Err(e) => e.to_compile_error().into(),
-        Ok(s) => s.to_token_stream().into(),
-    }
-}
-
-// #[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(cx: Context, name: &str) -> DomTree {
-///     cx.render(rsx! { h1 {"hello {name}"} })
-/// }
-/// ```
-#[proc_macro_attribute]
-pub fn fc(_attr: TokenStream, item: TokenStream) -> TokenStream {
-    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::*;
@@ -238,7 +183,17 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
 /// ```
 #[proc_macro]
 pub fn rsx(s: TokenStream) -> TokenStream {
-    match syn::parse::<rsx::RsxBody>(s) {
+    match syn::parse::<rsx::RsxBody<AS_RSX>>(s) {
+        Err(e) => e.to_compile_error().into(),
+        Ok(s) => s.to_token_stream().into(),
+    }
+}
+
+/// 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.
+#[proc_macro]
+pub fn html(s: TokenStream) -> TokenStream {
+    match syn::parse::<rsx::RsxBody<AS_HTML>>(s) {
         Err(e) => e.to_compile_error().into(),
         Ok(s) => s.to_token_stream().into(),
     }

+ 50 - 9
packages/core-macro/src/rsx/ambiguous.rs

@@ -15,17 +15,17 @@ use syn::{
     Error, Ident, LitStr, Result, Token,
 };
 
-pub enum AmbiguousElement {
-    Element(Element),
-    Component(Component),
+pub enum AmbiguousElement<const AS: HTML_OR_RSX> {
+    Element(Element<AS>),
+    Component(Component<AS>),
 }
 
-impl Parse for AmbiguousElement {
+impl Parse for AmbiguousElement<AS_RSX> {
     fn parse(input: ParseStream) -> Result<Self> {
         // Try to parse as an absolute path and immediately defer to the componetn
         if input.peek(Token![::]) {
             return input
-                .parse::<Component>()
+                .parse::<Component<AS_RSX>>()
                 .map(|c| AmbiguousElement::Component(c));
         }
 
@@ -34,7 +34,7 @@ impl Parse for AmbiguousElement {
         if let Ok(pat) = input.fork().parse::<syn::Path>() {
             if pat.segments.len() > 1 {
                 return input
-                    .parse::<Component>()
+                    .parse::<Component<AS_RSX>>()
                     .map(|c| AmbiguousElement::Component(c));
             }
         }
@@ -45,11 +45,11 @@ impl Parse for AmbiguousElement {
             let first_char = name_str.chars().next().unwrap();
             if first_char.is_ascii_uppercase() {
                 input
-                    .parse::<Component>()
+                    .parse::<Component<AS_RSX>>()
                     .map(|c| AmbiguousElement::Component(c))
             } else {
                 input
-                    .parse::<Element>()
+                    .parse::<Element<AS_RSX>>()
                     .map(|c| AmbiguousElement::Element(c))
             }
         } else {
@@ -61,7 +61,48 @@ impl Parse for AmbiguousElement {
     }
 }
 
-impl ToTokens for AmbiguousElement {
+impl Parse for AmbiguousElement<AS_HTML> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        // Try to parse as an absolute path and immediately defer to the componetn
+        if input.peek(Token![::]) {
+            return input
+                .parse::<Component<AS_HTML>>()
+                .map(|c| AmbiguousElement::Component(c));
+        }
+
+        // If not an absolute path, then parse the ident and check if it's a valid tag
+
+        if let Ok(pat) = input.fork().parse::<syn::Path>() {
+            if pat.segments.len() > 1 {
+                return input
+                    .parse::<Component<AS_HTML>>()
+                    .map(|c| AmbiguousElement::Component(c));
+            }
+        }
+
+        if let Ok(name) = input.fork().parse::<Ident>() {
+            let name_str = name.to_string();
+
+            let first_char = name_str.chars().next().unwrap();
+            if first_char.is_ascii_uppercase() {
+                input
+                    .parse::<Component<AS_HTML>>()
+                    .map(|c| AmbiguousElement::Component(c))
+            } else {
+                input
+                    .parse::<Element<AS_HTML>>()
+                    .map(|c| AmbiguousElement::Element(c))
+            }
+        } else {
+            if input.peek(LitStr) {
+                panic!("it's actually a litstr");
+            }
+            Err(Error::new(input.span(), "Not a valid Html tag"))
+        }
+    }
+}
+
+impl<const AS: HTML_OR_RSX> ToTokens for AmbiguousElement<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match self {
             AmbiguousElement::Element(el) => el.to_tokens(tokens),

+ 131 - 0
packages/core-macro/src/rsx/body.rs

@@ -0,0 +1,131 @@
+use crate::util::is_valid_tag;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{
+    parse::{Parse, ParseStream},
+    Error, Ident, Result, Token,
+};
+
+use super::*;
+
+pub struct RsxBody<const AS: HTML_OR_RSX> {
+    custom_context: Option<Ident>,
+    roots: Vec<BodyNode<AS>>,
+}
+
+/// The custom rusty variant of parsing rsx!
+impl Parse for RsxBody<AS_RSX> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        // if input.peek(LitStr) {
+        //     return input.parse::<LitStr>()?.parse::<RsxRender>();
+        // }
+
+        // try to parse the first ident and comma
+        let custom_context =
+            if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
+                let _ = input.parse::<Token![in]>()?;
+                let name = input.parse::<Ident>()?;
+                if is_valid_tag(&name.to_string()) {
+                    return Err(Error::new(
+                        input.span(),
+                        "Custom context cannot be an html element name",
+                    ));
+                } else {
+                    input.parse::<Token![,]>().unwrap();
+                    Some(name)
+                }
+            } else {
+                None
+            };
+
+        let mut body = Vec::new();
+        let mut children = Vec::new();
+        let mut manual_props = None;
+        let cfg: BodyParseConfig<AS_RSX> = BodyParseConfig {
+            allow_children: true,
+            allow_fields: false,
+            allow_manual_props: false,
+        };
+        cfg.parse_component_body(input, &mut body, &mut children, &mut manual_props)?;
+
+        Ok(Self {
+            roots: children,
+            custom_context,
+        })
+    }
+}
+
+/// The HTML variant of parsing rsx!
+impl Parse for RsxBody<AS_HTML> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        // if input.peek(LitStr) {
+        //     return input.parse::<LitStr>()?.parse::<RsxRender>();
+        // }
+
+        // try to parse the first ident and comma
+        let custom_context =
+            if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
+                let _ = input.parse::<Token![in]>()?;
+                let name = input.parse::<Ident>()?;
+                if is_valid_tag(&name.to_string()) {
+                    return Err(Error::new(
+                        input.span(),
+                        "Custom context cannot be an html element name",
+                    ));
+                } else {
+                    input.parse::<Token![,]>().unwrap();
+                    Some(name)
+                }
+            } else {
+                None
+            };
+
+        let mut body = Vec::new();
+        let mut children = Vec::new();
+        let mut manual_props = None;
+
+        let cfg: BodyParseConfig<AS_HTML> = BodyParseConfig {
+            allow_children: true,
+            allow_fields: false,
+            allow_manual_props: false,
+        };
+        cfg.parse_component_body(input, &mut body, &mut children, &mut manual_props)?;
+
+        Ok(Self {
+            roots: children,
+            custom_context,
+        })
+    }
+}
+
+/// Serialize the same way, regardless of flavor
+impl<const A: HTML_OR_RSX> ToTokens for RsxBody<A> {
+    fn to_tokens(&self, out_tokens: &mut TokenStream2) {
+        let inner = if self.roots.len() == 1 {
+            let inner = &self.roots[0];
+            quote! {#inner}
+        } else {
+            let childs = &self.roots;
+            quote! { __cx.fragment_from_iter([ #(#childs),* ]) }
+        };
+
+        match &self.custom_context {
+            // The `in cx` pattern allows directly rendering
+            Some(ident) => out_tokens.append_all(quote! {
+                #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
+                    use dioxus_elements::GlobalAttributes;
+
+                    #inner
+                }))
+            }),
+            // Otherwise we just build the LazyNode wrapper
+            None => out_tokens.append_all(quote! {
+                dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
+                    use dioxus_elements::GlobalAttributes;
+
+                    #inner
+                 })
+            }),
+        };
+    }
+}

+ 163 - 65
packages/core-macro/src/rsx/component.rs

@@ -19,18 +19,18 @@ use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
     ext::IdentExt,
     parse::{Parse, ParseBuffer, ParseStream},
-    token, Expr, ExprClosure, Ident, Result, Token,
+    token, Error, Expr, ExprClosure, Ident, Result, Token,
 };
 
-pub struct Component {
+pub struct Component<const AS: HTML_OR_RSX> {
     // accept any path-like argument
     name: syn::Path,
-    body: Vec<ComponentField>,
-    children: Vec<BodyNode>,
+    body: Vec<ComponentField<AS>>,
+    children: Vec<BodyNode<AS>>,
     manual_props: Option<Expr>,
 }
 
-impl Parse for Component {
+impl Parse for Component<AS_RSX> {
     fn parse(stream: ParseStream) -> Result<Self> {
         // let name = s.parse::<syn::ExprPath>()?;
         // todo: look into somehow getting the crate/super/etc
@@ -41,21 +41,45 @@ impl Parse for Component {
         let content: ParseBuffer;
         syn::braced!(content in stream);
 
-        let mut body: Vec<ComponentField> = Vec::new();
-        let mut children: Vec<BodyNode> = Vec::new();
+        let mut body: Vec<ComponentField<AS_RSX>> = Vec::new();
+        let mut children: Vec<BodyNode<AS_RSX>> = Vec::new();
         let mut manual_props = None;
 
-        parse_component_body(
-            &content,
-            &BodyParseConfig {
-                allow_children: true,
-                allow_fields: true,
-                allow_manual_props: true,
-            },
-            &mut body,
-            &mut children,
-            &mut manual_props,
-        )?;
+        let cfg: BodyParseConfig<AS_RSX> = BodyParseConfig {
+            allow_children: true,
+            allow_fields: true,
+            allow_manual_props: true,
+        };
+
+        cfg.parse_component_body(&content, &mut body, &mut children, &mut manual_props)?;
+
+        Ok(Self {
+            name,
+            body,
+            children,
+            manual_props,
+        })
+    }
+}
+impl Parse for Component<AS_HTML> {
+    fn parse(stream: ParseStream) -> Result<Self> {
+        let name = syn::Path::parse_mod_style(stream)?;
+
+        // parse the guts
+        let content: ParseBuffer;
+        syn::braced!(content in stream);
+
+        let mut body: Vec<ComponentField<AS_HTML>> = Vec::new();
+        let mut children: Vec<BodyNode<AS_HTML>> = Vec::new();
+        let mut manual_props = None;
+
+        let cfg: BodyParseConfig<AS_HTML> = BodyParseConfig {
+            allow_children: true,
+            allow_fields: true,
+            allow_manual_props: true,
+        };
+
+        cfg.parse_component_body(&content, &mut body, &mut children, &mut manual_props)?;
 
         Ok(Self {
             name,
@@ -66,64 +90,117 @@ impl Parse for Component {
     }
 }
 
-pub struct BodyParseConfig {
+pub struct BodyParseConfig<const AS: HTML_OR_RSX> {
     pub allow_fields: bool,
     pub allow_children: bool,
     pub allow_manual_props: bool,
 }
+impl BodyParseConfig<AS_RSX> {
+    // todo: unify this body parsing for both elements and components
+    // both are style rather ad-hoc, though components are currently more configured
+    pub fn parse_component_body(
+        &self,
+        content: &ParseBuffer,
+        body: &mut Vec<ComponentField<AS_RSX>>,
+        children: &mut Vec<BodyNode<AS_RSX>>,
+        manual_props: &mut Option<Expr>,
+    ) -> Result<()> {
+        'parsing: loop {
+            // [1] Break if empty
+            if content.is_empty() {
+                break 'parsing;
+            }
 
-// todo: unify this body parsing for both elements and components
-// both are style rather ad-hoc, though components are currently more configured
-pub fn parse_component_body(
-    content: &ParseBuffer,
-    cfg: &BodyParseConfig,
-    body: &mut Vec<ComponentField>,
-    children: &mut Vec<BodyNode>,
-    manual_props: &mut Option<Expr>,
-) -> Result<()> {
-    'parsing: loop {
-        // [1] Break if empty
-        if content.is_empty() {
-            break 'parsing;
-        }
+            if content.peek(Token![..]) {
+                if !self.allow_manual_props {
+                    return Err(Error::new(
+                        content.span(),
+                        "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.",
+                    ));
+                }
+                content.parse::<Token![..]>()?;
+                *manual_props = Some(content.parse::<Expr>()?);
+            } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
+                if !self.allow_fields {
+                    return Err(Error::new(
+                        content.span(),
+                        "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.",
+                    ));
+                }
+                body.push(content.parse::<ComponentField<AS_RSX>>()?);
+            } else {
+                if !self.allow_children {
+                    return Err(Error::new(
+                        content.span(),
+                        "This item is not allowed to accept children.",
+                    ));
+                }
+                children.push(content.parse::<BodyNode<AS_RSX>>()?);
+            }
 
-        if content.peek(Token![..]) {
-            if !cfg.allow_manual_props {
-                return Err(Error::new(
-                    content.span(),
-                    "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.",
-                ));
+            // consume comma if it exists
+            // we don't actually care if there *are* commas between attrs
+            if content.peek(Token![,]) {
+                let _ = content.parse::<Token![,]>();
             }
-            content.parse::<Token![..]>()?;
-            *manual_props = Some(content.parse::<Expr>()?);
-        } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
-            if !cfg.allow_fields {
-                return Err(Error::new(
-                    content.span(),
-                    "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.",
-                ));
+        }
+        Ok(())
+    }
+}
+impl BodyParseConfig<AS_HTML> {
+    // todo: unify this body parsing for both elements and components
+    // both are style rather ad-hoc, though components are currently more configured
+    pub fn parse_component_body(
+        &self,
+        content: &ParseBuffer,
+        body: &mut Vec<ComponentField<AS_HTML>>,
+        children: &mut Vec<BodyNode<AS_HTML>>,
+        manual_props: &mut Option<Expr>,
+    ) -> Result<()> {
+        'parsing: loop {
+            // [1] Break if empty
+            if content.is_empty() {
+                break 'parsing;
             }
-            body.push(content.parse::<ComponentField>()?);
-        } else {
-            if !cfg.allow_children {
-                return Err(Error::new(
-                    content.span(),
-                    "This item is not allowed to accept children.",
-                ));
+
+            if content.peek(Token![..]) {
+                if !self.allow_manual_props {
+                    return Err(Error::new(
+                        content.span(),
+                        "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.",
+                    ));
+                }
+                content.parse::<Token![..]>()?;
+                *manual_props = Some(content.parse::<Expr>()?);
+            } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
+                if !self.allow_fields {
+                    return Err(Error::new(
+                        content.span(),
+                        "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.",
+                    ));
+                }
+                body.push(content.parse::<ComponentField<AS_HTML>>()?);
+            } else {
+                if !self.allow_children {
+                    return Err(Error::new(
+                        content.span(),
+                        "This item is not allowed to accept children.",
+                    ));
+                }
+                children.push(content.parse::<BodyNode<AS_HTML>>()?);
             }
-            children.push(content.parse::<BodyNode>()?);
-        }
 
-        // consume comma if it exists
-        // we don't actually care if there *are* commas between attrs
-        if content.peek(Token![,]) {
-            let _ = content.parse::<Token![,]>();
+            // consume comma if it exists
+            // we don't actually care if there *are* commas between attrs
+            if content.peek(Token![,]) {
+                let _ = content.parse::<Token![,]>();
+            }
         }
+        Ok(())
     }
-    Ok(())
 }
 
-impl ToTokens for Component {
+impl<const AS: HTML_OR_RSX> ToTokens for Component<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
 
@@ -195,7 +272,7 @@ impl ToTokens for Component {
 }
 
 // the struct's fields info
-pub struct ComponentField {
+pub struct ComponentField<const AS: HTML_OR_RSX> {
     name: Ident,
     content: ContentField,
 }
@@ -222,7 +299,28 @@ impl ToTokens for ContentField {
     }
 }
 
-impl Parse for ComponentField {
+impl Parse for ComponentField<AS_RSX> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let name = Ident::parse_any(input)?;
+        input.parse::<Token![:]>()?;
+
+        let name_str = name.to_string();
+        let content = if name_str.starts_with("on") {
+            if input.peek(token::Brace) {
+                let content;
+                syn::braced!(content in input);
+                ContentField::OnHandlerRaw(content.parse()?)
+            } else {
+                ContentField::OnHandler(input.parse()?)
+            }
+        } else {
+            ContentField::ManExpr(input.parse::<Expr>()?)
+        };
+
+        Ok(Self { name, content })
+    }
+}
+impl Parse for ComponentField<AS_HTML> {
     fn parse(input: ParseStream) -> Result<Self> {
         let name = Ident::parse_any(input)?;
         input.parse::<Token![:]>()?;
@@ -244,7 +342,7 @@ impl Parse for ComponentField {
     }
 }
 
-impl ToTokens for ComponentField {
+impl<const AS: HTML_OR_RSX> ToTokens for ComponentField<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let ComponentField { name, content, .. } = self;
         tokens.append_all(quote! {

+ 69 - 20
packages/core-macro/src/rsx/element.rs

@@ -11,35 +11,65 @@ use syn::{
 // =======================================
 // Parse the VNode::Element type
 // =======================================
-pub struct Element {
+pub struct Element<const AS: HTML_OR_RSX> {
     name: Ident,
     key: Option<AttrType>,
     attributes: Vec<ElementAttr>,
     listeners: Vec<ElementAttr>,
-    children: Vec<BodyNode>,
+    children: Vec<BodyNode<AS>>,
     is_static: bool,
 }
 
-impl ToTokens for Element {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        let name = &self.name;
-        let attr = &self.attributes;
-        let childs = &self.children;
-        let listeners = &self.listeners;
+impl Parse for Element<AS_RSX> {
+    fn parse(stream: ParseStream) -> Result<Self> {
+        let name = Ident::parse(stream)?;
 
-        tokens.append_all(quote! {
-            __cx.element(
-                dioxus_elements::#name,
-                __cx.bump().alloc([ #(#listeners),* ]),
-                __cx.bump().alloc([ #(#attr),* ]),
-                __cx.bump().alloc([ #(#childs),* ]),
-                None,
-            )
-        });
+        // parse the guts
+        let content: ParseBuffer;
+        syn::braced!(content in stream);
+
+        let mut attributes: Vec<ElementAttr> = vec![];
+        let mut listeners: Vec<ElementAttr> = vec![];
+        let mut children: Vec<BodyNode<AS_RSX>> = vec![];
+        let mut key = None;
+
+        'parsing: loop {
+            // [1] Break if empty
+            if content.is_empty() {
+                break 'parsing;
+            }
+
+            if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
+                parse_element_body(
+                    &content,
+                    &mut attributes,
+                    &mut listeners,
+                    &mut key,
+                    name.clone(),
+                )?;
+            } else {
+                children.push(content.parse::<BodyNode<AS_RSX>>()?);
+            }
+
+            // consume comma if it exists
+            // we don't actually care if there *are* commas after elements/text
+            if content.peek(Token![,]) {
+                let _ = content.parse::<Token![,]>();
+            }
+        }
+
+        Ok(Self {
+            key,
+            name,
+            attributes,
+            children,
+            listeners,
+            is_static: false,
+        })
     }
 }
 
-impl Parse for Element {
+impl Parse for Element<AS_HTML> {
     fn parse(stream: ParseStream) -> Result<Self> {
         let name = Ident::parse(stream)?;
 
@@ -49,7 +79,7 @@ impl Parse for Element {
 
         let mut attributes: Vec<ElementAttr> = vec![];
         let mut listeners: Vec<ElementAttr> = vec![];
-        let mut children: Vec<BodyNode> = vec![];
+        let mut children: Vec<BodyNode<AS_HTML>> = vec![];
         let mut key = None;
 
         'parsing: loop {
@@ -67,7 +97,7 @@ impl Parse for Element {
                     name.clone(),
                 )?;
             } else {
-                children.push(content.parse::<BodyNode>()?);
+                children.push(content.parse::<BodyNode<AS_HTML>>()?);
             }
 
             // consume comma if it exists
@@ -88,6 +118,25 @@ impl Parse for Element {
     }
 }
 
+impl<const AS: HTML_OR_RSX> ToTokens for Element<AS> {
+    fn to_tokens(&self, tokens: &mut TokenStream2) {
+        let name = &self.name;
+        let attr = &self.attributes;
+        let childs = &self.children;
+        let listeners = &self.listeners;
+
+        tokens.append_all(quote! {
+            __cx.element(
+                dioxus_elements::#name,
+                __cx.bump().alloc([ #(#listeners),* ]),
+                __cx.bump().alloc([ #(#attr),* ]),
+                __cx.bump().alloc([ #(#childs),* ]),
+                None,
+            )
+        });
+    }
+}
+
 /// =======================================
 /// Parse a VElement's Attributes
 /// =======================================

+ 27 - 6
packages/core-macro/src/rsx/fragment.rs

@@ -10,7 +10,7 @@
 
 use syn::parse::ParseBuffer;
 
-use super::AmbiguousElement;
+use super::{AmbiguousElement, AS_HTML, AS_RSX, HTML_OR_RSX};
 
 use {
     proc_macro::TokenStream,
@@ -23,10 +23,31 @@ use {
     },
 };
 
-pub struct Fragment {
-    children: Vec<AmbiguousElement>,
+pub struct Fragment<const AS: HTML_OR_RSX> {
+    children: Vec<AmbiguousElement<AS>>,
 }
-impl Parse for Fragment {
+
+impl Parse for Fragment<AS_RSX> {
+    fn parse(input: ParseStream) -> Result<Self> {
+        input.parse::<Ident>()?;
+
+        let children = Vec::new();
+
+        // parse the guts
+        let content: ParseBuffer;
+        syn::braced!(content in input);
+        while !content.is_empty() {
+            content.parse::<AmbiguousElement<AS_RSX>>()?;
+
+            if content.peek(Token![,]) {
+                let _ = content.parse::<Token![,]>();
+            }
+        }
+        Ok(Self { children })
+    }
+}
+
+impl Parse for Fragment<AS_HTML> {
     fn parse(input: ParseStream) -> Result<Self> {
         input.parse::<Ident>()?;
 
@@ -36,7 +57,7 @@ impl Parse for Fragment {
         let content: ParseBuffer;
         syn::braced!(content in input);
         while !content.is_empty() {
-            content.parse::<AmbiguousElement>()?;
+            content.parse::<AmbiguousElement<AS_HTML>>()?;
 
             if content.peek(Token![,]) {
                 let _ = content.parse::<Token![,]>();
@@ -46,7 +67,7 @@ impl Parse for Fragment {
     }
 }
 
-impl ToTokens for Fragment {
+impl<const AS: HTML_OR_RSX> ToTokens for Fragment<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let childs = &self.children;
         let children = quote! {

+ 5 - 89
packages/core-macro/src/rsx/mod.rs

@@ -12,6 +12,7 @@
 //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
 
 mod ambiguous;
+mod body;
 mod component;
 mod element;
 mod fragment;
@@ -19,97 +20,12 @@ mod node;
 
 // Re-export the namespaces into each other
 pub use ambiguous::*;
+pub use body::*;
 pub use component::*;
 pub use element::*;
 pub use fragment::*;
 pub use node::*;
 
-use crate::util::is_valid_tag;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::{quote, ToTokens, TokenStreamExt};
-use syn::{
-    parse::{Parse, ParseStream},
-    Error, Ident, LitStr, Result, Token,
-};
-
-pub struct RsxBody {
-    custom_context: Option<Ident>,
-    roots: Vec<BodyNode>,
-}
-
-impl Parse for RsxBody {
-    fn parse(input: ParseStream) -> Result<Self> {
-        // if input.peek(LitStr) {
-        //     return input.parse::<LitStr>()?.parse::<RsxRender>();
-        // }
-
-        // try to parse the first ident and comma
-        let custom_context =
-            if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
-                let _ = input.parse::<Token![in]>()?;
-                let name = input.parse::<Ident>()?;
-                if is_valid_tag(&name.to_string()) {
-                    return Err(Error::new(
-                        input.span(),
-                        "Custom context cannot be an html element name",
-                    ));
-                } else {
-                    input.parse::<Token![,]>().unwrap();
-                    Some(name)
-                }
-            } else {
-                None
-            };
-
-        let mut body = Vec::new();
-        let mut children = Vec::new();
-        let mut manual_props = None;
-        parse_component_body(
-            input,
-            &BodyParseConfig {
-                allow_children: true,
-                allow_fields: false,
-                allow_manual_props: false,
-            },
-            &mut body,
-            &mut children,
-            &mut manual_props,
-        )?;
-
-        Ok(Self {
-            roots: children,
-            custom_context,
-        })
-    }
-}
-
-impl ToTokens for RsxBody {
-    fn to_tokens(&self, out_tokens: &mut TokenStream2) {
-        let inner = if self.roots.len() == 1 {
-            let inner = &self.roots[0];
-            quote! {#inner}
-        } else {
-            let childs = &self.roots;
-            quote! { __cx.fragment_from_iter([ #(#childs),* ]) }
-        };
-
-        match &self.custom_context {
-            // The `in cx` pattern allows directly rendering
-            Some(ident) => out_tokens.append_all(quote! {
-                #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
-                    use dioxus_elements::GlobalAttributes;
-
-                    #inner
-                }))
-            }),
-            // Otherwise we just build the LazyNode wrapper
-            None => out_tokens.append_all(quote! {
-                dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
-                    use dioxus_elements::GlobalAttributes;
-
-                    #inner
-                 })
-            }),
-        };
-    }
-}
+pub type HTML_OR_RSX = bool;
+pub const AS_HTML: bool = true;
+pub const AS_RSX: bool = false;

+ 27 - 5
packages/core-macro/src/rsx/node.rs

@@ -10,13 +10,13 @@ use syn::{
 // ==============================================
 // Parse any div {} as a VElement
 // ==============================================
-pub enum BodyNode {
-    Element(AmbiguousElement),
+pub enum BodyNode<const AS: HTML_OR_RSX> {
+    Element(AmbiguousElement<AS>),
     Text(TextNode),
     RawExpr(Expr),
 }
 
-impl Parse for BodyNode {
+impl Parse for BodyNode<AS_RSX> {
     fn parse(stream: ParseStream) -> Result<Self> {
         // Supposedly this approach is discouraged due to inability to return proper errors
         // TODO: Rework this to provide more informative errors
@@ -31,11 +31,33 @@ impl Parse for BodyNode {
             return Ok(BodyNode::Text(stream.parse::<TextNode>()?));
         }
 
-        Ok(BodyNode::Element(stream.parse::<AmbiguousElement>()?))
+        Ok(BodyNode::Element(
+            stream.parse::<AmbiguousElement<AS_RSX>>()?,
+        ))
+    }
+}
+impl Parse for BodyNode<AS_HTML> {
+    fn parse(stream: ParseStream) -> Result<Self> {
+        // Supposedly this approach is discouraged due to inability to return proper errors
+        // TODO: Rework this to provide more informative errors
+
+        if stream.peek(token::Brace) {
+            let content;
+            syn::braced!(content in stream);
+            return Ok(BodyNode::RawExpr(content.parse::<Expr>()?));
+        }
+
+        if stream.peek(LitStr) {
+            return Ok(BodyNode::Text(stream.parse::<TextNode>()?));
+        }
+
+        Ok(BodyNode::Element(
+            stream.parse::<AmbiguousElement<AS_HTML>>()?,
+        ))
     }
 }
 
-impl ToTokens for BodyNode {
+impl<const AS: HTML_OR_RSX> ToTokens for BodyNode<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match &self {
             BodyNode::Element(el) => el.to_tokens(tokens),

+ 0 - 0
packages/core-macro/src/styles/calc.rs → packages/core-macro/styles/calc.rs


+ 0 - 0
packages/core-macro/src/styles/codegen.rs → packages/core-macro/styles/codegen.rs


+ 0 - 0
packages/core-macro/src/styles/color.rs → packages/core-macro/styles/color.rs


+ 0 - 0
packages/core-macro/src/styles/mod.rs → packages/core-macro/styles/mod.rs


+ 0 - 0
packages/core-macro/src/styles/string/lexer.rs → packages/core-macro/styles/string/lexer.rs


+ 0 - 0
packages/core-macro/src/styles/string/mod.rs → packages/core-macro/styles/string/mod.rs


+ 0 - 0
packages/core-macro/src/styles/syn_parse.rs → packages/core-macro/styles/syn_parse.rs