(html): Evita duplicar atributos de Props

`html!` recopila los atributos literales de cada elemento y evita que
`Props` los duplique al renderizar.
This commit is contained in:
Manuel Cillero 2026-08-17 00:02:20 +02:00
parent 05187c9343
commit 745d492d5b
5 changed files with 274 additions and 42 deletions

View file

@ -1,6 +1,6 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, quote};
use syn::{Expr, Local, parse_quote, token::Brace};
use syn::{Expr, LitStr, Local, parse_quote, token::Brace};
use crate::maud::{ast::*, escape};
@ -71,6 +71,17 @@ impl Generator {
);
}
fn splice_attrs(&self, expr: Expr, exclude: &[LitStr], build: &mut Builder) {
let output_ident = &self.output_ident;
build.push_tokens(quote!(
pagetop::html::html_private::render_attrs_to!(
&(#expr),
&[#(#exclude),*],
&mut #output_ident
);
));
}
fn element(&self, element: Element, build: &mut Builder) {
let element_name = element.name.clone().unwrap_or_else(|| parse_quote!(div));
build.push_str("<");
@ -141,6 +152,21 @@ impl Generator {
fn attrs(&self, attrs: Vec<Attribute>, build: &mut Builder) {
let (classes, id, named_attrs, spliced) = split_attrs(attrs);
// Must run before `classes`/`id`/`named_attrs` are consumed below.
let literal_attr_names: Vec<LitStr> = {
let mut names = Vec::new();
if !classes.is_empty() {
names.push(LitStr::new("class", Span::call_site()));
}
if id.is_some() {
names.push(LitStr::new("id", Span::call_site()));
}
for (name, _) in &named_attrs {
names.push(LitStr::new(&name.to_string(), Span::call_site()));
}
names
};
if !classes.is_empty() {
let mut toggle_class_exprs = vec![];
@ -185,7 +211,7 @@ impl Generator {
self.attr(name, attr_type, build);
}
for expr in spliced {
self.splice(expr, build);
self.splice_attrs(expr, &literal_attr_names, build);
}
}