work on restoring fullstack

This commit is contained in:
Evan Almloff 2024-01-17 14:02:49 -06:00
parent 205a005142
commit 0c532c5e0c
20 changed files with 172 additions and 304 deletions

4
Cargo.lock generated
View file

@ -2606,10 +2606,9 @@ dependencies = [
"base64 0.21.7",
"bytes",
"ciborium",
"dioxus-core",
"dioxus-desktop",
"dioxus-hot-reload",
"dioxus-router",
"dioxus-lib",
"dioxus-ssr",
"dioxus-web",
"dioxus_server_macro",
@ -2829,6 +2828,7 @@ dependencies = [
"dioxus",
"dioxus-cli-config",
"dioxus-desktop",
"dioxus-fullstack",
"dioxus-lib",
"dioxus-liveview",
"dioxus-router",

View file

@ -25,7 +25,7 @@ tower-http = { version = "0.4.0", optional = true, features = ["fs", "compressio
salvo = { version = "0.63.0", optional = true, features = ["serve-static", "websocket", "compression"] }
http-body-util = { version = "0.1.0-rc.2", optional = true }
dioxus-core = { workspace = true }
dioxus-lib = { workspace = true }
# Dioxus + SSR
dioxus-ssr = { workspace = true, optional = true }
@ -38,9 +38,6 @@ dioxus-web = { workspace = true, features = ["hydrate"], optional = true }
# Desktop Integration
dioxus-desktop = { workspace = true, optional = true }
# Router Integration
dioxus-router = { workspace = true, optional = true }
tracing = { workspace = true }
tracing-futures = { workspace = true, optional = true }
once_cell = "1.17.1"
@ -70,13 +67,12 @@ web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "Ht
[features]
default = ["hot-reload"]
router = ["dioxus-router"]
hot-reload = ["serde_json", "futures-util"]
web = ["dioxus-web"]
desktop = ["dioxus-desktop"]
warp = ["dep:warp", "ssr"]
axum = ["dep:axum", "tower-http", "ssr"]
salvo = ["dep:salvo", "ssr", "http-body-util"]
ssr = ["server_fn/ssr", "dioxus_server_macro/ssr", "tokio", "tokio-util", "tokio-stream", "dioxus-ssr", "tower", "hyper", "http", "dioxus-router?/ssr", "tower-layer", "anymap", "tracing-futures", "pin-project", "thiserror"]
ssr = ["server_fn/ssr", "dioxus_server_macro/ssr", "tokio", "tokio-util", "tokio-stream", "dioxus-ssr", "tower", "hyper", "http", "tower-layer", "anymap", "tracing-futures", "pin-project", "thiserror"]
default-tls = ["server_fn/default-tls"]
rustls = ["server_fn/rustls"]

View file

@ -53,7 +53,7 @@ fn main() {
// Automatically handles server side rendering, hot reloading intigration, and hosting server functions
serve_dioxus_application(
"",
ServeConfigBuilder::new(app, ()),
ServerConfig::new(app, ()),
)
)
.run(([127, 0, 0, 1], 8080))

View file

@ -51,7 +51,7 @@ fn main() {
// build our application with some routes
let app = Router::new()
// Server side render the application, serve static assets, and register server functions
.serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
.serve_dioxus_application("", ServerConfig::new(app, ()))
.layer(
axum_session_auth::AuthSessionLayer::<
crate::auth::User,

View file

@ -9,8 +9,8 @@ publish = false
[dependencies]
dioxus-web = { workspace = true, features = ["hydrate"], optional = true }
dioxus = { workspace = true }
dioxus-router = { workspace = true }
dioxus-fullstack = { workspace = true, features = ["router"] }
dioxus-router = { workspace = true, features = ["fullstack"]}
dioxus-fullstack = { workspace = true }
axum = { version = "0.6.12", optional = true }
tokio = {workspace = true, features = ["full"], optional = true }
serde = { version = "1.0.159", features = ["derive"] }

View file

@ -9,8 +9,8 @@ publish = false
[dependencies]
dioxus-web = { workspace = true, features = ["hydrate"], optional = true }
dioxus = { workspace = true }
dioxus-fullstack = { workspace = true, features = ["router"] }
dioxus-router = { workspace = true}
dioxus-fullstack = { workspace = true }
dioxus-router = { workspace = true, features = ["fullstack"] }
tokio = { workspace = true, features = ["full"], optional = true }
serde = "1.0.159"

View file

@ -16,9 +16,9 @@ use serde::{Deserialize, Serialize};
#[tokio::main]
async fn main() {
pre_cache_static_routes_with_props(
&ServeConfigBuilder::new_with_router(dioxus_fullstack::router::FullstackRouterConfig::<
Route,
>::default())
&ServerConfig::new_with_router(
dioxus_fullstack::router::FullstackRouterConfig::<Route>::default(),
)
.assets_path("docs")
.incremental(IncrementalRendererConfig::default().static_dir("docs"))
.build(),

View file

@ -3,7 +3,7 @@
//! # Example
//! ```rust
//! #![allow(non_snake_case)]
//! use dioxus::prelude::*;
//! use dioxus_lib::prelude::*;
//! use dioxus_fullstack::prelude::*;
//!
//! fn main() {
@ -20,7 +20,7 @@
//! .serve(
//! axum::Router::new()
//! // Server side render the application, serve static assets, and register server functions
//! .serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
//! .serve_dioxus_application("", ServerConfig::new(app, ()))
//! .into_make_service(),
//! )
//! .await
@ -78,7 +78,7 @@ pub trait DioxusRouterExt<S> {
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// #[tokio::main]
@ -115,7 +115,7 @@ pub trait DioxusRouterExt<S> {
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// #[tokio::main]
@ -163,7 +163,7 @@ pub trait DioxusRouterExt<S> {
/// # Example
/// ```rust
/// #![allow(non_snake_case)]
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// #[tokio::main]
@ -173,7 +173,7 @@ pub trait DioxusRouterExt<S> {
/// .serve(
/// axum::Router::new()
/// // Server side render the application, serve static assets, and register server functions
/// .serve_static_assets(ServeConfigBuilder::new(app, ()))
/// .serve_static_assets("dist")
/// // Server render the application
/// // ...
/// .into_make_service(),
@ -194,7 +194,7 @@ pub trait DioxusRouterExt<S> {
/// # Example
/// ```rust
/// #![allow(non_snake_case)]
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// #[tokio::main]
@ -204,7 +204,7 @@ pub trait DioxusRouterExt<S> {
/// .serve(
/// axum::Router::new()
/// // Server side render the application, serve static assets, and register server functions
/// .serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
/// .serve_dioxus_application("", ServerConfig::new(app, ()))
/// .into_make_service(),
/// )
/// .await
@ -376,7 +376,7 @@ fn apply_request_parts_to_response<B>(
/// use std::sync::{Arc, Mutex};
///
/// use axum::routing::get;
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::{axum_adapter::render_handler_with_context, prelude::*};
///
/// fn app() -> Element {
@ -387,7 +387,7 @@ fn apply_request_parts_to_response<B>(
///
/// #[tokio::main]
/// async fn main() {
/// let cfg = ServeConfigBuilder::new(app, ())
/// let cfg = ServerConfig::new(app, ())
/// .assets_path("dist")
/// .build();
/// let ssr_state = SSRState::new(&cfg);

View file

@ -3,7 +3,7 @@
//! # Example
//! ```rust
//! #![allow(non_snake_case)]
//! use dioxus::prelude::*;
//! use dioxus_lib::prelude::*;
//! use dioxus_fullstack::prelude::*;
//!
//! fn main() {
@ -16,7 +16,7 @@
//! .unwrap()
//! .block_on(async move {
//! let router =
//! Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ()));
//! Router::new().serve_dioxus_application("", ServerConfig::new(app, ()));
//! Server::new(TcpListener::bind("127.0.0.1:8080"))
//! .serve(router)
//! .await;
@ -186,14 +186,14 @@ pub trait DioxusRouterExt {
/// # Example
/// ```rust
/// #![allow(non_snake_case)]
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
/// use salvo::prelude::*;
/// use std::{net::TcpListener, sync::Arc};
///
/// #[tokio::main]
/// async fn main() {
/// let router = Router::new().serve_dioxus_application("", ServeConfigBuilder::new(app, ()));
/// let router = Router::new().serve_dioxus_application("", ServerConfig::new(app, ()));
/// Server::new(TcpListener::bind("127.0.0.1:8080"))
/// .serve(router)
/// .await;

View file

@ -3,7 +3,7 @@
//! # Example
//! ```rust
//! #![allow(non_snake_case)]
//! use dioxus::prelude::*;
//! use dioxus_lib::prelude::*;
//! use dioxus_fullstack::prelude::*;
//!
//! fn main() {
@ -14,7 +14,7 @@
//! tokio::runtime::Runtime::new()
//! .unwrap()
//! .block_on(async move {
//! let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ()));
//! let routes = serve_dioxus_application("", ServerConfig::new(app, ()));
//! warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
//! });
//! }
@ -166,12 +166,12 @@ pub fn register_server_fns(server_fn_route: &'static str) -> BoxedFilter<(impl R
/// # Example
/// ```rust
/// #![allow(non_snake_case)]
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// #[tokio::main]
/// async fn main() {
/// let routes = serve_dioxus_application("", ServeConfigBuilder::new(app, ()));
/// let routes = serve_dioxus_application("", ServerConfig::new(app, ()));
/// warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
/// }
///

View file

@ -8,7 +8,7 @@ use serde::{de::DeserializeOwned, Serialize};
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
/// use dioxus_lib::prelude::*;
/// use dioxus_fullstack::prelude::*;
///
/// fn app() -> Element {

View file

@ -1,4 +1,4 @@
use dioxus_core::prelude::*;
use dioxus_lib::prelude::*;
use serde::{de::DeserializeOwned, Serialize};
use std::any::Any;
use std::cell::Cell;
@ -70,10 +70,10 @@ where
// // Cancel the current future
// if let Some(current) = state.task.take() {
// cx.remove_future(current);
// remove_future(current);
// }
// state.task.set(Some(cx.push_future(async move {
// state.task.set(Some(push_future(async move {
// let data;
// #[cfg(feature = "ssr")]
// {
@ -115,36 +115,36 @@ pub struct UseServerFuture<T> {
}
impl<T> UseServerFuture<T> {
// /// Restart the future with new dependencies.
// ///
// /// Will not cancel the previous future, but will ignore any values that it
// /// generates.
// pub fn restart(&self) {
// self.needs_regen.set(true);
// (self.update)();
// }
/// Restart the future with new dependencies.
///
/// Will not cancel the previous future, but will ignore any values that it
/// generates.
pub fn restart(&self) {
self.needs_regen.set(true);
(self.update)();
}
// /// Forcefully cancel a future
// pub fn cancel(&self) {
// if let Some(task) = self.task.take() {
// cx.remove_future(task);
// }
// }
/// Forcefully cancel a future
pub fn cancel(&self) {
if let Some(task) = self.task.take() {
remove_future(task);
}
}
// /// Return any value, even old values if the future has not yet resolved.
// ///
// /// If the future has never completed, the returned value will be `None`.
// pub fn value(&self) -> Ref<'_, T> {
// Ref::map(self.value.borrow(), |v| v.as_deref().unwrap())
// }
/// Return any value, even old values if the future has not yet resolved.
///
/// If the future has never completed, the returned value will be `None`.
pub fn value(&self) -> Ref<'_, T> {
Ref::map(self.value.borrow(), |v| v.as_deref().unwrap())
}
// /// Get the ID of the future in Dioxus' internal scheduler
// pub fn task(&self) -> Option<Task> {
// self.task.get()
// }
/// Get the ID of the future in Dioxus' internal scheduler
pub fn task(&self) -> Option<Task> {
self.task.get()
}
// /// Get the current state of the future.
// pub fn reloading(&self) -> bool {
// self.task.get().is_some()
// }
/// Get the current state of the future.
pub fn reloading(&self) -> bool {
self.task.get().is_some()
}
}

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use dioxus::prelude::Template;
use dioxus_lib::prelude::Template;
use tokio::sync::{
watch::{channel, Receiver},
RwLock,
@ -9,7 +9,7 @@ use tokio::sync::{
#[derive(Clone)]
pub struct HotReloadState {
// The cache of all templates that have been modified since the last time we checked
pub(crate) templates: Arc<RwLock<std::collections::HashSet<dioxus::prelude::Template>>>,
pub(crate) templates: Arc<RwLock<std::collections::HashSet<dioxus_lib::prelude::Template>>>,
// The channel to send messages to the hot reload thread
pub(crate) message_receiver: Receiver<Option<Template>>,
}

View file

@ -1,19 +1,23 @@
//! Launch helper macros for fullstack apps
#![allow(unused)]
use crate::prelude::*;
use dioxus_core::prelude::*;
#[cfg(feature = "router")]
use dioxus_router::prelude::*;
use dioxus_lib::prelude::*;
/// A builder for a fullstack app.
pub struct LaunchBuilder<Props: Clone> {
component: Component<Props>,
#[cfg(not(feature = "ssr"))]
props: Props,
/// The desktop renderer platform
pub struct FullstackPlatform;
impl<Props: Clone + 'static> dioxus_core::PlatformBuilder<Props> for FullstackPlatform {
type Config = Config;
fn launch(config: dioxus_core::CrossPlatformConfig<Props>, platform_config: Self::Config) {}
}
/// Settings for a fullstack app.
pub struct Config {
#[cfg(feature = "ssr")]
server_fn_route: &'static str,
#[cfg(feature = "ssr")]
server_cfg: ServeConfigBuilder<Props>,
server_cfg: ServeConfigBuilder,
#[cfg(feature = "ssr")]
addr: std::net::SocketAddr,
#[cfg(feature = "web")]
@ -22,38 +26,29 @@ pub struct LaunchBuilder<Props: Clone> {
desktop_cfg: dioxus_desktop::Config,
}
impl<Props: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static>
LaunchBuilder<Props>
{
/// Create a new builder for a fullstack app.
pub fn new(component: Component<Props>) -> Self
where
Props: Default,
{
Self::new_with_props(component, Default::default())
}
/// Create a new builder for a fullstack app with props.
pub fn new_with_props(component: Component<Props>, props: Props) -> Self
where
Props: Default,
{
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self {
component,
#[cfg(not(feature = "ssr"))]
props,
#[cfg(feature = "ssr")]
server_fn_route: "",
#[cfg(feature = "ssr")]
addr: std::net::SocketAddr::from(([127, 0, 0, 1], 8080)),
#[cfg(feature = "ssr")]
server_cfg: ServeConfigBuilder::new(component, props),
server_cfg: ServeConfigBuilder::new(),
#[cfg(feature = "web")]
web_cfg: dioxus_web::Config::default(),
#[cfg(feature = "desktop")]
desktop_cfg: dioxus_desktop::Config::default(),
}
}
}
impl Config {
/// Create a new config for a fullstack app.
pub fn new() -> Self {
Self::default()
}
/// Set the address to serve the app on.
#[cfg(feature = "ssr")]
@ -82,7 +77,7 @@ impl<Props: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync
/// Set the server config.
#[cfg(feature = "ssr")]
pub fn server_cfg(self, server_cfg: ServeConfigBuilder<Props>) -> Self {
pub fn server_cfg(self, server_cfg: ServeConfigBuilder) -> Self {
Self { server_cfg, ..self }
}
@ -210,17 +205,3 @@ impl<Props: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync
}
}
}
#[cfg(feature = "router")]
impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
where
<R as std::str::FromStr>::Err: std::fmt::Display,
R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
{
/// Create a new launch builder for the given router.
pub fn router() -> Self {
let component = crate::router::RouteWithCfg::<R>;
let props = crate::router::FullstackRouterConfig::default();
Self::new_with_props(component, props)
}
}

View file

@ -7,9 +7,6 @@ pub use once_cell;
mod html_storage;
#[cfg(feature = "router")]
pub mod router;
#[cfg(feature = "ssr")]
mod adapters;
#[cfg(feature = "ssr")]
@ -39,7 +36,6 @@ pub mod prelude {
use crate::hooks;
#[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"))]

View file

@ -1,19 +1,19 @@
//! A shared pool of renderers for efficient server side rendering.
use std::sync::Arc;
use crate::render::dioxus_core::NoOpMutations;
use crate::server_context::SERVER_CONTEXT;
use dioxus::prelude::VirtualDom;
use dioxus_lib::prelude::VirtualDom;
use dioxus_ssr::{
incremental::{IncrementalRendererConfig, RenderFreshness, WrapBody},
Renderer,
};
use serde::Serialize;
use std::sync::Arc;
use std::sync::RwLock;
use tokio::task::spawn_blocking;
use crate::prelude::*;
use dioxus::prelude::*;
use dioxus_lib::prelude::*;
enum SsrRendererPool {
Renderer(RwLock<Vec<Renderer>>),
@ -45,15 +45,17 @@ 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());
vdom.in_runtime(|| {
// Make sure the evaluator is initialized
dioxus_ssr::eval::init_eval();
});
let mut to = WriteBuffer { buffer: Vec::new() };
// before polling the future, we need to set the context
let prev_context =
SERVER_CONTEXT.with(|ctx| ctx.replace(server_context));
// poll the future, which may call server_context()
tracing::info!("Rebuilding vdom");
let _ = vdom.rebuild();
let _ = vdom.rebuild(&mut NoOpMutations);
vdom.wait_for_suspense().await;
tracing::info!("Suspense resolved");
// after polling the future, we need to restore the context
@ -119,7 +121,7 @@ impl SsrRendererPool {
.with(|ctx| ctx.replace(Box::new(server_context)));
// poll the future, which may call server_context()
tracing::info!("Rebuilding vdom");
let _ = vdom.rebuild();
let _ = vdom.rebuild(&mut NoOpMutations);
vdom.wait_for_suspense().await;
tracing::info!("Suspense resolved");
// after polling the future, we need to restore the context
@ -195,16 +197,18 @@ impl SSRState {
route: String,
cfg: &'a ServeConfig<P>,
server_context: &'a DioxusServerContext,
app: Component<P>,
props: P,
) -> impl std::future::Future<
Output = Result<RenderResponse, dioxus_ssr::incremental::IncrementalRendererError>,
> + Send
+ 'a {
async move {
let ServeConfig { app, props, .. } = cfg;
let ServeConfig { .. } = cfg;
let (freshness, html) = self
.renderers
.render_to(cfg, route, *app, props.clone(), server_context)
.render_to(cfg, route, app, props, server_context)
.await?;
Ok(RenderResponse { html, freshness })
@ -213,7 +217,9 @@ impl SSRState {
}
struct FullstackRenderer<P: Clone + Send + Sync + 'static> {
cfg: ServeConfig<P>,
component: Component<P>,
props: P,
cfg: ServeConfig,
server_context: DioxusServerContext,
}
@ -338,28 +344,6 @@ fn incremental_pre_renderer(
renderer
}
#[cfg(all(feature = "ssr", feature = "router"))]
/// Pre-caches all static routes
pub async fn pre_cache_static_routes_with_props<Rt>(
cfg: &crate::prelude::ServeConfig<crate::router::FullstackRouterConfig<Rt>>,
) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError>
where
Rt: dioxus_router::prelude::Routable + Send + Sync + Serialize,
<Rt as std::str::FromStr>::Err: std::fmt::Display,
{
let wrapper = FullstackRenderer {
cfg: cfg.clone(),
server_context: Default::default(),
};
let mut renderer = incremental_pre_renderer(
cfg.incremental
.as_ref()
.expect("incremental renderer config must be set to pre-cache static routes"),
);
dioxus_router::incremental::pre_cache_static_routes::<Rt, _>(&mut renderer, &wrapper).await
}
struct WriteBuffer {
buffer: Vec<u8>,
}

View file

@ -1,98 +0,0 @@
//! Fullstack router intigration
#![allow(non_snake_case)]
use dioxus::prelude::*;
/// Used by the launch macro
#[doc(hidden)]
pub fn RouteWithCfg<R>(props: FullstackRouterConfig<R>) -> Element
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
use dioxus_router::prelude::RouterConfig;
let cfg = props;
rsx! {
dioxus_router::prelude::Router::<R> {
config: move || {
RouterConfig::default()
.failure_external_navigation(cfg.failure_external_navigation)
.history({
#[cfg(feature = "ssr")]
let history = dioxus_router::prelude::MemoryHistory::with_initial_path(
crate::prelude::server_context()
.request_parts()
.unwrap()
.uri
.to_string()
.parse()
.unwrap_or_else(|err| {
tracing::error!("Failed to parse uri: {}", err);
"/"
.parse()
.unwrap_or_else(|err| {
panic!("Failed to parse uri: {}", err);
})
}),
);
#[cfg(not(feature = "ssr"))]
let history = dioxus_router::prelude::WebHistory::new(
None,
cfg.scroll_restoration,
);
history
})
}
}
}
}
fn default_external_navigation_handler() -> fn() -> Element {
dioxus_router::prelude::FailureExternalNavigation
}
/// The configuration for the router
#[derive(Props, serde::Serialize, serde::Deserialize)]
pub struct FullstackRouterConfig<R>
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
#[serde(skip)]
#[serde(default = "default_external_navigation_handler")]
failure_external_navigation: fn() -> Element,
scroll_restoration: bool,
#[serde(skip)]
phantom: std::marker::PhantomData<R>,
}
impl<R> Clone for FullstackRouterConfig<R>
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
fn clone(&self) -> Self {
*self
}
}
impl<R> Copy for FullstackRouterConfig<R>
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
}
impl<R> Default for FullstackRouterConfig<R>
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
fn default() -> Self {
Self {
failure_external_navigation: dioxus_router::prelude::FailureExternalNavigation,
scroll_restoration: true,
phantom: std::marker::PhantomData,
}
}
}

View file

@ -7,13 +7,11 @@ use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use dioxus::prelude::*;
use dioxus_lib::prelude::*;
/// A ServeConfig is used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus-ssr`].
#[derive(Clone)]
pub struct ServeConfigBuilder<P: Clone> {
pub(crate) app: Component<P>,
pub(crate) props: P,
pub struct ServeConfigBuilder {
pub(crate) root_id: Option<&'static str>,
pub(crate) index_path: Option<&'static str>,
pub(crate) assets_path: Option<&'static str>,
@ -41,24 +39,10 @@ impl dioxus_ssr::incremental::WrapBody for EmptyIncrementalRenderTemplate {
}
}
#[cfg(feature = "router")]
impl<R> ServeConfigBuilder<FullstackRouterConfig<R>>
where
R: dioxus_router::prelude::Routable,
<R as std::str::FromStr>::Err: std::fmt::Display,
{
/// Create a new ServeConfigBuilder to serve a router on the server.
pub fn new_with_router(cfg: FullstackRouterConfig<R>) -> Self {
Self::new(RouteWithCfg::<R>, cfg)
}
}
impl<P: Clone> ServeConfigBuilder<P> {
impl ServeConfigBuilder {
/// Create a new ServeConfigBuilder with the root component and props to render on the server.
pub fn new(app: Component<P>, props: P) -> Self {
pub fn new() -> Self {
Self {
app,
props,
root_id: None,
index_path: None,
assets_path: None,
@ -91,7 +75,7 @@ impl<P: Clone> ServeConfigBuilder<P> {
}
/// Build the ServeConfig
pub fn build(self) -> ServeConfig<P> {
pub fn build(self) -> ServeConfig {
let assets_path = self.assets_path.unwrap_or("dist");
let index_path = self
@ -104,8 +88,6 @@ impl<P: Clone> ServeConfigBuilder<P> {
let index = load_index_html(index_path, root_id);
ServeConfig {
app: self.app,
props: self.props,
index,
assets_path,
incremental: self.incremental,
@ -146,17 +128,22 @@ pub(crate) struct IndexHtml {
/// Used to configure how to serve a Dioxus application. It contains information about how to serve static assets, and what content to render with [`dioxus-ssr`].
/// See [`ServeConfigBuilder`] to create a ServeConfig
#[derive(Clone)]
pub struct ServeConfig<P: Clone> {
pub(crate) app: Component<P>,
pub(crate) props: P,
pub struct ServeConfig {
pub(crate) index: IndexHtml,
pub(crate) assets_path: &'static str,
pub(crate) incremental:
Option<std::sync::Arc<dioxus_ssr::incremental::IncrementalRendererConfig>>,
}
impl<P: Clone> From<ServeConfigBuilder<P>> for ServeConfig<P> {
fn from(builder: ServeConfigBuilder<P>) -> Self {
impl ServeConfig {
/// Create a new builder for a ServeConfig
pub fn builder() -> ServeConfigBuilder {
ServeConfigBuilder::new()
}
}
impl From<ServeConfigBuilder> for ServeConfig {
fn from(builder: ServeConfigBuilder) -> Self {
builder.build()
}
}

View file

@ -26,6 +26,7 @@ js-sys = { version = "0.3.63", optional = true }
gloo-utils = { version = "0.1.6", optional = true }
dioxus-liveview = { workspace = true, optional = true }
dioxus-ssr = { workspace = true, optional = true }
dioxus-fullstack = { workspace = true, optional = true }
tokio = { workspace = true, features = ["full"], optional = true }
dioxus-cli-config.workspace = true
@ -36,6 +37,7 @@ liveview = ["dioxus-liveview", "tokio", "dep:serde", "serde_json"]
wasm_test = []
serde = ["dep:serde", "gloo-utils?/serde"]
web = ["gloo", "web-sys", "wasm-bindgen", "gloo-utils", "js-sys"]
fullstack = ["dioxus-fullstack"]
[dev-dependencies]
axum = { version = "0.6.1", features = ["ws"] }

View file

@ -46,6 +46,43 @@ where
}
}
macro_rules! default_history {
($initial_route:ident) => {
{
// If we are on wasm32 and the web feature is enabled, use the web history.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
return Box::<AnyHistoryProviderImplWrapper::<WebHistory::<R>>>::default();
// If we are using dioxus fullstack and the ssr feature is enabled, use the memory history with the initial path set to the current path in fullstack
#[cfg(all(feature = "fullstack", feature = "ssr"))]
return dioxus_router::prelude::MemoryHistory::with_initial_path(
dioxus_fullstack::prelude::server_context()
.request_parts()
.unwrap()
.uri
.to_string()
.parse()
.unwrap_or_else(|err| {
tracing::error!("Failed to parse uri: {}", err);
"/"
.parse()
.unwrap_or_else(|err| {
panic!("Failed to parse uri: {}", err);
})
}),
);
// If we are not on wasm32 and the liveview feature is enabled, use the liveview history.
#[cfg(all(feature = "liveview"))]
return Box::new(AnyHistoryProviderImplWrapper::new(LiveviewHistory::new($initial_route)));
// Otherwise use the memory history.
#[cfg(all(
not(all(target_arch = "wasm32", feature = "web")),
not(all(feature = "liveview", not(target_arch = "wasm32"))),
))]
Box::new(AnyHistoryProviderImplWrapper::new(MemoryHistory::with_initial_path($initial_route)))
}
};
}
#[cfg(feature = "serde")]
impl<R: Routable + Clone> RouterConfig<R>
where
@ -53,18 +90,13 @@ where
R: serde::Serialize + serde::de::DeserializeOwned,
{
pub(crate) fn get_history(self) -> Box<dyn HistoryProvider<R>> {
self.history.unwrap_or_else(|| {
#[cfg(all(not(feature = "liveview"), target_arch = "wasm32", feature = "web"))]
let history = Box::<WebHistory<R>>::default();
#[cfg(all(
not(feature = "liveview"),
any(not(target_arch = "wasm32"), not(feature = "web"))
))]
let history = Box::<MemoryHistory<R>>::default();
#[cfg(feature = "liveview")]
let history = Box::<LiveviewHistory<R>>::default();
history
})
#[allow(unused)]
let initial_route = self.initial_route.clone().unwrap_or("/".parse().unwrap_or_else(|err|
panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err)
));
self.history
.take()
.unwrap_or_else(|| default_history!(initial_route))
}
}
@ -89,25 +121,13 @@ where
<R as std::str::FromStr>::Err: std::fmt::Display,
{
pub(crate) fn take_history(&mut self) -> Box<dyn AnyHistoryProvider> {
self.history.take().unwrap_or_else(|| {
#[allow(unused)]
#[allow(unused)]
let initial_route = self.initial_route.clone().unwrap_or("/".parse().unwrap_or_else(|err|
panic!("index route does not exist:\n{}\n use MemoryHistory::with_initial_path or RouterConfig::initial_route to set a custom path", err)
));
// If we are on wasm32 and the web feature is enabled, use the web history.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
let history = Box::<AnyHistoryProviderImplWrapper::<WebHistory::<R>>>::default();
// If we are not on wasm32 and the liveview feature is enabled, use the liveview history.
#[cfg(all(feature = "liveview", not(target_arch = "wasm32")))]
let history = Box::new(AnyHistoryProviderImplWrapper::new(LiveviewHistory::new(initial_route)));
// If neither of the above are true, use the memory history.
#[cfg(all(
not(all(target_arch = "wasm32", feature = "web")),
not(all(feature = "liveview", not(target_arch = "wasm32"))),
))]
let history = Box::new(AnyHistoryProviderImplWrapper::new(MemoryHistory::with_initial_path(initial_route)));
history
})
self.history
.take()
.unwrap_or_else(|| default_history!(initial_route))
}
}