Compare commits

..

2 Commits
4453 ... 4395

Author SHA1 Message Date
autofix-ci[bot]
d6ac197b0a [autofix.ci] apply automated fixes 2025-10-20 13:33:02 +00:00
Greg Johnston
31b68a4b76 leptos_actix-v0.8.6 2025-10-20 08:57:22 -04:00
39 changed files with 458 additions and 284 deletions

View File

@@ -88,7 +88,7 @@ jobs:
run: trunk --version
- name: Install Node.js
if: contains(inputs.directory, 'examples')
uses: actions/setup-node@v6
uses: actions/setup-node@v5
with:
node-version: 20
- uses: pnpm/action-setup@v4

82
Cargo.lock generated
View File

@@ -283,12 +283,6 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-executor"
version = "1.13.3"
@@ -493,30 +487,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitcode"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648bd963d2e5d465377acecfb4b827f9f553b6bc97a8f61715779e9ed9e52b74"
dependencies = [
"arrayvec",
"bitcode_derive",
"bytemuck",
"glam",
"serde",
]
[[package]]
name = "bitcode_derive"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffebfc2d28a12b262c303cb3860ee77b91bd83b1f20f0bd2a9693008e2f55a9e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "bitflags"
version = "2.9.4"
@@ -582,12 +552,6 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "bytemuck"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
[[package]]
name = "byteorder"
version = "1.5.0"
@@ -1342,12 +1306,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "glam"
version = "0.30.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460"
[[package]]
name = "glib"
version = "0.20.12"
@@ -1695,7 +1653,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.0",
"socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
@@ -1920,7 +1878,7 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "leptos"
version = "0.8.12"
version = "0.8.10"
dependencies = [
"any_spawner",
"base64",
@@ -2087,7 +2045,7 @@ dependencies = [
[[package]]
name = "leptos_macro"
version = "0.8.11"
version = "0.8.9"
dependencies = [
"attribute-derive",
"cfg-if",
@@ -2107,7 +2065,7 @@ dependencies = [
"rustc_version",
"serde",
"server_fn",
"server_fn_macro 0.8.8",
"server_fn_macro 0.8.7",
"syn 2.0.106",
"tracing",
"trybuild",
@@ -2131,7 +2089,7 @@ dependencies = [
[[package]]
name = "leptos_router"
version = "0.8.9"
version = "0.8.8"
dependencies = [
"any_spawner",
"either_of",
@@ -2155,7 +2113,7 @@ dependencies = [
[[package]]
name = "leptos_router_macro"
version = "0.8.6"
version = "0.8.5"
dependencies = [
"leptos",
"leptos_macro",
@@ -2796,7 +2754,7 @@ dependencies = [
"quinn-udp",
"rustc-hash 2.1.1",
"rustls",
"socket2 0.6.0",
"socket2 0.5.10",
"thiserror 2.0.17",
"tokio",
"tracing",
@@ -2833,7 +2791,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.6.0",
"socket2 0.5.10",
"tracing",
"windows-sys 0.60.2",
]
@@ -2915,7 +2873,7 @@ dependencies = [
[[package]]
name = "reactive_graph"
version = "0.2.9"
version = "0.2.8"
dependencies = [
"any_spawner",
"async-lock",
@@ -3465,13 +3423,12 @@ dependencies = [
[[package]]
name = "server_fn"
version = "0.8.8"
version = "0.8.7"
dependencies = [
"actix-web",
"actix-ws",
"axum",
"base64",
"bitcode",
"bytes",
"ciborium",
"const-str",
@@ -3529,7 +3486,7 @@ dependencies = [
[[package]]
name = "server_fn_macro"
version = "0.8.8"
version = "0.8.7"
dependencies = [
"const_format",
"convert_case 0.8.0",
@@ -3544,7 +3501,7 @@ dependencies = [
name = "server_fn_macro_default"
version = "0.8.5"
dependencies = [
"server_fn_macro 0.8.8",
"server_fn_macro 0.8.7",
"syn 2.0.106",
]
@@ -3791,7 +3748,7 @@ dependencies = [
[[package]]
name = "tachys"
version = "0.2.10"
version = "0.2.9"
dependencies = [
"any_spawner",
"async-trait",
@@ -3914,7 +3871,7 @@ dependencies = [
[[package]]
name = "throw_error"
version = "0.3.1"
version = "0.3.0"
dependencies = [
"anyhow",
"pin-project-lite",
@@ -4602,24 +4559,23 @@ dependencies = [
[[package]]
name = "wasm_split_helpers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a114b3073258dd5de3d812cdd048cca6842342755e828a14dbf15f843f2d1b84"
version = "0.1.2"
dependencies = [
"async-once-cell",
"or_poisoned",
"wasm_split_macros",
]
[[package]]
name = "wasm_split_macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56481f8ed1a9f9ae97ea7b08a5e2b12e8adf9a7818a6ba952b918e09c7be8bf0"
version = "0.1.3"
dependencies = [
"base16",
"digest",
"quote",
"sha2",
"syn 2.0.106",
"wasm-bindgen",
]
[[package]]

View File

@@ -45,31 +45,33 @@ rust-version = "1.88"
[workspace.dependencies]
# members
throw_error = { path = "./any_error/", version = "0.3.1" }
throw_error = { path = "./any_error/", version = "0.3.0" }
any_spawner = { path = "./any_spawner/", version = "0.3.0" }
const_str_slice_concat = { path = "./const_str_slice_concat", version = "0.1" }
either_of = { path = "./either_of/", version = "0.1.6" }
hydration_context = { path = "./hydration_context", version = "0.3.0" }
leptos = { path = "./leptos", version = "0.8.12" }
leptos = { path = "./leptos", version = "0.8.10" }
leptos_config = { path = "./leptos_config", version = "0.8.7" }
leptos_dom = { path = "./leptos_dom", version = "0.8.7" }
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.8.5" }
leptos_integration_utils = { path = "./integrations/utils", version = "0.8.6" }
leptos_macro = { path = "./leptos_macro", version = "0.8.11" }
leptos_router = { path = "./router", version = "0.8.9" }
leptos_router_macro = { path = "./router_macro", version = "0.8.6" }
leptos_macro = { path = "./leptos_macro", version = "0.8.9" }
leptos_router = { path = "./router", version = "0.8.8" }
leptos_router_macro = { path = "./router_macro", version = "0.8.5" }
leptos_server = { path = "./leptos_server", version = "0.8.5" }
leptos_meta = { path = "./meta", version = "0.8.5" }
next_tuple = { path = "./next_tuple", version = "0.1.0" }
oco_ref = { path = "./oco", version = "0.2.1" }
or_poisoned = { path = "./or_poisoned", version = "0.1.0" }
reactive_graph = { path = "./reactive_graph", version = "0.2.9" }
reactive_graph = { path = "./reactive_graph", version = "0.2.8" }
reactive_stores = { path = "./reactive_stores", version = "0.3.0" }
reactive_stores_macro = { path = "./reactive_stores_macro", version = "0.2.6" }
server_fn = { path = "./server_fn", version = "0.8.8" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.8" }
server_fn = { path = "./server_fn", version = "0.8.7" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.7" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.5" }
tachys = { path = "./tachys", version = "0.2.10" }
tachys = { path = "./tachys", version = "0.2.9" }
wasm_split_helpers = { path = "./wasm_split", version = "0.1.2" }
wasm_split_macros = { path = "./wasm_split_macros", version = "0.1.3" }
# members deps
async-once-cell = { default-features = false, version = "0.5.3" }
@@ -132,7 +134,6 @@ inventory = { default-features = false, version = "0.3.21" }
config = { default-features = false, version = "0.15.14" }
camino = { default-features = false, version = "1.2.1" }
ciborium = { default-features = false, version = "0.2.2" }
bitcode = { default-features = false, version = "0.6.6" }
multer = { default-features = false, version = "3.1.0" }
leptos-spin-macro = { default-features = false, version = "0.2.0" }
sledgehammer_utils = { default-features = false, version = "0.3.1" }
@@ -172,7 +173,6 @@ sha2 = { default-features = false, version = "0.10.8" }
subsecond = { default-features = false, version = "0.7.0-rc.0" }
dioxus-cli-config = { default-features = false, version = "0.7.0-rc.0" }
dioxus-devtools = { default-features = false, version = "0.7.0-rc.0" }
wasm_split_helpers = { default-features = false, version = "0.2.0" }
[profile.release]
codegen-units = 1

View File

@@ -1,6 +1,6 @@
[package]
name = "throw_error"
version = "0.3.1"
version = "0.3.0"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -440,14 +440,7 @@ pub fn FileUploadWithProgress() -> impl IntoView {
let mut entry =
FILES.entry(filename.to_string()).or_insert_with(|| {
println!("[{filename}]\tinserting channel");
// NOTE: this channel capacity is set arbitrarily for this demo code.
// it allows for up to exactly 1048 chunks to be sent, which sets an upper cap
// on upload size (the precise details vary by client)
// in a real system, you will want to create some more reasonable ways of
// sending and sharing notifications
//
// see https://github.com/leptos-rs/leptos/issues/4397 for related discussion
let (tx, rx) = broadcast(1048);
let (tx, rx) = broadcast(128);
File { total: 0, tx, rx }
});
entry.total += len;

View File

@@ -121,7 +121,7 @@ pub trait ExtendResponse: Sized {
// drop the owner, cleaning up the reactive runtime,
// once the stream is over
.chain(once(async move {
owner.unset_with_forced_cleanup();
owner.unset();
Default::default()
})),
));

View File

@@ -1,10 +1,9 @@
[package]
name = "leptos"
version = "0.8.12"
version = "0.8.10"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
homepage = "https://leptos.dev/"
description = "Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces."
readme = "../README.md"
rust-version.workspace = true
@@ -58,7 +57,7 @@ serde_qs = { workspace = true, default-features = true }
slotmap = { workspace = true, default-features = true }
futures = { workspace = true, default-features = true }
send_wrapper = { workspace = true, default-features = true }
wasm_split_helpers = { workspace = true, default-features = true }
wasm_split_helpers.workspace = true
subsecond = { workspace = true, default-features = true, optional = true }
dioxus-cli-config = { workspace = true, default-features = true, optional = true }
dioxus-devtools = { workspace = true, default-features = true, optional = true }
@@ -89,7 +88,6 @@ ssr = [
]
nightly = ["leptos_macro/nightly", "reactive_graph/nightly", "tachys/nightly"]
rkyv = ["server_fn/rkyv", "leptos_server/rkyv"]
bitcode = ["server_fn/bitcode"]
serde-lite = ["server_fn/serde-lite", "leptos_server/serde-lite"]
cbor = ["server_fn/cbor"]
msgpack = ["server_fn/msgpack"]
@@ -154,7 +152,6 @@ denylist = [
"trace-component-props",
"spin",
"islands",
"bitcode",
"serde-lite",
"cbor",
"msgpack",
@@ -166,6 +163,7 @@ skip_feature_sets = [
["csr", "hydrate"],
["ssr", "hydrate"],
["serde", "serde-lite"],
["serde-lite", "miniserde"],
["serde", "miniserde"],
["serde", "rkyv"],
["miniserde", "rkyv"],

View File

@@ -365,8 +365,7 @@ pub use serde_json;
pub use tracing;
#[doc(hidden)]
pub use wasm_bindgen;
#[doc(hidden)]
pub use wasm_split_helpers as wasm_split;
pub use wasm_split_helpers;
#[doc(hidden)]
pub use web_sys;

View File

@@ -221,15 +221,18 @@ fn env_w_default(
/// An enum that can be used to define the environment Leptos is running in.
/// Setting this to the `PROD` variant will not include the WebSocket code for `cargo-leptos` watch mode.
/// Defaults to `DEV`.
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Default,
)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub enum Env {
PROD,
#[default]
DEV,
}
impl Default for Env {
fn default() -> Self {
Self::DEV
}
}
fn env_from_str(input: &str) -> Result<Env, LeptosConfigError> {
let sanitized = input.to_lowercase();
match sanitized.as_ref() {
@@ -276,15 +279,18 @@ impl TryFrom<String> for Env {
/// An enum that can be used to define the websocket protocol Leptos uses for hotreloading
/// Defaults to `ws`.
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Default,
)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub enum ReloadWSProtocol {
#[default]
WS,
WSS,
}
impl Default for ReloadWSProtocol {
fn default() -> Self {
Self::WS
}
}
fn ws_from_str(input: &str) -> Result<ReloadWSProtocol, LeptosConfigError> {
let sanitized = input.to_lowercase();
match sanitized.as_ref() {

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_macro"
version = "0.8.11"
version = "0.8.9"
authors = ["Greg Johnston"]
license = "MIT"
repository = "https://github.com/leptos-rs/leptos"

View File

@@ -2,12 +2,12 @@ use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use proc_macro_error2::abort;
use quote::{format_ident, quote};
use quote::quote;
use std::{
hash::{DefaultHasher, Hash, Hasher},
mem,
};
use syn::{parse_macro_input, parse_quote, ItemFn, ReturnType, Stmt};
use syn::{parse_macro_input, ItemFn};
pub fn lazy_impl(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
let name = if !args.is_empty() {
@@ -16,7 +16,7 @@ pub fn lazy_impl(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
None
};
let fun = syn::parse::<ItemFn>(s).unwrap_or_else(|e| {
let mut fun = syn::parse::<ItemFn>(s).unwrap_or_else(|e| {
abort!(e.span(), "`lazy` can only be used on a function")
});
@@ -47,50 +47,29 @@ pub fn lazy_impl(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
let is_wasm = cfg!(feature = "csr") || cfg!(feature = "hydrate");
if is_wasm {
let mut fun = fun;
let mut return_wrapper = None;
if was_async {
fun.sig.asyncness = None;
let ty = match &fun.sig.output {
ReturnType::Default => quote! { () },
ReturnType::Type(_, ty) => quote! { #ty },
};
let sync_output: ReturnType = parse_quote! {
-> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = #ty> + ::std::marker::Send + ::std::marker::Sync>>
};
let async_output = mem::replace(&mut fun.sig.output, sync_output);
let stmts = mem::take(&mut fun.block.stmts);
fun.block.stmts.push(Stmt::Expr(parse_quote! {
::std::boxed::Box::pin(::leptos::__reexports::send_wrapper::SendWrapper::new(async move {
#( #stmts )*
}))
}, None));
return_wrapper = Some(quote! {
return_wrapper(let future = _; { future.await } #async_output),
});
}
let preload_name = format_ident!("__preload_{}", fun.sig.ident);
quote! {
#[::leptos::wasm_split::wasm_split(
#[::leptos::wasm_split_helpers::wasm_split(
#unique_name,
wasm_split_path = ::leptos::wasm_split,
preload(#[doc(hidden)] #[allow(non_snake_case)] #preload_name),
#return_wrapper
::leptos::__reexports::send_wrapper
)]
#fun
}
} else {
let mut fun = fun;
if !was_async {
fun.sig.asyncness = Some(Default::default());
}
let statements = &mut fun.block.stmts;
let old_statements = mem::take(statements);
statements.push(parse_quote! {
::leptos::prefetch_lazy_fn_on_server(#unique_name_str);
});
statements.push(
syn::parse(
quote! {
::leptos::prefetch_lazy_fn_on_server(#unique_name_str);
}
.into(),
)
.unwrap(),
);
statements.extend(old_statements);
quote! { #fun }
}

View File

@@ -15,18 +15,19 @@
}
},
"node_modules/@playwright/test": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
"integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
"integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.56.1"
"playwright": "1.44.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
"node": ">=16"
}
},
"node_modules/@types/node": {
@@ -45,6 +46,7 @@
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -54,33 +56,35 @@
}
},
"node_modules/playwright": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
"integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
"integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.56.1"
"playwright-core": "1.44.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
"node": ">=16"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
"integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
"node": ">=16"
}
},
"node_modules/typescript": {
@@ -107,12 +111,12 @@
},
"dependencies": {
"@playwright/test": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
"integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
"integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
"dev": true,
"requires": {
"playwright": "1.56.1"
"playwright": "1.44.1"
}
},
"@types/node": {
@@ -132,19 +136,19 @@
"optional": true
},
"playwright": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
"integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
"integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
"playwright-core": "1.56.1"
"playwright-core": "1.44.1"
}
},
"playwright-core": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
"integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
"integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
"dev": true
},
"typescript": {

View File

@@ -1,6 +1,6 @@
[package]
name = "reactive_graph"
version = "0.2.9"
version = "0.2.8"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -340,8 +340,6 @@ impl Owner {
}
/// Removes this from its state as the thread-local owner and drops it.
/// If there are other holders of this owner, it may not cleanup, if always cleaning up is required,
/// see [`Owner::unset_with_forced_cleanup`].
pub fn unset(self) {
OWNER.with_borrow_mut(|owner| {
if owner.as_ref().and_then(|n| n.upgrade()) == Some(self) {
@@ -350,23 +348,6 @@ impl Owner {
})
}
/// Removes this from its state as the thread-local owner and drops it.
/// Unlike [`Owner::unset`], this will always run cleanup on this owner,
/// even if there are other holders of this owner.
pub fn unset_with_forced_cleanup(self) {
OWNER.with_borrow_mut(|owner| {
if owner
.as_ref()
.and_then(|n| n.upgrade())
.map(|o| o == self)
.unwrap_or(false)
{
mem::take(owner);
}
});
self.cleanup();
}
/// Returns the current [`SharedContext`], if any.
#[cfg(feature = "hydration")]
pub fn current_shared_context(

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router"
version = "0.8.9"
version = "0.8.8"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"

View File

@@ -368,8 +368,8 @@ where
ev.prevent_default();
let to = path_name
+ if url.search.is_empty() { "" } else { "?" }
+ &url.search
+ &url.hash;
+ &Url::unescape(&url.search)
+ &Url::unescape(&url.hash);
let state = Reflect::get(&a, &JsValue::from_str("state"))
.ok()
.and_then(|value| {

View File

@@ -4,6 +4,7 @@ macro_rules! tuples {
($first:ident => $($ty:ident),*) => {
impl<$first, $($ty),*> PossibleRouteMatch for ($first, $($ty,)*)
where
Self: core::fmt::Debug,
$first: PossibleRouteMatch,
$($ty: PossibleRouteMatch),*,
{

View File

@@ -369,7 +369,7 @@ impl ResolvedStaticPath {
eprintln!("{e}");
}
}
owner.unset_with_forced_cleanup();
owner.unset();
}
}
});

View File

@@ -1,6 +1,6 @@
[package]
name = "leptos_router_macro"
version = "0.8.6"
version = "0.8.5"
authors = ["Greg Johnston", "Ben Wishovich"]
license = "MIT"
readme = "../README.md"

View File

@@ -7,7 +7,7 @@
use proc_macro::{TokenStream, TokenTree};
use proc_macro2::Span;
use proc_macro_error2::{abort, proc_macro_error, set_dummy};
use quote::{format_ident, quote, ToTokens};
use quote::{quote, ToTokens};
use syn::{
spanned::Spanned, FnArg, Ident, ImplItem, ItemImpl, Path, Type, TypePath,
};
@@ -267,7 +267,10 @@ fn lazy_route_impl(
};
let lazy_view_ident =
Ident::new(&format!("__{ty_name_to_snake}_View"), im.self_ty.span());
let preload_ident = format_ident!("__preload_{lazy_view_ident}");
let preload_lazy_view_ident = Ident::new(
&format!("__preload_{lazy_view_ident}"),
lazy_view_ident.span(),
);
im.items.push(
syn::parse::<ImplItem>(
@@ -277,7 +280,7 @@ fn lazy_route_impl(
// we don't split routes for wasm32 ssr
// but we don't require a `hydrate`/`csr` feature on leptos_router
#[cfg(target_arch = "wasm32")]
#preload_ident().await;
#preload_lazy_view_ident().await;
}
}
.into(),

View File

@@ -5,7 +5,7 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "RPC for any web framework."
readme = "../README.md"
version = "0.8.8"
version = "0.8.7"
rust-version.workspace = true
edition.workspace = true
@@ -64,7 +64,6 @@ http-body-util = { optional = true, workspace = true, default-features = true }
rkyv = { optional = true, workspace = true, default-features = true }
rmp-serde = { optional = true, workspace = true, default-features = true }
base64 = { workspace = true, default-features = true }
bitcode = { optional = true, workspace = true, default-features = true }
# client
gloo-net = { optional = true, workspace = true, default-features = true }
@@ -127,7 +126,6 @@ cbor = ["dep:ciborium"]
rkyv = ["dep:rkyv"]
msgpack = ["dep:rmp-serde"]
postcard = ["dep:postcard"]
bitcode = ["dep:bitcode"]
default-tls = ["reqwest?/default-tls"]
rustls = ["reqwest?/rustls-tls", "tokio-tungstenite?/rustls"]
reqwest = ["dep:reqwest", "dep:tokio-tungstenite", "dep:tokio"]

View File

@@ -1,49 +0,0 @@
use super::{Patch, Post, Put};
use crate::{ContentType, Decodes, Encodes, Format, FormatType};
use bytes::Bytes;
/// Serializes and deserializes with [`bitcode`].
pub struct BitcodeEncoding;
impl ContentType for BitcodeEncoding {
const CONTENT_TYPE: &'static str = "application/bitcode";
}
impl FormatType for BitcodeEncoding {
const FORMAT_TYPE: Format = Format::Binary;
}
impl<T> Encodes<T> for BitcodeEncoding
where
T: bitcode::Encode,
{
type Error = std::convert::Infallible;
fn encode(value: &T) -> Result<Bytes, Self::Error> {
Ok(Bytes::from(bitcode::encode(value)))
}
}
impl<T> Decodes<T> for BitcodeEncoding
where
T: bitcode::DecodeOwned,
{
type Error = bitcode::Error;
fn decode(bytes: Bytes) -> Result<T, Self::Error> {
bitcode::decode(bytes.as_ref())
}
}
/// Pass arguments and receive responses using `bitcode` in a `POST` request.
pub type Bitcode = Post<BitcodeEncoding>;
/// Pass arguments and receive responses using `bitcode` in the body of a `PATCH` request.
/// **Note**: Browser support for `PATCH` requests without JS/WASM may be poor.
/// Consider using a `POST` request if functionality without JS/WASM is required.
pub type PatchBitcode = Patch<BitcodeEncoding>;
/// Pass arguments and receive responses using `bitcode` in the body of a `PUT` request.
/// **Note**: Browser support for `PUT` requests without JS/WASM may be poor.
/// Consider using a `POST` request if functionality without JS/WASM is required.
pub type PutBitcode = Put<BitcodeEncoding>;

View File

@@ -50,11 +50,6 @@ mod postcard;
#[cfg(feature = "postcard")]
pub use postcard::*;
#[cfg(feature = "bitcode")]
mod bitcode;
#[cfg(feature = "bitcode")]
pub use bitcode::*;
mod patch;
pub use patch::*;
mod post;

View File

@@ -133,8 +133,6 @@ pub use ::bytes as bytes_export;
#[doc(hidden)]
pub use ::http as http_export;
use base64::{engine::general_purpose::STANDARD_NO_PAD, DecodeError, Engine};
#[cfg(feature = "bitcode")]
pub use bitcode;
// re-exported to make it possible to implement a custom Client without adding a separate
// dependency on `bytes`
pub use bytes::Bytes;

View File

@@ -10,8 +10,7 @@
feature = "multipart",
feature = "serde-lite",
feature = "cbor",
feature = "msgpack",
feature = "bitcode",
feature = "msgpack"
))
))]

View File

@@ -5,7 +5,7 @@ license = "MIT"
repository = "https://github.com/leptos-rs/leptos"
description = "RPC for any web framework."
readme = "../README.md"
version = "0.8.8"
version = "0.8.7"
edition.workspace = true
[dependencies]

View File

@@ -331,7 +331,6 @@ impl ServerFnCall {
enum PathInfo {
Serde,
Rkyv,
Bitcode,
None,
}
@@ -342,12 +341,6 @@ impl ServerFnCall {
Clone, #server_fn_path::rkyv::Archive, #server_fn_path::rkyv::Serialize, #server_fn_path::rkyv::Deserialize
},
),
Some("Bitcode") => (
PathInfo::Bitcode,
quote! {
Clone, #server_fn_path::bitcode::Encode, #server_fn_path::bitcode::Decode
},
),
Some("MultipartFormData")
| Some("Streaming")
| Some("StreamingText") => (PathInfo::None, quote! {}),
@@ -383,7 +376,6 @@ impl ServerFnCall {
#[serde(crate = #serde_path)]
}
}
PathInfo::Bitcode => quote! {},
PathInfo::Rkyv => quote! {},
PathInfo::None => quote! {},
};

View File

@@ -1,6 +1,6 @@
[package]
name = "tachys"
version = "0.2.10"
version = "0.2.9"
authors = ["Greg Johnston"]
license = "MIT"
readme = "../README.md"

View File

@@ -317,26 +317,6 @@ where
type State = ElementState<At::State, Ch::State>;
fn rebuild(self, state: &mut Self::State) {
// check whether the tag is the same, for custom elements
// because this is const `false` for all other element types,
// the compiler should be able to optimize it out
if E::TAG.is_empty() {
// see https://github.com/leptos-rs/leptos/issues/4412
let new_tag = self.tag.tag();
// this is not particularly efficient, but it saves us from
// having to keep track of the tag name for every element state
let old_tag = state.el.tag_name();
if new_tag != old_tag {
let mut new_state = self.build();
state.insert_before_this(&mut new_state);
state.unmount();
*state = new_state;
return;
}
}
// rebuild attributes and children for any element
let ElementState {
attrs, children, ..
} = state;

View File

@@ -468,8 +468,7 @@ where
//
// in this case, though, we can simply... discover that the data are already here, and then
// stuff them back into a new Future, which can safely be polled after its completion
if let Some(mut inner) = self.inner.as_mut().now_or_never() {
inner.dry_resolve();
if let Some(inner) = self.inner.as_mut().now_or_never() {
self.inner = Box::pin(async move { inner })
as Pin<Box<dyn Future<Output = T> + Send>>;
}

View File

@@ -642,13 +642,18 @@ struct DiffOpRemove {
at: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum DiffOpAddMode {
#[default]
Normal,
Append,
}
impl Default for DiffOpAddMode {
fn default() -> Self {
Self::Normal
}
}
fn apply_diff<T, VFS, V>(
parent: Option<&crate::renderer::types::Element>,
marker: &crate::renderer::types::Placeholder,

17
wasm_split/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "wasm_split_helpers"
version = "0.1.2"
authors = ["Greg Johnston"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/leptos-rs/leptos"
description = "Tools to support code-splitting and lazy loading for WebAssembly (WASM) binaries."
rust-version.workspace = true
edition.workspace = true
[dependencies]
async-once-cell = { default-features = true, workspace = true, features = [
"std",
] }
wasm_split_macros.workspace = true
or_poisoned.workspace = true

1
wasm_split/Makefile.toml Normal file
View File

@@ -0,0 +1 @@
extend = { path = "../cargo-make/main.toml" }

9
wasm_split/README.md Normal file
View File

@@ -0,0 +1,9 @@
# `wasm_split_helpers`
This crate provides functions that are used by the `wasm_split_macros` crate, which allows you to indicate that certain functions are appropriate split points for lazy-loaded code.
A build tool that supports this approach (like `cargo-leptos`) can then split a WebAssembly (WASM) binary into multiple chunks, which will be lazy-loaded when a split function is called.
This crate was adapted from an original prototype, which you can find [here](https://github.com/jbms/wasm-split-prototype), with an in-depth description of the approach [here](https://github.com/rustwasm/wasm-bindgen/issues/3939).
This functionality is provided in Leptos by the `#[lazy]` and `#[lazy_route]` macros.

107
wasm_split/src/lib.rs Normal file
View File

@@ -0,0 +1,107 @@
use std::{
ffi::c_void,
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
};
pub type LoadCallbackFn = unsafe extern "C" fn(*const c_void, bool) -> ();
pub type LoadFn = unsafe extern "C" fn(LoadCallbackFn, *const c_void) -> ();
type Lazy = async_once_cell::Lazy<Option<()>, SplitLoaderFuture>;
use or_poisoned::OrPoisoned;
pub use wasm_split_macros::wasm_split;
pub struct LazySplitLoader {
lazy: Pin<Arc<Lazy>>,
}
impl LazySplitLoader {
pub fn new(load: LoadFn) -> Self {
Self {
lazy: Arc::pin(Lazy::new(SplitLoaderFuture::new(
SplitLoader::new(load),
))),
}
}
}
pub async fn ensure_loaded(
loader: &'static std::thread::LocalKey<LazySplitLoader>,
) -> Option<()> {
*loader.with(|inner| inner.lazy.clone()).as_ref().await
}
#[derive(Clone, Copy, Debug)]
enum SplitLoaderState {
Deferred(LoadFn),
Pending,
Completed(Option<()>),
}
struct SplitLoader {
state: Mutex<SplitLoaderState>,
waker: Mutex<Option<Waker>>,
}
impl SplitLoader {
fn new(load: LoadFn) -> Arc<Self> {
Arc::new(SplitLoader {
state: Mutex::new(SplitLoaderState::Deferred(load)),
waker: Mutex::new(None),
})
}
fn complete(&self, value: bool) {
*self.state.lock().or_poisoned() =
SplitLoaderState::Completed(if value { Some(()) } else { None });
if let Some(waker) = self.waker.lock().or_poisoned().take() {
waker.wake();
}
}
}
struct SplitLoaderFuture {
loader: Arc<SplitLoader>,
}
impl SplitLoaderFuture {
fn new(loader: Arc<SplitLoader>) -> Self {
SplitLoaderFuture { loader }
}
}
impl Future for SplitLoaderFuture {
type Output = Option<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
let mut loader = self.loader.state.lock().or_poisoned();
match *loader {
SplitLoaderState::Deferred(load) => {
*loader = SplitLoaderState::Pending;
*self.loader.waker.lock().or_poisoned() =
Some(cx.waker().clone());
unsafe {
load(
load_callback,
Arc::<SplitLoader>::into_raw(self.loader.clone())
as *const c_void,
)
};
Poll::Pending
}
SplitLoaderState::Pending => {
*self.loader.waker.lock().or_poisoned() =
Some(cx.waker().clone());
Poll::Pending
}
SplitLoaderState::Completed(value) => Poll::Ready(value),
}
}
}
unsafe extern "C" fn load_callback(loader: *const c_void, success: bool) {
unsafe { Arc::from_raw(loader as *const SplitLoader) }.complete(success);
}

View File

@@ -0,0 +1,21 @@
[package]
name = "wasm_split_macros"
version = "0.1.3"
authors = ["Greg Johnston"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/leptos-rs/leptos"
description = "Tools to support code-splitting and lazy loading for WebAssembly (WASM) binaries."
rust-version.workspace = true
edition.workspace = true
[dependencies]
base16 = { workspace = true, default-features = true }
digest = { workspace = true, default-features = true }
quote = { workspace = true, default-features = true }
sha2 = { workspace = true, default-features = true }
syn = { workspace = true, default-features = true }
wasm-bindgen = { workspace = true, default-features = true }
[lib]
proc-macro = true

View File

@@ -0,0 +1 @@
extend = { path = "../cargo-make/main.toml" }

View File

@@ -0,0 +1,9 @@
# `wasm_split_macros`
This crate provides macros that are used along with the `wasm_split_helpers` crate, which allows you to indicate that certain functions are appropriate split points for lazy-loaded code.
A build tool that supports this approach (like `cargo-leptos`) can then split a WebAssembly (WASM) binary into multiple chunks, which will be lazy-loaded when a split function is called.
This crate was adapted from an original prototype, which you can find [here](https://github.com/jbms/wasm-split-prototype), with an in-depth description of the approach [here](https://github.com/rustwasm/wasm-bindgen/issues/3939).
This functionality is provided in Leptos by the `#[lazy]` and `#[lazy_route]` macros.

View File

@@ -0,0 +1,172 @@
use digest::Digest;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{
parse,
parse::{Parse, ParseStream},
parse_macro_input,
token::Comma,
Ident, ItemFn, Path, Result, ReturnType, Signature,
};
struct WasmSplitArgs {
module_ident: Ident,
_comma: Option<Comma>,
send_wrapper_path: Option<Path>,
}
impl Parse for WasmSplitArgs {
fn parse(input: ParseStream) -> Result<Self> {
let module_ident = input.parse()?;
let _comma = input.parse().ok();
let send_wrapper_path = input.parse().ok();
Ok(Self {
module_ident,
_comma,
send_wrapper_path,
})
}
}
#[proc_macro_attribute]
pub fn wasm_split(args: TokenStream, input: TokenStream) -> TokenStream {
let WasmSplitArgs {
module_ident,
send_wrapper_path,
..
} = parse_macro_input!(args);
let item_fn = parse_macro_input!(input as ItemFn);
let name = &item_fn.sig.ident;
let preload_name =
Ident::new(&format!("__preload_{}", item_fn.sig.ident), name.span());
let unique_identifier = base16::encode_lower(
&sha2::Sha256::digest(format!("{name} {span:?}", span = name.span()))
[..16],
);
let load_module_ident = format_ident!("__wasm_split_load_{module_ident}");
let split_loader_ident =
format_ident!("__wasm_split_loader_{unique_identifier}");
let impl_import_ident = format_ident!(
"__wasm_split_00{module_ident}00_import_{unique_identifier}_{name}"
);
let impl_export_ident = format_ident!(
"__wasm_split_00{module_ident}00_export_{unique_identifier}_{name}"
);
let mut import_sig = Signature {
ident: impl_import_ident.clone(),
asyncness: None,
..item_fn.sig.clone()
};
let mut export_sig = Signature {
ident: impl_export_ident.clone(),
asyncness: None,
..item_fn.sig.clone()
};
let was_async = item_fn.sig.asyncness.is_some();
if was_async {
let ty = match &item_fn.sig.output {
ReturnType::Default => quote! { () },
ReturnType::Type(_, ty) => quote! { #ty },
};
let async_output = parse::<ReturnType>(
quote! {
-> std::pin::Pin<Box<dyn std::future::Future<Output = #ty> + Send + Sync>>
}
.into(),
)
.unwrap();
export_sig.output = async_output.clone();
import_sig.output = async_output;
}
let wrapper_pub = item_fn.vis;
let mut wrapper_sig = item_fn.sig;
wrapper_sig.asyncness = Some(Default::default());
let mut args = Vec::new();
for (i, param) in wrapper_sig.inputs.iter_mut().enumerate() {
match param {
syn::FnArg::Typed(pat_type) => {
let param_ident = format_ident!("__wasm_split_arg_{i}");
args.push(param_ident.clone());
pat_type.pat = Box::new(syn::Pat::Ident(syn::PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: param_ident,
subpat: None,
}));
}
syn::FnArg::Receiver(_) => {
args.push(format_ident!("self"));
}
}
}
let attrs = item_fn.attrs;
let stmts = &item_fn.block.stmts;
let body = if was_async {
if let Some(send_wrapper_path) = send_wrapper_path {
quote! {
Box::pin(#send_wrapper_path::SendWrapper::new(async move {
#(#stmts)*
}))
}
} else {
quote! {
Box::pin(async move {
#(#stmts)*
})
}
}
} else {
quote! { #(#stmts)* }
};
let await_result = was_async.then(|| quote! { .await });
quote! {
thread_local! {
static #split_loader_ident: ::leptos::wasm_split_helpers::LazySplitLoader = ::leptos::wasm_split_helpers::LazySplitLoader::new(#load_module_ident);
}
#[link(wasm_import_module = "/pkg/__wasm_split.______________________.js")]
extern "C" {
#[no_mangle]
fn #load_module_ident (callback: unsafe extern "C" fn(*const ::std::ffi::c_void, bool), data: *const ::std::ffi::c_void) -> ();
#[allow(improper_ctypes)]
#[no_mangle]
#import_sig;
}
#[allow(non_snake_case)]
#(#attrs)*
#wrapper_pub #wrapper_sig {
#(#attrs)*
#[allow(improper_ctypes_definitions)]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" #export_sig {
#body
}
::leptos::wasm_split_helpers::ensure_loaded(&#split_loader_ident).await.unwrap();
unsafe { #impl_import_ident( #(#args),* ) } #await_result
}
#[doc(hidden)]
#[allow(non_snake_case)]
#wrapper_pub async fn #preload_name() {
::leptos::wasm_split_helpers::ensure_loaded(&#split_loader_ident).await.unwrap();
}
}
.into()
}