Skip to content

Commit

Permalink
rstml 0.12 and enforce braces in attribute values
Browse files Browse the repository at this point in the history
  • Loading branch information
blorbb committed Aug 2, 2024
1 parent 5ad17d6 commit 80e8d49
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 53 deletions.
2 changes: 1 addition & 1 deletion leptos/src/for_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ mod tests {
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
let list: HtmlElement<_, _, _, Dom> = view! {
<ol>
<For each=move || values.get() key=|i| *i let:i>
<For each={move || values.get()} key={|i| *i} let:i>
<li>{i}</li>
</For>
</ol>
Expand Down
10 changes: 5 additions & 5 deletions leptos/src/hydration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn AutoReload(
};

let script = include_str!("reload_script.js");
view! { <script nonce=nonce>{format!("{script}({reload_port:?}, {protocol})")}</script> }
view! { <script nonce={nonce}>{format!("{script}({reload_port:?}, {protocol})")}</script> }
})
}

Expand Down Expand Up @@ -54,15 +54,15 @@ pub fn HydrationScripts(
};

view! {
<link rel="modulepreload" href=format!("/{pkg_path}/{output_name}.js") nonce=nonce.clone()/>
<link rel="modulepreload" href={format!("/{pkg_path}/{output_name}.js")} nonce={nonce.clone()}/>
<link
rel="preload"
href=format!("/{pkg_path}/{wasm_output_name}.wasm")
href={format!("/{pkg_path}/{wasm_output_name}.wasm")}
r#as="fetch"
r#type="application/wasm"
crossorigin=nonce.clone().unwrap_or_default()
crossorigin={nonce.clone().unwrap_or_default()}
/>
<script type="module" nonce=nonce>
<script type="module" nonce={nonce}>
{format!("{script}({pkg_path:?}, {output_name:?}, {wasm_output_name:?})")}
</script>
}
Expand Down
14 changes: 7 additions & 7 deletions leptos/tests/ssr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ fn simple_ssr_test() {
let (value, set_value) = signal(0);
let rendered: HtmlElement<_, _, _, Dom> = view! {
<div>
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
<button on:click={move |_| set_value.update(|value| *value -= 1)}>"-1"</button>
<span>"Value: " {move || value.get().to_string()} "!"</span>
<button on:click=move |_| set_value.update(|value| *value += 1)>"+1"</button>
<button on:click={move |_| set_value.update(|value| *value += 1)}>"+1"</button>
</div>
};

Expand All @@ -29,9 +29,9 @@ fn ssr_test_with_components() {
let (value, set_value) = signal(initial_value);
view! {
<div>
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
<button on:click={move |_| set_value.update(|value| *value -= 1)}>"-1"</button>
<span>"Value: " {move || value.get().to_string()} "!"</span>
<button on:click=move |_| set_value.update(|value| *value += 1)>"+1"</button>
<button on:click={move |_| set_value.update(|value| *value += 1)}>"+1"</button>
</div>
}
}
Expand Down Expand Up @@ -60,9 +60,9 @@ fn ssr_test_with_snake_case_components() {
let (value, set_value) = signal(initial_value);
view! {
<div>
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
<button on:click={move |_| set_value.update(|value| *value -= 1)}>"-1"</button>
<span>"Value: " {move || value.get().to_string()} "!"</span>
<button on:click=move |_| set_value.update(|value| *value += 1)>"+1"</button>
<button on:click={move |_| set_value.update(|value| *value += 1)}>"+1"</button>
</div>
}
}
Expand Down Expand Up @@ -106,7 +106,7 @@ fn ssr_with_styles() {
let styles = "myclass";
let rendered: HtmlElement<_, _, _, Dom> = view! { class=styles,
<div>
<button class="btn" on:click=move |_| set_value.update(|value| *value -= 1)>
<button class="btn" on:click={move |_| set_value.update(|value| *value -= 1)}>
"-1"
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion leptos_hot_reload/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ syn = { version = "2", features = [
"printing",
] }
quote = "1"
rstml = "0.11.2"
rstml = "0.12.0"
proc-macro2 = { version = "1", features = ["span-locations", "nightly"] }
parking_lot = "0.12"
walkdir = "2"
Expand Down
4 changes: 2 additions & 2 deletions leptos_hot_reload/src/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rstml::node::{NodeElement, NodeName};
use rstml::node::{CustomNode, NodeElement, NodeName};

/// Converts `syn::Block` to simple expression
///
Expand Down Expand Up @@ -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<impl CustomNode>) -> bool {
is_component_tag_name(node.name())
}
2 changes: 1 addition & 1 deletion leptos_macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ proc-macro-error = { version = "1", default-features = false }
proc-macro2 = "1"
quote = "1"
syn = { version = "2", 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"
Expand Down
11 changes: 9 additions & 2 deletions leptos_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,17 @@ pub fn view(tokens: TokenStream) -> TokenStream {
let (nodes, errors) = parser.parse_recoverable(tokens).split_vec();
let errors = errors.into_iter().map(|e| e.emit_as_expr_tokens());
let nodes_output = view::render_view(&nodes, global_class.as_ref(), None);

// 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()
Expand Down
6 changes: 3 additions & 3 deletions leptos_macro/src/view/component_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<impl CustomNode>,
global_class: Option<&TokenTree>,
) -> TokenStream {
let name = node.name();
Expand Down Expand Up @@ -155,7 +155,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
}
Expand Down
63 changes: 42 additions & 21 deletions leptos_macro/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,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;
use syn::{spanned::Spanned, Expr, ExprRange, Lit, LitStr, RangeLimits, Stmt};
Expand Down Expand Up @@ -55,7 +55,7 @@ pub fn render_view(
}

fn element_children_to_tokens(
nodes: &[Node],
nodes: &[Node<impl CustomNode>],
parent_type: TagType,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand Down Expand Up @@ -83,7 +83,7 @@ fn element_children_to_tokens(
}

fn fragment_to_tokens(
nodes: &[Node],
nodes: &[Node<impl CustomNode>],
parent_type: TagType,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand All @@ -108,7 +108,7 @@ fn fragment_to_tokens(
}

fn children_to_tokens(
nodes: &[Node],
nodes: &[Node<impl CustomNode>],
parent_type: TagType,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand Down Expand Up @@ -152,7 +152,7 @@ fn children_to_tokens(
}

fn node_to_tokens(
node: &Node,
node: &Node<impl CustomNode>,
parent_type: TagType,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand Down Expand Up @@ -185,6 +185,7 @@ fn node_to_tokens(
global_class,
view_marker,
),
Node::Custom(node) => Some(node.to_token_stream()),
}
}

Expand All @@ -202,7 +203,7 @@ fn text_to_tokens(text: &LitStr) -> TokenStream {
}

pub(crate) fn element_to_tokens(
node: &NodeElement,
node: &NodeElement<impl CustomNode>,
mut parent_type: TagType,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand Down Expand Up @@ -244,7 +245,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);
}
}
Expand Down Expand Up @@ -357,7 +358,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() {
NodeName::Block(block) => matches!(
block.stmts.first(),
Expand Down Expand Up @@ -715,7 +716,7 @@ fn is_custom_element(tag: &str) -> bool {
tag.contains('-')
}

fn is_self_closing(node: &NodeElement) -> bool {
fn is_self_closing(node: &NodeElement<impl CustomNode>) -> bool {
// self-closing tags
// https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
[
Expand Down Expand Up @@ -863,20 +864,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
}
}
},
}
}

Expand Down
10 changes: 5 additions & 5 deletions leptos_macro/src/view/slot_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<impl CustomNode>,
slot: &KeyedAttribute,
parent_slots: Option<&mut HashMap<String, Vec<TokenStream>>>,
global_class: Option<&TokenTree>,
Expand All @@ -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)
Expand Down Expand Up @@ -214,10 +214,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<impl CustomNode>) -> 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
Expand Down
2 changes: 1 addition & 1 deletion router/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ pub fn RoutingProgress(

view! {
<Show when=move || is_showing.get() fallback=|| ()>
<progress min="0" max="100" value=move || progress.get()></progress>
<progress min="0" max="100" value={move || progress.get()}></progress>
</Show>
}
}
8 changes: 4 additions & 4 deletions router/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ where

view! {
<a
href=move || href.get().unwrap_or_default()
target=target
prop:state=state.map(|s| s.to_js_value())
prop:replace=replace
href={move || href.get().unwrap_or_default()}
target={target}
prop:state={state.map(|s| s.to_js_value())}
prop:replace={replace}
aria-current={
let is_active = is_active.clone();
move || if is_active.get() { Some("page") } else { None }
Expand Down

0 comments on commit 80e8d49

Please sign in to comment.