diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 128dc5179..49ed00901 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,7 @@ jobs: command: check test: + if: github.event.pull_request.draft == false name: Test Suite runs-on: ubuntu-latest steps: @@ -54,6 +55,7 @@ jobs: args: tests fmt: + if: github.event.pull_request.draft == false name: Rustfmt runs-on: ubuntu-latest steps: @@ -71,6 +73,7 @@ jobs: args: --all -- --check clippy: + if: github.event.pull_request.draft == false name: Clippy runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index 2ba3b55fa..d4e41af61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ dioxus-core = { path = "./packages/core", version = "^0.1.9" } dioxus-html = { path = "./packages/html", version = "^0.1.6", optional = true } dioxus-core-macro = { path = "./packages/core-macro", version = "^0.1.7", optional = true } dioxus-hooks = { path = "./packages/hooks", version = "^0.1.7", optional = true } +dioxus-rsx = { path = "./packages/rsx", optional = true } fermi = { path = "./packages/fermi", version = "^0.1.0", optional = true } dioxus-web = { path = "./packages/web", version = "^0.0.5", optional = true } @@ -30,7 +31,7 @@ dioxus-interpreter-js = { path = "./packages/interpreter", version = "^0.0.0", o [features] default = ["macro", "hooks", "html"] -macro = ["dioxus-core-macro"] +macro = ["dioxus-core-macro", "dioxus-rsx"] hooks = ["dioxus-hooks"] html = ["dioxus-html"] ssr = ["dioxus-ssr"] @@ -42,6 +43,7 @@ router = ["dioxus-router"] members = [ "packages/core", "packages/core-macro", + "packages/rsx", "packages/html", "packages/hooks", "packages/web", diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index 8026bcdaf..ed5d34e53 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] proc-macro = true [dependencies] +dioxus-rsx = { path = "../rsx" } proc-macro-error = "1.0.4" proc-macro2 = { version = "1.0.6" } quote = "1.0" diff --git a/packages/core-macro/src/htm.rs b/packages/core-macro/src/htm.rs deleted file mode 100644 index ca53096f6..000000000 --- a/packages/core-macro/src/htm.rs +++ /dev/null @@ -1,443 +0,0 @@ -//! -//! TODO: -//! - [ ] Support for VComponents -//! - [ ] Support for inline format in text -//! - [ ] Support for expressions in attribute positions -//! - [ ] Support for iterators -//! - [ ] support for inline html! -//! -//! -//! -//! -//! -//! -//! - -use { - proc_macro2::TokenStream as TokenStream2, - quote::{quote, ToTokens, TokenStreamExt}, - syn::{ - ext::IdentExt, - parse::{Parse, ParseStream}, - token, Error, Expr, ExprClosure, Ident, LitStr, Result, Token, - }, -}; - -// ============================================== -// Parse any stream coming from the html! macro -// ============================================== -pub struct HtmlRender { - kind: NodeOrList, -} - -impl Parse for HtmlRender { - fn parse(input: ParseStream) -> Result { - if input.peek(LitStr) { - return input.parse::()?.parse::(); - } - - // let __cx: Ident = s.parse()?; - // s.parse::()?; - // if elements are in an array, return a bumpalo::collections::Vec rather than a Node. - let kind = if input.peek(token::Bracket) { - let nodes_toks; - syn::bracketed!(nodes_toks in input); - let mut nodes: Vec> = vec![nodes_toks.parse()?]; - while nodes_toks.peek(Token![,]) { - nodes_toks.parse::()?; - nodes.push(nodes_toks.parse()?); - } - NodeOrList::List(NodeList(nodes)) - } else { - NodeOrList::Node(input.parse()?) - }; - Ok(HtmlRender { kind }) - } -} - -impl ToTokens for HtmlRender { - fn to_tokens(&self, out_tokens: &mut TokenStream2) { - let new_toks = ToToksCtx::new(&self.kind).to_token_stream(); - - // create a lazy tree that accepts a bump allocator - let final_tokens = quote! { - dioxus::prelude::LazyNodes::new(move |__cx| { - let bump = __cx.bump(); - - #new_toks - }) - }; - - final_tokens.to_tokens(out_tokens); - } -} - -/// ============================================= -/// Parse any child as a node or list of nodes -/// ============================================= -/// - [ ] Allow iterators -/// -/// -enum NodeOrList { - Node(Node), - List(NodeList), -} - -impl ToTokens for ToToksCtx<&NodeOrList> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self.inner { - NodeOrList::Node(node) => self.recurse(node).to_tokens(tokens), - NodeOrList::List(list) => self.recurse(list).to_tokens(tokens), - } - } -} - -struct NodeList(Vec>); - -impl ToTokens for ToToksCtx<&NodeList> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let nodes = self.inner.0.iter().map(|node| self.recurse(node)); - tokens.append_all(quote! { - dioxus::bumpalo::vec![in bump; - #(#nodes),* - ] - }); - } -} - -enum Node { - Element(Element), - Text(TextNode), -} - -impl ToTokens for ToToksCtx<&Node> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match &self.inner { - Node::Element(el) => self.recurse(el).to_tokens(tokens), - Node::Text(txt) => self.recurse(txt).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 { - Ok(if s.peek(Token![<]) { - Node::Element(s.parse()?) - } else { - Node::Text(s.parse()?) - }) - } -} - -/// ======================================= -/// Parse the VNode::Element type -/// ======================================= -/// - [ ] Allow VComponent -/// -/// -struct Element { - name: Ident, - attrs: Vec, - children: MaybeExpr>, -} - -impl ToTokens for ToToksCtx<&Element> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - // let __cx = self.__cx; - let name = &self.inner.name; - // let name = &self.inner.name.to_string(); - tokens.append_all(quote! { - __cx.element(dioxus_elements::#name) - // dioxus::builder::ElementBuilder::new( #name) - }); - for attr in self.inner.attrs.iter() { - self.recurse(attr).to_tokens(tokens); - } - - // if is_valid_svg_tag(&name.to_string()) { - // tokens.append_all(quote! { - // .namespace(Some("http://www.w3.org/2000/svg")) - // }); - // } - - 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); - for child in children { - 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 Element { - fn parse(s: ParseStream) -> Result { - s.parse::()?; - let name = Ident::parse_any(s)?; - let mut attrs = vec![]; - let _children: Vec = vec![]; - - // keep looking for attributes - while !s.peek(Token![>]) { - // self-closing - if s.peek(Token![/]) { - s.parse::()?; - s.parse::]>()?; - return Ok(Self { - name, - attrs, - children: MaybeExpr::Literal(vec![]), - }); - } - attrs.push(s.parse()?); - } - s.parse::]>()?; - - // 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::()?; - s.parse::()?; - let close = Ident::parse_any(s)?; - if close != name { - return Err(Error::new_spanned( - close, - "closing element does not match opening", - )); - } - s.parse::]>()?; - - Ok(Self { - name, - attrs, - children, - }) - } -} - -/// ======================================= -/// Parse a VElement's Attributes -/// ======================================= -/// - [ ] Allow expressions as attribute -/// -/// -struct Attr { - name: Ident, - ty: AttrType, -} - -impl Parse for Attr { - fn parse(s: ParseStream) -> Result { - let mut name = Ident::parse_any(s)?; - let name_str = name.to_string(); - s.parse::()?; - - // Check if this is an event handler - // If so, parse into literal tokens - let ty = if name_str.starts_with("on") { - // remove the "on" bit - name = Ident::new(name_str.trim_start_matches("on"), name.span()); - let content; - syn::braced!(content in s); - // AttrType::Value(content.parse()?) - AttrType::Event(content.parse()?) - // AttrType::Event(content.parse()?) - } else { - let lit_str = if name_str == "style" && s.peek(token::Brace) { - // special-case to deal with literal styles. - let outer; - syn::braced!(outer in s); - // double brace for inline style. - // todo!("Style support not ready yet"); - - // if outer.peek(token::Brace) { - // let inner; - // syn::braced!(inner in outer); - // let styles: Styles = inner.parse()?; - // MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site())) - // } else { - // just parse as an expression - MaybeExpr::Expr(outer.parse()?) - // } - } else { - s.parse()? - }; - AttrType::Value(lit_str) - }; - Ok(Attr { name, ty }) - } -} - -impl ToTokens for ToToksCtx<&Attr> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let name = self.inner.name.to_string(); - let _attr_stream = TokenStream2::new(); - match &self.inner.ty { - AttrType::Value(value) => { - let value = self.recurse(value); - if name == "xmlns" { - tokens.append_all(quote! { - .namespace(Some(#value)) - }); - } else { - tokens.append_all(quote! { - .attr(#name, format_args_f!(#value)) - }); - } - } - AttrType::Event(event) => { - tokens.append_all(quote! { - .on(#name, #event) - }); - } - } - } -} - -enum AttrType { - Value(MaybeExpr), - Event(ExprClosure), - // todo Bool(MaybeExpr) -} - -/// ======================================= -/// Parse just plain text -/// ======================================= -/// - [ ] Perform formatting automatically -/// -/// -struct TextNode(MaybeExpr); - -impl Parse for TextNode { - fn parse(s: ParseStream) -> Result { - Ok(Self(s.parse()?)) - } -} - -impl ToTokens for ToToksCtx<&TextNode> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let mut token_stream = TokenStream2::new(); - self.recurse(&self.inner.0).to_tokens(&mut token_stream); - tokens.append_all(quote! { - __cx.text(format_args_f!(#token_stream)) - }); - } -} - -#[allow(clippy::large_enum_variant)] -enum MaybeExpr { - Literal(T), - Expr(Expr), -} - -impl Parse for MaybeExpr { - fn parse(s: ParseStream) -> Result { - if s.peek(token::Brace) { - let content; - syn::braced!(content in s); - Ok(MaybeExpr::Expr(content.parse()?)) - } else { - Ok(MaybeExpr::Literal(s.parse()?)) - } - } -} - -impl<'a, T> ToTokens for ToToksCtx<&'a MaybeExpr> -where - T: 'a, - ToToksCtx<&'a T>: ToTokens, -{ - fn to_tokens(&self, tokens: &mut TokenStream2) { - match &self.inner { - MaybeExpr::Literal(v) => self.recurse(v).to_tokens(tokens), - MaybeExpr::Expr(expr) => expr.to_tokens(tokens), - } - } -} - -/// ToTokens context -struct ToToksCtx { - inner: T, -} - -impl<'a, T> ToToksCtx { - fn new(inner: T) -> Self { - ToToksCtx { inner } - } - - fn recurse(&self, inner: U) -> ToToksCtx { - ToToksCtx { inner } - } -} - -impl ToTokens for ToToksCtx<&LitStr> { - fn to_tokens(&self, tokens: &mut TokenStream2) { - self.inner.to_tokens(tokens) - } -} - -#[cfg(test)] -mod test { - fn parse(input: &str) -> super::Result { - syn::parse_str(input) - } - - #[test] - fn div() { - parse("bump,
").unwrap(); - } - - #[test] - fn nested() { - parse("bump,
\"text\"
").unwrap(); - } - - #[test] - fn complex() { - parse( - "bump, -
{contact_details}
- ", - ) - .unwrap(); - } -} diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 7099311ce..96ea0ce87 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -2,11 +2,9 @@ use proc_macro::TokenStream; use quote::ToTokens; use syn::parse_macro_input; -pub(crate) mod ifmt; -pub(crate) mod inlineprops; -pub(crate) mod props; -pub(crate) mod router; -pub(crate) mod rsx; +mod ifmt; +mod inlineprops; +mod props; #[proc_macro] pub fn format_args_f(input: TokenStream) -> TokenStream { @@ -180,42 +178,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token #[proc_macro_error::proc_macro_error] #[proc_macro] pub fn rsx(s: TokenStream) -> TokenStream { - match syn::parse::(s) { + match syn::parse::(s) { Err(err) => err.to_compile_error().into(), Ok(stream) => stream.to_token_stream().into(), } } -/// Derive macro used to mark an enum as Routable. -/// -/// This macro can only be used on enums. Every varient of the macro needs to be marked -/// with the `at` attribute to specify the URL of the route. It generates an implementation of -/// `yew_router::Routable` trait and `const`s for the routes passed which are used with `Route` -/// component. -/// -/// # Example -/// -/// ``` -/// # use yew_router::Routable; -/// #[derive(Debug, Clone, Copy, PartialEq, Routable)] -/// enum Routes { -/// #[at("/")] -/// Home, -/// #[at("/secure")] -/// Secure, -/// #[at("/profile/{id}")] -/// Profile(u32), -/// #[at("/404")] -/// NotFound, -/// } -/// ``` -#[proc_macro_derive(Routable, attributes(at, not_found))] -pub fn routable_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - use router::{routable_derive_impl, Routable}; - let input = parse_macro_input!(input as Routable); - routable_derive_impl(input).into() -} - /// Derive props for a component within the component definition. /// /// This macro provides a simple transformation from `Scope<{}>` to `Scope

`, diff --git a/packages/core-macro/src/router.rs b/packages/core-macro/src/router.rs deleted file mode 100644 index 3edad75e3..000000000 --- a/packages/core-macro/src/router.rs +++ /dev/null @@ -1,214 +0,0 @@ -#![allow(dead_code)] - -use proc_macro2::TokenStream; -use quote::quote; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Fields, Ident, LitStr, Variant}; - -const AT_ATTR_IDENT: &str = "at"; -const NOT_FOUND_ATTR_IDENT: &str = "not_found"; - -pub struct Routable { - ident: Ident, - ats: Vec, - variants: Punctuated, - not_found_route: Option, -} - -impl Parse for Routable { - fn parse(input: ParseStream) -> syn::Result { - let DeriveInput { ident, data, .. } = input.parse()?; - - let data = match data { - Data::Enum(data) => data, - Data::Struct(s) => { - return Err(syn::Error::new( - s.struct_token.span(), - "expected enum, found struct", - )) - } - Data::Union(u) => { - return Err(syn::Error::new( - u.union_token.span(), - "expected enum, found union", - )) - } - }; - - let (not_found_route, ats) = parse_variants_attributes(&data.variants)?; - - Ok(Self { - ident, - variants: data.variants, - ats, - not_found_route, - }) - } -} - -fn parse_variants_attributes( - variants: &Punctuated, -) -> syn::Result<(Option, Vec)> { - let mut not_founds = vec![]; - let mut ats: Vec = vec![]; - - let mut not_found_attrs = vec![]; - - for variant in variants.iter() { - if let Fields::Unnamed(ref field) = variant.fields { - return Err(syn::Error::new( - field.span(), - "only named fields are supported", - )); - } - - let attrs = &variant.attrs; - let at_attrs = attrs - .iter() - .filter(|attr| attr.path.is_ident(AT_ATTR_IDENT)) - .collect::>(); - - let attr = match at_attrs.len() { - 1 => *at_attrs.first().unwrap(), - 0 => { - return Err(syn::Error::new( - variant.span(), - format!( - "{} attribute must be present on every variant", - AT_ATTR_IDENT - ), - )) - } - _ => { - return Err(syn::Error::new_spanned( - quote! { #(#at_attrs)* }, - format!("only one {} attribute must be present", AT_ATTR_IDENT), - )) - } - }; - - let lit = attr.parse_args::()?; - ats.push(lit); - - for attr in attrs.iter() { - if attr.path.is_ident(NOT_FOUND_ATTR_IDENT) { - not_found_attrs.push(attr); - not_founds.push(variant.ident.clone()) - } - } - } - - if not_founds.len() > 1 { - return Err(syn::Error::new_spanned( - quote! { #(#not_found_attrs)* }, - format!("there can only be one {}", NOT_FOUND_ATTR_IDENT), - )); - } - - Ok((not_founds.into_iter().next(), ats)) -} - -impl Routable { - // fn build_from_path(&self) -> TokenStream { - // let from_path_matches = self.variants.iter().enumerate().map(|(i, variant)| { - // let ident = &variant.ident; - // let right = match &variant.fields { - // Fields::Unit => quote! { Self::#ident }, - // Fields::Named(field) => { - // let fields = field.named.iter().map(|it| { - // //named fields have idents - // it.ident.as_ref().unwrap() - // }); - // quote! { Self::#ident { #(#fields: params.get(stringify!(#fields))?.parse().ok()?,)* } } - // } - // Fields::Unnamed(_) => unreachable!(), // already checked - // }; - - // let left = self.ats.get(i).unwrap(); - // quote! { - // #left => ::std::option::Option::Some(#right) - // } - // }); - - // quote! { - // fn from_path(path: &str, params: &::std::collections::HashMap<&str, &str>) -> ::std::option::Option { - // match path { - // #(#from_path_matches),*, - // _ => ::std::option::Option::None, - // } - // } - // } - // } - - // fn build_to_path(&self) -> TokenStream { - // let to_path_matches = self.variants.iter().enumerate().map(|(i, variant)| { - // let ident = &variant.ident; - // let mut right = self.ats.get(i).unwrap().value(); - - // match &variant.fields { - // Fields::Unit => quote! { Self::#ident => ::std::string::ToString::to_string(#right) }, - // Fields::Named(field) => { - // let fields = field - // .named - // .iter() - // .map(|it| it.ident.as_ref().unwrap()) - // .collect::>(); - - // for field in fields.iter() { - // // :param -> {param} - // // so we can pass it to `format!("...", param)` - // right = right.replace(&format!(":{}", field), &format!("{{{}}}", field)) - // } - - // quote! { - // Self::#ident { #(#fields),* } => ::std::format!(#right, #(#fields = #fields),*) - // } - // } - // Fields::Unnamed(_) => unreachable!(), // already checked - // } - // }); - - // quote! { - // fn to_path(&self) -> ::std::string::String { - // match self { - // #(#to_path_matches),*, - // } - // } - // } - // } -} - -pub fn routable_derive_impl(input: Routable) -> TokenStream { - let Routable { - // ats, - // not_found_route, - // ident, - .. - } = &input; - - // let from_path = input.build_from_path(); - // let to_path = input.build_to_path(); - - quote! { - // #[automatically_derived] - // impl ::dioxus::router::Routable for #ident { - - // fn recognize(pathname: &str) -> ::std::option::Option { - // todo!() - // // ::std::thread_local! { - // // static ROUTER: ::dioxus::router::__macro::Router = ::dioxus::router::__macro::build_router::<#ident>(); - // // } - // // let route = ROUTER.with(|router| ::dioxus::router::__macro::recognize_with_router(router, pathname)); - // // { - // // let route = ::std::clone::Clone::clone(&route); - // // #cache_thread_local_ident.with(move |val| { - // // *val.borrow_mut() = route; - // // }); - // // } - // // route - // } - // } - } -} diff --git a/packages/core-macro/src/rsxtemplate.rs b/packages/core-macro/src/rsxtemplate.rs deleted file mode 100644 index 9222d7117..000000000 --- a/packages/core-macro/src/rsxtemplate.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::{rsx::RsxBody, util::is_valid_svg_tag}; - -use { - proc_macro::TokenStream, - proc_macro2::{Span, TokenStream as TokenStream2}, - quote::{quote, ToTokens, TokenStreamExt}, - syn::{ - ext::IdentExt, - parse::{Parse, ParseStream}, - token, Error, Expr, ExprClosure, Ident, LitBool, LitStr, Path, Result, Token, - }, -}; - -// ============================================== -// Parse any stream coming from the html! macro -// ============================================== -pub struct RsxTemplate { - inner: RsxBody, -} - -impl Parse for RsxTemplate { - fn parse(s: ParseStream) -> Result { - if s.peek(LitStr) { - use std::str::FromStr; - - let lit = s.parse::()?; - let g = lit.span(); - let mut value = lit.value(); - if value.ends_with('\n') { - value.pop(); - if value.ends_with('\r') { - value.pop(); - } - } - let lit = LitStr::new(&value, lit.span()); - - // panic!("{:#?}", lit); - match lit.parse::() { - Ok(r) => Ok(Self { inner: r }), - Err(e) => Err(e), - } - } else { - panic!("Not a str lit") - } - // let t = s.parse::()?; - - // let new_stream = TokenStream::from(t.to_s) - - // let cx: Ident = s.parse()?; - // s.parse::()?; - // if elements are in an array, return a bumpalo::collections::Vec rather than a Node. - // let kind = if s.peek(token::Bracket) { - // let nodes_toks; - // syn::bracketed!(nodes_toks in s); - // let mut nodes: Vec> = vec![nodes_toks.parse()?]; - // while nodes_toks.peek(Token![,]) { - // nodes_toks.parse::()?; - // nodes.push(nodes_toks.parse()?); - // } - // NodeOrList::List(NodeList(nodes)) - // } else { - // NodeOrList::Node(s.parse()?) - // }; - // Ok(HtmlRender { kind }) - } -} - -impl ToTokens for RsxTemplate { - fn to_tokens(&self, out_tokens: &mut TokenStream2) { - self.inner.to_tokens(out_tokens); - // let new_toks = ToToksCtx::new(&self.kind).to_token_stream(); - - // // create a lazy tree that accepts a bump allocator - // let final_tokens = quote! { - // dioxus::prelude::LazyNodes::new(move |cx| { - // let bump = &cx.bump(); - - // #new_toks - // }) - // }; - - // final_tokens.to_tokens(out_tokens); - } -} diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml new file mode 100644 index 000000000..4cad9064c --- /dev/null +++ b/packages/rsx/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "dioxus-rsx" +version = "0.0.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +once_cell = "1.8" +proc-macro-error = "1.0.4" +proc-macro2 = { version = "1.0.6" } +quote = "1.0" +syn = { version = "1.0.11", features = ["full", "extra-traits"] } diff --git a/packages/core-macro/src/rsx/component.rs b/packages/rsx/src/component.rs similarity index 98% rename from packages/core-macro/src/rsx/component.rs rename to packages/rsx/src/component.rs index 4e9d449cf..17006e6ac 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/rsx/src/component.rs @@ -23,10 +23,10 @@ use syn::{ }; pub struct Component { - name: syn::Path, - body: Vec, - children: Vec, - manual_props: Option, + pub name: syn::Path, + pub body: Vec, + pub children: Vec, + pub manual_props: Option, } impl Parse for Component { diff --git a/packages/core-macro/src/rsx/element.rs b/packages/rsx/src/element.rs similarity index 92% rename from packages/core-macro/src/rsx/element.rs rename to packages/rsx/src/element.rs index ebc401b6e..fa8fc9f51 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/rsx/src/element.rs @@ -11,12 +11,11 @@ use syn::{ // Parse the VNode::Element type // ======================================= pub struct Element { - name: Ident, - key: Option, - attributes: Vec, - listeners: Vec, - children: Vec, - _is_static: bool, + pub name: Ident, + pub key: Option, + pub attributes: Vec, + pub children: Vec, + pub _is_static: bool, } impl Parse for Element { @@ -28,7 +27,6 @@ impl Parse for Element { syn::braced!(content in stream); let mut attributes: Vec = vec![]; - let mut listeners: Vec = vec![]; let mut children: Vec = vec![]; let mut key = None; let mut _el_ref = None; @@ -54,6 +52,7 @@ impl Parse for Element { }); } else { let value = content.parse::()?; + attributes.push(ElementAttrNamed { el_name: el_name.clone(), attr: ElementAttr::CustomAttrExpression { name, value }, @@ -82,7 +81,7 @@ impl Parse for Element { content.parse::()?; if name_str.starts_with("on") { - listeners.push(ElementAttrNamed { + attributes.push(ElementAttrNamed { el_name: el_name.clone(), attr: ElementAttr::EventTokens { name, @@ -182,7 +181,6 @@ impl Parse for Element { name: el_name, attributes, children, - listeners, _is_static: false, }) } @@ -193,14 +191,21 @@ impl ToTokens for Element { let name = &self.name; let children = &self.children; - let listeners = &self.listeners; - let attr = &self.attributes; - let key = match &self.key { Some(ty) => quote! { Some(format_args_f!(#ty)) }, None => quote! { None }, }; + let listeners = self + .attributes + .iter() + .filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. })); + + let attr = self + .attributes + .iter() + .filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. })); + tokens.append_all(quote! { __cx.element( dioxus_elements::#name, @@ -213,29 +218,28 @@ impl ToTokens for Element { } } -enum ElementAttr { - // attribute: "valuee {}" +pub enum ElementAttr { + /// attribute: "valuee {}" AttrText { name: Ident, value: LitStr }, - // attribute: true, + /// attribute: true, AttrExpression { name: Ident, value: Expr }, - // "attribute": "value {}" + /// "attribute": "value {}" CustomAttrText { name: LitStr, value: LitStr }, - // "attribute": true, + /// "attribute": true, CustomAttrExpression { name: LitStr, value: Expr }, - // // onclick: move |_| {} + // /// onclick: move |_| {} // EventClosure { name: Ident, closure: ExprClosure }, - - // onclick: {} + /// onclick: {} EventTokens { name: Ident, tokens: Expr }, } -struct ElementAttrNamed { - el_name: Ident, - attr: ElementAttr, +pub struct ElementAttrNamed { + pub el_name: Ident, + pub attr: ElementAttr, } impl ToTokens for ElementAttrNamed { diff --git a/packages/core-macro/src/rsx/mod.rs b/packages/rsx/src/lib.rs similarity index 96% rename from packages/core-macro/src/rsx/mod.rs rename to packages/rsx/src/lib.rs index 5ff5d85f7..e20d80dec 100644 --- a/packages/core-macro/src/rsx/mod.rs +++ b/packages/rsx/src/lib.rs @@ -15,6 +15,8 @@ mod component; mod element; mod node; +pub mod pretty; + // Re-export the namespaces into each other pub use component::*; pub use element::*; @@ -29,8 +31,8 @@ use syn::{ }; pub struct CallBody { - custom_context: Option, - roots: Vec, + pub custom_context: Option, + pub roots: Vec, } impl Parse for CallBody { diff --git a/packages/core-macro/src/rsx/node.rs b/packages/rsx/src/node.rs similarity index 95% rename from packages/core-macro/src/rsx/node.rs rename to packages/rsx/src/node.rs index d5d701767..eeee421b0 100644 --- a/packages/core-macro/src/rsx/node.rs +++ b/packages/rsx/src/node.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, - token, Expr, LitStr, Result, Token, + token, Attribute, Expr, LitStr, Result, Token, }; /* @@ -20,6 +20,7 @@ pub enum BodyNode { Component(Component), Text(LitStr), RawExpr(Expr), + Meta(Attribute), } impl Parse for BodyNode { @@ -79,6 +80,7 @@ impl ToTokens for BodyNode { BodyNode::RawExpr(exp) => tokens.append_all(quote! { __cx.fragment_from_iter(#exp) }), + BodyNode::Meta(_) => {} } } } diff --git a/packages/rsx/src/pretty.rs b/packages/rsx/src/pretty.rs new file mode 100644 index 000000000..26ad54006 --- /dev/null +++ b/packages/rsx/src/pretty.rs @@ -0,0 +1 @@ +//! pretty printer for rsx! diff --git a/src/lib.rs b/src/lib.rs index 2db210bee..488b1ee9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -353,7 +353,7 @@ pub mod events { pub mod prelude { pub use dioxus_core::prelude::*; - pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props, Routable}; + pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props}; pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; pub use dioxus_hooks::*; pub use dioxus_html as dioxus_elements;