Merge branch 'master' into jk/breaking-merged

This commit is contained in:
Jonathan Kelley committed 2024-01-11 16:04:36 -08:00
commit 2ae3241cc0
211 files changed
+21135 -3770

No files matched your search

+134 -38
View File
@@ -8,7 +8,8 @@ use syn::{
parse::{Parse, ParseBuffer, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Ident, LitStr, Result, Token,
token::Brace,
Expr, Ident, LitStr, Result, Token,
};
// =======================================
@@ -18,8 +19,8 @@ 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,
}
@@ -32,7 +33,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;
@@ -42,7 +43,24 @@ 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
// "def": 456,
if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
let name = content.parse::<LitStr>()?;
let ident = name.clone();
@@ -50,13 +68,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;
@@ -68,6 +86,8 @@ impl Parse for Element {
continue;
}
// Parse
// abc: 123,
if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
let name = content.parse::<Ident>()?;
@@ -79,60 +99,120 @@ impl Parse for Element {
let span = content.span();
if name_str.starts_with("on") {
attributes.push(ElementAttrNamed {
// check for any duplicate event listeners
if attributes.iter().any(|f| {
if let AttributeType::Named(ElementAttrNamed {
attr:
ElementAttr {
name: ElementAttrName::BuiltIn(n),
value: ElementAttrValue::EventTokens(_),
},
..
}) = f
{
n == &name_str
} else {
false
}
}) {
return Err(syn::Error::new(
name.span(),
format!("Duplicate event listener `{}`", name),
));
}
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::BuiltIn(name),
value: ElementAttrValue::EventTokens(content.parse()?),
},
});
}));
} else if name_str == "key" {
key = Some(content.parse()?);
} else {
match name_str.as_str() {
"key" => {
key = Some(content.parse()?);
}
_ => {
let value = content.parse::<ElementAttrValue>()?;
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::BuiltIn(name),
value,
},
});
}
}
let value = content.parse::<ElementAttrValue>()?;
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::BuiltIn(name),
value,
},
}));
}
if content.is_empty() {
break;
}
// todo: add a message saying you need to include commas between fields
if content.parse::<Token![,]>().is_err() {
missing_trailing_comma!(span);
}
continue;
}
// Parse shorthand fields
if content.peek(Ident)
&& !content.peek2(Brace)
&& !content.peek2(Token![:])
&& !content.peek2(Token![-])
{
let name = content.parse::<Ident>()?;
let name_ = name.clone();
// If the shorthand field is children, these are actually children!
if name == "children" {
return Err(syn::Error::new(
name.span(),
r#"Shorthand element children are not supported.
To pass children into elements, wrap them in curly braces.
Like so:
div {{ {{children}} }}
"#,
));
};
let value = ElementAttrValue::Shorthand(name.clone());
attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr {
name: ElementAttrName::BuiltIn(name),
value,
},
}));
if content.is_empty() {
break;
}
if content.parse::<Token![,]>().is_err() {
missing_trailing_comma!(name_.span());
}
continue;
}
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<ElementAttrNamed> = Vec::new();
let mut merged_attributes: Vec<AttributeType> = Vec::new();
for attr in &attributes {
if let Some(old_attr_index) = merged_attributes
let attr_index = merged_attributes
.iter()
.position(|a| a.attr.name == attr.attr.name)
{
.position(|a| a.matches_attr_name(attr));
if let Some(old_attr_index) = attr_index {
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());
continue;
}
merged_attributes.push(attr.clone());
}
while !content.is_empty() {
@@ -173,15 +253,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! {
::dioxus::core::Element::new(