1
0
Эх сурвалжийг харах

switch to launch builder instead of macro for fullstack

Evan Almloff 1 жил өмнө
parent
commit
9f891f9478

+ 9 - 1
packages/fullstack/Cargo.toml

@@ -34,7 +34,13 @@ dioxus-ssr = { workspace = true, optional = true }
 hyper = { version = "0.14.25", optional = true }
 http = { version = "0.2.9", optional = true }
 
-# Router Intigration
+# Web Integration
+dioxus-web = { workspace = true, features = ["hydrate"], optional = true }
+
+# Desktop Integration
+dioxus-desktop = { workspace = true, optional = true }
+
+# Router Integration
 dioxus-router = { workspace = true, optional = true }
 
 log = { workspace = true }
@@ -65,6 +71,8 @@ web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "Ht
 default = ["hot-reload", "default-tls"]
 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"]

+ 1 - 1
packages/fullstack/examples/axum-auth/src/main.rs

@@ -73,7 +73,7 @@ fn main() {
             });
     }
 }
-
+// 
 fn app(cx: Scope) -> Element {
     let user_name = use_state(cx, || "?".to_string());
     let permissions = use_state(cx, || "?".to_string());

+ 2 - 2
packages/fullstack/examples/axum-desktop/src/server.rs

@@ -10,8 +10,8 @@ use dioxus_fullstack::prelude::*;
 async fn main() {
     let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
 
-    PostServerData::register_explicit();
-    GetServerData::register_explicit();
+    let _ = PostServerData::register_explicit();
+    let _ = GetServerData::register_explicit();
 
     axum::Server::bind(&addr)
         .serve(

+ 2 - 6
packages/fullstack/examples/axum-hello-world/Cargo.toml

@@ -7,14 +7,10 @@ publish = false
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-dioxus-web = { workspace = true, features = ["hydrate"], optional = true }
 dioxus = { workspace = true }
 dioxus-fullstack = { workspace = true }
 axum = { version = "0.6.12", optional = true }
-tokio = { workspace = true, features = ["full"], optional = true }
 serde = "1.0.159"
-execute = "0.2.12"
-tower-http = { version = "0.4.1", features = ["auth"] }
 simple_logger = "4.2.0"
 wasm-logger = "0.2.0"
 log.workspace = true
@@ -22,5 +18,5 @@ reqwest = "0.11.18"
 
 [features]
 default = []
-ssr = ["axum", "tokio", "dioxus-fullstack/axum"]
-web = ["dioxus-web"]
+ssr = ["axum", "dioxus-fullstack/axum"]
+web = ["dioxus-fullstack/web"]

+ 6 - 4
packages/fullstack/examples/axum-hello-world/src/main.rs

@@ -7,8 +7,12 @@
 
 #![allow(non_snake_case, unused)]
 use dioxus::prelude::*;
-use dioxus_fullstack::{launch, prelude::*};
+use dioxus_fullstack::{
+    launch::{self, LaunchBuilder},
+    prelude::*,
+};
 use serde::{Deserialize, Serialize};
+use wasm_logger::Config;
 
 #[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)]
 struct AppProps {
@@ -66,7 +70,5 @@ fn main() {
     #[cfg(feature = "ssr")]
     simple_logger::SimpleLogger::new().init().unwrap();
 
-    launch!(@([127, 0, 0, 1], 8080), app, {
-        serve_cfg: ServeConfigBuilder::new(app, AppProps { count: 0 }),
-    });
+    LaunchBuilder::new_with_props(app, AppProps { count: 0 }).launch()
 }

+ 3 - 3
packages/fullstack/examples/axum-router/Cargo.toml

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

+ 12 - 4
packages/fullstack/examples/axum-router/src/main.rs

@@ -12,12 +12,20 @@ use dioxus_fullstack::prelude::*;
 use dioxus_router::prelude::*;
 
 fn main() {
-    launch_router!(@([127, 0, 0, 1], 8080), Route, {
-        incremental: IncrementalRendererConfig::default().invalidate_after(std::time::Duration::from_secs(120)),
-    });
+    let config = LaunchBuilder::<FullstackRouterConfig<Route>>::router();
+    #[cfg(feature = "ssr")]
+    config
+        .incremental(
+            IncrementalRendererConfig::default()
+                .invalidate_after(std::time::Duration::from_secs(120)),
+        )
+        .launch();
+
+    #[cfg(not(feature = "ssr"))]
+    config.launch();
 }
 
-#[derive(Clone, Routable, Debug, PartialEq)]
+#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
 enum Route {
     #[route("/")]
     Home {},

+ 1 - 3
packages/fullstack/examples/salvo-hello-world/src/main.rs

@@ -64,7 +64,5 @@ fn main() {
     #[cfg(feature = "ssr")]
     simple_logger::SimpleLogger::new().init().unwrap();
 
-    launch!(@([127, 0, 0, 1], 8080), app, {
-        serve_cfg: ServeConfigBuilder::new(app, AppProps { count: 0 }),
-    });
+    LaunchBuilder::new_with_props(app, AppProps { count: 0 }).launch()
 }

+ 1 - 3
packages/fullstack/examples/warp-hello-world/src/main.rs

@@ -64,7 +64,5 @@ fn main() {
     #[cfg(feature = "ssr")]
     simple_logger::SimpleLogger::new().init().unwrap();
 
-    launch!(@([127, 0, 0, 1], 8080), app, {
-        serve_cfg: ServeConfigBuilder::new(app, AppProps { count: 0 }),
-    });
+    LaunchBuilder::new_with_props(app, AppProps { count: 0 }).launch()
 }

+ 2 - 1
packages/fullstack/src/adapters/axum_adapter.rs

@@ -369,7 +369,8 @@ fn apply_request_parts_to_response<B>(
     }
 }
 
-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 {

+ 9 - 1
packages/fullstack/src/adapters/salvo_adapter.rs

@@ -375,10 +375,18 @@ async fn convert_response(response: HyperResponse, res: &mut Response) {
     }
 }
 
-struct SSRHandler<P: Clone> {
+/// A handler that renders a Dioxus application to HTML using server-side rendering.
+pub struct SSRHandler<P: Clone> {
     cfg: ServeConfig<P>,
 }
 
+impl<P: Clone> SSRHandler<P> {
+    /// Creates a new SSR handler with the given configuration.
+    pub fn new(cfg: ServeConfig<P>) -> Self {
+        Self { cfg }
+    }
+}
+
 #[async_trait]
 impl<P: Clone + serde::Serialize + Send + Sync + 'static> Handler for SSRHandler<P> {
     async fn handle(

+ 194 - 149
packages/fullstack/src/launch.rs

@@ -1,173 +1,218 @@
 //! Launch helper macros for fullstack apps
 #![allow(unused)]
-
-#[macro_export]
-/// Launch a server with a router
-macro_rules! launch_router {
-    (@router_config) => {
-        dioxus_fullstack::router::FullstackRouterConfig::default()
-    };
-
-    (@router_config $router_cfg:expr) => {
-        $router_cfg
-    };
-
-    (@$address:expr, $route:ty, $(cfg: $router_cfg:expr,)? {$($rule:ident $(: $cfg:expr)?,)*}) => {
-        dioxus_fullstack::launch!(
-            @$address,
-            dioxus_fullstack::router::RouteWithCfg::<$route>,
-            (dioxus_fullstack::launch_router!(@router_config $($router_cfg)?)),
-            {
-                $($rule $(: $cfg)?,)*
-            }
-        )
-    };
+use crate::prelude::*;
+use dioxus::prelude::*;
+#[cfg(feature = "router")]
+use dioxus_router::prelude::*;
+
+/// A builder for a fullstack app.
+pub struct LaunchBuilder<Props: Clone> {
+    component: Component<Props>,
+    #[cfg(not(feature = "ssr"))]
+    props: Props,
+    #[cfg(feature = "ssr")]
+    server_fn_route: &'static str,
+    #[cfg(feature = "ssr")]
+    server_cfg: ServeConfigBuilder<Props>,
+    #[cfg(feature = "ssr")]
+    addr: std::net::SocketAddr,
+    #[cfg(feature = "web")]
+    web_cfg: dioxus_web::Config,
+    #[cfg(feature = "desktop")]
+    desktop_cfg: dioxus_desktop::Config,
 }
 
-#[macro_export]
-/// Launch a server
-macro_rules! launch {
-    (@web_cfg $server_cfg:ident $wcfg:expr) => {
-        #[cfg(feature = "web")]
-        let web_cfg = $wcfg;
-    };
+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())
+    }
 
-    (@web_cfg $server_cfg:ident) => {
-        #[cfg(feature = "web")]
-        let web_cfg = dioxus_web::Config::new();
-    };
+    /// Create a new builder for a fullstack app with props.
+    pub fn new_with_props(component: Component<Props>, props: Props) -> Self
+    where
+        Props: Default,
+    {
+        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),
+            #[cfg(feature = "web")]
+            web_cfg: dioxus_web::Config::default(),
+            #[cfg(feature = "desktop")]
+            desktop_cfg: dioxus_desktop::Config::default(),
+        }
+    }
 
-    (@serve_cfg $server_cfg:ident $cfg:expr) => {
-        #[cfg(feature = "ssr")]
-        let $server_cfg = $cfg;
-    };
+    /// Set the address to serve the app on.
+    #[cfg(feature = "ssr")]
+    pub fn addr(self, addr: impl Into<std::net::SocketAddr>) -> Self {
+        let addr = addr.into();
+        Self { addr, ..self }
+    }
 
-    (@hot_reload $server_cfg:ident) => {
-        #[cfg(feature = "ssr")]
-        {
-            dioxus_fullstack::prelude::hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
-                std::process::Command::new("cargo")
-                    .args(&["run", "--features", "ssr"])
-                    .spawn()
-                    .unwrap()
-                    .wait()
-                    .unwrap();
-                std::process::Command::new("cargo")
-                    .args(&["run", "--features", "web"])
-                    .spawn()
-                    .unwrap()
-                    .wait()
-                    .unwrap();
-                true
-            }));
+    /// Set the route to the server functions.
+    #[cfg(feature = "ssr")]
+    pub fn server_fn_route(self, server_fn_route: &'static str) -> Self {
+        Self {
+            server_fn_route,
+            ..self
         }
-    };
+    }
 
-    (@hot_reload $server_cfg:ident $hot_reload_cfg:expr) => {
-        #[cfg(feature = "ssr")]
-        {
-            dioxus_fullstack::prelude::hot_reload_init!($hot_reload_cfg);
+    /// Set the incremental renderer config.
+    #[cfg(feature = "ssr")]
+    pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self {
+        Self {
+            server_cfg: self.server_cfg.incremental(cfg),
+            ..self
         }
-    };
-
-    (@incremental $server_cfg:ident) => {
-        #[cfg(feature = "ssr")]
-        let $server_cfg = $server_cfg.incremental(dioxus_fullstack::prelude::IncrementalRendererConfig::default());
-    };
-
-    (@incremental $server_cfg:ident $cfg:expr) => {
-        #[cfg(feature = "ssr")]
-        let $server_cfg = $server_cfg.incremental($cfg);
-    };
-
-    (@props_type) => {
-        Default::default()
-    };
-
-    (@props_type $props:expr) => {
-        $props
-    };
+    }
 
-    (@ $address:expr, $comp:path, $(( $props:expr ),)? {$($rule:ident $(: $cfg:expr)?,)*}) => {
-        #[cfg(feature = "web")]
-        {
-            #[allow(unused)]
-            let web_cfg = dioxus_web::Config::new();
+    /// Set the server config.
+    #[cfg(feature = "ssr")]
+    pub fn server_cfg(self, server_cfg: ServeConfigBuilder<Props>) -> Self {
+        Self { server_cfg, ..self }
+    }
 
-            $(
-                dioxus_fullstack::prelude::launch!(@$rule server_cfg $($cfg)?);
-            )*
+    /// Set the web config.
+    #[cfg(feature = "web")]
+    pub fn web_cfg(self, web_cfg: dioxus_web::Config) -> Self {
+        Self { web_cfg, ..self }
+    }
 
-            dioxus_web::launch_with_props(
-                $comp,
-                dioxus_fullstack::prelude::get_root_props_from_document().expect("Failed to get root props from document"),
-                web_cfg.hydrate(true),
-            );
+    /// Set the desktop config.
+    #[cfg(feature = "desktop")]
+    pub fn desktop_cfg(self, desktop_cfg: dioxus_desktop::Config) -> Self {
+        Self {
+            desktop_cfg,
+            ..self
         }
+    }
+
+    /// Launch the app.
+    pub fn launch(self) {
         #[cfg(feature = "ssr")]
+        tokio::runtime::Runtime::new()
+            .unwrap()
+            .block_on(async move {
+                self.launch_server().await;
+            });
+        #[cfg(not(feature = "ssr"))]
         {
-            let server_cfg = dioxus_fullstack::prelude::ServeConfigBuilder::new($comp, dioxus_fullstack::prelude::launch!(@props_type $($props)?));
+            #[cfg(feature = "web")]
+            self.launch_web();
+            #[cfg(feature = "desktop")]
+            self.launch_desktop();
+        }
+    }
 
-            $(
-                dioxus_fullstack::prelude::launch!(@$rule server_cfg $($cfg)?);
-            )*
+    #[cfg(feature = "web")]
+    fn launch_web(self) {
+        let cfg = self.web_cfg.hydrate(true);
+        dioxus_web::launch_with_props(self.component, get_root_props_from_document().unwrap(), cfg);
+    }
 
-            tokio::runtime::Runtime::new()
-                .unwrap()
-                .block_on(async move {
-                    let addr = std::net::SocketAddr::from($address);
+    #[cfg(feature = "desktop")]
+    fn launch_desktop(self) {
+        let cfg = self.desktop_cfg;
+        dioxus_desktop::launch_with_props(self.component, self.props, cfg);
+    }
 
-                    dioxus_fullstack::launch::launch_server(addr, server_cfg.build()).await;
-                });
+    /// Launch a server with the given configuration
+    /// This will use the routing integration of the currently enabled integration feature
+    #[cfg(feature = "ssr")]
+    async fn launch_server(self) {
+        let addr = self.addr;
+        println!("Listening on {}", addr);
+        let cfg = self.server_cfg.build();
+        let server_fn_route = self.server_fn_route;
+        #[cfg(all(feature = "axum", not(feature = "warp"), not(feature = "salvo")))]
+        {
+            use crate::adapters::axum_adapter::{render_handler, DioxusRouterExt};
+            use axum::routing::get;
+            use tower::ServiceBuilder;
+
+            let ssr_state = SSRState::new(&cfg);
+            let router = axum::Router::new().register_server_fns(server_fn_route);
+            #[cfg(not(feature = "desktop"))]
+            let router = router
+                .serve_static_assets(cfg.assets_path)
+                .connect_hot_reload()
+                .fallback(get(render_handler).with_state((cfg, ssr_state)));
+            let router = router
+                .layer(
+                    ServiceBuilder::new()
+                        .layer(tower_http::compression::CompressionLayer::new().gzip(true)),
+                )
+                .into_make_service();
+            axum::Server::bind(&addr).serve(router).await.unwrap();
+        }
+        #[cfg(all(feature = "warp", not(feature = "axum"), not(feature = "salvo")))]
+        {
+            use warp::Filter;
+            // First register the server functions
+            let router = register_server_fns(server_fn_route);
+            #[cfg(not(feature = "desktop"))]
+            let router = {
+                // Serve the dist folder and the index.html file
+                let serve_dir = warp::fs::dir(cfg.assets_path);
+
+                router
+                    .or(connect_hot_reload())
+                    // Then the index route
+                    .or(warp::path::end().and(render_ssr(cfg.clone())))
+                    // Then the static assets
+                    .or(serve_dir)
+                    // Then all other routes
+                    .or(render_ssr(cfg))
+            };
+            warp::serve(router.boxed().with(warp::filters::compression::gzip()))
+                .run(addr)
+                .await;
+        }
+        #[cfg(all(feature = "salvo", not(feature = "axum"), not(feature = "warp")))]
+        {
+            use crate::adapters::salvo_adapter::{DioxusRouterExt, SSRHandler};
+            use salvo::conn::Listener;
+            let router = salvo::Router::new().register_server_fns(server_fn_route);
+            #[cfg(not(feature = "desktop"))]
+            let router = router
+                .serve_static_assets(cfg.assets_path)
+                .connect_hot_reload()
+                .push(salvo::Router::with_path("/<**any_path>").get(SSRHandler::new(cfg)));
+            let router = router.hoop(
+                salvo::compression::Compression::new()
+                    .enable_gzip(salvo::prelude::CompressionLevel::Default),
+            );
+            salvo::Server::new(salvo::conn::tcp::TcpListener::new(addr).bind().await)
+                .serve(router)
+                .await;
         }
-    };
+    }
 }
 
-/// Launch a server with the given configeration
-/// This will use the routing intigration of the currently enabled intigration feature
-#[cfg(feature = "ssr")]
-pub async fn launch_server<P: Clone + serde::Serialize + Send + Sync + 'static>(
-    addr: std::net::SocketAddr,
-    cfg: crate::prelude::ServeConfig<P>,
-) {
-    #[cfg(all(feature = "axum", not(feature = "warp"), not(feature = "salvo")))]
-    {
-        use crate::adapters::axum_adapter::DioxusRouterExt;
-        use tower::ServiceBuilder;
-
-        axum::Server::bind(&addr)
-            .serve(
-                axum::Router::new()
-                    .serve_dioxus_application("", cfg)
-                    .layer(
-                        ServiceBuilder::new()
-                            .layer(tower_http::compression::CompressionLayer::new().gzip(true)),
-                    )
-                    .into_make_service(),
-            )
-            .await
-            .unwrap();
-    }
-    #[cfg(all(feature = "warp", not(feature = "axum"), not(feature = "salvo")))]
-    {
-        use warp::Filter;
-        warp::serve(
-            crate::prelude::serve_dioxus_application("", cfg)
-                .with(warp::filters::compression::gzip()),
-        )
-        .run(addr)
-        .await;
-    }
-    #[cfg(all(feature = "salvo", not(feature = "axum"), not(feature = "warp")))]
-    {
-        use crate::adapters::salvo_adapter::DioxusRouterExt;
-        use salvo::conn::Listener;
-        let router = salvo::Router::new().serve_dioxus_application("", cfg).hoop(
-            salvo::compression::Compression::new()
-                .enable_gzip(salvo::prelude::CompressionLevel::Default),
-        );
-        salvo::Server::new(salvo::conn::tcp::TcpListener::new(addr).bind().await)
-            .serve(router)
-            .await;
+#[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)
     }
 }

+ 3 - 1
packages/fullstack/src/lib.rs

@@ -39,10 +39,13 @@ 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(all(feature = "ssr", feature = "router"))]
     pub use crate::render::pre_cache_static_routes_with_props;
     #[cfg(feature = "ssr")]
     pub use crate::render::SSRState;
+    #[cfg(feature = "router")]
+    pub use crate::router::FullstackRouterConfig;
     #[cfg(feature = "ssr")]
     pub use crate::serve_config::{ServeConfig, ServeConfigBuilder};
     #[cfg(all(feature = "ssr", feature = "axum"))]
@@ -54,7 +57,6 @@ pub mod prelude {
     pub use crate::server_fn::DioxusServerFn;
     #[cfg(feature = "ssr")]
     pub use crate::server_fn::{ServerFnMiddleware, ServerFnTraitObj, ServerFunction};
-    pub use crate::{launch, launch_router};
     pub use dioxus_server_macro::*;
     #[cfg(feature = "ssr")]
     pub use dioxus_ssr::incremental::IncrementalRendererConfig;