Merge branch 'master' into fullstack-binary-size-compile-times
This commit is contained in:
commit
b180b501f7
310 files changed
+13994
-4978
No files matched your search
@@ -22,8 +22,9 @@ axum = { version = "0.6.1", features = ["ws", "macros"], default-features = fals
|
||||
tower-http = { version = "0.4.0", optional = true, features = ["fs", "compression-gzip"] }
|
||||
|
||||
# salvo
|
||||
salvo = { version = "0.46.0", optional = true, features = ["serve-static", "websocket", "compression"] }
|
||||
salvo = { version = "0.63.0", optional = true, features = ["serve-static", "websocket", "compression"] }
|
||||
http-body-util = { version = "0.1.0-rc.2", optional = true }
|
||||
serde = "1.0.159"
|
||||
|
||||
# Dioxus + SSR
|
||||
dioxus = { workspace = true }
|
||||
@@ -67,6 +68,10 @@ dioxus-hot-reload = { workspace = true }
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] }
|
||||
|
||||
[target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies]
|
||||
# This is only for debug mode, and it appears mobile does not support some packages this uses
|
||||
manganis-cli-support = { git = "https://github.com/DioxusLabs/collect-assets", features = ["webp", "html"] }
|
||||
|
||||
[features]
|
||||
default = ["hot-reload"]
|
||||
router = ["dioxus-router"]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -1,2 +1,4 @@
|
||||
dist
|
||||
target
|
||||
target
|
||||
static
|
||||
.dioxus
|
||||
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -24,11 +24,10 @@ fn app(cx: Scope<AppProps>) -> Element {
|
||||
|
||||
let mut count = use_state(cx, || 0);
|
||||
let text = use_state(cx, || "...".to_string());
|
||||
let eval = use_eval(cx);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"Server state: {state}"
|
||||
}
|
||||
div { "Server state: {state}" }
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -11,7 +11,7 @@ dioxus-web = { workspace = true, features=["hydrate"], optional = true }
|
||||
dioxus = { workspace = true }
|
||||
dioxus-fullstack = { workspace = true }
|
||||
serde = "1.0.159"
|
||||
salvo = { version = "0.37.9", optional = true }
|
||||
salvo = { version = "0.63.0", optional = true }
|
||||
execute = "0.2.12"
|
||||
reqwest = "0.11.18"
|
||||
simple_logger = "4.2.0"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
docs
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -1,3 +1,4 @@
|
||||
dist
|
||||
target
|
||||
static
|
||||
static
|
||||
.dioxus
|
||||
@@ -254,7 +254,6 @@ where
|
||||
|
||||
fn register_server_fns(self, server_fn_route: &'static str) -> Self {
|
||||
self.register_server_fns_with_handler(server_fn_route, |func| {
|
||||
use crate::layer::Service;
|
||||
move |req: Request<Body>| {
|
||||
let mut service = crate::server_fn_service(Default::default(), func);
|
||||
async move {
|
||||
@@ -277,6 +276,9 @@ where
|
||||
fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self {
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
|
||||
// Copy over any assets we find
|
||||
crate::collect_assets::copy_assets();
|
||||
|
||||
let assets_path = assets_path.into();
|
||||
|
||||
// Serve all files in dist folder except index.html
|
||||
@@ -369,15 +371,65 @@ fn apply_request_parts_to_response<B>(
|
||||
}
|
||||
}
|
||||
|
||||
/// SSR renderer handler for Axum
|
||||
pub async fn render_handler<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
State((cfg, ssr_state)): State<(ServeConfig<P>, SSRState)>,
|
||||
/// SSR renderer handler for Axum with added context injection.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,no_run
|
||||
/// #![allow(non_snake_case)]
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// use axum::routing::get;
|
||||
/// use dioxus::prelude::*;
|
||||
/// use dioxus_fullstack::{axum_adapter::render_handler_with_context, prelude::*};
|
||||
///
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// render! {
|
||||
/// "hello!"
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let cfg = ServeConfigBuilder::new(app, ())
|
||||
/// .assets_path("dist")
|
||||
/// .build();
|
||||
/// let ssr_state = SSRState::new(&cfg);
|
||||
///
|
||||
/// // This could be any state you want to be accessible from your server
|
||||
/// // functions using `[DioxusServerContext::get]`.
|
||||
/// let state = Arc::new(Mutex::new("state".to_string()));
|
||||
///
|
||||
/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||
/// axum::Server::bind(&addr)
|
||||
/// .serve(
|
||||
/// axum::Router::new()
|
||||
/// // Register server functions, etc.
|
||||
/// // Note you probably want to use `register_server_fns_with_handler`
|
||||
/// // to inject the context into server functions running outside
|
||||
/// // of an SSR render context.
|
||||
/// .fallback(get(render_handler_with_context).with_state((
|
||||
/// move |ctx| ctx.insert(state.clone()).unwrap(),
|
||||
/// cfg,
|
||||
/// ssr_state,
|
||||
/// )))
|
||||
/// .into_make_service(),
|
||||
/// )
|
||||
/// .await
|
||||
/// .unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn render_handler_with_context<
|
||||
P: Clone + serde::Serialize + Send + Sync + 'static,
|
||||
F: FnMut(&mut DioxusServerContext),
|
||||
>(
|
||||
State((mut inject_context, cfg, ssr_state)): State<(F, ServeConfig<P>, SSRState)>,
|
||||
request: Request<Body>,
|
||||
) -> impl IntoResponse {
|
||||
let (parts, _) = request.into_parts();
|
||||
let url = parts.uri.path_and_query().unwrap().to_string();
|
||||
let parts: Arc<RwLock<http::request::Parts>> = Arc::new(RwLock::new(parts.into()));
|
||||
let server_context = DioxusServerContext::new(parts.clone());
|
||||
let mut server_context = DioxusServerContext::new(parts.clone());
|
||||
inject_context(&mut server_context);
|
||||
|
||||
match ssr_state.render(url, &cfg, &server_context).await {
|
||||
Ok(rendered) => {
|
||||
@@ -395,6 +447,14 @@ pub async fn render_handler<P: Clone + serde::Serialize + Send + Sync + 'static>
|
||||
}
|
||||
}
|
||||
|
||||
/// SSR renderer handler for Axum
|
||||
pub async fn render_handler<P: Clone + serde::Serialize + Send + Sync + 'static>(
|
||||
State((cfg, ssr_state)): State<(ServeConfig<P>, SSRState)>,
|
||||
request: Request<Body>,
|
||||
) -> impl IntoResponse {
|
||||
render_handler_with_context(State((|_: &mut _| (), cfg, ssr_state)), request).await
|
||||
}
|
||||
|
||||
fn report_err<E: std::fmt::Display>(e: E) -> Response<BoxBody> {
|
||||
Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
|
||||
@@ -89,26 +89,26 @@ impl Service for ServerFnHandler {
|
||||
let parts = Arc::new(RwLock::new(parts));
|
||||
|
||||
// Because the future returned by `server_fn_handler` is `Send`, and the future returned by this function must be send, we need to spawn a new runtime
|
||||
let (resp_tx, resp_rx) = tokio::sync::oneshot::channel();
|
||||
let pool = get_local_pool();
|
||||
pool.spawn_pinned({
|
||||
let function = function.clone();
|
||||
let mut server_context = server_context.clone();
|
||||
server_context.parts = parts;
|
||||
move || async move {
|
||||
let data = match function.encoding() {
|
||||
Encoding::Url | Encoding::Cbor => &body,
|
||||
Encoding::GetJSON | Encoding::GetCBOR => &query,
|
||||
};
|
||||
let server_function_future = function.call((), data);
|
||||
let server_function_future =
|
||||
ProvideServerContext::new(server_function_future, server_context.clone());
|
||||
let resp = server_function_future.await;
|
||||
|
||||
resp_tx.send(resp).unwrap();
|
||||
}
|
||||
});
|
||||
let result = resp_rx.await.unwrap();
|
||||
let result = pool
|
||||
.spawn_pinned({
|
||||
let function = function.clone();
|
||||
let mut server_context = server_context.clone();
|
||||
server_context.parts = parts;
|
||||
move || async move {
|
||||
let data = match function.encoding() {
|
||||
Encoding::Url | Encoding::Cbor => &body,
|
||||
Encoding::GetJSON | Encoding::GetCBOR => &query,
|
||||
};
|
||||
let server_function_future = function.call((), data);
|
||||
let server_function_future = ProvideServerContext::new(
|
||||
server_function_future,
|
||||
server_context.clone(),
|
||||
);
|
||||
server_function_future.await
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
let mut res = http::Response::builder();
|
||||
|
||||
// Set the headers from the server context
|
||||
|
||||
@@ -241,6 +241,9 @@ impl DioxusRouterExt for Router {
|
||||
}
|
||||
|
||||
fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self {
|
||||
// Copy over any assets we find
|
||||
crate::collect_assets::copy_assets();
|
||||
|
||||
let assets_path = assets_path.into();
|
||||
|
||||
// Serve all files in dist folder except index.html
|
||||
|
||||
@@ -187,6 +187,9 @@ pub fn serve_dioxus_application<P: Clone + serde::Serialize + Send + Sync + 'sta
|
||||
// Serve the dist folder and the index.html file
|
||||
let serve_dir = warp::fs::dir(cfg.assets_path);
|
||||
|
||||
// Copy over any assets we find
|
||||
crate::collect_assets::copy_assets();
|
||||
|
||||
connect_hot_reload()
|
||||
// First register the server functions
|
||||
.or(register_server_fns(server_fn_route))
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#[cfg(any(feature = "axum", feature = "warp", feature = "salvo"))]
|
||||
pub fn copy_assets() {
|
||||
#[cfg(all(
|
||||
debug_assertions,
|
||||
any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
)
|
||||
))]
|
||||
{
|
||||
// The CLI will copy assets to the current working directory
|
||||
if std::env::var_os("DIOXUS_ACTIVE").is_some() {
|
||||
return;
|
||||
}
|
||||
use manganis_cli_support::AssetManifest;
|
||||
use manganis_cli_support::AssetManifestExt;
|
||||
use manganis_cli_support::Config;
|
||||
use std::path::PathBuf;
|
||||
let config = Config::current();
|
||||
let asset_location = config.assets_serve_location();
|
||||
let asset_location = PathBuf::from(asset_location);
|
||||
let _ = std::fs::remove_dir_all(&asset_location);
|
||||
|
||||
println!("Finding assets... (Note: if you run a dioxus desktop application with the CLI. This process will be significantly faster.)");
|
||||
let manifest = AssetManifest::load();
|
||||
let has_assets = manifest
|
||||
.packages()
|
||||
.iter()
|
||||
.any(|package| !package.assets().is_empty());
|
||||
|
||||
if has_assets {
|
||||
println!("Copying and optimizing assets...");
|
||||
manifest.copy_static_assets_to(&asset_location).unwrap();
|
||||
println!("Copied assets to {}", asset_location.display());
|
||||
} else {
|
||||
println!("No assets found");
|
||||
}
|
||||
}
|
||||
#[cfg(not(all(
|
||||
debug_assertions,
|
||||
any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
)
|
||||
)))]
|
||||
{
|
||||
println!(
|
||||
"Skipping assets in release mode. You compile assets with the dioxus-cli in release mode"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,11 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||
/// use dioxus_fullstack::prelude::*;
|
||||
///
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// let state1 = use_state(cx, || from_server(|| {
|
||||
/// let state1 = server_cached(cx, || from_server(|| {
|
||||
/// 1234
|
||||
/// }));
|
||||
///
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
pub fn server_cached<O: 'static + Serialize + DeserializeOwned>(server_fn: impl Fn() -> O) -> O {
|
||||
|
||||
@@ -3,7 +3,9 @@ use tracing_futures::Instrument;
|
||||
|
||||
use http::{Request, Response};
|
||||
|
||||
/// A layer that wraps a service. This can be used to add additional information to the request, or response on top of some other service
|
||||
pub trait Layer: Send + Sync + 'static {
|
||||
/// Wrap a boxed service with this layer
|
||||
fn layer(&self, inner: BoxedService) -> BoxedService;
|
||||
}
|
||||
|
||||
@@ -17,7 +19,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A service is a function that takes a request and returns an async response
|
||||
pub trait Service {
|
||||
/// Run the service and produce a future that resolves to a response
|
||||
fn run(
|
||||
&mut self,
|
||||
req: http::Request<hyper::body::Body>,
|
||||
@@ -55,6 +59,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A boxed service is a type-erased service that can be used without knowing the underlying type
|
||||
pub struct BoxedService(pub Box<dyn Service + Send>);
|
||||
|
||||
impl tower::Service<http::Request<hyper::body::Body>> for BoxedService {
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod router;
|
||||
mod adapters;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use adapters::*;
|
||||
mod collect_assets;
|
||||
mod hooks;
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
|
||||
mod hot_reload;
|
||||
@@ -40,6 +41,8 @@ pub mod prelude {
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub use crate::html_storage::deserialize::get_root_props_from_document;
|
||||
pub use crate::launch::LaunchBuilder;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use crate::layer::{Layer, Service};
|
||||
#[cfg(all(feature = "ssr", feature = "router"))]
|
||||
pub use crate::render::pre_cache_static_routes_with_props;
|
||||
#[cfg(feature = "ssr")]
|
||||
|
||||
@@ -45,6 +45,8 @@ impl SsrRendererPool {
|
||||
.expect("couldn't spawn runtime")
|
||||
.block_on(async move {
|
||||
let mut vdom = VirtualDom::new_with_props(component, props);
|
||||
// Make sure the evaluator is initialized
|
||||
dioxus_ssr::eval::init_eval(vdom.base_scope());
|
||||
let mut to = WriteBuffer { buffer: Vec::new() };
|
||||
// before polling the future, we need to set the context
|
||||
let prev_context =
|
||||
|
||||
Reference in new issue
Block a user