Merge pull request #132 from DioxusLabs/jk/partialexpansion

feat: split out rsx into its own crate
This commit is contained in:
Jonathan Kelley 2022-02-19 00:02:10 -05:00 committed by GitHub
commit 93b4f745af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 64 additions and 809 deletions

View file

@ -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:

View file

@ -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",

View file

@ -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"

View file

@ -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<Self> {
if input.peek(LitStr) {
return input.parse::<LitStr>()?.parse::<HtmlRender>();
}
// let __cx: Ident = s.parse()?;
// s.parse::<Token![,]>()?;
// 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<MaybeExpr<Node>> = vec![nodes_toks.parse()?];
while nodes_toks.peek(Token![,]) {
nodes_toks.parse::<Token![,]>()?;
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<MaybeExpr<Node>>);
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<Self> {
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<Attr>,
children: MaybeExpr<Vec<Node>>,
}
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<Self> {
s.parse::<Token![<]>()?;
let name = Ident::parse_any(s)?;
let mut attrs = vec![];
let _children: Vec<Node> = vec![];
// keep looking for attributes
while !s.peek(Token![>]) {
// self-closing
if s.peek(Token![/]) {
s.parse::<Token![/]>()?;
s.parse::<Token![>]>()?;
return Ok(Self {
name,
attrs,
children: MaybeExpr::Literal(vec![]),
});
}
attrs.push(s.parse()?);
}
s.parse::<Token![>]>()?;
// 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 != name {
return Err(Error::new_spanned(
close,
"closing element does not match opening",
));
}
s.parse::<Token![>]>()?;
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<Self> {
let mut name = Ident::parse_any(s)?;
let name_str = name.to_string();
s.parse::<Token![=]>()?;
// 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<LitStr>),
Event(ExprClosure),
// todo Bool(MaybeExpr<LitBool>)
}
/// =======================================
/// Parse just plain text
/// =======================================
/// - [ ] Perform formatting automatically
///
///
struct TextNode(MaybeExpr<LitStr>);
impl Parse for TextNode {
fn parse(s: ParseStream) -> Result<Self> {
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<T> {
Literal(T),
Expr(Expr),
}
impl<T: Parse> Parse for MaybeExpr<T> {
fn parse(s: ParseStream) -> Result<Self> {
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<T>>
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<T> {
inner: T,
}
impl<'a, T> ToToksCtx<T> {
fn new(inner: T) -> Self {
ToToksCtx { inner }
}
fn recurse<U>(&self, inner: U) -> ToToksCtx<U> {
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<super::HtmlRender> {
syn::parse_str(input)
}
#[test]
fn div() {
parse("bump, <div class=\"test\"/>").unwrap();
}
#[test]
fn nested() {
parse("bump, <div class=\"test\"><div />\"text\"</div>").unwrap();
}
#[test]
fn complex() {
parse(
"bump,
<section style={{
display: flex;
flex-direction: column;
max-width: 95%;
}} class=\"map-panel\">{contact_details}</section>
",
)
.unwrap();
}
}

View file

@ -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::<rsx::CallBody>(s) {
match syn::parse::<dioxus_rsx::CallBody>(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<P>`,

View file

@ -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<LitStr>,
variants: Punctuated<Variant, syn::token::Comma>,
not_found_route: Option<Ident>,
}
impl Parse for Routable {
fn parse(input: ParseStream) -> syn::Result<Self> {
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<Variant, syn::token::Comma>,
) -> syn::Result<(Option<Ident>, Vec<LitStr>)> {
let mut not_founds = vec![];
let mut ats: Vec<LitStr> = 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::<Vec<_>>();
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::<LitStr>()?;
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<Self> {
// 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::<Vec<_>>();
// 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<Self> {
// 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
// }
// }
}
}

View file

@ -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<Self> {
if s.peek(LitStr) {
use std::str::FromStr;
let lit = s.parse::<LitStr>()?;
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::<crate::rsx::RsxBody>() {
Ok(r) => Ok(Self { inner: r }),
Err(e) => Err(e),
}
} else {
panic!("Not a str lit")
}
// let t = s.parse::<LitStr>()?;
// let new_stream = TokenStream::from(t.to_s)
// let cx: Ident = s.parse()?;
// s.parse::<Token![,]>()?;
// 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<MaybeExpr<Node>> = vec![nodes_toks.parse()?];
// while nodes_toks.peek(Token![,]) {
// nodes_toks.parse::<Token![,]>()?;
// 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);
}
}

13
packages/rsx/Cargo.toml Normal file
View file

@ -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"] }

View file

@ -23,10 +23,10 @@ use syn::{
};
pub struct Component {
name: syn::Path,
body: Vec<ComponentField>,
children: Vec<BodyNode>,
manual_props: Option<Expr>,
pub name: syn::Path,
pub body: Vec<ComponentField>,
pub children: Vec<BodyNode>,
pub manual_props: Option<Expr>,
}
impl Parse for Component {

View file

@ -11,12 +11,11 @@ use syn::{
// Parse the VNode::Element type
// =======================================
pub struct Element {
name: Ident,
key: Option<LitStr>,
attributes: Vec<ElementAttrNamed>,
listeners: Vec<ElementAttrNamed>,
children: Vec<BodyNode>,
_is_static: bool,
pub name: Ident,
pub key: Option<LitStr>,
pub attributes: Vec<ElementAttrNamed>,
pub children: Vec<BodyNode>,
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<ElementAttrNamed> = vec![];
let mut listeners: Vec<ElementAttrNamed> = vec![];
let mut children: Vec<BodyNode> = vec![];
let mut key = None;
let mut _el_ref = None;
@ -54,6 +52,7 @@ impl Parse for Element {
});
} else {
let value = content.parse::<Expr>()?;
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::CustomAttrExpression { name, value },
@ -82,7 +81,7 @@ impl Parse for Element {
content.parse::<Token![:]>()?;
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 {

View file

@ -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<Ident>,
roots: Vec<BodyNode>,
pub custom_context: Option<Ident>,
pub roots: Vec<BodyNode>,
}
impl Parse for CallBody {

View file

@ -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(_) => {}
}
}
}

View file

@ -0,0 +1 @@
//! pretty printer for rsx!

View file

@ -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;