Merge remote-tracking branch 'origin' into leptos_0.8

This commit is contained in:
Greg Johnston
2025-02-25 07:39:43 -05:00
66 changed files with 1262 additions and 593 deletions

View File

@@ -21,10 +21,10 @@ leptos_macro = { workspace = true, features = ["actix"] }
leptos_meta = { workspace = true, features = ["nonce"] }
leptos_router = { workspace = true, features = ["ssr"] }
server_fn = { workspace = true, features = ["actix"] }
serde_json = "1.0"
serde_json = { workspace = true }
parking_lot = "0.12.3"
tracing = { version = "0.1", optional = true }
tokio = { version = "1.41", features = ["rt", "fs"] }
tokio = { version = "1.43", features = ["rt", "fs"] }
send_wrapper = "0.6.0"
dashmap = "6"
once_cell = "1"

View File

@@ -274,14 +274,13 @@ pub fn redirect(path: &str) {
///
/// This can then be set up at an appropriate route in your application:
///
/// ```
/// ```no_run
/// use actix_web::*;
///
/// fn register_server_functions() {
/// // call ServerFn::register() for each of the server functions you've defined
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// // make sure you actually register your server functions
@@ -297,7 +296,6 @@ pub fn redirect(path: &str) {
/// .run()
/// .await
/// }
/// # }
/// ```
///
/// ## Provided Context Types
@@ -432,7 +430,7 @@ pub fn handle_server_fns_with_context(
/// but requires some client-side JavaScript.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use actix_web::{App, HttpServer};
/// use leptos::prelude::*;
/// use leptos_router::Method;
@@ -443,7 +441,6 @@ pub fn handle_server_fns_with_context(
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -463,7 +460,6 @@ pub fn handle_server_fns_with_context(
/// .run()
/// .await
/// }
/// # }
/// ```
///
/// ## Provided Context Types
@@ -491,7 +487,7 @@ where
/// sending down its HTML. The app will become interactive once it has fully loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use actix_web::{App, HttpServer};
/// use leptos::prelude::*;
/// use leptos_router::Method;
@@ -502,7 +498,6 @@ where
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -525,7 +520,6 @@ where
/// .run()
/// .await
/// }
/// # }
/// ```
///
/// ## Provided Context Types
@@ -551,7 +545,7 @@ where
/// `async` resources have loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use actix_web::{App, HttpServer};
/// use leptos::prelude::*;
/// use leptos_router::Method;
@@ -562,7 +556,6 @@ where
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[actix_web::main]
/// async fn main() -> std::io::Result<()> {
/// let conf = get_configuration(Some("Cargo.toml")).unwrap();
@@ -582,7 +575,6 @@ where
/// .run()
/// .await
/// }
/// # }
/// ```
///
/// ## Provided Context Types

View File

@@ -24,14 +24,14 @@ leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true }
once_cell = "1"
parking_lot = "0.12.3"
tokio = { version = "1.41", default-features = false }
tokio = { version = "1.43", default-features = false }
tower = { version = "0.5.1", features = ["util"] }
tower-http = "0.6.2"
tracing = { version = "0.1.41", optional = true }
[dev-dependencies]
axum = "0.8.1"
tokio = { version = "1.41", features = ["net", "rt-multi-thread"] }
tokio = { version = "1.43", features = ["net", "rt-multi-thread"] }
[features]
wasm = []

View File

@@ -1,5 +1,6 @@
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![allow(clippy::type_complexity)]
//! Provides functions to easily integrate Leptos with Axum.
//!
@@ -278,12 +279,11 @@ pub fn generate_request_and_parts(
///
/// This can then be set up at an appropriate route in your application:
///
/// ```
/// ```no_run
/// use axum::{handler::Handler, routing::post, Router};
/// use leptos::prelude::*;
/// use std::net::SocketAddr;
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[cfg(feature = "default")]
/// #[tokio::main]
/// async fn main() {
@@ -299,7 +299,9 @@ pub fn generate_request_and_parts(
/// .await
/// .unwrap();
/// }
/// # }
///
/// # #[cfg(not(feature = "default"))]
/// # fn main() { }
/// ```
/// Leptos provides a generic implementation of `handle_server_fns`. If access to more specific parts of the Request is desired,
/// you can specify your own server fn handler based on this one and give it it's own route in the server macro.
@@ -440,7 +442,7 @@ pub type PinnedHtmlStream =
/// to route it using [leptos_router], serving an HTML stream of your application.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use axum::{handler::Handler, Router};
/// use leptos::{config::get_configuration, prelude::*};
/// use std::{env, net::SocketAddr};
@@ -450,7 +452,6 @@ pub type PinnedHtmlStream =
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[cfg(feature = "default")]
/// #[tokio::main]
/// async fn main() {
@@ -469,7 +470,9 @@ pub type PinnedHtmlStream =
/// .await
/// .unwrap();
/// }
/// # }
///
/// # #[cfg(not(feature = "default"))]
/// # fn main() { }
/// ```
///
/// ## Provided Context Types
@@ -528,7 +531,7 @@ where
/// sending down its HTML. The app will become interactive once it has fully loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use axum::{handler::Handler, Router};
/// use leptos::{config::get_configuration, prelude::*};
/// use std::{env, net::SocketAddr};
@@ -538,7 +541,6 @@ where
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[cfg(feature = "default")]
/// #[tokio::main]
/// async fn main() {
@@ -557,7 +559,9 @@ where
/// .await
/// .unwrap();
/// }
/// # }
///
/// # #[cfg(not(feature = "default"))]
/// # fn main() { }
/// ```
///
/// ## Provided Context Types
@@ -941,7 +945,7 @@ fn provide_contexts(
/// `async` resources have loaded.
///
/// This can then be set up at an appropriate route in your application:
/// ```
/// ```no_run
/// use axum::{handler::Handler, Router};
/// use leptos::{config::get_configuration, prelude::*};
/// use std::{env, net::SocketAddr};
@@ -951,7 +955,6 @@ fn provide_contexts(
/// view! { <main>"Hello, world!"</main> }
/// }
///
/// # if false { // don't actually try to run a server in a doctest...
/// #[cfg(feature = "default")]
/// #[tokio::main]
/// async fn main() {
@@ -971,7 +974,9 @@ fn provide_contexts(
/// .await
/// .unwrap();
/// }
/// # }
///
/// # #[cfg(not(feature = "default"))]
/// # fn main() { }
/// ```
///
/// ## Provided Context Types
@@ -1988,7 +1993,8 @@ where
/// This is provided as a convenience, but is a fairly simple function. If you need to adapt it,
/// simply reuse the source code of this function in your own application.
#[cfg(feature = "default")]
pub fn file_and_error_handler<S, IV>(
pub fn file_and_error_handler_with_context<S, IV>(
additional_context: impl Fn() + 'static + Clone + Send,
shell: fn(LeptosOptions) -> IV,
) -> impl Fn(
Uri,
@@ -2004,40 +2010,68 @@ where
LeptosOptions: FromRef<S>,
{
move |uri: Uri, State(state): State<S>, req: Request<Body>| {
Box::pin(async move {
let options = LeptosOptions::from_ref(&state);
let res = get_static_file(uri, &options.site_root, req.headers());
let res = res.await.unwrap();
Box::pin({
let additional_context = additional_context.clone();
async move {
let options = LeptosOptions::from_ref(&state);
let res =
get_static_file(uri, &options.site_root, req.headers());
let res = res.await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut res = handle_response_inner(
move || {
provide_context(state.clone());
},
move || shell(options),
req,
|app, chunks| {
Box::pin(async move {
let app = app
.to_html_stream_in_order()
.collect::<String>()
.await;
let chunks = chunks();
Box::pin(once(async move { app }).chain(chunks))
as PinnedStream<String>
})
},
)
.await;
*res.status_mut() = StatusCode::NOT_FOUND;
res
if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut res = handle_response_inner(
move || {
additional_context();
provide_context(state.clone());
},
move || shell(options),
req,
|app, chunks| {
Box::pin(async move {
let app = app
.to_html_stream_in_order()
.collect::<String>()
.await;
let chunks = chunks();
Box::pin(once(async move { app }).chain(chunks))
as PinnedStream<String>
})
},
)
.await;
*res.status_mut() = StatusCode::NOT_FOUND;
res
}
}
})
}
}
/// A reasonable handler for serving static files (like JS/WASM/CSS) and 404 errors.
///
/// This is provided as a convenience, but is a fairly simple function. If you need to adapt it,
/// simply reuse the source code of this function in your own application.
#[cfg(feature = "default")]
pub fn file_and_error_handler<S, IV>(
shell: fn(LeptosOptions) -> IV,
) -> impl Fn(
Uri,
State<S>,
Request<Body>,
) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
+ Clone
+ Send
+ 'static
where
IV: IntoView + 'static,
S: Send + Sync + Clone + 'static,
LeptosOptions: FromRef<S>,
{
file_and_error_handler_with_context(move || (), shell)
}
#[cfg(feature = "default")]
async fn get_static_file(
uri: Uri,