From 160d86abbe1b325e3123202aef29025dcd96f4eb Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 21 Jul 2021 22:55:04 -0400 Subject: [PATCH] wip: serious refactor with const generics --- packages/core-macro/src/html/mod.rs | 27 -- packages/core-macro/src/lib.rs | 69 +---- packages/core-macro/src/rsx/ambiguous.rs | 59 ++++- packages/core-macro/src/rsx/body.rs | 131 ++++++++++ packages/core-macro/src/rsx/component.rs | 236 +++++++++++++----- packages/core-macro/src/rsx/element.rs | 97 +++++-- packages/core-macro/src/rsx/fragment.rs | 33 ++- packages/core-macro/src/rsx/mod.rs | 94 +------ packages/core-macro/src/rsx/node.rs | 32 ++- packages/core-macro/{src => }/styles/calc.rs | 0 .../core-macro/{src => }/styles/codegen.rs | 0 packages/core-macro/{src => }/styles/color.rs | 0 packages/core-macro/{src => }/styles/mod.rs | 0 .../{src => }/styles/string/lexer.rs | 0 .../core-macro/{src => }/styles/string/mod.rs | 0 .../core-macro/{src => }/styles/syn_parse.rs | 0 16 files changed, 492 insertions(+), 286 deletions(-) delete mode 100644 packages/core-macro/src/html/mod.rs create mode 100644 packages/core-macro/src/rsx/body.rs rename packages/core-macro/{src => }/styles/calc.rs (100%) rename packages/core-macro/{src => }/styles/codegen.rs (100%) rename packages/core-macro/{src => }/styles/color.rs (100%) rename packages/core-macro/{src => }/styles/mod.rs (100%) rename packages/core-macro/{src => }/styles/string/lexer.rs (100%) rename packages/core-macro/{src => }/styles/string/mod.rs (100%) rename packages/core-macro/{src => }/styles/syn_parse.rs (100%) diff --git a/packages/core-macro/src/html/mod.rs b/packages/core-macro/src/html/mod.rs deleted file mode 100644 index 6ad20acad..000000000 --- a/packages/core-macro/src/html/mod.rs +++ /dev/null @@ -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 { - 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); diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 6cee65f8d..8c3c00bf1 100644 --- a/packages/core-macro/src/lib.rs +++ b/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::(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::(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::(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::(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::(s) { + match syn::parse::>(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::>(s) { Err(e) => e.to_compile_error().into(), Ok(s) => s.to_token_stream().into(), } diff --git a/packages/core-macro/src/rsx/ambiguous.rs b/packages/core-macro/src/rsx/ambiguous.rs index 7b894c6e2..d75b07fc5 100644 --- a/packages/core-macro/src/rsx/ambiguous.rs +++ b/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 { + Element(Element), + Component(Component), } -impl Parse for AmbiguousElement { +impl Parse for AmbiguousElement { fn parse(input: ParseStream) -> Result { // Try to parse as an absolute path and immediately defer to the componetn if input.peek(Token![::]) { return input - .parse::() + .parse::>() .map(|c| AmbiguousElement::Component(c)); } @@ -34,7 +34,7 @@ impl Parse for AmbiguousElement { if let Ok(pat) = input.fork().parse::() { if pat.segments.len() > 1 { return input - .parse::() + .parse::>() .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::() + .parse::>() .map(|c| AmbiguousElement::Component(c)) } else { input - .parse::() + .parse::>() .map(|c| AmbiguousElement::Element(c)) } } else { @@ -61,7 +61,48 @@ impl Parse for AmbiguousElement { } } -impl ToTokens for AmbiguousElement { +impl Parse for AmbiguousElement { + fn parse(input: ParseStream) -> Result { + // Try to parse as an absolute path and immediately defer to the componetn + if input.peek(Token![::]) { + return input + .parse::>() + .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::() { + if pat.segments.len() > 1 { + return input + .parse::>() + .map(|c| AmbiguousElement::Component(c)); + } + } + + if let Ok(name) = input.fork().parse::() { + let name_str = name.to_string(); + + let first_char = name_str.chars().next().unwrap(); + if first_char.is_ascii_uppercase() { + input + .parse::>() + .map(|c| AmbiguousElement::Component(c)) + } else { + input + .parse::>() + .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 ToTokens for AmbiguousElement { fn to_tokens(&self, tokens: &mut TokenStream2) { match self { AmbiguousElement::Element(el) => el.to_tokens(tokens), diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs new file mode 100644 index 000000000..cfbc405d2 --- /dev/null +++ b/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 { + custom_context: Option, + roots: Vec>, +} + +/// The custom rusty variant of parsing rsx! +impl Parse for RsxBody { + fn parse(input: ParseStream) -> Result { + // if input.peek(LitStr) { + // return input.parse::()?.parse::(); + // } + + // 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::()?; + let name = input.parse::()?; + if is_valid_tag(&name.to_string()) { + return Err(Error::new( + input.span(), + "Custom context cannot be an html element name", + )); + } else { + input.parse::().unwrap(); + Some(name) + } + } else { + None + }; + + let mut body = Vec::new(); + let mut children = Vec::new(); + let mut manual_props = None; + let cfg: BodyParseConfig = 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 { + fn parse(input: ParseStream) -> Result { + // if input.peek(LitStr) { + // return input.parse::()?.parse::(); + // } + + // 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::()?; + let name = input.parse::()?; + if is_valid_tag(&name.to_string()) { + return Err(Error::new( + input.span(), + "Custom context cannot be an html element name", + )); + } else { + input.parse::().unwrap(); + Some(name) + } + } else { + None + }; + + let mut body = Vec::new(); + let mut children = Vec::new(); + let mut manual_props = None; + + let cfg: BodyParseConfig = 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 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 + }) + }), + }; + } +} diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index c3a5c143b..79a4d26c4 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/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 { // accept any path-like argument name: syn::Path, - body: Vec, - children: Vec, + body: Vec>, + children: Vec>, manual_props: Option, } -impl Parse for Component { +impl Parse for Component { fn parse(stream: ParseStream) -> Result { // let name = s.parse::()?; // 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 = Vec::new(); - let mut children: Vec = Vec::new(); + let mut body: Vec> = Vec::new(); + let mut children: Vec> = 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 = 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 { + fn parse(stream: ParseStream) -> Result { + let name = syn::Path::parse_mod_style(stream)?; + + // parse the guts + let content: ParseBuffer; + syn::braced!(content in stream); + + let mut body: Vec> = Vec::new(); + let mut children: Vec> = Vec::new(); + let mut manual_props = None; + + let cfg: BodyParseConfig = 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 { pub allow_fields: bool, pub allow_children: bool, pub allow_manual_props: bool, } - -// 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, - children: &mut Vec, - manual_props: &mut Option, -) -> Result<()> { - 'parsing: loop { - // [1] Break if empty - if content.is_empty() { - break 'parsing; - } - - 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.", - )); +impl BodyParseConfig { + // 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>, + children: &mut Vec>, + manual_props: &mut Option, + ) -> Result<()> { + 'parsing: loop { + // [1] Break if empty + if content.is_empty() { + break 'parsing; } - content.parse::()?; - *manual_props = Some(content.parse::()?); - } 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.", - )); - } - body.push(content.parse::()?); - } else { - if !cfg.allow_children { - return Err(Error::new( - content.span(), - "This item is not allowed to accept children.", - )); - } - children.push(content.parse::()?); - } - // consume comma if it exists - // we don't actually care if there *are* commas between attrs - if content.peek(Token![,]) { - let _ = content.parse::(); + 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::()?; + *manual_props = Some(content.parse::()?); + } 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::>()?); + } else { + if !self.allow_children { + return Err(Error::new( + content.span(), + "This item is not allowed to accept children.", + )); + } + children.push(content.parse::>()?); + } + + // consume comma if it exists + // we don't actually care if there *are* commas between attrs + if content.peek(Token![,]) { + let _ = content.parse::(); + } } + Ok(()) + } +} +impl BodyParseConfig { + // 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>, + children: &mut Vec>, + manual_props: &mut Option, + ) -> 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::()?; + *manual_props = Some(content.parse::()?); + } 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::>()?); + } else { + if !self.allow_children { + return Err(Error::new( + content.span(), + "This item is not allowed to accept children.", + )); + } + children.push(content.parse::>()?); + } + + // consume comma if it exists + // we don't actually care if there *are* commas between attrs + if content.peek(Token![,]) { + let _ = content.parse::(); + } + } + Ok(()) } - Ok(()) } -impl ToTokens for Component { +impl ToTokens for Component { 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 { name: Ident, content: ContentField, } @@ -222,7 +299,28 @@ impl ToTokens for ContentField { } } -impl Parse for ComponentField { +impl Parse for ComponentField { + fn parse(input: ParseStream) -> Result { + let name = Ident::parse_any(input)?; + input.parse::()?; + + 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::()?) + }; + + Ok(Self { name, content }) + } +} +impl Parse for ComponentField { fn parse(input: ParseStream) -> Result { let name = Ident::parse_any(input)?; input.parse::()?; @@ -244,7 +342,7 @@ impl Parse for ComponentField { } } -impl ToTokens for ComponentField { +impl ToTokens for ComponentField { fn to_tokens(&self, tokens: &mut TokenStream2) { let ComponentField { name, content, .. } = self; tokens.append_all(quote! { diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index 41d39fe9d..af0e6daa3 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -11,35 +11,16 @@ use syn::{ // ======================================= // Parse the VNode::Element type // ======================================= -pub struct Element { +pub struct Element { name: Ident, key: Option, attributes: Vec, listeners: Vec, - children: Vec, + children: Vec>, 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; - - tokens.append_all(quote! { - __cx.element( - dioxus_elements::#name, - __cx.bump().alloc([ #(#listeners),* ]), - __cx.bump().alloc([ #(#attr),* ]), - __cx.bump().alloc([ #(#childs),* ]), - None, - ) - }); - } -} - -impl Parse for Element { +impl Parse for Element { fn parse(stream: ParseStream) -> Result { let name = Ident::parse(stream)?; @@ -49,7 +30,7 @@ impl Parse for Element { let mut attributes: Vec = vec![]; let mut listeners: Vec = vec![]; - let mut children: Vec = vec![]; + let mut children: Vec> = vec![]; let mut key = None; 'parsing: loop { @@ -67,7 +48,7 @@ impl Parse for Element { name.clone(), )?; } else { - children.push(content.parse::()?); + children.push(content.parse::>()?); } // consume comma if it exists @@ -88,6 +69,74 @@ impl Parse for Element { } } +impl Parse for Element { + fn parse(stream: ParseStream) -> Result { + let name = Ident::parse(stream)?; + + // parse the guts + let content: ParseBuffer; + syn::braced!(content in stream); + + let mut attributes: Vec = vec![]; + let mut listeners: Vec = vec![]; + let mut children: Vec> = 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::>()?); + } + + // consume comma if it exists + // we don't actually care if there *are* commas after elements/text + if content.peek(Token![,]) { + let _ = content.parse::(); + } + } + + Ok(Self { + key, + name, + attributes, + children, + listeners, + is_static: false, + }) + } +} + +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; + + 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 /// ======================================= diff --git a/packages/core-macro/src/rsx/fragment.rs b/packages/core-macro/src/rsx/fragment.rs index 812252a25..e1b57b2d4 100644 --- a/packages/core-macro/src/rsx/fragment.rs +++ b/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,11 @@ use { }, }; -pub struct Fragment { - children: Vec, +pub struct Fragment { + children: Vec>, } -impl Parse for Fragment { + +impl Parse for Fragment { fn parse(input: ParseStream) -> Result { input.parse::()?; @@ -36,7 +37,7 @@ impl Parse for Fragment { let content: ParseBuffer; syn::braced!(content in input); while !content.is_empty() { - content.parse::()?; + content.parse::>()?; if content.peek(Token![,]) { let _ = content.parse::(); @@ -46,7 +47,27 @@ impl Parse for Fragment { } } -impl ToTokens for Fragment { +impl Parse for Fragment { + fn parse(input: ParseStream) -> Result { + input.parse::()?; + + let children = Vec::new(); + + // parse the guts + let content: ParseBuffer; + syn::braced!(content in input); + while !content.is_empty() { + content.parse::>()?; + + if content.peek(Token![,]) { + let _ = content.parse::(); + } + } + Ok(Self { children }) + } +} + +impl ToTokens for Fragment { fn to_tokens(&self, tokens: &mut TokenStream2) { let childs = &self.children; let children = quote! { diff --git a/packages/core-macro/src/rsx/mod.rs b/packages/core-macro/src/rsx/mod.rs index c30a4d0d3..ce8bef36e 100644 --- a/packages/core-macro/src/rsx/mod.rs +++ b/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, - roots: Vec, -} - -impl Parse for RsxBody { - fn parse(input: ParseStream) -> Result { - // if input.peek(LitStr) { - // return input.parse::()?.parse::(); - // } - - // 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::()?; - let name = input.parse::()?; - if is_valid_tag(&name.to_string()) { - return Err(Error::new( - input.span(), - "Custom context cannot be an html element name", - )); - } else { - input.parse::().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; diff --git a/packages/core-macro/src/rsx/node.rs b/packages/core-macro/src/rsx/node.rs index e027711e6..33bb44e65 100644 --- a/packages/core-macro/src/rsx/node.rs +++ b/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 { + Element(AmbiguousElement), Text(TextNode), RawExpr(Expr), } -impl Parse for BodyNode { +impl Parse for BodyNode { fn parse(stream: ParseStream) -> Result { // 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::()?)); } - Ok(BodyNode::Element(stream.parse::()?)) + Ok(BodyNode::Element( + stream.parse::>()?, + )) + } +} +impl Parse for BodyNode { + fn parse(stream: ParseStream) -> Result { + // 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::()?)); + } + + if stream.peek(LitStr) { + return Ok(BodyNode::Text(stream.parse::()?)); + } + + Ok(BodyNode::Element( + stream.parse::>()?, + )) } } -impl ToTokens for BodyNode { +impl ToTokens for BodyNode { fn to_tokens(&self, tokens: &mut TokenStream2) { match &self { BodyNode::Element(el) => el.to_tokens(tokens), diff --git a/packages/core-macro/src/styles/calc.rs b/packages/core-macro/styles/calc.rs similarity index 100% rename from packages/core-macro/src/styles/calc.rs rename to packages/core-macro/styles/calc.rs diff --git a/packages/core-macro/src/styles/codegen.rs b/packages/core-macro/styles/codegen.rs similarity index 100% rename from packages/core-macro/src/styles/codegen.rs rename to packages/core-macro/styles/codegen.rs diff --git a/packages/core-macro/src/styles/color.rs b/packages/core-macro/styles/color.rs similarity index 100% rename from packages/core-macro/src/styles/color.rs rename to packages/core-macro/styles/color.rs diff --git a/packages/core-macro/src/styles/mod.rs b/packages/core-macro/styles/mod.rs similarity index 100% rename from packages/core-macro/src/styles/mod.rs rename to packages/core-macro/styles/mod.rs diff --git a/packages/core-macro/src/styles/string/lexer.rs b/packages/core-macro/styles/string/lexer.rs similarity index 100% rename from packages/core-macro/src/styles/string/lexer.rs rename to packages/core-macro/styles/string/lexer.rs diff --git a/packages/core-macro/src/styles/string/mod.rs b/packages/core-macro/styles/string/mod.rs similarity index 100% rename from packages/core-macro/src/styles/string/mod.rs rename to packages/core-macro/styles/string/mod.rs diff --git a/packages/core-macro/src/styles/syn_parse.rs b/packages/core-macro/styles/syn_parse.rs similarity index 100% rename from packages/core-macro/src/styles/syn_parse.rs rename to packages/core-macro/styles/syn_parse.rs