feat: replace vendored wasm-split with out-of-repository version (#4369)

This commit is contained in:
WorldSEnder
2025-10-25 03:13:55 +02:00
committed by GitHub
parent 0edbd9b3b5
commit fbf46ca58c
14 changed files with 51 additions and 369 deletions

17
Cargo.lock generated
View File

@@ -1695,7 +1695,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.5.10",
"socket2 0.6.0",
"tokio",
"tower-service",
"tracing",
@@ -2796,7 +2796,7 @@ dependencies = [
"quinn-udp",
"rustc-hash 2.1.1",
"rustls",
"socket2 0.5.10",
"socket2 0.6.0",
"thiserror 2.0.17",
"tokio",
"tracing",
@@ -2833,7 +2833,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.5.10",
"socket2 0.6.0",
"tracing",
"windows-sys 0.60.2",
]
@@ -4602,23 +4602,24 @@ dependencies = [
[[package]]
name = "wasm_split_helpers"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a114b3073258dd5de3d812cdd048cca6842342755e828a14dbf15f843f2d1b84"
dependencies = [
"async-once-cell",
"or_poisoned",
"wasm_split_macros",
]
[[package]]
name = "wasm_split_macros"
version = "0.1.3"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56481f8ed1a9f9ae97ea7b08a5e2b12e8adf9a7818a6ba952b918e09c7be8bf0"
dependencies = [
"base16",
"digest",
"quote",
"sha2",
"syn 2.0.106",
"wasm-bindgen",
]
[[package]]

View File

@@ -70,8 +70,6 @@ server_fn = { path = "./server_fn", version = "0.8.8" }
server_fn_macro = { path = "./server_fn_macro", version = "0.8.8" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.8.5" }
tachys = { path = "./tachys", version = "0.2.10" }
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" }
@@ -174,6 +172,7 @@ 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

@@ -57,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
wasm_split_helpers = { workspace = true, default-features = 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 }

View File

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

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::quote;
use quote::{format_ident, quote};
use std::{
hash::{DefaultHasher, Hash, Hasher},
mem,
};
use syn::{parse_macro_input, ItemFn};
use syn::{parse_macro_input, parse_quote, ItemFn, ReturnType, Stmt};
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 mut fun = syn::parse::<ItemFn>(s).unwrap_or_else(|e| {
let fun = syn::parse::<ItemFn>(s).unwrap_or_else(|e| {
abort!(e.span(), "`lazy` can only be used on a function")
});
@@ -47,29 +47,50 @@ 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_helpers::wasm_split(
#[::leptos::wasm_split::wasm_split(
#unique_name,
::leptos::__reexports::send_wrapper
wasm_split_path = ::leptos::wasm_split,
preload(#[doc(hidden)] #[allow(non_snake_case)] #preload_name),
#return_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(
syn::parse(
quote! {
::leptos::prefetch_lazy_fn_on_server(#unique_name_str);
}
.into(),
)
.unwrap(),
);
statements.push(parse_quote! {
::leptos::prefetch_lazy_fn_on_server(#unique_name_str);
});
statements.extend(old_statements);
quote! { #fun }
}

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::{quote, ToTokens};
use quote::{format_ident, quote, ToTokens};
use syn::{
spanned::Spanned, FnArg, Ident, ImplItem, ItemImpl, Path, Type, TypePath,
};
@@ -267,10 +267,7 @@ fn lazy_route_impl(
};
let lazy_view_ident =
Ident::new(&format!("__{ty_name_to_snake}_View"), im.self_ty.span());
let preload_lazy_view_ident = Ident::new(
&format!("__preload_{lazy_view_ident}"),
lazy_view_ident.span(),
);
let preload_ident = format_ident!("__preload_{lazy_view_ident}");
im.items.push(
syn::parse::<ImplItem>(
@@ -280,7 +277,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_lazy_view_ident().await;
#preload_ident().await;
}
}
.into(),

View File

@@ -1,17 +0,0 @@
[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

View File

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

View File

@@ -1,9 +0,0 @@
# `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.

View File

@@ -1,107 +0,0 @@
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

@@ -1,21 +0,0 @@
[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

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

View File

@@ -1,9 +0,0 @@
# `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

@@ -1,172 +0,0 @@
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()
}