diff --git a/leptos_hot_reload/Cargo.toml b/leptos_hot_reload/Cargo.toml index afaed4667..afa193fd0 100644 --- a/leptos_hot_reload/Cargo.toml +++ b/leptos_hot_reload/Cargo.toml @@ -20,7 +20,7 @@ syn = { version = "2.0", features = [ "printing", ] } quote = "1.0" -rstml = "0.11.2" +rstml = "0.12.0" proc-macro2 = { version = "1.0", features = ["span-locations", "nightly"] } parking_lot = "0.12.3" walkdir = "2.5" diff --git a/leptos_hot_reload/src/parsing.rs b/leptos_hot_reload/src/parsing.rs index 697b0d538..e14f80196 100644 --- a/leptos_hot_reload/src/parsing.rs +++ b/leptos_hot_reload/src/parsing.rs @@ -1,4 +1,4 @@ -use rstml::node::{NodeElement, NodeName}; +use rstml::node::{CustomNode, NodeElement, NodeName}; /// Converts `syn::Block` to simple expression /// @@ -65,6 +65,6 @@ pub fn is_component_tag_name(name: &NodeName) -> bool { } #[must_use] -pub fn is_component_node(node: &NodeElement) -> bool { +pub fn is_component_node(node: &NodeElement) -> bool { is_component_tag_name(node.name()) } diff --git a/leptos_macro/Cargo.toml b/leptos_macro/Cargo.toml index c96ecc3d8..fdba5e3d5 100644 --- a/leptos_macro/Cargo.toml +++ b/leptos_macro/Cargo.toml @@ -22,7 +22,7 @@ proc-macro-error = { version = "1.0", default-features = false } proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } -rstml = "0.11.2" +rstml = "0.12.0" leptos_hot_reload = { workspace = true } server_fn_macro = { workspace = true } convert_case = "0.6.0" diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index c0f7b4b44..bce5a1092 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -306,10 +306,17 @@ pub fn view(tokens: TokenStream) -> TokenStream { global_class.as_ref(), normalized_call_site(proc_macro::Span::call_site()), ); + + // The allow lint needs to be put here instead of at the expansion of + // view::attribute_value(). Adding this next to the expanded expression + // seems to break rust-analyzer, but it works when the allow is put here. quote! { { - #(#errors;)* - #nodes_output + #[allow(unused_braces)] + { + #(#errors;)* + #nodes_output + } } } .into() diff --git a/leptos_macro/src/view/component_builder.rs b/leptos_macro/src/view/component_builder.rs index baeb02b1a..2c85f5b77 100644 --- a/leptos_macro/src/view/component_builder.rs +++ b/leptos_macro/src/view/component_builder.rs @@ -3,13 +3,13 @@ use crate::view::attribute_absolute; use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; use rstml::node::{ - KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, NodeName, + CustomNode, KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, NodeName }; use std::collections::HashMap; use syn::{spanned::Spanned, Expr, ExprPath, ExprRange, RangeLimits, Stmt}; pub(crate) fn component_to_tokens( - node: &NodeElement, + node: &NodeElement, global_class: Option<&TokenTree>, ) -> TokenStream { let name = node.name(); @@ -151,7 +151,7 @@ pub(crate) fn component_to_tokens( } })) } else if let NodeAttribute::Attribute(node) = attr { - attribute_absolute(node, idx >= spread_marker) + attribute_absolute(&node, idx >= spread_marker) } else { None } diff --git a/leptos_macro/src/view/mod.rs b/leptos_macro/src/view/mod.rs index 77fbb7d56..ac075c6c0 100644 --- a/leptos_macro/src/view/mod.rs +++ b/leptos_macro/src/view/mod.rs @@ -10,8 +10,8 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use proc_macro_error::abort; use quote::{quote, quote_spanned, ToTokens}; use rstml::node::{ - KeyedAttribute, Node, NodeAttribute, NodeBlock, NodeElement, NodeName, - NodeNameFragment, + CustomNode, KVAttributeValue, KeyedAttribute, Node, NodeAttribute, + NodeBlock, NodeElement, NodeName, NodeNameFragment, }; use std::collections::{HashMap, HashSet}; use syn::{ @@ -89,7 +89,7 @@ pub fn render_view( } fn element_children_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -117,7 +117,7 @@ fn element_children_to_tokens( } fn fragment_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -142,7 +142,7 @@ fn fragment_to_tokens( } fn children_to_tokens( - nodes: &[Node], + nodes: &[Node], parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -186,7 +186,7 @@ fn children_to_tokens( } fn node_to_tokens( - node: &Node, + node: &Node, parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -219,6 +219,7 @@ fn node_to_tokens( global_class, view_marker, ), + Node::Custom(node) => Some(node.to_token_stream()), } } @@ -236,7 +237,7 @@ fn text_to_tokens(text: &LitStr) -> TokenStream { } pub(crate) fn element_to_tokens( - node: &NodeElement, + node: &NodeElement, mut parent_type: TagType, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -298,7 +299,7 @@ pub(crate) fn element_to_tokens( } } NodeAttribute::Attribute(node) => { - if let Some(content) = attribute_absolute(node, true) { + if let Some(content) = attribute_absolute(&node, true) { attributes.push(content); } } @@ -411,7 +412,7 @@ pub(crate) fn element_to_tokens( } } -fn is_spread_marker(node: &NodeElement) -> bool { +fn is_spread_marker(node: &NodeElement) -> bool { match node.name() { NodeName::Block(block) => matches!( block.stmts.first(), @@ -763,7 +764,7 @@ fn is_custom_element(tag: &str) -> bool { tag.contains('-') } -fn is_self_closing(node: &NodeElement) -> bool { +fn is_self_closing(node: &NodeElement) -> bool { // self-closing tags // https://developer.mozilla.org/en-US/docs/Glossary/Empty_element [ @@ -911,20 +912,40 @@ fn attribute_name(name: &NodeName) -> TokenStream { } fn attribute_value(attr: &KeyedAttribute) -> TokenStream { - match attr.value() { - Some(value) => { - if let Expr::Lit(lit) = value { - if cfg!(feature = "nightly") { - if let Lit::Str(str) = &lit.lit { - return quote! { - ::leptos::tachys::view::static_types::Static::<#str> - }; + match attr.possible_value.to_value() { + None => quote! { true }, + Some(value) => match &value.value { + KVAttributeValue::Expr(expr) => { + // value must be a block or literal + if !matches!(expr, Expr::Block(_) | Expr::Lit(_)) { + emit_error!( + expr.span(), + "attribute values must be surrounded by braces or be literals"; + help = "wrap the expression in braces: {{{}}}", expr.to_token_stream(), + ) + } + + if let Expr::Lit(lit) = expr { + if cfg!(feature = "nightly") { + if let Lit::Str(str) = &lit.lit { + return quote! { + ::leptos::tachys::view::static_types::Static::<#str> + }; + } } } + + quote! { + {#expr} + } } - quote! { #value } - } - None => quote! { true }, + // any value in braces: expand as-is to give proper r-a support + KVAttributeValue::InvalidBraced(block) => { + quote! { + #block + } + } + }, } } diff --git a/leptos_macro/src/view/slot_helper.rs b/leptos_macro/src/view/slot_helper.rs index 0f292a9ce..a9e4d59b8 100644 --- a/leptos_macro/src/view/slot_helper.rs +++ b/leptos_macro/src/view/slot_helper.rs @@ -2,12 +2,12 @@ use super::{convert_to_snake_case, ident_from_tag_name}; use crate::view::{fragment_to_tokens, TagType}; use proc_macro2::{Ident, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; -use rstml::node::{KeyedAttribute, NodeAttribute, NodeElement}; +use rstml::node::{CustomNode, KeyedAttribute, NodeAttribute, NodeElement}; use std::collections::HashMap; use syn::spanned::Spanned; pub(crate) fn slot_to_tokens( - node: &NodeElement, + node: &NodeElement, slot: &KeyedAttribute, parent_slots: Option<&mut HashMap>>, global_class: Option<&TokenTree>, @@ -32,7 +32,7 @@ pub(crate) fn slot_to_tokens( let attrs = node.attributes().iter().filter_map(|node| { if let NodeAttribute::Attribute(node) = node { - if is_slot(node) { + if is_slot(&node) { None } else { Some(node) @@ -213,10 +213,10 @@ pub(crate) fn is_slot(node: &KeyedAttribute) -> bool { key == "slot" || key.starts_with("slot:") } -pub(crate) fn get_slot(node: &NodeElement) -> Option<&KeyedAttribute> { +pub(crate) fn get_slot(node: &NodeElement) -> Option<&KeyedAttribute> { node.attributes().iter().find_map(|node| { if let NodeAttribute::Attribute(node) = node { - if is_slot(node) { + if is_slot(&node) { Some(node) } else { None