preload lazy routes without creating data twice

This commit is contained in:
Greg Johnston
2025-07-17 08:09:05 -04:00
parent 81b37a3867
commit f8fa6de987
2 changed files with 48 additions and 21 deletions

View File

@@ -32,7 +32,7 @@ where
}
async fn preload(&self) {
T::data().view().await;
T::preload().await;
}
}
@@ -40,6 +40,10 @@ pub trait LazyRoute: Send + 'static {
fn data() -> Self;
fn view(self) -> impl Future<Output = AnyView>;
fn preload() -> impl Future<Output = ()> {
async {}
}
}
#[derive(Debug)]

View File

@@ -8,9 +8,7 @@ use proc_macro::{TokenStream, TokenTree};
use proc_macro2::Span;
use proc_macro_error2::{abort, proc_macro_error};
use quote::{quote, ToTokens};
use syn::{
spanned::Spanned, Block, Ident, ImplItem, ItemImpl, Path, Type, TypePath,
};
use syn::{spanned::Spanned, Ident, ImplItem, ItemImpl, Path, Type, TypePath};
const RFC3986_UNRESERVED: [char; 4] = ['-', '.', '_', '~'];
const RFC3986_PCHAR_OTHER: [char; 1] = ['@'];
@@ -233,6 +231,28 @@ fn lazy_route_impl(
_ => abort!(self_ty.span(), "only path types are supported"),
};
let lazy_view_ident = Ident::new(&ty_name_to_snake, im.self_ty.span());
let preload_lazy_view_ident = Ident::new(
&format!("__preload_{lazy_view_ident}"),
lazy_view_ident.span(),
);
im.items.push(
syn::parse::<ImplItem>(
quote! {
async fn preload() {
// TODO for 0.9 this is not precise
// 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;
}
}
.into(),
)
.unwrap_or_else(|e| {
abort!(e.span(), "could not parse preload item impl")
}),
);
let item = im.items.iter_mut().find_map(|item| match item {
ImplItem::Fn(inner) => {
@@ -244,28 +264,31 @@ fn lazy_route_impl(
}
_ => None,
});
match item {
None => abort!(im.span(), "must contain a fn called `view`"),
Some(fun) => {
let body = fun.block.clone();
let new_block = quote! {{
#[::leptos::lazy(#lazy_view_ident)]
async fn view(this: #self_ty) -> ::leptos::prelude::AnyView {
#body
let body = std::mem::replace(
&mut fun.block,
syn::parse(
quote! {
{
#lazy_view_ident(self).await
}
}
.into(),
)
.unwrap(),
);
view(self).await
}};
let block =
syn::parse::<Block>(new_block.into()).unwrap_or_else(|e| {
abort!(
e.span(),
"`lazy_route` can only be used on an `impl` block"
)
});
fun.block = block;
return quote! {
#[::leptos::lazy]
async fn #lazy_view_ident(this: #self_ty) -> ::leptos::prelude::AnyView {
#body
}
#im
}.into();
}
}
quote! { #im }.into()
}