diff --git a/Cargo.lock b/Cargo.lock index 710d6f778..c96c8ad61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -787,7 +787,6 @@ name = "axum-hello-world" version = "0.1.0" dependencies = [ "dioxus", - "dioxus-fullstack", "reqwest", "serde", "simple_logger", @@ -2363,6 +2362,7 @@ dependencies = [ "env_logger", "futures-util", "rand 0.8.5", + "serde", "thiserror", "tokio", "tracing", diff --git a/packages/config-macro/src/lib.rs b/packages/config-macro/src/lib.rs index 245fbab4a..6991312ba 100644 --- a/packages/config-macro/src/lib.rs +++ b/packages/config-macro/src/lib.rs @@ -7,7 +7,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; #[proc_macro] -pub fn server(input: TokenStream) -> TokenStream { +pub fn server_only(input: TokenStream) -> TokenStream { if cfg!(any(feature = "ssr", feature = "liveview")) { let input = TokenStream2::from(input); quote! { diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs index 739c454d9..9f8f9b45e 100644 --- a/packages/core/src/any_props.rs +++ b/packages/core/src/any_props.rs @@ -5,28 +5,16 @@ pub(crate) type BoxedAnyProps = Box; /// A trait for a component that can be rendered. pub trait AnyProps: 'static { + /// Render the component with the internal props. fn render(&self) -> RenderReturn; + /// Check if the props are the same as the type erased props of another component. fn memoize(&self, other: &dyn Any) -> bool; + /// Get the props as a type erased `dyn Any`. fn props(&self) -> &dyn Any; + /// Duplicate this component into a new boxed component. fn duplicate(&self) -> BoxedAnyProps; } -/// Create a new boxed props object. -pub(crate) fn new_any_props, P: Clone + 'static, M: 'static>( - render_fn: F, - memo: fn(&P, &P) -> bool, - props: P, - name: &'static str, -) -> VProps { - VProps { - render_fn, - memo, - props, - name, - phantom: std::marker::PhantomData, - } -} - /// A component along with the props the component uses to render. pub struct VProps, P, M> { render_fn: F, @@ -36,6 +24,41 @@ pub struct VProps, P, M> { phantom: std::marker::PhantomData, } +impl, P: Clone, M> Clone for VProps { + fn clone(&self) -> Self { + Self { + render_fn: self.render_fn.clone(), + memo: self.memo, + props: self.props.clone(), + name: self.name, + phantom: std::marker::PhantomData, + } + } +} + +impl + Clone, P: Clone + 'static, M: 'static> VProps { + /// Create a [`VProps`] object. + pub fn new( + render_fn: F, + memo: fn(&P, &P) -> bool, + props: P, + name: &'static str, + ) -> VProps { + VProps { + render_fn, + memo, + props, + name, + phantom: std::marker::PhantomData, + } + } + + /// Get the current props of the VProps object + pub fn props(&self) -> &P { + &self.props + } +} + impl + Clone, P: Clone + 'static, M: 'static> AnyProps for VProps { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 063d665ba..7dc56f2d2 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1,7 +1,4 @@ -use crate::{ - any_props::{new_any_props, BoxedAnyProps}, - innerlude::ScopeState, -}; +use crate::{any_props::BoxedAnyProps, innerlude::ScopeState, VProps}; use crate::{arena::ElementId, Element, Event}; use crate::{ innerlude::{ElementRef, EventHandler, MountId}, @@ -529,7 +526,7 @@ impl VComponent { P: Properties + 'static, { let render_fn = component.id(); - let props = Box::new(new_any_props( + let props = Box::new(VProps::new( component,

::memoize, props, diff --git a/packages/core/src/platform.rs b/packages/core/src/platform.rs index 2b8c7d2c4..ed27a13a4 100644 --- a/packages/core/src/platform.rs +++ b/packages/core/src/platform.rs @@ -1,7 +1,7 @@ use std::any::Any; use crate::{ - any_props::{new_any_props, AnyProps, VProps}, + any_props::{AnyProps, VProps}, properties::ComponentFunction, VirtualDom, }; @@ -44,38 +44,37 @@ impl ClonableAny for T { } /// The platform-independent part of the config needed to launch an application. +#[derive(Clone)] pub struct CrossPlatformConfig { /// The root component function. - component: P, - /// The contexts to provide to the root component. - root_contexts: Vec, + props: P, + // /// The contexts to provide to the root component. + // root_contexts: Vec, } -impl, Props: Clone + 'static, M: 'static> - CrossPlatformConfig> -{ - /// Create a new cross-platform config. - pub fn new(component: F, props: Props, root_contexts: Vec) -> Self { - CrossPlatformConfig { - component: new_any_props(component, |_, _| true, props, "root"), - root_contexts, - } - } -} +impl CrossPlatformConfig

{} impl CrossPlatformConfig

{ - /// Push a new context into the root component's context. - pub fn push_context(&mut self, context: T) { - self.root_contexts.push(BoxedContext::new(context)); + /// Create a new cross-platform config. + pub fn new(props: P) -> Self { + CrossPlatformConfig { + props, + // root_contexts, + } } + // /// Push a new context into the root component's context. + // pub fn push_context(&mut self, context: T) { + // self.root_contexts.push(BoxedContext::new(context)); + // } + /// Build a virtual dom from the config. pub fn build_vdom(self) -> VirtualDom { - let mut vdom = VirtualDom::new_with_component(self.component); + let mut vdom = VirtualDom::new_with_component(self.props); - for context in self.root_contexts { - vdom.insert_boxed_root_context(context); - } + // for context in self.root_contexts { + // vdom.insert_boxed_root_context(context); + // } vdom } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 0480fd7f1..1bd35b337 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -3,7 +3,7 @@ //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust. use crate::{ - any_props::{new_any_props, AnyProps}, + any_props::AnyProps, arena::ElementId, innerlude::{ DirtyScope, ElementRef, ErrorBoundary, NoOpMutations, SchedulerMsg, ScopeState, VNodeMount, @@ -13,7 +13,7 @@ use crate::{ nodes::{Template, TemplateId}, runtime::{Runtime, RuntimeGuard}, scopes::ScopeId, - AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task, + AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task, VProps, }; use futures_util::{pin_mut, StreamExt}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -263,7 +263,7 @@ impl VirtualDom { root: impl ComponentFunction, root_props: P, ) -> Self { - Self::new_with_component(new_any_props(root, |_, _| true, root_props, "root")) + Self::new_with_component(VProps::new(root, |_, _| true, root_props, "root")) } /// Create a new virtualdom and build it immediately diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 54635a4f7..76c1ade8e 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -25,6 +25,8 @@ dioxus-fullstack = { workspace = true, optional = true } dioxus-liveview = { workspace = true, optional = true } # dioxus-tui = { workspace = true, optional = true } +serde = { version = "1.0.136", optional = true } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] dioxus-hot-reload = { workspace = true, optional = true } @@ -40,11 +42,14 @@ launch = ["dioxus-config-macro"] router = ["dioxus-router"] # Platforms -fullstack = ["dioxus-fullstack", "dioxus-config-macro/fullstack"] +fullstack = ["dioxus-fullstack", "dioxus-config-macro/fullstack", "serde"] desktop = ["dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"] web = ["dioxus-web", "dioxus-fullstack?/web", "dioxus-config-macro/web"] ssr = ["dioxus-fullstack?/ssr", "dioxus-config-macro/ssr"] liveview = ["dioxus-desktop", "dioxus-config-macro/liveview"] +axum = ["dioxus-fullstack?/axum"] +salvo = ["dioxus-fullstack?/salvo"] +warp = ["dioxus-fullstack?/warp"] # tui = ["dioxus-tui", "dioxus-config-macro/tui"] [dev-dependencies] diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 351952a5a..70a59b867 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -13,18 +13,26 @@ pub struct LaunchBuilder = CurrentPlat platform_config: Option<>::Config>, } -// Default platform builder -impl, Props: Clone + Default + 'static, M: 'static> - LaunchBuilder> +#[cfg(feature = "fullstack")] +// Fullstack platform builder +impl< + F: ComponentFunction + Send + Sync, + Props: Clone + Send + Sync + 'static, + M: Send + Sync + 'static, + > LaunchBuilder> { /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. - pub fn new(component: F) -> Self { + pub fn new(component: F) -> Self + where + Props: Default, + { Self { - cross_platform_config: CrossPlatformConfig::new( + cross_platform_config: CrossPlatformConfig::new(VProps::new( component, + |_, _| true, Default::default(), - Default::default(), - ), + "root", + )), platform_config: None, } } @@ -32,19 +40,59 @@ impl, Props: Clone + Default + 'static, M: 'stati /// Create a new builder for your application with some root props. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. pub fn new_with_props(component: F, props: Props) -> Self { Self { - cross_platform_config: CrossPlatformConfig::new(component, props, Default::default()), + cross_platform_config: CrossPlatformConfig::new(VProps::new( + component, + |_, _| true, + props, + "root", + )), + platform_config: None, + } + } +} + +#[cfg(not(feature = "fullstack"))] +// Default platform builder +impl, Props: Clone + 'static, M: 'static> + LaunchBuilder> +{ + /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. + pub fn new(component: F) -> Self + where + Props: Default, + { + Self { + cross_platform_config: CrossPlatformConfig::new(VProps::new( + component, + |_, _| true, + Default::default(), + "root", + )), + platform_config: None, + } + } + + /// Create a new builder for your application with some root props. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate. + pub fn new_with_props(component: F, props: Props) -> Self { + Self { + cross_platform_config: CrossPlatformConfig::new(VProps::new( + component, + |_, _| true, + props, + "root", + )), platform_config: None, } } } impl> LaunchBuilder { - /// Inject state into the root component's context. - pub fn context(mut self, state: impl Any + Clone + 'static) -> Self { - self.cross_platform_config - .push_context(BoxedContext::new(state)); - self - } + // /// Inject state into the root component's context. + // pub fn context(mut self, state: impl Any + Clone + 'static) -> Self { + // self.cross_platform_config + // .push_context(BoxedContext::new(state)); + // self + // } /// Provide a platform-specific config to the builder. pub fn cfg( @@ -89,13 +137,37 @@ impl LaunchBuilder { } } -#[cfg(feature = "desktop")] +#[cfg(feature = "fullstack")] +impl LaunchBuilder { + /// Launch your fullstack application. + pub fn launch_fullstack(self) { + dioxus_fullstack::FullstackPlatform::launch( + self.cross_platform_config, + self.platform_config.unwrap_or_default(), + ); + } +} + +#[cfg(feature = "fullstack")] +type CurrentPlatform = dioxus_fullstack::FullstackPlatform; +#[cfg(all(feature = "desktop", not(feature = "fullstack")))] type CurrentPlatform = dioxus_desktop::DesktopPlatform; -#[cfg(all(feature = "web", not(feature = "desktop")))] +#[cfg(all(feature = "web", not(any(feature = "desktop", feature = "fullstack"))))] type CurrentPlatform = dioxus_web::WebPlatform; -#[cfg(not(any(feature = "desktop", feature = "web")))] +#[cfg(not(any(feature = "desktop", feature = "web", feature = "fullstack")))] type CurrentPlatform = (); +#[cfg(feature = "fullstack")] +/// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch(component: impl ComponentFunction + Send + Sync) +where + Props: Default + Send + Sync + Clone + 'static, + Marker: Send + Sync + 'static, +{ + LaunchBuilder::new(component).launch() +} + +#[cfg(not(feature = "fullstack"))] /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options. pub fn launch(component: impl ComponentFunction) where @@ -104,7 +176,7 @@ where LaunchBuilder::new(component).launch() } -#[cfg(feature = "web")] +#[cfg(all(feature = "web", not(feature = "fullstack")))] /// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options. pub fn launch_web(component: impl ComponentFunction) where @@ -113,7 +185,7 @@ where LaunchBuilder::new(component).launch_web() } -#[cfg(feature = "desktop")] +#[cfg(all(feature = "desktop", not(feature = "fullstack")))] /// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options. pub fn launch_desktop(component: impl ComponentFunction) where @@ -121,3 +193,14 @@ where { LaunchBuilder::new(component).launch_desktop() } + +#[cfg(feature = "fullstack")] +/// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options. +pub fn launch_fullstack( + component: impl ComponentFunction + Send + Sync, +) where + Props: Default + Send + Sync + Clone + 'static, + Marker: Send + Sync + 'static, +{ + LaunchBuilder::new(component).launch_fullstack() +} diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 8d85b9dd5..0c9f880ec 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -57,6 +57,9 @@ pub mod prelude { pub use dioxus_hot_reload::{self, hot_reload_init}; pub use dioxus_core; + + #[cfg(feature = "fullstack")] + pub use dioxus_fullstack::prelude::*; } #[cfg(feature = "web")] diff --git a/packages/fullstack/examples/axum-hello-world/Cargo.toml b/packages/fullstack/examples/axum-hello-world/Cargo.toml index 4dda8df5b..39011da87 100644 --- a/packages/fullstack/examples/axum-hello-world/Cargo.toml +++ b/packages/fullstack/examples/axum-hello-world/Cargo.toml @@ -7,8 +7,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus = { workspace = true } -dioxus-fullstack = { workspace = true } +dioxus = { workspace = true, features = ["fullstack"]} serde = "1.0.159" simple_logger = "4.2.0" tracing-wasm = "0.2.1" @@ -18,5 +17,5 @@ reqwest = "0.11.18" [features] default = [] -ssr = ["dioxus-fullstack/axum"] -web = ["dioxus-fullstack/web"] +ssr = ["dioxus/axum"] +web = ["dioxus/web"] diff --git a/packages/fullstack/examples/axum-hello-world/src/main.rs b/packages/fullstack/examples/axum-hello-world/src/main.rs index a6bd8f321..33127af38 100644 --- a/packages/fullstack/examples/axum-hello-world/src/main.rs +++ b/packages/fullstack/examples/axum-hello-world/src/main.rs @@ -7,27 +7,17 @@ #![allow(non_snake_case, unused)] use dioxus::prelude::*; -use dioxus_fullstack::{ - launch::{self, LaunchBuilder}, - prelude::*, -}; use serde::{Deserialize, Serialize}; -#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)] -struct AppProps { - count: i32, -} - -fn app(cx: Scope) -> Element { - let state = - use_server_future((), |()| async move { get_server_data().await.unwrap() })?.value(); +fn app() -> Element { + // let state = use_server_future(|| async move { get_server_data().await.unwrap() })?; + // let state = state.value(); let mut count = use_signal(|| 0); let text = use_signal(|| "...".to_string()); - let eval = use_eval(cx); 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!" } @@ -50,9 +40,7 @@ fn app(cx: Scope) -> Element { #[server] async fn post_server_data(data: String) -> Result<(), ServerFnError> { - let axum::extract::Host(host): axum::extract::Host = extract().await?; println!("Server received: {}", data); - println!("{:?}", host); Ok(()) } @@ -68,5 +56,5 @@ fn main() { #[cfg(feature = "ssr")] tracing_subscriber::fmt::init(); - LaunchBuilder::new_with_props(app, AppProps { count: 0 }).launch() + launch(app); } diff --git a/packages/fullstack/src/adapters/axum_adapter.rs b/packages/fullstack/src/adapters/axum_adapter.rs index 981a0c01d..b96fe4b81 100644 --- a/packages/fullstack/src/adapters/axum_adapter.rs +++ b/packages/fullstack/src/adapters/axum_adapter.rs @@ -63,6 +63,7 @@ use axum::{ routing::{get, post}, Router, }; +use dioxus_lib::prelude::dioxus_core::{AnyProps, CrossPlatformConfig}; use server_fn::{Encoding, ServerFunctionRegistry}; use std::sync::Arc; use std::sync::RwLock; @@ -215,10 +216,11 @@ pub trait DioxusRouterExt { /// todo!() /// } /// ``` - fn serve_dioxus_application( + fn serve_dioxus_application( self, server_fn_route: &'static str, - cfg: impl Into>, + cfg: impl Into, + dioxus_config: CrossPlatformConfig

, ) -> Self; } @@ -313,10 +315,11 @@ where self } - fn serve_dioxus_application( + fn serve_dioxus_application( self, server_fn_route: &'static str, - cfg: impl Into>, + cfg: impl Into, + dioxus_config: CrossPlatformConfig

, ) -> Self { let cfg = cfg.into(); let ssr_state = SSRState::new(&cfg); @@ -325,7 +328,7 @@ where self.serve_static_assets(cfg.assets_path) .connect_hot_reload() .register_server_fns(server_fn_route) - .fallback(get(render_handler).with_state((cfg, ssr_state))) + .fallback(get(render_handler).with_state((cfg, dioxus_config, ssr_state))) } fn connect_hot_reload(self) -> Self { @@ -416,10 +419,15 @@ fn apply_request_parts_to_response( /// } /// ``` pub async fn render_handler_with_context< - P: Clone + serde::Serialize + Send + Sync + 'static, + P: AnyProps + Clone + Send + Sync + 'static, F: FnMut(&mut DioxusServerContext), >( - State((mut inject_context, cfg, ssr_state)): State<(F, ServeConfig

, SSRState)>, + State((mut inject_context, cfg, ssr_state, dioxus_config)): State<( + F, + ServeConfig, + SSRState, + CrossPlatformConfig

, + )>, request: Request, ) -> impl IntoResponse { let (parts, _) = request.into_parts(); @@ -428,7 +436,10 @@ pub async fn render_handler_with_context< let mut server_context = DioxusServerContext::new(parts.clone()); inject_context(&mut server_context); - match ssr_state.render(url, &cfg, &server_context).await { + match ssr_state + .render(url, &cfg, dioxus_config, &server_context) + .await + { Ok(rendered) => { let crate::render::RenderResponse { html, freshness } = rendered; let mut response = axum::response::Html::from(html).into_response(); @@ -445,11 +456,15 @@ pub async fn render_handler_with_context< } /// SSR renderer handler for Axum -pub async fn render_handler( - State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, +pub async fn render_handler( + State((cfg, dioxus_config, ssr_state)): State<(ServeConfig, CrossPlatformConfig

, SSRState)>, request: Request, ) -> impl IntoResponse { - render_handler_with_context(State((|_: &mut _| (), cfg, ssr_state)), request).await + render_handler_with_context( + State((|_: &mut _| (), cfg, ssr_state, dioxus_config)), + request, + ) + .await } fn report_err(e: E) -> Response { diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index 9ba94544e..af98b7558 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -1,17 +1,34 @@ //! Launch helper macros for fullstack apps #![allow(unused)] use crate::prelude::*; -use dioxus_lib::prelude::{dioxus_core::AnyProps, *}; +use dioxus_lib::prelude::{ + dioxus_core::{AnyProps, CrossPlatformConfig}, + *, +}; /// The desktop renderer platform pub struct FullstackPlatform; -impl dioxus_core::PlatformBuilder +impl dioxus_core::PlatformBuilder for FullstackPlatform { type Config = Config; - fn launch(config: dioxus_core::CrossPlatformConfig, platform_config: Self::Config) {} + fn launch(config: dioxus_core::CrossPlatformConfig, platform_config: Self::Config) { + #[cfg(feature = "ssr")] + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async move { + platform_config.launch_server(config).await; + }); + #[cfg(not(feature = "ssr"))] + { + #[cfg(feature = "web")] + platform_config.launch_web(config); + #[cfg(feature = "desktop")] + platform_config.launch_desktop(config); + } + } } /// Settings for a fullstack app. @@ -99,31 +116,33 @@ impl Config { } /// Launch the app. - pub fn launch(self) { + pub fn launch(self, dioxus_config: CrossPlatformConfig

) { #[cfg(feature = "ssr")] tokio::runtime::Runtime::new() .unwrap() .block_on(async move { - self.launch_server().await; + self.launch_server(dioxus_config).await; }); #[cfg(not(feature = "ssr"))] { #[cfg(feature = "web")] - self.launch_web(); + self.launch_web(dioxus_config); #[cfg(feature = "desktop")] - self.launch_desktop(); + self.launch_desktop(dioxus_config); } } #[cfg(feature = "web")] /// Launch the web application - pub fn launch_web(self) { + pub fn launch_web(self, dioxus_config: CrossPlatformConfig

) { + use dioxus_lib::prelude::dioxus_core::{CrossPlatformConfig, PlatformBuilder}; + #[cfg(not(feature = "ssr"))] { let cfg = self.web_cfg.hydrate(true); - dioxus_web::launch_with_props( - self.component, - get_root_props_from_document().unwrap(), + dioxus_web::WebPlatform::launch( + // TODO: this should pull the props from the document + dioxus_config, cfg, ); } @@ -131,14 +150,17 @@ impl Config { #[cfg(feature = "desktop")] /// Launch the web application - pub fn launch_desktop(self) { + pub fn launch_desktop(self, dioxus_config: CrossPlatformConfig

) { let cfg = self.desktop_cfg; dioxus_desktop::launch_with_props(self.component, self.props, cfg); } #[cfg(feature = "ssr")] /// Launch a server application - pub async fn launch_server(self) { + pub async fn launch_server( + self, + dioxus_config: CrossPlatformConfig

, + ) { let addr = self.addr; println!("Listening on {}", addr); let cfg = self.server_cfg.build(); @@ -155,7 +177,7 @@ impl Config { let router = router .serve_static_assets(cfg.assets_path) .connect_hot_reload() - .fallback(get(render_handler).with_state((cfg, ssr_state))); + .fallback(get(render_handler).with_state((cfg, dioxus_config, ssr_state))); let router = router .layer( ServiceBuilder::new() diff --git a/packages/fullstack/src/lib.rs b/packages/fullstack/src/lib.rs index 3767a41c2..949320ad6 100644 --- a/packages/fullstack/src/lib.rs +++ b/packages/fullstack/src/lib.rs @@ -14,7 +14,8 @@ pub use adapters::*; mod hooks; #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] mod hot_reload; -pub mod launch; +mod launch; +pub use launch::*; #[cfg(feature = "ssr")] mod layer; #[cfg(feature = "ssr")] diff --git a/packages/fullstack/src/render.rs b/packages/fullstack/src/render.rs index dc0f9c93e..e05e4c1d5 100644 --- a/packages/fullstack/src/render.rs +++ b/packages/fullstack/src/render.rs @@ -1,5 +1,6 @@ //! A shared pool of renderers for efficient server side rendering. - +use crate::render::dioxus_core::AnyProps; +use crate::render::dioxus_core::CrossPlatformConfig; use crate::render::dioxus_core::NoOpMutations; use crate::server_context::SERVER_CONTEXT; use dioxus_lib::prelude::VirtualDom; @@ -21,15 +22,15 @@ enum SsrRendererPool { } impl SsrRendererPool { - async fn render_to( + async fn render_to( &self, - cfg: &ServeConfig

, + cfg: &ServeConfig, route: String, - component: Component

, - props: P, + dioxus_config: CrossPlatformConfig

, server_context: &DioxusServerContext, ) -> Result<(RenderFreshness, String), dioxus_ssr::incremental::IncrementalRendererError> { let wrapper = FullstackRenderer { + serialized_props: None, cfg: cfg.clone(), server_context: server_context.clone(), }; @@ -44,7 +45,7 @@ impl SsrRendererPool { tokio::runtime::Runtime::new() .expect("couldn't spawn runtime") .block_on(async move { - let mut vdom = VirtualDom::new_with_props(component, props); + let mut vdom = dioxus_config.build_vdom(); vdom.in_runtime(|| { // Make sure the evaluator is initialized dioxus_ssr::eval::init_eval(); @@ -111,8 +112,7 @@ impl SsrRendererPool { match renderer .render( route, - component, - props, + dioxus_config, &mut *to, |vdom| { Box::pin(async move { @@ -169,7 +169,7 @@ pub struct SSRState { impl SSRState { /// Create a new [`SSRState`]. - pub fn new(cfg: &ServeConfig

) -> Self { + pub fn new(cfg: &ServeConfig) -> Self { if cfg.incremental.is_some() { return Self { renderers: Arc::new(SsrRendererPool::Incremental(RwLock::new(vec![ @@ -192,13 +192,12 @@ impl SSRState { } /// Render the application to HTML. - pub fn render<'a, P: 'static + Clone + serde::Serialize + Send + Sync>( + pub fn render<'a, P: AnyProps + Clone + Send + Sync>( &'a self, route: String, - cfg: &'a ServeConfig

, + cfg: &'a ServeConfig, + dioxus_config: CrossPlatformConfig

, server_context: &'a DioxusServerContext, - app: Component

, - props: P, ) -> impl std::future::Future< Output = Result, > + Send @@ -208,7 +207,7 @@ impl SSRState { let (freshness, html) = self .renderers - .render_to(cfg, route, app, props, server_context) + .render_to(cfg, route, dioxus_config, server_context) .await?; Ok(RenderResponse { html, freshness }) @@ -216,16 +215,13 @@ impl SSRState { } } -struct FullstackRenderer { - component: Component

, - props: P, +struct FullstackRenderer { + serialized_props: Option, cfg: ServeConfig, server_context: DioxusServerContext, } -impl dioxus_ssr::incremental::WrapBody - for FullstackRenderer

-{ +impl dioxus_ssr::incremental::WrapBody for FullstackRenderer { fn render_before_body( &self, to: &mut R, @@ -242,9 +238,10 @@ impl dioxus_ssr::incremental::Wrap to: &mut R, ) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> { // serialize the props - crate::html_storage::serialize::encode_props_in_element(&self.cfg.props, to).map_err( - |err| dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new(err)), - )?; + // TODO: restore props serialization + // crate::html_storage::serialize::encode_props_in_element(&self.cfg.props, to).map_err( + // |err| dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new(err)), + // )?; // serialize the server state crate::html_storage::serialize::encode_in_element( &*self.server_context.html_data().map_err(|_| { diff --git a/packages/server-macro/src/lib.rs b/packages/server-macro/src/lib.rs index 871de33bc..ecf07056c 100644 --- a/packages/server-macro/src/lib.rs +++ b/packages/server-macro/src/lib.rs @@ -98,9 +98,9 @@ pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { } }; - let server_fn_path: syn::Path = syn::parse_quote!(::dioxus_fullstack::prelude::server_fn); + let server_fn_path: syn::Path = syn::parse_quote!(::dioxus::fullstack::prelude::server_fn); let trait_obj_wrapper: syn::Type = - syn::parse_quote!(::dioxus_fullstack::prelude::ServerFnTraitObj); + syn::parse_quote!(::dioxus::fullstack::prelude::ServerFnTraitObj); let mut args: ServerFnArgs = match syn::parse(args) { Ok(args) => args, Err(e) => return e.to_compile_error().into(), @@ -125,7 +125,7 @@ pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { #tokens #[cfg(feature = "ssr")] #server_fn_path::inventory::submit! { - ::dioxus_fullstack::prelude::ServerFnMiddleware { + ::dioxus::fullstack::prelude::ServerFnMiddleware { prefix: #struct_name::PREFIX, url: #struct_name::URL, middleware: || vec![ diff --git a/packages/ssr/src/incremental.rs b/packages/ssr/src/incremental.rs index c30823747..0d1ebf2e9 100644 --- a/packages/ssr/src/incremental.rs +++ b/packages/ssr/src/incremental.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] use crate::fs_cache::ValidCachedPath; -use dioxus_core::{Element, VirtualDom}; +use dioxus_core::{AnyProps, CrossPlatformConfig, VirtualDom}; use rustc_hash::FxHasher; use std::{ future::Future, @@ -69,18 +69,17 @@ impl IncrementalRenderer { self.invalidate_after.is_some() } - async fn render_and_cache<'a, P: Clone + 'static, R: WrapBody + Send + Sync>( + async fn render_and_cache<'a, P: AnyProps + 'static, R: WrapBody + Send + Sync>( &'a mut self, route: String, - comp: fn(P) -> Element, - props: P, + dioxus_config: CrossPlatformConfig

, output: &'a mut (impl AsyncWrite + Unpin + Send), rebuild_with: impl FnOnce(&mut VirtualDom) -> Pin + '_>>, renderer: &'a R, ) -> Result { let mut html_buffer = WriteBuffer { buffer: Vec::new() }; { - let mut vdom = VirtualDom::new_with_props(comp, props); + let mut vdom = dioxus_config.build_vdom(); vdom.in_runtime(crate::eval::init_eval); rebuild_with(&mut vdom).await; @@ -168,11 +167,10 @@ impl IncrementalRenderer { } /// Render a route or get it from cache. - pub async fn render( + pub async fn render( &mut self, route: String, - component: fn(P) -> Element, - props: P, + dioxus_config: CrossPlatformConfig

, output: &mut (impl AsyncWrite + Unpin + std::marker::Send), rebuild_with: impl FnOnce(&mut VirtualDom) -> Pin + '_>>, renderer: &R, @@ -183,7 +181,7 @@ impl IncrementalRenderer { } else { // if not, create it let freshness = self - .render_and_cache(route, component, props, output, rebuild_with, renderer) + .render_and_cache(route, dioxus_config, output, rebuild_with, renderer) .await?; tracing::trace!("cache miss"); Ok(freshness) diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 9df564eac..9b52e9a95 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -60,7 +60,7 @@ use std::rc::Rc; pub use crate::cfg::Config; #[cfg(feature = "file_engine")] pub use crate::file_engine::WebFileEngineExt; -use dioxus_core::CrossPlatformConfig; +use dioxus_core::{AnyProps, CrossPlatformConfig}; use futures_util::{ future::{select, Either}, pin_mut, FutureExt, StreamExt, @@ -99,7 +99,10 @@ mod rehydrate; /// wasm_bindgen_futures::spawn_local(app_fut); /// } /// ``` -pub async fn run_with_props(dioxus_config: CrossPlatformConfig, web_config: Config) { +pub async fn run_with_props( + dioxus_config: CrossPlatformConfig

, + web_config: Config, +) { tracing::info!("Starting up"); let mut dom = dioxus_config.build_vdom(); @@ -123,7 +126,7 @@ pub async fn run_with_props(dioxus_config: CrossPlatformConfig, web_config: Conf let (tx, mut rx) = futures_channel::mpsc::unbounded(); #[cfg(feature = "hydrate")] - let should_hydrate = cfg.hydrate; + let should_hydrate = web_config.hydrate; #[cfg(not(feature = "hydrate"))] let should_hydrate = false; diff --git a/packages/web/src/platform.rs b/packages/web/src/platform.rs index 38d20a85a..c17679399 100644 --- a/packages/web/src/platform.rs +++ b/packages/web/src/platform.rs @@ -5,10 +5,10 @@ use crate::Config; /// The web renderer platform pub struct WebPlatform; -impl PlatformBuilder for WebPlatform { +impl PlatformBuilder

for WebPlatform { type Config = Config; - fn launch(config: CrossPlatformConfig, platform_config: Self::Config) { + fn launch(config: CrossPlatformConfig

, platform_config: Self::Config) { wasm_bindgen_futures::spawn_local(async move { crate::run_with_props(config, platform_config).await; });