mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 15:44:42 -05:00
Compare commits
4 Commits
chore-remo
...
lazy-serve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4efaac6a94 | ||
|
|
972e9c1d1c | ||
|
|
3a6c2fc547 | ||
|
|
e679b72ebb |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -4342,6 +4342,7 @@ name = "wasm_split_helpers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-once-cell",
|
||||
"or_poisoned",
|
||||
"wasm_split_macros",
|
||||
]
|
||||
|
||||
|
||||
@@ -305,7 +305,10 @@ impl LazyRoute for ViewD {
|
||||
}
|
||||
}
|
||||
|
||||
// Server functions can be made lazy by combining the two macros,
|
||||
// with `#[server]` coming first, then `#[lazy]`
|
||||
#[server]
|
||||
#[lazy]
|
||||
async fn d_data() -> Result<Vec<i32>, ServerFnError> {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
|
||||
Ok(vec![1, 1, 2, 3, 5, 8, 13])
|
||||
|
||||
@@ -348,6 +348,7 @@ pub use web_sys;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __reexports {
|
||||
pub use send_wrapper;
|
||||
pub use wasm_bindgen_futures;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,10 @@ pub fn lazy_impl(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
|
||||
let is_wasm = cfg!(feature = "csr") || cfg!(feature = "hydrate");
|
||||
if is_wasm {
|
||||
quote! {
|
||||
#[::leptos::wasm_split_helpers::wasm_split(#unique_name)]
|
||||
#[::leptos::wasm_split_helpers::wasm_split(
|
||||
#unique_name,
|
||||
::leptos::__reexports::send_wrapper
|
||||
)]
|
||||
#fun
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1525,7 +1525,10 @@ impl Parse for ServerFnBody {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
true
|
||||
// in ssr mode, remove the "lazy" macro
|
||||
// the lazy macro doesn't do anything on the server anyway, but it can cause confusion for rust-analyzer
|
||||
// when the lazy macro is applied to both the function and the dummy
|
||||
!(cfg!(feature = "ssr") && matches!(attr.meta.path().segments.last(), Some(PathSegment { ident, .. }) if ident == "lazy") )
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@ async-once-cell = { default-features = true, workspace = true, features = [
|
||||
"std",
|
||||
] }
|
||||
wasm_split_macros.workspace = true
|
||||
or_poisoned.workspace = true
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::{
|
||||
cell::Cell,
|
||||
ffi::c_void,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
@@ -12,18 +11,19 @@ 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<Rc<Lazy>>,
|
||||
lazy: Pin<Arc<Lazy>>,
|
||||
}
|
||||
|
||||
impl LazySplitLoader {
|
||||
pub fn new(load: LoadFn) -> Self {
|
||||
Self {
|
||||
lazy: Rc::pin(Lazy::new(SplitLoaderFuture::new(SplitLoader::new(
|
||||
load,
|
||||
)))),
|
||||
lazy: Arc::pin(Lazy::new(SplitLoaderFuture::new(
|
||||
SplitLoader::new(load),
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,36 +42,33 @@ enum SplitLoaderState {
|
||||
}
|
||||
|
||||
struct SplitLoader {
|
||||
state: Cell<SplitLoaderState>,
|
||||
waker: Cell<Option<Waker>>,
|
||||
state: Mutex<SplitLoaderState>,
|
||||
waker: Mutex<Option<Waker>>,
|
||||
}
|
||||
|
||||
impl SplitLoader {
|
||||
fn new(load: LoadFn) -> Rc<Self> {
|
||||
Rc::new(SplitLoader {
|
||||
state: Cell::new(SplitLoaderState::Deferred(load)),
|
||||
waker: Cell::new(None),
|
||||
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.set(SplitLoaderState::Completed(if value {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}));
|
||||
if let Some(waker) = self.waker.take() {
|
||||
*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: Rc<SplitLoader>,
|
||||
loader: Arc<SplitLoader>,
|
||||
}
|
||||
|
||||
impl SplitLoaderFuture {
|
||||
fn new(loader: Rc<SplitLoader>) -> Self {
|
||||
fn new(loader: Arc<SplitLoader>) -> Self {
|
||||
SplitLoaderFuture { loader }
|
||||
}
|
||||
}
|
||||
@@ -80,21 +77,24 @@ impl Future for SplitLoaderFuture {
|
||||
type Output = Option<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
|
||||
match self.loader.state.get() {
|
||||
let mut loader = self.loader.state.lock().or_poisoned();
|
||||
match *loader {
|
||||
SplitLoaderState::Deferred(load) => {
|
||||
self.loader.state.set(SplitLoaderState::Pending);
|
||||
self.loader.waker.set(Some(cx.waker().clone()));
|
||||
*loader = SplitLoaderState::Pending;
|
||||
*self.loader.waker.lock().or_poisoned() =
|
||||
Some(cx.waker().clone());
|
||||
unsafe {
|
||||
load(
|
||||
load_callback,
|
||||
Rc::<SplitLoader>::into_raw(self.loader.clone())
|
||||
Arc::<SplitLoader>::into_raw(self.loader.clone())
|
||||
as *const c_void,
|
||||
)
|
||||
};
|
||||
Poll::Pending
|
||||
}
|
||||
SplitLoaderState::Pending => {
|
||||
self.loader.waker.set(Some(cx.waker().clone()));
|
||||
*self.loader.waker.lock().or_poisoned() =
|
||||
Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
SplitLoaderState::Completed(value) => Poll::Ready(value),
|
||||
@@ -103,5 +103,5 @@ impl Future for SplitLoaderFuture {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn load_callback(loader: *const c_void, success: bool) {
|
||||
unsafe { Rc::from_raw(loader as *const SplitLoader) }.complete(success);
|
||||
unsafe { Arc::from_raw(loader as *const SplitLoader) }.complete(success);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,40 @@
|
||||
use digest::Digest;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, Ident, ItemFn, ReturnType, Signature};
|
||||
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 module_ident = parse_macro_input!(args as Ident);
|
||||
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;
|
||||
@@ -45,9 +74,9 @@ pub fn wasm_split(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
ReturnType::Default => quote! { () },
|
||||
ReturnType::Type(_, ty) => quote! { #ty },
|
||||
};
|
||||
let async_output = syn::parse::<ReturnType>(
|
||||
let async_output = parse::<ReturnType>(
|
||||
quote! {
|
||||
-> std::pin::Pin<Box<dyn std::future::Future<Output = #ty>>>
|
||||
-> std::pin::Pin<Box<dyn std::future::Future<Output = #ty> + Send + Sync>>
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
@@ -83,10 +112,18 @@ pub fn wasm_split(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let stmts = &item_fn.block.stmts;
|
||||
|
||||
let body = if was_async {
|
||||
quote! {
|
||||
Box::pin(async move {
|
||||
#(#stmts)*
|
||||
})
|
||||
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)* }
|
||||
|
||||
Reference in New Issue
Block a user