mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
rstml 0.12 and enforce braces in attribute values
This commit is contained in:
parent
58476bb98e
commit
6a4fc96835
7 changed files with 63 additions and 35 deletions
|
@ -20,7 +20,7 @@ syn = { version = "2.0", features = [
|
||||||
"printing",
|
"printing",
|
||||||
] }
|
] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
rstml = "0.11.2"
|
rstml = "0.12.0"
|
||||||
proc-macro2 = { version = "1.0", features = ["span-locations", "nightly"] }
|
proc-macro2 = { version = "1.0", features = ["span-locations", "nightly"] }
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
walkdir = "2.5"
|
walkdir = "2.5"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use rstml::node::{NodeElement, NodeName};
|
use rstml::node::{CustomNode, NodeElement, NodeName};
|
||||||
|
|
||||||
/// Converts `syn::Block` to simple expression
|
/// Converts `syn::Block` to simple expression
|
||||||
///
|
///
|
||||||
|
@ -65,6 +65,6 @@ pub fn is_component_tag_name(name: &NodeName) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_component_node(node: &NodeElement) -> bool {
|
pub fn is_component_node(node: &NodeElement<impl CustomNode>) -> bool {
|
||||||
is_component_tag_name(node.name())
|
is_component_tag_name(node.name())
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ proc-macro-error = { version = "1.0", default-features = false }
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "2.0", features = ["full"] }
|
syn = { version = "2.0", features = ["full"] }
|
||||||
rstml = "0.11.2"
|
rstml = "0.12.0"
|
||||||
leptos_hot_reload = { workspace = true }
|
leptos_hot_reload = { workspace = true }
|
||||||
server_fn_macro = { workspace = true }
|
server_fn_macro = { workspace = true }
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
|
|
|
@ -306,12 +306,19 @@ pub fn view(tokens: TokenStream) -> TokenStream {
|
||||||
global_class.as_ref(),
|
global_class.as_ref(),
|
||||||
normalized_call_site(proc_macro::Span::call_site()),
|
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! {
|
quote! {
|
||||||
|
{
|
||||||
|
#[allow(unused_braces)]
|
||||||
{
|
{
|
||||||
#(#errors;)*
|
#(#errors;)*
|
||||||
#nodes_output
|
#nodes_output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,13 @@ use crate::view::attribute_absolute;
|
||||||
use proc_macro2::{Ident, TokenStream, TokenTree};
|
use proc_macro2::{Ident, TokenStream, TokenTree};
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
use rstml::node::{
|
use rstml::node::{
|
||||||
KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, NodeName,
|
CustomNode, KeyedAttributeValue, NodeAttribute, NodeBlock, NodeElement, NodeName
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use syn::{spanned::Spanned, Expr, ExprPath, ExprRange, RangeLimits, Stmt};
|
use syn::{spanned::Spanned, Expr, ExprPath, ExprRange, RangeLimits, Stmt};
|
||||||
|
|
||||||
pub(crate) fn component_to_tokens(
|
pub(crate) fn component_to_tokens(
|
||||||
node: &NodeElement,
|
node: &NodeElement<impl CustomNode>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let name = node.name();
|
let name = node.name();
|
||||||
|
@ -151,7 +151,7 @@ pub(crate) fn component_to_tokens(
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else if let NodeAttribute::Attribute(node) = attr {
|
} else if let NodeAttribute::Attribute(node) = attr {
|
||||||
attribute_absolute(node, idx >= spread_marker)
|
attribute_absolute(&node, idx >= spread_marker)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
||||||
use proc_macro_error::abort;
|
use proc_macro_error::abort;
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
use rstml::node::{
|
use rstml::node::{
|
||||||
KeyedAttribute, Node, NodeAttribute, NodeBlock, NodeElement, NodeName,
|
CustomNode, KVAttributeValue, KeyedAttribute, Node, NodeAttribute,
|
||||||
NodeNameFragment,
|
NodeBlock, NodeElement, NodeName, NodeNameFragment,
|
||||||
};
|
};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use syn::{
|
use syn::{
|
||||||
|
@ -89,7 +89,7 @@ pub fn render_view(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element_children_to_tokens(
|
fn element_children_to_tokens(
|
||||||
nodes: &[Node],
|
nodes: &[Node<impl CustomNode>],
|
||||||
parent_type: TagType,
|
parent_type: TagType,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -117,7 +117,7 @@ fn element_children_to_tokens(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fragment_to_tokens(
|
fn fragment_to_tokens(
|
||||||
nodes: &[Node],
|
nodes: &[Node<impl CustomNode>],
|
||||||
parent_type: TagType,
|
parent_type: TagType,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -142,7 +142,7 @@ fn fragment_to_tokens(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children_to_tokens(
|
fn children_to_tokens(
|
||||||
nodes: &[Node],
|
nodes: &[Node<impl CustomNode>],
|
||||||
parent_type: TagType,
|
parent_type: TagType,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -186,7 +186,7 @@ fn children_to_tokens(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_to_tokens(
|
fn node_to_tokens(
|
||||||
node: &Node,
|
node: &Node<impl CustomNode>,
|
||||||
parent_type: TagType,
|
parent_type: TagType,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -219,6 +219,7 @@ fn node_to_tokens(
|
||||||
global_class,
|
global_class,
|
||||||
view_marker,
|
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(
|
pub(crate) fn element_to_tokens(
|
||||||
node: &NodeElement,
|
node: &NodeElement<impl CustomNode>,
|
||||||
mut parent_type: TagType,
|
mut parent_type: TagType,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -298,7 +299,7 @@ pub(crate) fn element_to_tokens(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeAttribute::Attribute(node) => {
|
NodeAttribute::Attribute(node) => {
|
||||||
if let Some(content) = attribute_absolute(node, true) {
|
if let Some(content) = attribute_absolute(&node, true) {
|
||||||
attributes.push(content);
|
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<impl CustomNode>) -> bool {
|
||||||
match node.name() {
|
match node.name() {
|
||||||
NodeName::Block(block) => matches!(
|
NodeName::Block(block) => matches!(
|
||||||
block.stmts.first(),
|
block.stmts.first(),
|
||||||
|
@ -763,7 +764,7 @@ fn is_custom_element(tag: &str) -> bool {
|
||||||
tag.contains('-')
|
tag.contains('-')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_self_closing(node: &NodeElement) -> bool {
|
fn is_self_closing(node: &NodeElement<impl CustomNode>) -> bool {
|
||||||
// self-closing tags
|
// self-closing tags
|
||||||
// https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
|
// https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
|
||||||
[
|
[
|
||||||
|
@ -911,9 +912,20 @@ fn attribute_name(name: &NodeName) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_value(attr: &KeyedAttribute) -> TokenStream {
|
fn attribute_value(attr: &KeyedAttribute) -> TokenStream {
|
||||||
match attr.value() {
|
match attr.possible_value.to_value() {
|
||||||
Some(value) => {
|
None => quote! { true },
|
||||||
if let Expr::Lit(lit) = value {
|
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 cfg!(feature = "nightly") {
|
||||||
if let Lit::Str(str) = &lit.lit {
|
if let Lit::Str(str) = &lit.lit {
|
||||||
return quote! {
|
return quote! {
|
||||||
|
@ -922,9 +934,18 @@ fn attribute_value(attr: &KeyedAttribute) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
quote! { #value }
|
|
||||||
|
quote! {
|
||||||
|
{#expr}
|
||||||
}
|
}
|
||||||
None => quote! { true },
|
}
|
||||||
|
// any value in braces: expand as-is to give proper r-a support
|
||||||
|
KVAttributeValue::InvalidBraced(block) => {
|
||||||
|
quote! {
|
||||||
|
#block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ use super::{convert_to_snake_case, ident_from_tag_name};
|
||||||
use crate::view::{fragment_to_tokens, TagType};
|
use crate::view::{fragment_to_tokens, TagType};
|
||||||
use proc_macro2::{Ident, TokenStream, TokenTree};
|
use proc_macro2::{Ident, TokenStream, TokenTree};
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
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 std::collections::HashMap;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
pub(crate) fn slot_to_tokens(
|
pub(crate) fn slot_to_tokens(
|
||||||
node: &NodeElement,
|
node: &NodeElement<impl CustomNode>,
|
||||||
slot: &KeyedAttribute,
|
slot: &KeyedAttribute,
|
||||||
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
|
||||||
global_class: Option<&TokenTree>,
|
global_class: Option<&TokenTree>,
|
||||||
|
@ -32,7 +32,7 @@ pub(crate) fn slot_to_tokens(
|
||||||
|
|
||||||
let attrs = node.attributes().iter().filter_map(|node| {
|
let attrs = node.attributes().iter().filter_map(|node| {
|
||||||
if let NodeAttribute::Attribute(node) = node {
|
if let NodeAttribute::Attribute(node) = node {
|
||||||
if is_slot(node) {
|
if is_slot(&node) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(node)
|
Some(node)
|
||||||
|
@ -213,10 +213,10 @@ pub(crate) fn is_slot(node: &KeyedAttribute) -> bool {
|
||||||
key == "slot" || key.starts_with("slot:")
|
key == "slot" || key.starts_with("slot:")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_slot(node: &NodeElement) -> Option<&KeyedAttribute> {
|
pub(crate) fn get_slot(node: &NodeElement<impl CustomNode>) -> Option<&KeyedAttribute> {
|
||||||
node.attributes().iter().find_map(|node| {
|
node.attributes().iter().find_map(|node| {
|
||||||
if let NodeAttribute::Attribute(node) = node {
|
if let NodeAttribute::Attribute(node) = node {
|
||||||
if is_slot(node) {
|
if is_slot(&node) {
|
||||||
Some(node)
|
Some(node)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
Loading…
Reference in a new issue