implement spreading props in the rsx macro

This commit is contained in:
Evan Almloff committed 2023-09-22 10:04:34 -05:00
1 parent 9e167dfdb7
commit 5b65c4cfb4
7 files changed
+208 -102

No files matched your search

+67 -29
View File
@@ -8,7 +8,7 @@ use syn::{
parse::{Parse, ParseBuffer, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Ident, LitStr, Result, Token,
Expr, Ident, LitStr, Result, Token,
};
// =======================================
@@ -18,11 +18,10 @@ use syn::{
pub struct Element {
pub name: ElementName,
pub key: Option<IfmtInput>,
pub attributes: Vec<ElementAttrNamed>,
pub merged_attributes: Vec<ElementAttrNamed>,
pub attributes: Vec<AttributeType>,
pub merged_attributes: Vec<AttributeType>,
pub children: Vec<BodyNode>,
pub brace: syn::token::Brace,
pub extra_attributes: Option<Expr>,
}
impl Parse for Element {
@@ -33,7 +32,7 @@ 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;
@@ -43,9 +42,20 @@ impl Parse for Element {
// "def": 456,
// abc: 123,
loop {
if content.peek(Token![...]) {
content.parse::<Token![...]>()?;
extra_attributes = Some(content.parse::<Expr>()?);
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
@@ -56,13 +66,13 @@ impl Parse for Element {
content.parse::<Token![:]>()?;
let value = content.parse::<ElementAttrValue>()?;
attributes.push(ElementAttrNamed {
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::Custom(name),
value,
},
});
}));
if content.is_empty() {
break;
@@ -85,13 +95,13 @@ 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 {
name: ElementAttrName::BuiltIn(name),
value: ElementAttrValue::EventTokens(content.parse()?),
},
});
}));
} else {
match name_str.as_str() {
"key" => {
@@ -99,13 +109,13 @@ impl Parse for Element {
}
_ => {
let value = content.parse::<ElementAttrValue>()?;
attributes.push(ElementAttrNamed {
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::BuiltIn(name),
value,
},
});
}));
}
}
}
@@ -114,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);
}
@@ -126,12 +135,26 @@ impl Parse for Element {
// Deduplicate any attributes that can be combined
// For example, if there are two `class` attributes, combine them into one
let mut merged_attributes: Vec<ElementAttrNamed> = Vec::new();
let mut merged_attributes: Vec<AttributeType> = Vec::new();
for attr in &attributes {
if let Some(old_attr_index) = merged_attributes
.iter()
.position(|a| a.attr.name == attr.attr.name)
{
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;
@@ -165,7 +188,6 @@ impl Parse for Element {
merged_attributes,
children,
brace,
extra_attributes,
})
}
}
@@ -180,15 +202,31 @@ impl ToTokens for Element {
None => quote! { None },
};
let listeners = self
.merged_attributes
.iter()
.filter(|f| matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
let listeners = self.merged_attributes.iter().filter(|f| {
matches!(
f,
AttributeType::Named(ElementAttrNamed {
attr: ElementAttr {
value: ElementAttrValue::EventTokens { .. },
..
},
..
})
)
});
let attr = self
.merged_attributes
.iter()
.filter(|f| !matches!(f.attr.value, ElementAttrValue::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(