Merge branch 'master' into fullstack-binary-size-compile-times
This commit is contained in:
commit
b180b501f7
310 files changed
+13994
-4978
No files matched your search
@@ -20,7 +20,10 @@ quote = { version = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
internment = { version = "0.7.0", optional = true }
|
||||
krates = { version = "0.12.6", optional = true }
|
||||
tracing.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["html"]
|
||||
hot_reload = ["krates", "internment", "dioxus-core"]
|
||||
serde = ["dep:serde"]
|
||||
html = []
|
||||
@@ -0,0 +1,354 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use super::*;
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{parse_quote, spanned::Spanned, Expr, ExprIf, Ident, LitStr};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum AttributeType {
|
||||
Named(ElementAttrNamed),
|
||||
Spread(Expr),
|
||||
}
|
||||
|
||||
impl AttributeType {
|
||||
pub fn start(&self) -> Span {
|
||||
match self {
|
||||
AttributeType::Named(n) => n.attr.start(),
|
||||
AttributeType::Spread(e) => e.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_combine(&self, other: &Self) -> Option<Self> {
|
||||
match (self, other) {
|
||||
(Self::Named(a), Self::Named(b)) => a.try_combine(b).map(Self::Named),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for AttributeType {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
AttributeType::Named(n) => tokens.append_all(quote! { #n }),
|
||||
AttributeType::Spread(e) => tokens.append_all(quote! { (&#e).into() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ElementAttrNamed {
|
||||
pub el_name: ElementName,
|
||||
pub attr: ElementAttr,
|
||||
}
|
||||
|
||||
impl Hash for ElementAttrNamed {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.attr.name.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ElementAttrNamed {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.attr == other.attr
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ElementAttrNamed {}
|
||||
|
||||
impl ElementAttrNamed {
|
||||
pub(crate) fn try_combine(&self, other: &Self) -> Option<Self> {
|
||||
if self.el_name == other.el_name && self.attr.name == other.attr.name {
|
||||
if let Some(separator) = self.attr.name.multi_attribute_separator() {
|
||||
return Some(ElementAttrNamed {
|
||||
el_name: self.el_name.clone(),
|
||||
attr: ElementAttr {
|
||||
name: self.attr.name.clone(),
|
||||
value: self.attr.value.combine(separator, &other.attr.value),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ElementAttrNamed {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ElementAttrNamed { el_name, attr } = self;
|
||||
|
||||
let ns = |name: &ElementAttrName| match (el_name, name) {
|
||||
(ElementName::Ident(i), ElementAttrName::BuiltIn(_)) => {
|
||||
quote! { dioxus_elements::#i::#name.1 }
|
||||
}
|
||||
_ => quote! { None },
|
||||
};
|
||||
let volitile = |name: &ElementAttrName| match (el_name, name) {
|
||||
(ElementName::Ident(i), ElementAttrName::BuiltIn(_)) => {
|
||||
quote! { dioxus_elements::#i::#name.2 }
|
||||
}
|
||||
_ => quote! { false },
|
||||
};
|
||||
let attribute = |name: &ElementAttrName| match name {
|
||||
ElementAttrName::BuiltIn(name) => match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.0 },
|
||||
ElementName::Custom(_) => {
|
||||
let as_string = name.to_string();
|
||||
quote!(#as_string)
|
||||
}
|
||||
},
|
||||
ElementAttrName::Custom(s) => quote! { #s },
|
||||
};
|
||||
|
||||
let attribute = {
|
||||
match &attr.value {
|
||||
ElementAttrValue::AttrLiteral(_)
|
||||
| ElementAttrValue::AttrExpr(_)
|
||||
| ElementAttrValue::AttrOptionalExpr { .. } => {
|
||||
let name = &self.attr.name;
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
let value = &self.attr.value;
|
||||
let value = quote! { #value };
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#attribute,
|
||||
#value,
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttrValue::EventTokens(tokens) => match &self.attr.name {
|
||||
ElementAttrName::BuiltIn(name) => {
|
||||
quote! {
|
||||
dioxus_elements::events::#name(__cx, #tokens)
|
||||
}
|
||||
}
|
||||
ElementAttrName::Custom(_) => todo!(),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub struct ElementAttr {
|
||||
pub name: ElementAttrName,
|
||||
pub value: ElementAttrValue,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementAttrValue {
|
||||
/// attribute: "value"
|
||||
AttrLiteral(IfmtInput),
|
||||
/// attribute: if bool { "value" }
|
||||
AttrOptionalExpr {
|
||||
condition: Expr,
|
||||
value: Box<ElementAttrValue>,
|
||||
},
|
||||
/// attribute: true
|
||||
AttrExpr(Expr),
|
||||
/// onclick: move |_| {}
|
||||
EventTokens(Expr),
|
||||
}
|
||||
|
||||
impl Parse for ElementAttrValue {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(if input.peek(Token![if]) {
|
||||
let if_expr = input.parse::<ExprIf>()?;
|
||||
if is_if_chain_terminated(&if_expr) {
|
||||
ElementAttrValue::AttrExpr(Expr::If(if_expr))
|
||||
} else {
|
||||
ElementAttrValue::AttrOptionalExpr {
|
||||
condition: *if_expr.cond,
|
||||
value: {
|
||||
let stmts = if_expr.then_branch.stmts;
|
||||
Box::new(syn::parse2(quote! {
|
||||
#(#stmts)*
|
||||
})?)
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if input.peek(LitStr) {
|
||||
let value = input.parse()?;
|
||||
ElementAttrValue::AttrLiteral(value)
|
||||
} else {
|
||||
let value = input.parse::<Expr>()?;
|
||||
ElementAttrValue::AttrExpr(value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ElementAttrValue {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
ElementAttrValue::AttrLiteral(lit) => tokens.append_all(quote! { #lit }),
|
||||
ElementAttrValue::AttrOptionalExpr { condition, value } => {
|
||||
tokens.append_all(quote! { if #condition { Some(#value) } else { None } })
|
||||
}
|
||||
ElementAttrValue::AttrExpr(expr) => tokens.append_all(quote! { #expr }),
|
||||
ElementAttrValue::EventTokens(expr) => tokens.append_all(quote! { #expr }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementAttrValue {
|
||||
fn to_str_expr(&self) -> Option<TokenStream2> {
|
||||
match self {
|
||||
ElementAttrValue::AttrLiteral(lit) => Some(quote!(#lit.to_string())),
|
||||
ElementAttrValue::AttrOptionalExpr { value, .. } => value.to_str_expr(),
|
||||
ElementAttrValue::AttrExpr(expr) => Some(quote!(#expr.to_string())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn combine(&self, separator: &str, other: &Self) -> Self {
|
||||
match (self, other) {
|
||||
(Self::AttrLiteral(lit1), Self::AttrLiteral(lit2)) => {
|
||||
let fmt = lit1.clone().join(lit2.clone(), separator);
|
||||
Self::AttrLiteral(fmt)
|
||||
}
|
||||
(Self::AttrLiteral(expr1), Self::AttrExpr(expr2)) => {
|
||||
let mut ifmt = expr1.clone();
|
||||
ifmt.push_str(separator);
|
||||
ifmt.push_expr(expr2.clone());
|
||||
Self::AttrLiteral(ifmt)
|
||||
}
|
||||
(Self::AttrExpr(expr1), Self::AttrLiteral(expr2)) => {
|
||||
let mut ifmt = expr2.clone();
|
||||
ifmt.push_str(separator);
|
||||
ifmt.push_expr(expr1.clone());
|
||||
Self::AttrLiteral(ifmt)
|
||||
}
|
||||
(Self::AttrExpr(expr1), Self::AttrExpr(expr2)) => {
|
||||
let mut ifmt = IfmtInput::default();
|
||||
ifmt.push_expr(expr1.clone());
|
||||
ifmt.push_str(separator);
|
||||
ifmt.push_expr(expr2.clone());
|
||||
Self::AttrLiteral(ifmt)
|
||||
}
|
||||
(
|
||||
Self::AttrOptionalExpr {
|
||||
condition: condition1,
|
||||
value: value1,
|
||||
},
|
||||
Self::AttrOptionalExpr {
|
||||
condition: condition2,
|
||||
value: value2,
|
||||
},
|
||||
) => {
|
||||
let first_as_string = value1.to_str_expr();
|
||||
let second_as_string = value2.to_str_expr();
|
||||
Self::AttrExpr(parse_quote! {
|
||||
{
|
||||
let mut __combined = String::new();
|
||||
if #condition1 {
|
||||
__combined.push_str(&#first_as_string);
|
||||
}
|
||||
if #condition2 {
|
||||
if __combined.len() > 0 {
|
||||
__combined.push_str(&#separator);
|
||||
}
|
||||
__combined.push_str(&#second_as_string);
|
||||
}
|
||||
__combined
|
||||
}
|
||||
})
|
||||
}
|
||||
(Self::AttrOptionalExpr { condition, value }, other) => {
|
||||
let first_as_string = value.to_str_expr();
|
||||
let second_as_string = other.to_str_expr();
|
||||
Self::AttrExpr(parse_quote! {
|
||||
{
|
||||
let mut __combined = #second_as_string;
|
||||
if #condition {
|
||||
__combined.push_str(&#separator);
|
||||
__combined.push_str(&#first_as_string);
|
||||
}
|
||||
__combined
|
||||
}
|
||||
})
|
||||
}
|
||||
(other, Self::AttrOptionalExpr { condition, value }) => {
|
||||
let first_as_string = other.to_str_expr();
|
||||
let second_as_string = value.to_str_expr();
|
||||
Self::AttrExpr(parse_quote! {
|
||||
{
|
||||
let mut __combined = #first_as_string;
|
||||
if #condition {
|
||||
__combined.push_str(&#separator);
|
||||
__combined.push_str(&#second_as_string);
|
||||
}
|
||||
__combined
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementAttrName {
|
||||
BuiltIn(Ident),
|
||||
Custom(LitStr),
|
||||
}
|
||||
|
||||
impl ElementAttrName {
|
||||
fn multi_attribute_separator(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
ElementAttrName::BuiltIn(i) => match i.to_string().as_str() {
|
||||
"class" => Some(" "),
|
||||
"style" => Some(";"),
|
||||
_ => None,
|
||||
},
|
||||
ElementAttrName::Custom(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Span {
|
||||
match self {
|
||||
ElementAttrName::BuiltIn(i) => i.span(),
|
||||
ElementAttrName::Custom(s) => s.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ElementAttrName {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
ElementAttrName::BuiltIn(i) => tokens.append_all(quote! { #i }),
|
||||
ElementAttrName::Custom(s) => tokens.append_all(quote! { #s }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ElementAttrName {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ElementAttrName::BuiltIn(i) => write!(f, "{}", i),
|
||||
ElementAttrName::Custom(s) => write!(f, "{}", s.value()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementAttr {
|
||||
pub fn start(&self) -> Span {
|
||||
self.name.start()
|
||||
}
|
||||
|
||||
pub fn is_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ElementAttr {
|
||||
value: ElementAttrValue::AttrExpr(_) | ElementAttrValue::EventTokens(_),
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -151,8 +151,8 @@ impl ToTokens for Component {
|
||||
}
|
||||
None => {
|
||||
let mut toks = match prop_gen_args {
|
||||
Some(gen_args) => quote! { fc_to_builder(#name #gen_args) },
|
||||
None => quote! { fc_to_builder(#name) },
|
||||
Some(gen_args) => quote! { fc_to_builder(__cx, #name #gen_args) },
|
||||
None => quote! { fc_to_builder(__cx, #name) },
|
||||
};
|
||||
for field in &self.fields {
|
||||
match field.name.to_string().as_str() {
|
||||
|
||||
+97
-186
@@ -8,7 +8,7 @@ use syn::{
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
Error, Expr, Ident, LitStr, Result, Token,
|
||||
Expr, Ident, LitStr, Result, Token,
|
||||
};
|
||||
|
||||
// =======================================
|
||||
@@ -18,7 +18,8 @@ use syn::{
|
||||
pub struct Element {
|
||||
pub name: ElementName,
|
||||
pub key: Option<IfmtInput>,
|
||||
pub attributes: Vec<ElementAttrNamed>,
|
||||
pub attributes: Vec<AttributeType>,
|
||||
pub merged_attributes: Vec<AttributeType>,
|
||||
pub children: Vec<BodyNode>,
|
||||
pub brace: syn::token::Brace,
|
||||
}
|
||||
@@ -31,10 +32,9 @@ impl Parse for Element {
|
||||
let content: ParseBuffer;
|
||||
let brace = syn::braced!(content in stream);
|
||||
|
||||
let mut attributes: Vec<ElementAttrNamed> = vec![];
|
||||
let mut attributes: Vec<AttributeType> = vec![];
|
||||
let mut children: Vec<BodyNode> = vec![];
|
||||
let mut key = None;
|
||||
let mut _el_ref = None;
|
||||
|
||||
// parse fields with commas
|
||||
// break when we don't get this pattern anymore
|
||||
@@ -42,6 +42,22 @@ impl Parse for Element {
|
||||
// "def": 456,
|
||||
// abc: 123,
|
||||
loop {
|
||||
if content.peek(Token![..]) {
|
||||
content.parse::<Token![..]>()?;
|
||||
let expr = content.parse::<Expr>()?;
|
||||
let span = expr.span();
|
||||
attributes.push(attribute::AttributeType::Spread(expr));
|
||||
|
||||
if content.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
if content.parse::<Token![,]>().is_err() {
|
||||
missing_trailing_comma!(span);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the raw literal fields
|
||||
if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
|
||||
let name = content.parse::<LitStr>()?;
|
||||
@@ -49,19 +65,14 @@ impl Parse for Element {
|
||||
|
||||
content.parse::<Token![:]>()?;
|
||||
|
||||
if content.peek(LitStr) {
|
||||
let value = content.parse()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::CustomAttrText { name, value },
|
||||
});
|
||||
} else {
|
||||
let value = content.parse::<Expr>()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::CustomAttrExpression { name, value },
|
||||
});
|
||||
}
|
||||
let value = content.parse::<ElementAttrValue>()?;
|
||||
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::Custom(name),
|
||||
value,
|
||||
},
|
||||
}));
|
||||
|
||||
if content.is_empty() {
|
||||
break;
|
||||
@@ -84,41 +95,27 @@ impl Parse for Element {
|
||||
let span = content.span();
|
||||
|
||||
if name_str.starts_with("on") {
|
||||
attributes.push(ElementAttrNamed {
|
||||
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::EventTokens {
|
||||
name,
|
||||
tokens: content.parse()?,
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(name),
|
||||
value: ElementAttrValue::EventTokens(content.parse()?),
|
||||
},
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
match name_str.as_str() {
|
||||
"key" => {
|
||||
key = Some(content.parse()?);
|
||||
}
|
||||
"classes" => todo!("custom class list not supported yet"),
|
||||
// "namespace" => todo!("custom namespace not supported yet"),
|
||||
"node_ref" => {
|
||||
_el_ref = Some(content.parse::<Expr>()?);
|
||||
}
|
||||
_ => {
|
||||
if content.peek(LitStr) {
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::AttrText {
|
||||
name,
|
||||
value: content.parse()?,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::AttrExpression {
|
||||
name,
|
||||
value: content.parse()?,
|
||||
},
|
||||
});
|
||||
}
|
||||
let value = content.parse::<ElementAttrValue>()?;
|
||||
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(name),
|
||||
value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +124,6 @@ impl Parse for Element {
|
||||
break;
|
||||
}
|
||||
|
||||
// todo: add a message saying you need to include commas between fields
|
||||
if content.parse::<Token![,]>().is_err() {
|
||||
missing_trailing_comma!(span);
|
||||
}
|
||||
@@ -137,6 +133,37 @@ impl Parse for Element {
|
||||
break;
|
||||
}
|
||||
|
||||
// Deduplicate any attributes that can be combined
|
||||
// For example, if there are two `class` attributes, combine them into one
|
||||
let mut merged_attributes: Vec<AttributeType> = Vec::new();
|
||||
for attr in &attributes {
|
||||
if let Some(old_attr_index) = merged_attributes.iter().position(|a| {
|
||||
matches!((a, attr), (
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(old_name),
|
||||
..
|
||||
},
|
||||
..
|
||||
}),
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(new_name),
|
||||
..
|
||||
},
|
||||
..
|
||||
}),
|
||||
) if old_name == new_name)
|
||||
}) {
|
||||
let old_attr = &mut merged_attributes[old_attr_index];
|
||||
if let Some(combined) = old_attr.try_combine(attr) {
|
||||
*old_attr = combined;
|
||||
}
|
||||
} else {
|
||||
merged_attributes.push(attr.clone());
|
||||
}
|
||||
}
|
||||
|
||||
while !content.is_empty() {
|
||||
if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
|
||||
attr_after_element!(content.span());
|
||||
@@ -158,6 +185,7 @@ impl Parse for Element {
|
||||
key,
|
||||
name: el_name,
|
||||
attributes,
|
||||
merged_attributes,
|
||||
children,
|
||||
brace,
|
||||
})
|
||||
@@ -174,15 +202,31 @@ impl ToTokens for Element {
|
||||
None => quote! { None },
|
||||
};
|
||||
|
||||
let listeners = self
|
||||
.attributes
|
||||
.iter()
|
||||
.filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. }));
|
||||
let listeners = self.merged_attributes.iter().filter(|f| {
|
||||
matches!(
|
||||
f,
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr: ElementAttr {
|
||||
value: ElementAttrValue::EventTokens { .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
let attr = self
|
||||
.attributes
|
||||
.iter()
|
||||
.filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. }));
|
||||
let attr = self.merged_attributes.iter().filter(|f| {
|
||||
!matches!(
|
||||
f,
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr: ElementAttr {
|
||||
value: ElementAttrValue::EventTokens { .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
})
|
||||
)
|
||||
});
|
||||
|
||||
tokens.append_all(quote! {
|
||||
__cx.element(
|
||||
@@ -264,136 +308,3 @@ impl ToTokens for ElementName {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementAttr {
|
||||
/// `attribute: "value"`
|
||||
AttrText { name: Ident, value: IfmtInput },
|
||||
|
||||
/// `attribute: true`
|
||||
AttrExpression { name: Ident, value: Expr },
|
||||
|
||||
/// `"attribute": "value"`
|
||||
CustomAttrText { name: LitStr, value: IfmtInput },
|
||||
|
||||
/// `"attribute": true`
|
||||
CustomAttrExpression { name: LitStr, value: Expr },
|
||||
|
||||
// /// onclick: move |_| {}
|
||||
// EventClosure { name: Ident, closure: ExprClosure },
|
||||
/// onclick: {}
|
||||
EventTokens { name: Ident, tokens: Expr },
|
||||
}
|
||||
|
||||
impl ElementAttr {
|
||||
pub fn start(&self) -> Span {
|
||||
match self {
|
||||
ElementAttr::AttrText { name, .. } => name.span(),
|
||||
ElementAttr::AttrExpression { name, .. } => name.span(),
|
||||
ElementAttr::CustomAttrText { name, .. } => name.span(),
|
||||
ElementAttr::CustomAttrExpression { name, .. } => name.span(),
|
||||
ElementAttr::EventTokens { name, .. } => name.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub struct ElementAttrNamed {
|
||||
pub el_name: ElementName,
|
||||
pub attr: ElementAttr,
|
||||
}
|
||||
|
||||
impl ToTokens for ElementAttrNamed {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ElementAttrNamed { el_name, attr } = self;
|
||||
|
||||
let ns = |name| match el_name {
|
||||
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 },
|
||||
ElementName::Custom(_) => quote! { None },
|
||||
};
|
||||
let volitile = |name| match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.2 },
|
||||
ElementName::Custom(_) => quote! { false },
|
||||
};
|
||||
let attribute = |name: &Ident| match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.0 },
|
||||
ElementName::Custom(_) => {
|
||||
let as_string = name.to_string();
|
||||
quote!(#as_string)
|
||||
}
|
||||
};
|
||||
|
||||
let attribute = match attr {
|
||||
ElementAttr::AttrText { name, value } => {
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#attribute,
|
||||
#value,
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::AttrExpression { name, value } => {
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#attribute,
|
||||
#value,
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::CustomAttrText { name, value } => {
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#name,
|
||||
#value,
|
||||
None,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::CustomAttrExpression { name, value } => {
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#name,
|
||||
#value,
|
||||
None,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::EventTokens { name, tokens } => {
|
||||
quote! {
|
||||
dioxus_elements::events::#name(__cx, #tokens)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// ::dioxus::core::Attribute {
|
||||
// name: stringify!(#name),
|
||||
// namespace: None,
|
||||
// volatile: false,
|
||||
// mounted_node: Default::default(),
|
||||
// value: ::dioxus::core::AttributeValue::Text(#value),
|
||||
// }
|
||||
@@ -1,12 +1,12 @@
|
||||
macro_rules! missing_trailing_comma {
|
||||
($span:expr) => {
|
||||
return Err(Error::new($span, "missing trailing comma"));
|
||||
return Err(syn::Error::new($span, "missing trailing comma"));
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! attr_after_element {
|
||||
($span:expr) => {
|
||||
return Err(Error::new($span, "expected element\n = help move the attribute above all the children and text elements"));
|
||||
return Err(syn::Error::new($span, "expected element\n = help move the attribute above all the children and text elements"));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{File, Macro};
|
||||
|
||||
pub enum DiffResult {
|
||||
@@ -10,13 +11,30 @@ pub enum DiffResult {
|
||||
pub fn find_rsx(new: &File, old: &File) -> DiffResult {
|
||||
let mut rsx_calls = Vec::new();
|
||||
if new.items.len() != old.items.len() {
|
||||
tracing::trace!(
|
||||
"found not hot reload-able change {:#?} != {:#?}",
|
||||
new.items
|
||||
.iter()
|
||||
.map(|i| i.to_token_stream().to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
old.items
|
||||
.iter()
|
||||
.map(|i| i.to_token_stream().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
return DiffResult::CodeChanged;
|
||||
}
|
||||
for (new, old) in new.items.iter().zip(old.items.iter()) {
|
||||
if find_rsx_item(new, old, &mut rsx_calls) {
|
||||
tracing::trace!(
|
||||
"found not hot reload-able change {:#?} != {:#?}",
|
||||
new.to_token_stream().to_string(),
|
||||
old.to_token_stream().to_string()
|
||||
);
|
||||
return DiffResult::CodeChanged;
|
||||
}
|
||||
}
|
||||
tracing::trace!("found hot reload-able changes {:#?}", rsx_calls);
|
||||
DiffResult::RsxChanged(rsx_calls)
|
||||
}
|
||||
|
||||
@@ -94,6 +112,9 @@ fn find_rsx_item(
|
||||
(syn::ImplItem::Macro(new_item), syn::ImplItem::Macro(old_item)) => {
|
||||
old_item != new_item
|
||||
}
|
||||
(syn::ImplItem::Verbatim(stream), syn::ImplItem::Verbatim(stream2)) => {
|
||||
stream.to_string() != stream2.to_string()
|
||||
}
|
||||
_ => true,
|
||||
} {
|
||||
return true;
|
||||
@@ -186,10 +207,12 @@ fn find_rsx_trait(
|
||||
}
|
||||
}
|
||||
(syn::TraitItem::Fn(new_item), syn::TraitItem::Fn(old_item)) => {
|
||||
if let (Some(new_block), Some(old_block)) = (&new_item.default, &old_item.default) {
|
||||
find_rsx_block(new_block, old_block, rsx_calls)
|
||||
} else {
|
||||
true
|
||||
match (&new_item.default, &old_item.default) {
|
||||
(Some(new_block), Some(old_block)) => {
|
||||
find_rsx_block(new_block, old_block, rsx_calls)
|
||||
}
|
||||
(None, None) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
(syn::TraitItem::Type(new_item), syn::TraitItem::Type(old_item)) => {
|
||||
@@ -198,6 +221,9 @@ fn find_rsx_trait(
|
||||
(syn::TraitItem::Macro(new_item), syn::TraitItem::Macro(old_item)) => {
|
||||
old_item != new_item
|
||||
}
|
||||
(syn::TraitItem::Verbatim(stream), syn::TraitItem::Verbatim(stream2)) => {
|
||||
stream.to_string() != stream2.to_string()
|
||||
}
|
||||
_ => true,
|
||||
} {
|
||||
return true;
|
||||
@@ -355,6 +381,11 @@ fn find_rsx_expr(
|
||||
|| new_expr.or2_token != old_expr.or2_token
|
||||
|| new_expr.output != old_expr.output
|
||||
}
|
||||
(syn::Expr::Const(new_expr), syn::Expr::Const(old_expr)) => {
|
||||
find_rsx_block(&new_expr.block, &old_expr.block, rsx_calls)
|
||||
|| new_expr.attrs != old_expr.attrs
|
||||
|| new_expr.const_token != old_expr.const_token
|
||||
}
|
||||
(syn::Expr::Continue(new_expr), syn::Expr::Continue(old_expr)) => old_expr != new_expr,
|
||||
(syn::Expr::Field(new_expr), syn::Expr::Field(old_expr)) => {
|
||||
find_rsx_expr(&new_expr.base, &old_expr.base, rsx_calls)
|
||||
@@ -402,6 +433,7 @@ fn find_rsx_expr(
|
||||
|| new_expr.attrs != old_expr.attrs
|
||||
|| new_expr.bracket_token != old_expr.bracket_token
|
||||
}
|
||||
(syn::Expr::Infer(new_expr), syn::Expr::Infer(old_expr)) => new_expr != old_expr,
|
||||
(syn::Expr::Let(new_expr), syn::Expr::Let(old_expr)) => {
|
||||
find_rsx_expr(&new_expr.expr, &old_expr.expr, rsx_calls)
|
||||
|| new_expr.attrs != old_expr.attrs
|
||||
@@ -589,7 +621,9 @@ fn find_rsx_expr(
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
(syn::Expr::Verbatim(_), syn::Expr::Verbatim(_)) => false,
|
||||
(syn::Expr::Verbatim(stream), syn::Expr::Verbatim(stream2)) => {
|
||||
stream.to_string() != stream2.to_string()
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,17 +51,24 @@ impl<Ctx: HotReloadingContext> FileMap<Ctx> {
|
||||
fn find_rs_files(
|
||||
root: PathBuf,
|
||||
filter: &mut impl FnMut(&Path) -> bool,
|
||||
) -> io::Result<FileMapSearchResult> {
|
||||
) -> FileMapSearchResult {
|
||||
let mut files = HashMap::new();
|
||||
let mut errors = Vec::new();
|
||||
if root.is_dir() {
|
||||
for entry in (fs::read_dir(root)?).flatten() {
|
||||
let read_dir = match fs::read_dir(root) {
|
||||
Ok(read_dir) => read_dir,
|
||||
Err(err) => {
|
||||
errors.push(err);
|
||||
return FileMapSearchResult { map: files, errors };
|
||||
}
|
||||
};
|
||||
for entry in read_dir.flatten() {
|
||||
let path = entry.path();
|
||||
if !filter(&path) {
|
||||
let FileMapSearchResult {
|
||||
map,
|
||||
errors: child_errors,
|
||||
} = find_rs_files(path, filter)?;
|
||||
} = find_rs_files(path, filter);
|
||||
errors.extend(child_errors);
|
||||
files.extend(map);
|
||||
}
|
||||
@@ -69,14 +76,20 @@ impl<Ctx: HotReloadingContext> FileMap<Ctx> {
|
||||
} else if root.extension().and_then(|s| s.to_str()) == Some("rs") {
|
||||
if let Ok(mut file) = File::open(root.clone()) {
|
||||
let mut src = String::new();
|
||||
file.read_to_string(&mut src)?;
|
||||
files.insert(root, (src, None));
|
||||
match file.read_to_string(&mut src) {
|
||||
Ok(_) => {
|
||||
files.insert(root, (src, None));
|
||||
}
|
||||
Err(err) => {
|
||||
errors.push(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(FileMapSearchResult { map: files, errors })
|
||||
FileMapSearchResult { map: files, errors }
|
||||
}
|
||||
|
||||
let FileMapSearchResult { map, errors } = find_rs_files(path, &mut filter)?;
|
||||
let FileMapSearchResult { map, errors } = find_rs_files(path, &mut filter);
|
||||
let result = Self {
|
||||
map,
|
||||
in_workspace: HashMap::new(),
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // dumb compiler does not see the struct being used...
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
|
||||
pub struct IfmtInput {
|
||||
pub source: Option<LitStr>,
|
||||
pub segments: Vec<Segment>,
|
||||
@@ -27,8 +27,29 @@ impl IfmtInput {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(mut self, other: Self, separator: &str) -> Self {
|
||||
if !self.segments.is_empty() {
|
||||
self.segments.push(Segment::Literal(separator.to_string()));
|
||||
}
|
||||
self.segments.extend(other.segments);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn push_expr(&mut self, expr: Expr) {
|
||||
self.segments.push(Segment::Formatted(FormattedSegment {
|
||||
format_args: String::new(),
|
||||
segment: FormattedSegmentType::Expr(Box::new(expr)),
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
self.segments.push(Segment::Literal(s.to_string()));
|
||||
}
|
||||
|
||||
pub fn is_static(&self) -> bool {
|
||||
matches!(self.segments.as_slice(), &[Segment::Literal(_)] | &[])
|
||||
self.segments
|
||||
.iter()
|
||||
.all(|seg| matches!(seg, Segment::Literal(_)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+70
-74
@@ -16,6 +16,7 @@
|
||||
|
||||
#[macro_use]
|
||||
mod errors;
|
||||
mod attribute;
|
||||
mod component;
|
||||
mod element;
|
||||
#[cfg(feature = "hot_reload")]
|
||||
@@ -26,6 +27,7 @@ mod node;
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
|
||||
// Re-export the namespaces into each other
|
||||
pub use attribute::*;
|
||||
pub use component::*;
|
||||
#[cfg(feature = "hot_reload")]
|
||||
use dioxus_core::{Template, TemplateAttribute, TemplateNode};
|
||||
@@ -193,7 +195,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||
let mut context = DynamicContext::default();
|
||||
|
||||
let key = match self.roots.get(0) {
|
||||
let key = match self.roots.first() {
|
||||
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
|
||||
Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(),
|
||||
_ => None,
|
||||
@@ -204,12 +206,17 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
None => quote! { None },
|
||||
};
|
||||
|
||||
let spndbg = format!("{:?}", self.roots[0].span());
|
||||
let root_col = spndbg
|
||||
.rsplit_once("..")
|
||||
.and_then(|(_, after)| after.split_once(')').map(|(before, _)| before))
|
||||
.unwrap_or_default();
|
||||
|
||||
let root_col = match self.roots.first() {
|
||||
Some(first_root) => {
|
||||
let first_root_span = format!("{:?}", first_root.span());
|
||||
first_root_span
|
||||
.rsplit_once("..")
|
||||
.and_then(|(_, after)| after.split_once(')').map(|(before, _)| before))
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
}
|
||||
_ => "0".to_string(),
|
||||
};
|
||||
let root_printer = self.roots.iter().enumerate().map(|(idx, root)| {
|
||||
context.current_path.push(idx as u8);
|
||||
let out = context.render_static_node(root);
|
||||
@@ -247,14 +254,13 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
node_paths: &[ #(#node_paths),* ],
|
||||
attr_paths: &[ #(#attr_paths),* ],
|
||||
};
|
||||
::dioxus::core::VNode {
|
||||
parent: None,
|
||||
key: #key_tokens,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()).into(),
|
||||
dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
|
||||
dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
|
||||
}
|
||||
::dioxus::core::VNode::new(
|
||||
#key_tokens,
|
||||
TEMPLATE,
|
||||
dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(#root_count, __cx.bump()),
|
||||
__cx.bump().alloc([ #( #node_printer ),* ]),
|
||||
__cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -262,7 +268,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
||||
#[cfg(feature = "hot_reload")]
|
||||
#[derive(Default, Debug)]
|
||||
struct DynamicMapping {
|
||||
attribute_to_idx: std::collections::HashMap<ElementAttr, Vec<usize>>,
|
||||
attribute_to_idx: std::collections::HashMap<AttributeType, Vec<usize>>,
|
||||
last_attribute_idx: usize,
|
||||
node_to_idx: std::collections::HashMap<BodyNode, Vec<usize>>,
|
||||
last_element_idx: usize,
|
||||
@@ -278,7 +284,7 @@ impl DynamicMapping {
|
||||
new
|
||||
}
|
||||
|
||||
fn get_attribute_idx(&mut self, attr: &ElementAttr) -> Option<usize> {
|
||||
fn get_attribute_idx(&mut self, attr: &AttributeType) -> Option<usize> {
|
||||
self.attribute_to_idx
|
||||
.get_mut(attr)
|
||||
.and_then(|idxs| idxs.pop())
|
||||
@@ -288,7 +294,7 @@ impl DynamicMapping {
|
||||
self.node_to_idx.get_mut(node).and_then(|idxs| idxs.pop())
|
||||
}
|
||||
|
||||
fn insert_attribute(&mut self, attr: ElementAttr) -> usize {
|
||||
fn insert_attribute(&mut self, attr: AttributeType) -> usize {
|
||||
let idx = self.last_attribute_idx;
|
||||
self.last_attribute_idx += 1;
|
||||
|
||||
@@ -309,18 +315,18 @@ impl DynamicMapping {
|
||||
fn add_node(&mut self, node: BodyNode) {
|
||||
match node {
|
||||
BodyNode::Element(el) => {
|
||||
for attr in el.attributes {
|
||||
match &attr.attr {
|
||||
ElementAttr::CustomAttrText { value, .. }
|
||||
| ElementAttr::AttrText { value, .. }
|
||||
if value.is_static() => {}
|
||||
|
||||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::AttrText { .. }
|
||||
| ElementAttr::CustomAttrText { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. } => {
|
||||
self.insert_attribute(attr.attr);
|
||||
for attr in el.merged_attributes {
|
||||
match &attr {
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr:
|
||||
ElementAttr {
|
||||
value: ElementAttrValue::AttrLiteral(input),
|
||||
..
|
||||
},
|
||||
..
|
||||
}) if input.is_static() => {}
|
||||
_ => {
|
||||
self.insert_attribute(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +354,7 @@ impl DynamicMapping {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct DynamicContext<'a> {
|
||||
dynamic_nodes: Vec<&'a BodyNode>,
|
||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||
dynamic_attributes: Vec<&'a AttributeType>,
|
||||
current_path: Vec<u8>,
|
||||
|
||||
node_paths: Vec<Vec<u8>>,
|
||||
@@ -367,9 +373,16 @@ impl<'a> DynamicContext<'a> {
|
||||
let element_name_rust = el.name.to_string();
|
||||
|
||||
let mut static_attrs = Vec::new();
|
||||
for attr in &el.attributes {
|
||||
match &attr.attr {
|
||||
ElementAttr::AttrText { name, value } if value.is_static() => {
|
||||
for attr in &el.merged_attributes {
|
||||
match &attr {
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr:
|
||||
ElementAttr {
|
||||
value: ElementAttrValue::AttrLiteral(value),
|
||||
name,
|
||||
},
|
||||
..
|
||||
}) if value.is_static() => {
|
||||
let value = value.source.as_ref().unwrap();
|
||||
let attribute_name_rust = name.to_string();
|
||||
let (name, namespace) =
|
||||
@@ -382,22 +395,9 @@ impl<'a> DynamicContext<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
||||
let value = value.source.as_ref().unwrap();
|
||||
static_attrs.push(TemplateAttribute::Static {
|
||||
name: intern(name.value().as_str()),
|
||||
namespace: None,
|
||||
value: intern(value.value().as_str()),
|
||||
})
|
||||
}
|
||||
|
||||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::AttrText { .. }
|
||||
| ElementAttr::CustomAttrText { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. } => {
|
||||
_ => {
|
||||
let idx = match mapping {
|
||||
Some(mapping) => mapping.get_attribute_idx(&attr.attr)?,
|
||||
Some(mapping) => mapping.get_attribute_idx(attr)?,
|
||||
None => self.dynamic_attributes.len(),
|
||||
};
|
||||
self.dynamic_attributes.push(attr);
|
||||
@@ -467,13 +467,27 @@ impl<'a> DynamicContext<'a> {
|
||||
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name },
|
||||
ElementName::Custom(_) => quote! { None },
|
||||
};
|
||||
let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
|
||||
ElementAttr::AttrText { name, value } if value.is_static() => {
|
||||
let static_attrs = el.merged_attributes.iter().map(|attr| match attr {
|
||||
AttributeType::Named(ElementAttrNamed {
|
||||
attr:
|
||||
ElementAttr {
|
||||
value: ElementAttrValue::AttrLiteral(value),
|
||||
name,
|
||||
},
|
||||
..
|
||||
}) if value.is_static() => {
|
||||
let value = value.to_static().unwrap();
|
||||
let ns = ns(quote!(#name.1));
|
||||
let name = match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.0 },
|
||||
ElementName::Custom(_) => {
|
||||
let ns = {
|
||||
match name {
|
||||
ElementAttrName::BuiltIn(name) => ns(quote!(#name.1)),
|
||||
ElementAttrName::Custom(_) => quote!(None),
|
||||
}
|
||||
};
|
||||
let name = match (el_name, name) {
|
||||
(ElementName::Ident(_), ElementAttrName::BuiltIn(_)) => {
|
||||
quote! { #el_name::#name.0 }
|
||||
}
|
||||
_ => {
|
||||
let as_string = name.to_string();
|
||||
quote! { #as_string }
|
||||
}
|
||||
@@ -490,25 +504,7 @@ impl<'a> DynamicContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
ElementAttr::CustomAttrText { name, value } if value.is_static() => {
|
||||
let value = value.to_static().unwrap();
|
||||
quote! {
|
||||
::dioxus::core::TemplateAttribute::Static {
|
||||
name: #name,
|
||||
namespace: None,
|
||||
value: #value,
|
||||
|
||||
// todo: we don't diff these so we never apply the volatile flag
|
||||
// volatile: dioxus_elements::#el_name::#name.2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::AttrText { .. }
|
||||
| ElementAttr::CustomAttrText { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. } => {
|
||||
_ => {
|
||||
let ct = self.dynamic_attributes.len();
|
||||
self.dynamic_attributes.push(attr);
|
||||
self.attr_paths.push(self.current_path.clone());
|
||||
|
||||
@@ -126,7 +126,7 @@ impl ToTokens for BodyNode {
|
||||
}),
|
||||
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
||||
{
|
||||
let ___nodes = (#exp).into_vnode(__cx);
|
||||
let ___nodes = (#exp).into_dyn_node(__cx);
|
||||
___nodes
|
||||
}
|
||||
}),
|
||||
@@ -145,7 +145,7 @@ impl ToTokens for BodyNode {
|
||||
// And then we can return them into the dyn loop
|
||||
tokens.append_all(quote! {
|
||||
{
|
||||
let ___nodes =(#expr).into_iter().map(|#pat| { #renderer }).into_vnode(__cx);
|
||||
let ___nodes =(#expr).into_iter().map(|#pat| { #renderer }).into_dyn_node(__cx);
|
||||
___nodes
|
||||
}
|
||||
})
|
||||
@@ -154,7 +154,7 @@ impl ToTokens for BodyNode {
|
||||
if is_if_chain_terminated(chain) {
|
||||
tokens.append_all(quote! {
|
||||
{
|
||||
let ___nodes = (#chain).into_vnode(__cx);
|
||||
let ___nodes = (#chain).into_dyn_node(__cx);
|
||||
___nodes
|
||||
}
|
||||
});
|
||||
@@ -211,7 +211,7 @@ impl ToTokens for BodyNode {
|
||||
|
||||
tokens.append_all(quote! {
|
||||
{
|
||||
let ___nodes = (#body).into_vnode(__cx);
|
||||
let ___nodes = (#body).into_dyn_node(__cx);
|
||||
___nodes
|
||||
}
|
||||
});
|
||||
@@ -260,7 +260,7 @@ impl Parse for ForLoop {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_if_chain_terminated(chain: &ExprIf) -> bool {
|
||||
pub(crate) fn is_if_chain_terminated(chain: &ExprIf) -> bool {
|
||||
let mut current = chain;
|
||||
loop {
|
||||
if let Some((_, else_block)) = ¤t.else_branch {
|
||||
|
||||
Reference in new issue
Block a user