Ver código fonte

Add fullstack platform to serve and build commands

Evan Almloff 1 ano atrás
pai
commit
bd743fa2f9

+ 26 - 0
packages/cli/src/cli/build.rs

@@ -47,6 +47,32 @@ impl Build {
             Platform::Desktop => {
                 crate::builder::build_desktop(&crate_config, false)?;
             }
+            Platform::Fullstack => {
+                {
+                    let mut web_config = crate_config.clone();
+                    let web_feature = self.build.client_feature;
+                    let features = &mut web_config.features;
+                    match features {
+                        Some(features) => {
+                            features.push(web_feature);
+                        }
+                        None => web_config.features = Some(vec![web_feature]),
+                    };
+                    crate::builder::build(&crate_config, false)?;
+                }
+                {
+                    let mut desktop_config = crate_config.clone();
+                    let desktop_feature = self.build.server_feature;
+                    let features = &mut desktop_config.features;
+                    match features {
+                        Some(features) => {
+                            features.push(desktop_feature);
+                        }
+                        None => desktop_config.features = Some(vec![desktop_feature]),
+                    };
+                    crate::builder::build_desktop(&desktop_config, false)?;
+                }
+            }
         }
 
         let temp = gen_page(&crate_config.dioxus_config, false);

+ 35 - 0
packages/cli/src/cli/cfg.rs

@@ -35,6 +35,30 @@ pub struct ConfigOptsBuild {
     /// Space separated list of features to activate
     #[clap(long)]
     pub features: Option<Vec<String>>,
+
+    /// The feature to use for the client in a fullstack app [default: "web"]
+    #[clap(long, default_value_t = { "web".to_string() })]
+    pub client_feature: String,
+
+    /// The feature to use for the server in a fullstack app [default: "ssr"]
+    #[clap(long, default_value_t = { "ssr".to_string() })]
+    pub server_feature: String,
+}
+
+impl From<ConfigOptsServe> for ConfigOptsBuild {
+    fn from(serve: ConfigOptsServe) -> Self {
+        Self {
+            target: serve.target,
+            release: serve.release,
+            verbose: serve.verbose,
+            example: serve.example,
+            profile: serve.profile,
+            platform: serve.platform,
+            features: serve.features,
+            client_feature: serve.client_feature,
+            server_feature: serve.server_feature,
+        }
+    }
 }
 
 #[derive(Clone, Debug, Default, Deserialize, Parser)]
@@ -89,6 +113,14 @@ pub struct ConfigOptsServe {
     /// Space separated list of features to activate
     #[clap(long)]
     pub features: Option<Vec<String>>,
+
+    /// The feature to use for the client in a fullstack app [default: "web"]
+    #[clap(long, default_value_t = { "web".to_string() })]
+    pub client_feature: String,
+
+    /// The feature to use for the server in a fullstack app [default: "ssr"]
+    #[clap(long, default_value_t = { "ssr".to_string() })]
+    pub server_feature: String,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize, Debug)]
@@ -99,6 +131,9 @@ pub enum Platform {
     #[clap(name = "desktop")]
     #[serde(rename = "desktop")]
     Desktop,
+    #[clap(name = "fullstack")]
+    #[serde(rename = "fullstack")]
+    Fullstack,
 }
 
 /// Config options for the bundling system.

+ 5 - 1
packages/cli/src/cli/serve.rs

