mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 16:02:33 -05:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6ac197b0a | ||
|
|
31b68a4b76 |
2
.github/workflows/run-cargo-make-task.yml
vendored
2
.github/workflows/run-cargo-make-task.yml
vendored
@@ -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
82
Cargo.lock
generated
@@ -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]]
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "throw_error"
|
||||
version = "0.3.1"
|
||||
version = "0.3.0"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
})),
|
||||
));
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "reactive_graph"
|
||||
version = "0.2.9"
|
||||
version = "0.2.8"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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),*,
|
||||
{
|
||||
|
||||
@@ -369,7 +369,7 @@ impl ResolvedStaticPath {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
}
|
||||
owner.unset_with_forced_cleanup();
|
||||
owner.unset();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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>;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
feature = "multipart",
|
||||
feature = "serde-lite",
|
||||
feature = "cbor",
|
||||
feature = "msgpack",
|
||||
feature = "bitcode",
|
||||
feature = "msgpack"
|
||||
))
|
||||
))]
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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! {},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tachys"
|
||||
version = "0.2.10"
|
||||
version = "0.2.9"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
readme = "../README.md"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
|
||||
@@ -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
17
wasm_split/Cargo.toml
Normal 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
1
wasm_split/Makefile.toml
Normal file
@@ -0,0 +1 @@
|
||||
extend = { path = "../cargo-make/main.toml" }
|
||||
9
wasm_split/README.md
Normal file
9
wasm_split/README.md
Normal 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
107
wasm_split/src/lib.rs
Normal 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);
|
||||
}
|
||||
21
wasm_split_macros/Cargo.toml
Normal file
21
wasm_split_macros/Cargo.toml
Normal 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
|
||||
1
wasm_split_macros/Makefile.toml
Normal file
1
wasm_split_macros/Makefile.toml
Normal file
@@ -0,0 +1 @@
|
||||
extend = { path = "../cargo-make/main.toml" }
|
||||
9
wasm_split_macros/README.md
Normal file
9
wasm_split_macros/README.md
Normal 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.
|
||||
172
wasm_split_macros/src/lib.rs
Normal file
172
wasm_split_macros/src/lib.rs
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user