mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 16:54:41 -05:00
Compare commits
16 Commits
version-up
...
leptos_0.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b317d47fd5 | ||
|
|
35bb79bb05 | ||
|
|
76aa56b6fb | ||
|
|
bd15fc5e65 | ||
|
|
b5f16d259d | ||
|
|
1ca279aca8 | ||
|
|
2e692a7611 | ||
|
|
d4122db0ef | ||
|
|
3aef9e2d21 | ||
|
|
0653e676dd | ||
|
|
4c0ead55e4 | ||
|
|
8ff3af1ded | ||
|
|
246c9e449d | ||
|
|
7789d58e7b | ||
|
|
4c6f3d067f | ||
|
|
0388903110 |
26
Cargo.toml
26
Cargo.toml
@@ -28,24 +28,24 @@ members = [
|
||||
exclude = ["benchmarks", "examples"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.6.13"
|
||||
version = "0.6.15"
|
||||
rust-version = "1.75"
|
||||
|
||||
[workspace.dependencies]
|
||||
oco_ref = { path = "./oco", version = "0.1.0" }
|
||||
leptos = { path = "./leptos", version = "0.6.13" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.6.13" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.13" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.6.13" }
|
||||
leptos_reactive = { path = "./leptos_reactive", version = "0.6.13" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.6.13" }
|
||||
server_fn = { path = "./server_fn", version = "0.6.13" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.6.13" }
|
||||
leptos = { path = "./leptos", version = "0.6.15" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.6.15" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.15" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.6.15" }
|
||||
leptos_reactive = { path = "./leptos_reactive", version = "0.6.15" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.6.15" }
|
||||
server_fn = { path = "./server_fn", version = "0.6.15" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.6.15" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.6" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.6.13" }
|
||||
leptos_router = { path = "./router", version = "0.6.13" }
|
||||
leptos_meta = { path = "./meta", version = "0.6.13" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.13" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.6.15" }
|
||||
leptos_router = { path = "./router", version = "0.6.15" }
|
||||
leptos_meta = { path = "./meta", version = "0.6.15" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.15" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -22,4 +22,4 @@ rstest = "0.17.0"
|
||||
|
||||
[dev-dependencies.web-sys]
|
||||
features = ["HtmlElement", "XPathResult"]
|
||||
version = "0.3.61"
|
||||
version = "0.3.70"
|
||||
|
||||
@@ -23,11 +23,7 @@ pub fn copy_to_clipboard(el: HtmlElement<AnyElement>, content: &str) {
|
||||
evt.prevent_default();
|
||||
evt.stop_propagation();
|
||||
|
||||
let _ = window()
|
||||
.navigator()
|
||||
.clipboard()
|
||||
.expect("navigator.clipboard to be available")
|
||||
.write_text(&content);
|
||||
let _ = window().navigator().clipboard().write_text(&content);
|
||||
|
||||
let _ = el.clone().inner_html(format!("Copied \"{}\"", &content));
|
||||
});
|
||||
@@ -76,9 +72,13 @@ pub fn App() -> impl IntoView {
|
||||
let data = "Hello World!";
|
||||
|
||||
view! {
|
||||
<a href="#" use:copy_to_clipboard=data>"Copy \"" {data} "\" to clipboard"</a>
|
||||
<a href="#" use:copy_to_clipboard=data>
|
||||
"Copy \""
|
||||
{data}
|
||||
"\" to clipboard"
|
||||
</a>
|
||||
// automatically applies the directive to every root element in `SomeComponent`
|
||||
<SomeComponent use:highlight />
|
||||
<SomeComponent use:highlight/>
|
||||
// no value will default to `().into()`
|
||||
<button use:add_dot>"Add a dot"</button>
|
||||
// `5.into()` automatically called
|
||||
|
||||
@@ -27,7 +27,7 @@ thiserror = "1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
serde_toml = "0.0.1"
|
||||
toml = "0.8.8"
|
||||
web-sys = { version = "0.3.67", features = ["FileList", "File"] }
|
||||
web-sys = { version = "0.3.70", features = ["FileList", "File"] }
|
||||
strum = { version = "0.25.0", features = ["strum_macros", "derive"] }
|
||||
notify = { version = "6.1.1", optional = true }
|
||||
pin-project-lite = "0.2.13"
|
||||
|
||||
@@ -15,7 +15,7 @@ console_error_panic_hook = "0.1.7"
|
||||
uuid = { version = "1", features = ["v4", "js", "serde"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
web-sys = { version = "0.3.60", features = ["Storage"] }
|
||||
web-sys = { version = "0.3.70", features = ["Storage"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
||||
@@ -1030,6 +1030,8 @@ where
|
||||
let full_path = format!("http://leptos.dev{path}");
|
||||
|
||||
let (tx, rx) = futures::channel::oneshot::channel();
|
||||
|
||||
let current_span = tracing::Span::current();
|
||||
spawn_task!(async move {
|
||||
let app = {
|
||||
let full_path = full_path.clone();
|
||||
@@ -1063,7 +1065,7 @@ where
|
||||
*writable = new_res_parts;
|
||||
|
||||
_ = tx.send(html);
|
||||
});
|
||||
}.instrument(current_span));
|
||||
|
||||
let html = rx.await.expect("to complete HTML rendering");
|
||||
|
||||
@@ -1860,3 +1862,4 @@ where
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(format!("{e:?}")))
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ pub fn html_parts_separated(
|
||||
idle(() => {{
|
||||
import('{pkg_path}/{output_name}{js_hash}.js')
|
||||
.then(mod => {{
|
||||
mod.default('{pkg_path}/{wasm_output_name}{wasm_hash}.wasm').then({import_callback});
|
||||
mod.default({{module_or_path: '{pkg_path}/{wasm_output_name}{wasm_hash}.wasm'}}).then({import_callback});
|
||||
}})
|
||||
}});
|
||||
</script>
|
||||
|
||||
@@ -28,7 +28,7 @@ server_fn = { workspace = true, features = [
|
||||
"url",
|
||||
"cbor",
|
||||
] }
|
||||
web-sys = { version = "0.3.63", features = [
|
||||
web-sys = { version = "0.3.70", features = [
|
||||
"ShadowRoot",
|
||||
"ShadowRootInit",
|
||||
"ShadowRootMode",
|
||||
|
||||
@@ -1211,6 +1211,17 @@ impl IntoView for Rc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoView for std::sync::Arc<str> {
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
instrument(level = "trace", name = "#text", skip_all)
|
||||
)]
|
||||
#[inline(always)]
|
||||
fn into_view(self) -> View {
|
||||
View::Text(Text::new(self.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoView for Oco<'static, str> {
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
|
||||
@@ -18,7 +18,7 @@ cfg-if = "1"
|
||||
html-escape = "0.2"
|
||||
itertools = "0.12"
|
||||
prettyplease = "0.2.4"
|
||||
proc-macro-error = { version = "1", default-features = false }
|
||||
proc-macro-error2 = { version = "2", default-features = false }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
|
||||
@@ -128,7 +128,7 @@ impl ToTokens for Model {
|
||||
_ => None,
|
||||
});
|
||||
if let Some(semi) = ends_semi {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
semi.span(),
|
||||
"A component that ends with a `view!` macro followed by a \
|
||||
semicolon will return (), an empty view. This is usually \
|
||||
@@ -416,41 +416,61 @@ impl ToTokens for Model {
|
||||
let island_props = if is_island_with_children
|
||||
|| is_island_with_other_props
|
||||
{
|
||||
let (destructure, prop_builders) = if is_island_with_other_props
|
||||
{
|
||||
let prop_names = props
|
||||
.iter()
|
||||
.filter_map(|prop| {
|
||||
if prop.name.ident == "children" {
|
||||
None
|
||||
} else {
|
||||
let name = &prop.name.ident;
|
||||
Some(quote! { #name, })
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
let destructure = quote! {
|
||||
let #props_serialized_name {
|
||||
#prop_names
|
||||
} = props;
|
||||
let (destructure, prop_builders, optional_props) =
|
||||
if is_island_with_other_props {
|
||||
let prop_names = props
|
||||
.iter()
|
||||
.filter_map(|prop| {
|
||||
if prop.name.ident == "children" {
|
||||
None
|
||||
} else {
|
||||
let name = &prop.name.ident;
|
||||
Some(quote! { #name, })
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
let destructure = quote! {
|
||||
let #props_serialized_name {
|
||||
#prop_names
|
||||
} = props;
|
||||
};
|
||||
let prop_builders = props
|
||||
.iter()
|
||||
.filter_map(|prop| {
|
||||
if prop.name.ident == "children"
|
||||
|| prop.prop_opts.optional
|
||||
{
|
||||
None
|
||||
} else {
|
||||
let name = &prop.name.ident;
|
||||
Some(quote! {
|
||||
.#name(#name)
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
let optional_props = props
|
||||
.iter()
|
||||
.filter_map(|prop| {
|
||||
if prop.name.ident == "children"
|
||||
|| !prop.prop_opts.optional
|
||||
{
|
||||
None
|
||||
} else {
|
||||
let name = &prop.name.ident;
|
||||
Some(quote! {
|
||||
if let Some(#name) = #name {
|
||||
props.#name = Some(#name)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
(destructure, prop_builders, optional_props)
|
||||
} else {
|
||||
(quote! {}, quote! {}, quote! {})
|
||||
};
|
||||
let prop_builders = props
|
||||
.iter()
|
||||
.filter_map(|prop| {
|
||||
if prop.name.ident == "children" {
|
||||
None
|
||||
} else {
|
||||
let name = &prop.name.ident;
|
||||
Some(quote! {
|
||||
.#name(#name)
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
(destructure, prop_builders)
|
||||
} else {
|
||||
(quote! {}, quote! {})
|
||||
};
|
||||
let children = if is_island_with_children {
|
||||
quote! {
|
||||
.children(::std::boxed::Box::new(move || ::leptos::Fragment::lazy(|| ::std::vec![
|
||||
@@ -469,10 +489,13 @@ impl ToTokens for Model {
|
||||
|
||||
quote! {{
|
||||
#destructure
|
||||
#props_name::builder()
|
||||
let mut props = #props_name::builder()
|
||||
#prop_builders
|
||||
#children
|
||||
.build()
|
||||
#children.build();
|
||||
|
||||
#optional_props
|
||||
|
||||
props
|
||||
}}
|
||||
} else {
|
||||
quote! {}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#![allow(private_macro_use)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_error;
|
||||
extern crate proc_macro_error2;
|
||||
|
||||
use component::DummyModel;
|
||||
use proc_macro::TokenStream;
|
||||
@@ -313,7 +313,7 @@ mod slot;
|
||||
/// # ;
|
||||
/// # }
|
||||
/// ```
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
#[proc_macro]
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
@@ -391,7 +391,7 @@ fn normalized_call_site(site: proc_macro::Span) -> Option<String> {
|
||||
/// syntax as the [view!] macro. In hydration or server-side rendering mode,
|
||||
/// behaves exactly as the `view` macro. In client-side rendering mode, uses a `<template>`
|
||||
/// node to efficiently render the element. Should only be used with a single root element.
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
#[proc_macro]
|
||||
pub fn template(tokens: TokenStream) -> TokenStream {
|
||||
if cfg!(feature = "csr") {
|
||||
@@ -583,7 +583,7 @@ pub fn template(tokens: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
let is_transparent = if !args.is_empty() {
|
||||
@@ -699,7 +699,7 @@ pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn island(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
let Ok(mut dummy) = syn::parse::<DummyModel>(s.clone()) else {
|
||||
@@ -839,7 +839,7 @@ pub fn island(_args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
if !args.is_empty() {
|
||||
|
||||
@@ -43,7 +43,6 @@ pub(crate) fn fragment_to_tokens(
|
||||
let mut nodes = nodes
|
||||
.iter()
|
||||
.filter_map(|node| {
|
||||
let span = node.span();
|
||||
let node = node_to_tokens(
|
||||
node,
|
||||
parent_type,
|
||||
@@ -52,10 +51,8 @@ pub(crate) fn fragment_to_tokens(
|
||||
None,
|
||||
)?;
|
||||
|
||||
let node = quote_spanned!(span => { #node });
|
||||
|
||||
Some(quote! {
|
||||
::leptos::IntoView::into_view(#[allow(unused_braces)] #node)
|
||||
::leptos::IntoView::into_view(#[allow(unused_braces)] { #node })
|
||||
})
|
||||
})
|
||||
.peekable();
|
||||
@@ -135,7 +132,7 @@ pub(crate) fn node_to_tokens(
|
||||
Node::RawText(r) => {
|
||||
let text = r.to_string_best();
|
||||
if text == "cx," {
|
||||
proc_macro_error::abort!(
|
||||
proc_macro_error2::abort!(
|
||||
r.span(),
|
||||
"`cx,` is not used with the `view!` macro in 0.5."
|
||||
)
|
||||
@@ -188,7 +185,7 @@ pub(crate) fn element_to_tokens(
|
||||
match parent_type {
|
||||
TagType::Unknown => {
|
||||
// We decided this warning was too aggressive, but I'll leave it here in case we want it later
|
||||
/* proc_macro_error::emit_warning!(name.span(), "The view macro is assuming this is an HTML element, \
|
||||
/* proc_macro_error2::emit_warning!(name.span(), "The view macro is assuming this is an HTML element, \
|
||||
but it is ambiguous; if it is an SVG or MathML element, prefix with svg:: or math::"); */
|
||||
quote! {
|
||||
::leptos::leptos_dom::html::#name()
|
||||
@@ -285,7 +282,7 @@ pub(crate) fn element_to_tokens(
|
||||
};
|
||||
|
||||
if is_self_closing(node) && !node.children.is_empty() {
|
||||
proc_macro_error::abort!(
|
||||
proc_macro_error2::abort!(
|
||||
node.name().span(),
|
||||
format!(
|
||||
"<{tag}> is a self-closing tag and cannot have children."
|
||||
@@ -306,9 +303,7 @@ pub(crate) fn element_to_tokens(
|
||||
global_class,
|
||||
None,
|
||||
)
|
||||
.unwrap_or(quote_spanned! {
|
||||
Span::call_site()=> ::leptos::leptos_dom::Unit
|
||||
}),
|
||||
.unwrap_or(quote! { ::leptos::leptos_dom::Unit }),
|
||||
),
|
||||
Node::Text(node) => Some(quote! { #node }),
|
||||
Node::RawText(node) => {
|
||||
@@ -337,16 +332,17 @@ pub(crate) fn element_to_tokens(
|
||||
quote! {}
|
||||
};
|
||||
let ide_helper_close_tag = ide_helper_close_tag.into_iter();
|
||||
let result = quote_spanned! {node.span()=> {
|
||||
#(#ide_helper_close_tag)*
|
||||
#name
|
||||
#(#attrs)*
|
||||
#(#bindings)*
|
||||
#(#class_attrs)*
|
||||
#(#style_attrs)*
|
||||
#global_class_expr
|
||||
#(#children)*
|
||||
#view_marker
|
||||
let result = quote! {
|
||||
{
|
||||
#(#ide_helper_close_tag)*
|
||||
#name
|
||||
#(#attrs)*
|
||||
#(#bindings)*
|
||||
#(#class_attrs)*
|
||||
#(#style_attrs)*
|
||||
#global_class_expr
|
||||
#(#children)*
|
||||
#view_marker
|
||||
}
|
||||
};
|
||||
|
||||
@@ -406,18 +402,14 @@ pub(crate) fn attribute_to_tokens(
|
||||
let event_type = if is_custom {
|
||||
event_type
|
||||
} else if let Some(ev_name) = event_name_ident {
|
||||
quote_spanned! {
|
||||
ev_name.span()=> #ev_name
|
||||
}
|
||||
quote! { #ev_name }
|
||||
} else {
|
||||
event_type
|
||||
};
|
||||
|
||||
let event_type = if is_force_undelegated {
|
||||
let undelegated = if let Some(undelegated) = undelegated_ident {
|
||||
quote_spanned! {
|
||||
undelegated.span()=> #undelegated
|
||||
}
|
||||
quote! { #undelegated }
|
||||
} else {
|
||||
quote! { undelegated }
|
||||
};
|
||||
@@ -478,7 +470,7 @@ pub(crate) fn attribute_to_tokens(
|
||||
&& node.value().and_then(value_to_string).is_none()
|
||||
{
|
||||
let span = node.key.span();
|
||||
proc_macro_error::emit_error!(span, "Combining a global class (view! { class = ... }) \
|
||||
proc_macro_error2::emit_error!(span, "Combining a global class (view! { class = ... }) \
|
||||
and a dynamic `class=` attribute on an element causes runtime inconsistencies. You can \
|
||||
toggle individual classes dynamically with the `class:name=value` syntax. \n\nSee this issue \
|
||||
for more information and an example: https://github.com/leptos-rs/leptos/issues/773")
|
||||
|
||||
@@ -384,7 +384,7 @@ fn child_to_tokens(
|
||||
match node {
|
||||
Node::Element(node) => {
|
||||
if is_component_node(node) {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
node.name().span(),
|
||||
"component children not allowed in template!, use view! \
|
||||
instead"
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::{
|
||||
};
|
||||
use crate::view::directive_call_from_attribute_node;
|
||||
use proc_macro2::{Ident, TokenStream, TokenTree};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use rstml::node::{NodeAttribute, NodeElement};
|
||||
use std::collections::HashMap;
|
||||
use syn::spanned::Spanned;
|
||||
@@ -70,10 +70,8 @@ pub(crate) fn component_to_tokens(
|
||||
})
|
||||
.unwrap_or_else(|| quote! { #name });
|
||||
|
||||
let value = quote_spanned!(value.span()=> { #value });
|
||||
|
||||
quote_spanned! {attr.span()=>
|
||||
.#name(#[allow(unused_braces)] #value)
|
||||
quote! {
|
||||
.#name(#[allow(unused_braces)] { #value })
|
||||
}
|
||||
});
|
||||
|
||||
@@ -102,7 +100,13 @@ pub(crate) fn component_to_tokens(
|
||||
.filter(|attr| attr.key.to_string().starts_with("on:"))
|
||||
.map(|attr| {
|
||||
let (event_type, handler) = event_from_attribute_node(attr, true);
|
||||
let on = quote_spanned!(attr.key.span()=> on);
|
||||
// HACK(chrisp60): rstml and leptos has a different definition on attribute keys.
|
||||
// This retains precise span information for the "on" in "on:some_event_name".
|
||||
//
|
||||
// A similar hack is done in `event_from_attribute_node` to retain the precise
|
||||
// event name span.
|
||||
let on = attr.key.to_token_stream().into_iter().next();
|
||||
|
||||
quote! {
|
||||
.#on(#event_type, #handler)
|
||||
}
|
||||
@@ -169,8 +173,7 @@ pub(crate) fn component_to_tokens(
|
||||
items_to_bind.iter().map(|ident| quote! { #ident, });
|
||||
|
||||
let clonables = items_to_clone.iter().map(|ident| {
|
||||
let ident_ref = quote_spanned!(ident.span()=> &#ident);
|
||||
quote! { let #ident = ::core::clone::Clone::clone(#ident_ref); }
|
||||
quote! { let #ident = ::core::clone::Clone::clone(&#ident); }
|
||||
});
|
||||
|
||||
if bindables.len() > 0 {
|
||||
@@ -202,7 +205,7 @@ pub(crate) fn component_to_tokens(
|
||||
.span();
|
||||
let slot = Ident::new(&slot, span);
|
||||
let value = if values.len() > 1 {
|
||||
quote_spanned! {span=>
|
||||
quote! {
|
||||
::std::vec![
|
||||
#(#values)*
|
||||
]
|
||||
@@ -221,28 +224,16 @@ pub(crate) fn component_to_tokens(
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let name_ref = quote_spanned! {name.span()=>
|
||||
&#name
|
||||
};
|
||||
|
||||
let build = quote_spanned! {name.span()=>
|
||||
.build()
|
||||
};
|
||||
|
||||
let component_props_builder = quote_spanned! {name.span()=>
|
||||
::leptos::component_props_builder(#name_ref #generics)
|
||||
};
|
||||
|
||||
#[allow(unused_mut)] // used in debug
|
||||
let mut component = quote_spanned! {node.span()=>
|
||||
let mut component = quote! {
|
||||
::leptos::component_view(
|
||||
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||
#name_ref,
|
||||
#component_props_builder
|
||||
&#name,
|
||||
::leptos::component_props_builder(&#name #generics)
|
||||
#(#props)*
|
||||
#(#slots)*
|
||||
#children
|
||||
#build
|
||||
.build()
|
||||
#dyn_attrs
|
||||
#(#spread_bindings)*
|
||||
)
|
||||
@@ -256,7 +247,7 @@ pub(crate) fn component_to_tokens(
|
||||
if events_and_directives.is_empty() {
|
||||
component
|
||||
} else {
|
||||
quote_spanned! {node.span()=>
|
||||
quote! {
|
||||
::leptos::IntoView::into_view(#[allow(unused_braces)] {#component})
|
||||
#(#events_and_directives)*
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ impl IdeTagHelper {
|
||||
/// Emit warning if tag is component.
|
||||
pub fn save_tag_completion(&mut self, name: &NodeName) {
|
||||
if is_component_tag_name(name) {
|
||||
proc_macro_error::emit_warning!(
|
||||
proc_macro_error2::emit_warning!(
|
||||
name.span(),
|
||||
"BUG: Component tag is used in regular tag completion."
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{attribute_value, Mode};
|
||||
use convert_case::{Case::Snake, Casing};
|
||||
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
||||
use quote::{quote, quote_spanned};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use rstml::node::{KeyedAttribute, Node, NodeElement, NodeName};
|
||||
use syn::{
|
||||
spanned::Spanned,
|
||||
@@ -439,7 +439,7 @@ fn fancy_class_name<'a>(
|
||||
}
|
||||
|
||||
_ => {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
elem.span(),
|
||||
"class name elements must be string \
|
||||
literals"
|
||||
@@ -459,7 +459,7 @@ fn fancy_class_name<'a>(
|
||||
}
|
||||
|
||||
_ => {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
class_name.span(),
|
||||
"class name must be a string literal or array of \
|
||||
string literals"
|
||||
@@ -475,7 +475,7 @@ fn fancy_class_name<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
tuple.span(),
|
||||
"class tuples must have two elements."
|
||||
)
|
||||
@@ -496,7 +496,7 @@ fn ident_from_tag_name(tag_name: &NodeName) -> Ident {
|
||||
.expect("element needs to have a name"),
|
||||
NodeName::Block(_) => {
|
||||
let span = tag_name.span();
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
span,
|
||||
"blocks not allowed in tag-name position"
|
||||
);
|
||||
@@ -529,7 +529,7 @@ fn fancy_style_name<'a>(
|
||||
{
|
||||
s.value()
|
||||
} else {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
style_name.span(),
|
||||
"style name must be a string literal"
|
||||
);
|
||||
@@ -544,7 +544,7 @@ fn fancy_style_name<'a>(
|
||||
value,
|
||||
));
|
||||
} else {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
tuple.span(),
|
||||
"style tuples must have two elements."
|
||||
)
|
||||
@@ -567,12 +567,41 @@ pub(crate) fn event_from_attribute_node(
|
||||
|
||||
let handler = attribute_value(attr);
|
||||
|
||||
let (event_type, _, name_undelegated) = parse_event_name(&event_name);
|
||||
let (event_type, is_custom, name_undelegated) =
|
||||
parse_event_name(&event_name);
|
||||
|
||||
// HACK(chrisp60): in the code above, the original span information is lost
|
||||
// as the event name is parsed from a stringified version of the tokens.
|
||||
//
|
||||
// This assumes that the attribute key is structured as "on:some_event_name" and
|
||||
// just skips the "on:" part, isolating the "some_event_name" tokens. In turn,
|
||||
// we keep the span information from the original event identifier.
|
||||
//
|
||||
// .nth(2) is because syn parses follows
|
||||
// token 0: "on"
|
||||
// token 1: ":"
|
||||
// token 2: "event"
|
||||
//
|
||||
// There are cleaners ways to do this but this is a legacy branch.
|
||||
let original_tokens = attr
|
||||
.key
|
||||
.to_token_stream()
|
||||
.into_iter()
|
||||
.nth(2)
|
||||
.expect("tokens following on:"); // see previous call to .expect in this same function
|
||||
|
||||
// is_custom wraps the event type in a struct definition, so don't use
|
||||
// our original tokens.
|
||||
let absolute_ev = if is_custom {
|
||||
quote! { ::leptos::leptos_dom::ev::#event_type }
|
||||
} else {
|
||||
quote! { ::leptos::leptos_dom::ev::#original_tokens }
|
||||
};
|
||||
|
||||
let event_type = if force_undelegated || name_undelegated {
|
||||
quote! { ::leptos::leptos_dom::ev::undelegated(::leptos::leptos_dom::ev::#event_type) }
|
||||
quote! { ::leptos::leptos_dom::ev::undelegated(#absolute_ev) }
|
||||
} else {
|
||||
quote! { ::leptos::leptos_dom::ev::#event_type }
|
||||
quote! { ::leptos::leptos_dom::ev::#absolute_ev }
|
||||
};
|
||||
(event_type, handler)
|
||||
}
|
||||
|
||||
@@ -81,16 +81,14 @@ pub(crate) fn fragment_to_tokens_ssr(
|
||||
};
|
||||
|
||||
let nodes = nodes.iter().map(|node| {
|
||||
let span = node.span();
|
||||
let node = root_node_to_tokens_ssr(node, global_class, None);
|
||||
let node = quote_spanned!(span=> { #node });
|
||||
|
||||
quote! {
|
||||
::leptos::IntoView::into_view(#[allow(unused_braces)] #node)
|
||||
::leptos::IntoView::into_view(#[allow(unused_braces)] { #node })
|
||||
}
|
||||
});
|
||||
|
||||
quote_spanned! {original_span=>
|
||||
quote! {
|
||||
{
|
||||
::leptos::Fragment::lazy(|| ::std::vec![
|
||||
#(#nodes),*
|
||||
@@ -466,7 +464,7 @@ fn attribute_to_tokens_ssr<'a>(
|
||||
&& attr.value().and_then(value_to_string).is_none()
|
||||
{
|
||||
let span = attr.key.span();
|
||||
proc_macro_error::emit_error!(span, "Combining a global class (view! { class = ... }) \
|
||||
proc_macro_error2::emit_error!(span, "Combining a global class (view! { class = ... }) \
|
||||
and a dynamic `class=` attribute on an element causes runtime inconsistencies. You can \
|
||||
toggle individual classes dynamically with the `class:name=value` syntax. \n\nSee this issue \
|
||||
for more information and an example: https://github.com/leptos-rs/leptos/issues/773")
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(crate) fn slot_to_tokens(
|
||||
let component_name = ident_from_tag_name(node.name());
|
||||
|
||||
let Some(parent_slots) = parent_slots else {
|
||||
proc_macro_error::emit_error!(
|
||||
proc_macro_error2::emit_error!(
|
||||
node.name().span(),
|
||||
"slots cannot be used inside HTML elements"
|
||||
);
|
||||
@@ -61,10 +61,8 @@ pub(crate) fn slot_to_tokens(
|
||||
})
|
||||
.unwrap_or_else(|| quote! { #name });
|
||||
|
||||
let value = quote_spanned!(value.span()=> { #value });
|
||||
|
||||
quote_spanned! {attr.span()=>
|
||||
.#name(#[allow(unused_braces)] #value)
|
||||
quote! {
|
||||
.#name(#[allow(unused_braces)] { #value })
|
||||
}
|
||||
});
|
||||
|
||||
@@ -168,7 +166,7 @@ pub(crate) fn slot_to_tokens(
|
||||
.span();
|
||||
let slot = Ident::new(&slot, span);
|
||||
let value = if values.len() > 1 {
|
||||
quote_spanned! {span=>
|
||||
quote! {
|
||||
::std::vec![
|
||||
#(#values)*
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_meta"
|
||||
version = "0.6.13"
|
||||
version = "0.6.15"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -42,6 +42,7 @@ use std::{
|
||||
ops::{Add, Deref},
|
||||
path::Path,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// "Owned Clones Once": a smart pointer that can be either a reference,
|
||||
@@ -67,6 +68,8 @@ pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
|
||||
Borrowed(&'a T),
|
||||
/// A reference counted pointer to a value.
|
||||
Counted(Rc<T>),
|
||||
/// A reference atomically-counted pointer to a value.
|
||||
AtomicallyCounted(Arc<T>),
|
||||
/// An owned value.
|
||||
Owned(<T as ToOwned>::Owned),
|
||||
}
|
||||
@@ -77,6 +80,7 @@ impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
|
||||
match self {
|
||||
Oco::Borrowed(v) => v.to_owned(),
|
||||
Oco::Counted(v) => v.as_ref().to_owned(),
|
||||
Oco::AtomicallyCounted(v) => v.as_ref().to_owned(),
|
||||
Oco::Owned(v) => v,
|
||||
}
|
||||
}
|
||||
@@ -129,6 +133,7 @@ impl<T: ?Sized + ToOwned> Deref for Oco<'_, T> {
|
||||
Oco::Borrowed(v) => v,
|
||||
Oco::Owned(v) => v.borrow(),
|
||||
Oco::Counted(v) => v,
|
||||
Oco::AtomicallyCounted(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,6 +284,7 @@ where
|
||||
match self {
|
||||
Self::Borrowed(v) => Self::Borrowed(v),
|
||||
Self::Counted(v) => Self::Counted(Rc::clone(v)),
|
||||
Self::AtomicallyCounted(v) => Self::AtomicallyCounted(Arc::clone(v)),
|
||||
Self::Owned(v) => Self::Counted(Rc::from(v.borrow())),
|
||||
}
|
||||
}
|
||||
@@ -304,6 +310,7 @@ where
|
||||
match &*self {
|
||||
Self::Borrowed(v) => Self::Borrowed(v),
|
||||
Self::Counted(v) => Self::Counted(Rc::clone(v)),
|
||||
Self::AtomicallyCounted(v) => Self::AtomicallyCounted(Arc::clone(v)),
|
||||
Self::Owned(v) => {
|
||||
let rc = Rc::from(v.borrow());
|
||||
*self = Self::Counted(rc.clone());
|
||||
@@ -413,6 +420,7 @@ where
|
||||
Oco::Borrowed(v) => Cow::Borrowed(v),
|
||||
Oco::Owned(v) => Cow::Owned(v),
|
||||
Oco::Counted(v) => Cow::Owned(v.as_ref().to_owned()),
|
||||
Oco::AtomicallyCounted(v) => Cow::Owned(v.as_ref().to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,6 +434,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<Arc<T>> for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(v: Arc<T>) -> Self {
|
||||
Oco::AtomicallyCounted(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<Box<T>> for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
@@ -446,6 +463,7 @@ impl From<Oco<'_, str>> for String {
|
||||
match v {
|
||||
Oco::Borrowed(v) => v.to_owned(),
|
||||
Oco::Counted(v) => v.as_ref().to_owned(),
|
||||
Oco::AtomicallyCounted(v) => v.as_ref().to_owned(),
|
||||
Oco::Owned(v) => v,
|
||||
}
|
||||
}
|
||||
@@ -475,6 +493,7 @@ impl<'a> From<Oco<'a, str>> for Oco<'a, [u8]> {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v.as_bytes()),
|
||||
Oco::Owned(v) => Oco::Owned(v.into_bytes()),
|
||||
Oco::Counted(v) => Oco::Counted(v.into()),
|
||||
Oco::AtomicallyCounted(v) => Oco::AtomicallyCounted(v.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
leptos = { version = "0.6.13", features = ["csr"] }
|
||||
leptos_meta = { version = "0.6.13", features = ["csr"] }
|
||||
leptos_router = { version = "0.6.13", features = ["csr"] }
|
||||
leptos = { version = "0.6.15", features = ["csr"] }
|
||||
leptos_meta = { version = "0.6.15", features = ["csr"] }
|
||||
leptos_router = { version = "0.6.15", features = ["csr"] }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_router"
|
||||
version = "0.6.13"
|
||||
version = "0.6.15"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston", "Ben Wishovich"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -322,11 +322,15 @@ impl RouterContextInner {
|
||||
|
||||
let resolved = resolved_to.to_string();
|
||||
let state = options.state.clone();
|
||||
set_reference.update(move |r| *r = resolved);
|
||||
|
||||
set_state.update({
|
||||
let next_state = state.clone();
|
||||
move |state| *state = next_state
|
||||
// batch these so the history update is atomic
|
||||
batch(|| {
|
||||
set_reference.update(move |r| *r = resolved);
|
||||
|
||||
set_state.update({
|
||||
let next_state = state.clone();
|
||||
move |state| *state = next_state
|
||||
});
|
||||
});
|
||||
|
||||
let global_suspense =
|
||||
|
||||
@@ -40,7 +40,8 @@ where
|
||||
{
|
||||
async fn from_req(req: Request) -> Result<Self, ServerFnError<CustErr>> {
|
||||
let string_data = req.as_query().unwrap_or_default();
|
||||
let args = serde_qs::from_str::<Self>(string_data)
|
||||
let args = serde_qs::Config::new(5, false)
|
||||
.deserialize_str::<Self>(string_data)
|
||||
.map_err(|e| ServerFnError::Args(e.to_string()))?;
|
||||
Ok(args)
|
||||
}
|
||||
@@ -74,7 +75,8 @@ where
|
||||
{
|
||||
async fn from_req(req: Request) -> Result<Self, ServerFnError<CustErr>> {
|
||||
let string_data = req.try_into_string().await?;
|
||||
let args = serde_qs::from_str::<Self>(&string_data)
|
||||
let args = serde_qs::Config::new(5, false)
|
||||
.deserialize_str::<Self>(&string_data)
|
||||
.map_err(|e| ServerFnError::Args(e.to_string()))?;
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
@@ -192,8 +192,10 @@ fn streaming_request(
|
||||
let headers = Headers::new()?;
|
||||
headers.append("Content-Type", content_type)?;
|
||||
headers.append("Accept", accepts)?;
|
||||
let mut init = RequestInit::new();
|
||||
init.headers(&headers).method("POST").body(Some(&stream));
|
||||
let init = RequestInit::new();
|
||||
init.set_headers(&headers);
|
||||
init.set_method("POST");
|
||||
init.set_body(&stream);
|
||||
|
||||
// Chrome requires setting `duplex: "half"` on streaming requests
|
||||
Reflect::set(
|
||||
|
||||
Reference in New Issue
Block a user