@@ -12,6 +12,7 @@ pub struct Serve {
 impl Serve {
     pub async fn serve(self, bin: Option<PathBuf>) -> Result<()> {
         let mut crate_config = crate::CrateConfig::new(bin)?;
+        let serve_cfg = self.serve.clone();
 
         // change the relase state.
         crate_config.with_hot_reload(self.serve.hot_reload);
@@ -49,7 +50,10 @@ impl Serve {
                     .await?;
             }
             cfg::Platform::Desktop => {
-                server::desktop::startup(crate_config.clone()).await?;
+                server::desktop::startup(crate_config.clone(), &serve_cfg).await?;
+            }
+            cfg::Platform::Fullstack => {
+                server::fullstack::startup(crate_config.clone(), &serve_cfg).await?;
             }
         }
         Ok(())

+ 60 - 45
packages/cli/src/server/desktop/mod.rs

@@ -1,4 +1,5 @@
 use crate::{
+    cfg::ConfigOptsServe,
     server::{
         output::{print_console_info, PrettierOptions},
         setup_file_watcher, setup_file_watcher_hot_reload,
@@ -19,7 +20,16 @@ use tokio::sync::broadcast::{self};
 #[cfg(feature = "plugin")]
 use plugin::PluginManager;
 
-pub async fn startup(config: CrateConfig) -> Result<()> {
+use super::Platform;
+
+pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
+    startup_with_platform::<DesktopPlatform>(config, serve).await
+}
+
+pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
+    config: CrateConfig,
+    serve: &ConfigOptsServe,
+) -> Result<()> {
     // ctrl-c shutdown checker
     let _crate_config = config.clone();
     let _ = ctrlc::set_handler(move || {
@@ -29,17 +39,19 @@ pub async fn startup(config: CrateConfig) -> Result<()> {
     });
 
     match config.hot_reload {
-        true => serve_hot_reload(config).await?,
-        false => serve_default(config).await?,
+        true => serve_hot_reload::<P>(config, serve).await?,
+        false => serve_default::<P>(config, serve).await?,
     }
 
     Ok(())
 }
 
 /// Start the server without hot reload
-pub async fn serve_default(config: CrateConfig) -> Result<()> {
-    let (child, first_build_result) = start_desktop(&config)?;
-    let currently_running_child: RwLock<Child> = RwLock::new(child);
+async fn serve_default<P: Platform + Send + 'static>(
+    config: CrateConfig,
+    serve: &ConfigOptsServe,
+) -> Result<()> {
+    let platform = RwLock::new(P::start(&config, serve)?);
 
     log::info!("🚀 Starting development server...");
 
@@ -49,49 +61,29 @@ pub async fn serve_default(config: CrateConfig) -> Result<()> {
         {
             let config = config.clone();
 
-            move || {
-                let mut current_child = currently_running_child.write().unwrap();
-                current_child.kill()?;
-                let (child, result) = start_desktop(&config)?;
-                *current_child = child;
-                Ok(result)
-            }
+            move || platform.write().unwrap().rebuild(&config)
         },
         &config,
         None,
     )
     .await?;
 
-    // Print serve info
-    print_console_info(
-        &config,
-        PrettierOptions {
-            changed: vec![],
-            warnings: first_build_result.warnings,
-            elapsed_time: first_build_result.elapsed_time,
-        },
-        None,
-    );
-
     std::future::pending::<()>().await;
 
     Ok(())
 }
 
-/// Start the server without hot reload
-
 /// Start dx serve with hot reload
-pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
-    let (_, first_build_result) = start_desktop(&config)?;
-
-    println!("🚀 Starting development server...");
+async fn serve_hot_reload<P: Platform + Send + 'static>(
+    config: CrateConfig,
+    serve: &ConfigOptsServe,
+) -> Result<()> {
+    let platform = RwLock::new(P::start(&config, serve)?);
 
     // Setup hot reload
     let FileMapBuildResult { map, errors } =
         FileMap::<HtmlCtx>::create(config.crate_dir.clone()).unwrap();
 
-    println!("🚀 Starting development server...");
-
     for err in errors {
         log::error!("{}", err);
     }
@@ -119,24 +111,13 @@ pub async fn serve_hot_reload(config: CrateConfig) -> Result<()> {
                 for channel in &mut *channels.lock().unwrap() {
                     send_msg(HotReloadMsg::Shutdown, channel);
                 }
-                Ok(start_desktop(&config)?.1)
+                Ok(platform.write().unwrap().rebuild(&config)?)
             }
         },
         None,
     )
     .await?;
 
-    // Print serve info
-    print_console_info(
-        &config,
-        PrettierOptions {
-            changed: vec![],
-            warnings: first_build_result.warnings,
-            elapsed_time: first_build_result.elapsed_time,
-        },
-        None,
-    );
-
     clear_paths();
 
     match LocalSocketListener::bind("@dioxusin") {
@@ -228,7 +209,7 @@ fn send_msg(msg: HotReloadMsg, channel: &mut impl std::io::Write) -> bool {
     }
 }
 
-pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
+fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
     // Run the desktop application
     let result = crate::builder::build_desktop(config, true)?;
 
@@ -246,3 +227,37 @@ pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
         }
     }
 }
+
+pub(crate) struct DesktopPlatform {
+    currently_running_child: Child,
+}
+
+impl Platform for DesktopPlatform {
+    fn start(config: &CrateConfig, _serve: &ConfigOptsServe) -> Result<Self> {
+        let (child, first_build_result) = start_desktop(&config)?;
+
+        log::info!("🚀 Starting development server...");
+
+        // Print serve info
+        print_console_info(
+            &config,
+            PrettierOptions {
+                changed: vec![],
+                warnings: first_build_result.warnings,
+                elapsed_time: first_build_result.elapsed_time,
+            },
+            None,
+        );
+
+        Ok(Self {
+            currently_running_child: child,
+        })
+    }
+
+    fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult> {
+        self.currently_running_child.kill()?;
+        let (child, result) = start_desktop(&config)?;
+        self.currently_running_child = child;
+        Ok(result)
+    }
+}

+ 72 - 0
packages/cli/src/server/fullstack/mod.rs

@@ -0,0 +1,72 @@
+use crate::{
+    cfg::{ConfigOptsBuild, ConfigOptsServe},
+    CrateConfig, Result,
+};
+
+use super::{desktop, Platform};
+
+pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
+    desktop::startup_with_platform::<FullstackPlatform>(config, serve).await
+}
+
+struct FullstackPlatform {
+    serve: ConfigOptsServe,
+    desktop: desktop::DesktopPlatform,
+}
+
+impl Platform for FullstackPlatform {
+    fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
+    where
+        Self: Sized,
+    {
+        {
+            build_web(serve.clone())?;
+        }
+
+        let mut desktop_config = config.clone();
+        let desktop_feature = serve.server_feature.clone();
+        let features = &mut desktop_config.features;
+        match features {
+            Some(features) => {
+                features.push(desktop_feature);
+            }
+            None => desktop_config.features = Some(vec![desktop_feature]),
+        };
+        let desktop = desktop::DesktopPlatform::start(&desktop_config, serve)?;
+
+        Ok(Self {
+            desktop,
+            serve: serve.clone(),
+        })
+    }
+
+    fn rebuild(&mut self, crate_config: &CrateConfig) -> Result<crate::BuildResult> {
+        build_web(self.serve.clone())?;
+        {
+            let mut desktop_config = crate_config.clone();
+            let desktop_feature = self.serve.server_feature.clone();
+            let features = &mut desktop_config.features;
+            match features {
+                Some(features) => {
+                    features.push(desktop_feature);
+                }
+                None => desktop_config.features = Some(vec![desktop_feature]),
+            };
+            self.desktop.rebuild(&desktop_config)
+        }
+    }
+}
+
+fn build_web(serve: ConfigOptsServe) -> Result<()> {
+    let mut web_config: ConfigOptsBuild = serve.into();
+    let web_feature = web_config.client_feature.clone();
+    let features = &mut web_config.features;
+    match features {
+        Some(features) => {
+            features.push(web_feature);
+        }
+        None => web_config.features = Some(vec![web_feature]),
+    };
+    web_config.platform = Some(crate::cfg::Platform::Web);
+    crate::cli::build::Build { build: web_config }.build(None)
+}

+ 9 - 1
packages/cli/src/server/mod.rs

@@ -1,4 +1,4 @@
-use crate::{BuildResult, CrateConfig, Result};
+use crate::{cfg::ConfigOptsServe, BuildResult, CrateConfig, Result};
 
 use cargo_metadata::diagnostic::Diagnostic;
 use dioxus_core::Template;
@@ -14,6 +14,7 @@ use tokio::sync::broadcast::Sender;
 mod output;
 use output::*;
 pub mod desktop;
+pub mod fullstack;
 pub mod web;
 
 /// Sets up a file watcher
@@ -180,3 +181,10 @@ async fn setup_file_watcher_hot_reload<F: Fn() -> Result<BuildResult> + Send + '
 
     Ok(watcher)
 }
+
+pub(crate) trait Platform {
+    fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
+    where
+        Self: Sized;
+    fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult>;
+}

+ 2 - 2
packages/cli/src/server/web/mod.rs

@@ -67,7 +67,7 @@ pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Res
 }
 
 /// Start the server without hot reload
-pub async fn serve_default(
+async fn serve_default(
     ip: String,
     port: u16,
     config: CrateConfig,
@@ -128,7 +128,7 @@ pub async fn serve_default(
 }
 
 /// Start dx serve with hot reload
-pub async fn serve_hot_reload(
+async fn serve_hot_reload(
     ip: String,
     port: u16,
     config: CrateConfig,