From 245003a5d430ab8e368094cd32208178183fc24e Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 22 May 2024 14:24:15 +0200 Subject: [PATCH] Create a Static Site Generation platform; Deduplicate hot reloading code (#2226) * create static site generation helpers in the router crate * work on integrating static site generation into fullstack * move ssg into a separate crate * integrate ssg with the launch builder * simplify ssg example * fix static_routes for child routes * move CLI hot reloading websocket code into dioxus-hot-reload * fix some unused imports * use the same hot reloading websocket code for fullstack * fix fullstack hot reloading * move cli hot reloading logic into the hot reload crate * ssg example working with dx serve * add more examples * fix clippy * fix formatting * fix hot reload doctest imports * fix axum imports * don't run server doc tests * Fix hot reload websocket doc examples --- Cargo.lock | 65 +++- Cargo.toml | 12 +- packages/cli-config/src/config.rs | 5 + packages/cli/Cargo.toml | 2 +- packages/cli/src/builder.rs | 5 +- packages/cli/src/cli/build.rs | 2 +- packages/cli/src/cli/serve.rs | 8 +- packages/cli/src/server/desktop/mod.rs | 32 +- packages/cli/src/server/mod.rs | 44 ++- packages/cli/src/server/web/hot_reload.rs | 82 ----- packages/cli/src/server/web/mod.rs | 52 ++-- packages/cli/src/server/web/server.rs | 52 +--- packages/config-macro/Cargo.toml | 1 + packages/config-macro/src/lib.rs | 17 +- packages/dioxus/Cargo.toml | 6 +- packages/dioxus/src/launch.rs | 41 ++- packages/dioxus/src/lib.rs | 4 + packages/fullstack/Cargo.toml | 7 +- .../examples/static-hydrated/Cargo.toml | 20 -- .../examples/static-hydrated/Dioxus.toml | 46 --- .../examples/static-hydrated/src/main.rs | 90 ------ packages/fullstack/src/axum_adapter.rs | 129 ++------ packages/fullstack/src/config.rs | 18 +- packages/fullstack/src/hot_reload.rs | 62 ---- .../fullstack/src/html_storage/deserialize.rs | 20 -- .../fullstack/src/html_storage/serialize.rs | 13 - packages/fullstack/src/lib.rs | 34 +-- packages/fullstack/src/render.rs | 58 ++-- packages/fullstack/src/serve_config.rs | 26 +- packages/fullstack/src/server_context.rs | 3 +- packages/hot-reload/Cargo.toml | 11 + .../src/assets/autoreload.js | 5 - packages/hot-reload/src/lib.rs | 9 + packages/hot-reload/src/websocket.rs | 279 ++++++++++++++++++ packages/router/Cargo.toml | 4 +- packages/router/src/components/router.rs | 8 +- packages/router/src/contexts/router.rs | 37 ++- packages/router/src/incremental.rs | 65 +--- packages/router/src/lib.rs | 1 + packages/router/src/routable.rs | 73 +++-- packages/router/tests/site_map.rs | 53 ++++ packages/ssr/Cargo.toml | 3 +- packages/ssr/src/incremental_cfg.rs | 30 +- packages/static-generation/.gitignore | 1 + packages/static-generation/Cargo.toml | 34 +++ packages/static-generation/README.md | 83 ++++++ .../examples/github-pages/.gitignore | 4 + .../examples/github-pages/Cargo.toml | 17 ++ .../examples/github-pages/src/main.rs | 70 +++++ .../examples/router}/.gitignore | 2 +- .../examples/router/Cargo.toml | 16 + .../examples/router/src/main.rs | 54 ++++ .../examples/simple/.gitignore | 4 + .../examples/simple/Cargo.toml | 16 + .../examples/simple/src/main.rs | 17 ++ packages/static-generation/src/config.rs | 189 ++++++++++++ packages/static-generation/src/launch.rs | 85 ++++++ packages/static-generation/src/lib.rs | 8 + packages/static-generation/src/ssg.rs | 164 ++++++++++ 59 files changed, 1507 insertions(+), 791 deletions(-) delete mode 100644 packages/cli/src/server/web/hot_reload.rs delete mode 100644 packages/fullstack/examples/static-hydrated/Cargo.toml delete mode 100644 packages/fullstack/examples/static-hydrated/Dioxus.toml delete mode 100644 packages/fullstack/examples/static-hydrated/src/main.rs delete mode 100644 packages/fullstack/src/hot_reload.rs rename packages/{cli => hot-reload}/src/assets/autoreload.js (73%) create mode 100644 packages/hot-reload/src/websocket.rs create mode 100644 packages/router/tests/site_map.rs create mode 100644 packages/static-generation/.gitignore create mode 100644 packages/static-generation/Cargo.toml create mode 100644 packages/static-generation/README.md create mode 100644 packages/static-generation/examples/github-pages/.gitignore create mode 100644 packages/static-generation/examples/github-pages/Cargo.toml create mode 100644 packages/static-generation/examples/github-pages/src/main.rs rename packages/{fullstack/examples/static-hydrated => static-generation/examples/router}/.gitignore (53%) create mode 100644 packages/static-generation/examples/router/Cargo.toml create mode 100644 packages/static-generation/examples/router/src/main.rs create mode 100644 packages/static-generation/examples/simple/.gitignore create mode 100644 packages/static-generation/examples/simple/Cargo.toml create mode 100644 packages/static-generation/examples/simple/src/main.rs create mode 100644 packages/static-generation/src/config.rs create mode 100644 packages/static-generation/src/launch.rs create mode 100644 packages/static-generation/src/lib.rs create mode 100644 packages/static-generation/src/ssg.rs diff --git a/Cargo.lock b/Cargo.lock index 950cb252a..072db81f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2096,6 +2096,7 @@ dependencies = [ "dioxus-router", "dioxus-signals", "dioxus-ssr", + "dioxus-static-site-generation", "dioxus-web", "env_logger 0.10.2", "futures-util", @@ -2415,17 +2416,22 @@ dependencies = [ name = "dioxus-hot-reload" version = "0.5.2" dependencies = [ + "axum", "chrono", "dioxus-core 0.5.2", "dioxus-html", "dioxus-rsx", "execute", + "futures-util", "ignore", "interprocess-docfix", "notify", "once_cell", "serde", "serde_json", + "tokio", + "tokio-stream", + "tracing", ] [[package]] @@ -2583,6 +2589,7 @@ dependencies = [ "dioxus-ssr", "gloo", "gloo-utils 0.1.7", + "http 1.1.0", "js-sys", "serde", "serde_json", @@ -2650,6 +2657,7 @@ dependencies = [ "async-trait", "chrono", "dioxus", + "dioxus-cli-config", "dioxus-core 0.5.2", "dioxus-html", "dioxus-signals", @@ -2666,6 +2674,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "dioxus-static-site-generation" +version = "0.5.2" +dependencies = [ + "axum", + "dioxus", + "dioxus-cli-config", + "dioxus-fullstack", + "dioxus-hot-reload", + "dioxus-lib", + "dioxus-router", + "dioxus-ssr", + "dioxus-web", + "http 1.1.0", + "tokio", + "tower", + "tower-http", + "tracing", +] + [[package]] name = "dioxus-tailwind" version = "0.0.0" @@ -3668,6 +3696,15 @@ dependencies = [ "url", ] +[[package]] +name = "github-pages-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tower", + "tracing-subscriber", +] + [[package]] name = "gix-actor" version = "0.31.1" @@ -7585,6 +7622,14 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "router-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tracing-subscriber", +] + [[package]] name = "rsa" version = "0.9.6" @@ -8222,6 +8267,14 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +[[package]] +name = "simple-static-generation" +version = "0.1.0" +dependencies = [ + "dioxus", + "tracing-subscriber", +] + [[package]] name = "simple_logger" version = "4.3.3" @@ -8606,18 +8659,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static-hydrated" -version = "0.1.0" -dependencies = [ - "dioxus", - "dioxus-fullstack", - "dioxus-router", - "dioxus-web", - "serde", - "tokio", -] - [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index dd9d8bb32..fd153d125 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,12 +29,15 @@ members = [ "packages/hot-reload", "packages/fullstack", "packages/server-macro", + "packages/static-generation", "packages/fullstack/examples/axum-hello-world", "packages/fullstack/examples/axum-router", "packages/fullstack/examples/axum-streaming", "packages/fullstack/examples/axum-desktop", "packages/fullstack/examples/axum-auth", - "packages/fullstack/examples/static-hydrated", + "packages/static-generation/examples/simple", + "packages/static-generation/examples/router", + "packages/static-generation/examples/github-pages", # Full project examples "examples/tailwind", "examples/PWA-example", @@ -76,8 +79,8 @@ dioxus-cli-config = { path = "packages/cli-config", version = "0.5.0", default-f generational-box = { path = "packages/generational-box", version = "0.5.0" } dioxus-hot-reload = { path = "packages/hot-reload", version = "0.5.0" } dioxus-fullstack = { path = "packages/fullstack", version = "0.5.0" } +dioxus-static-site-generation = { path = "packages/static-generation", version = "0.5.0" } dioxus_server_macro = { path = "packages/server-macro", version = "0.5.0", default-features = false } -dioxus-ext = { path = "packages/extension" } tracing = "0.1.37" tracing-futures = "0.2.5" toml = "0.8" @@ -101,7 +104,7 @@ axum = "0.7.0" axum-server = { version = "0.6.0", default-features = false } tower = "0.4.13" http = "1.0.0" -tower-http = "0.5.1" +tower-http = "0.5.2" hyper = "1.0.0" hyper-rustls = "0.26.0" serde_json = "1.0.61" @@ -122,9 +125,6 @@ isnta = "1.36.1" [profile.dev.package.insta] opt-level = 3 -[profile.dev.package.similar] -opt-level = 3 - [profile.dev.package.dioxus-core-macro] opt-level = 3 diff --git a/packages/cli-config/src/config.rs b/packages/cli-config/src/config.rs index eeef85c28..e119a90d5 100644 --- a/packages/cli-config/src/config.rs +++ b/packages/cli-config/src/config.rs @@ -22,6 +22,11 @@ pub enum Platform { #[cfg_attr(feature = "cli", clap(name = "fullstack"))] #[serde(rename = "fullstack")] Fullstack, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[cfg_attr(feature = "cli", clap(name = "fullstack"))] + #[serde(rename = "static-generation")] + StaticGeneration, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 7cbbf4acb..cf3646902 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -93,7 +93,7 @@ rsx-rosetta = { workspace = true } dioxus-rsx = { workspace = true } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-hot-reload = { workspace = true } +dioxus-hot-reload = { workspace = true, features = ["serve"] } interprocess = { workspace = true } # interprocess-docfix = { version = "1.2.2" } ignore = "0.4.22" diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index f7fed41e7..a83d709d1 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -564,10 +564,7 @@ pub fn gen_page(config: &CrateConfig, manifest: Option<&AssetManifest>, serve: b replace_or_insert_before("{script_include}", &script_str, "{}", - include_str!("./assets/autoreload.js") - ); + html += &format!("", dioxus_hot_reload::RECONNECT_SCRIPT); } let base_path = match &config.dioxus_config.web.app.base_path { diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index d90e9ef9e..3cc7b4804 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -65,7 +65,7 @@ impl Build { // argument is explicitly set to `None`. crate::builder::build_desktop(&crate_config, false, self.build.skip_assets, None)? } - Platform::Fullstack => { + Platform::Fullstack | Platform::StaticGeneration => { // Fullstack mode must be built with web configs on the desktop // (server) binary as well as the web binary let _config = AssetConfigDropGuard::new(); diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index 14f19bb7a..e3257f34a 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -3,7 +3,7 @@ use manganis_cli_support::AssetManifest; use super::*; use cargo_toml::Dependency::{Detailed, Inherited, Simple}; -use std::{fs::create_dir_all, io::Write, path::PathBuf}; +use std::fs::create_dir_all; /// Run the WASM project on dev-server #[derive(Clone, Debug, Parser)] @@ -18,7 +18,7 @@ impl Serve { let mut crate_config = dioxus_cli_config::CrateConfig::new(bin)?; let serve_cfg = self.serve.clone(); - // change the relase state. + // change the release state. let hot_reload = self.serve.hot_reload || crate_config.dioxus_config.application.hot_reload; crate_config.with_hot_reload(hot_reload); crate_config.with_cross_origin_policy(self.serve.cross_origin_policy); @@ -67,7 +67,9 @@ impl Serve { match platform { Platform::Web => web::startup(crate_config.clone(), &serve_cfg).await?, Platform::Desktop => desktop::startup(crate_config.clone(), &serve_cfg).await?, - Platform::Fullstack => fullstack::startup(crate_config.clone(), &serve_cfg).await?, + Platform::Fullstack | Platform::StaticGeneration => { + fullstack::startup(crate_config.clone(), &serve_cfg).await? + } _ => unreachable!(), } diff --git a/packages/cli/src/server/desktop/mod.rs b/packages/cli/src/server/desktop/mod.rs index 6e3cc4f5f..678772c6c 100644 --- a/packages/cli/src/server/desktop/mod.rs +++ b/packages/cli/src/server/desktop/mod.rs @@ -1,3 +1,4 @@ +use crate::server::SharedFileMap; use crate::{ cfg::ConfigOptsServe, server::{ @@ -16,7 +17,6 @@ use std::{ process::{Child, Command}, sync::{Arc, RwLock}, }; -use tokio::sync::broadcast::{self}; #[cfg(feature = "plugin")] use crate::plugin::PluginManager; @@ -33,7 +33,7 @@ pub(crate) async fn startup_with_platform( ) -> Result<()> { set_ctrl_c(&config); - let hot_reload_state = match config.hot_reload { + let file_map = match config.hot_reload { true => { let FileMapBuildResult { map, errors } = FileMap::::create(config.crate_dir.clone()).unwrap(); @@ -44,16 +44,16 @@ pub(crate) async fn startup_with_platform( let file_map = Arc::new(Mutex::new(map)); - let hot_reload_tx = broadcast::channel(100).0; - - Some(HotReloadState { - messages: hot_reload_tx.clone(), - file_map: file_map.clone(), - }) + Some(file_map.clone()) } false => None, }; + let hot_reload_state = HotReloadState { + receiver: Default::default(), + file_map, + }; + serve::

(config, serve_cfg, hot_reload_state).await?; Ok(()) @@ -73,15 +73,15 @@ fn set_ctrl_c(config: &CrateConfig) { async fn serve( config: CrateConfig, serve: &ConfigOptsServe, - hot_reload_state: Option, + hot_reload_state: HotReloadState, ) -> Result<()> { let hot_reload: tokio::task::JoinHandle> = tokio::spawn({ let hot_reload_state = hot_reload_state.clone(); async move { - match hot_reload_state { - Some(hot_reload_state) => { + match hot_reload_state.file_map.clone() { + Some(file_map) => { // The open interprocess sockets - start_desktop_hot_reload(hot_reload_state).await?; + start_desktop_hot_reload(hot_reload_state, file_map).await?; } None => { std::future::pending::<()>().await; @@ -113,7 +113,10 @@ async fn serve( Ok(()) } -async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()> { +async fn start_desktop_hot_reload( + hot_reload_state: HotReloadState, + file_map: SharedFileMap, +) -> Result<()> { let metadata = cargo_metadata::MetadataCommand::new() .no_deps() .exec() @@ -137,7 +140,6 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<() // listen for connections std::thread::spawn({ - let file_map = hot_reload_state.file_map.clone(); let channels = channels.clone(); let aborted = aborted.clone(); move || { @@ -184,7 +186,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<() } }); - let mut hot_reload_rx = hot_reload_state.messages.subscribe(); + let mut hot_reload_rx = hot_reload_state.receiver.subscribe(); while let Ok(msg) = hot_reload_rx.recv().await { let channels = &mut *channels.lock().unwrap(); diff --git a/packages/cli/src/server/mod.rs b/packages/cli/src/server/mod.rs index 2afb6acc7..cb8c15758 100644 --- a/packages/cli/src/server/mod.rs +++ b/packages/cli/src/server/mod.rs @@ -2,14 +2,12 @@ use crate::{cfg::ConfigOptsServe, BuildResult, Result}; use dioxus_cli_config::CrateConfig; use cargo_metadata::diagnostic::Diagnostic; -use dioxus_core::Template; -use dioxus_hot_reload::HotReloadMsg; +use dioxus_hot_reload::{HotReloadMsg, HotReloadReceiver}; use dioxus_html::HtmlCtx; use dioxus_rsx::hot_reload::*; use fs_extra::dir::CopyOptions; use notify::{RecommendedWatcher, Watcher}; use std::{path::PathBuf, sync::Arc}; -use tokio::sync::broadcast::{self}; mod output; use output::*; @@ -19,25 +17,14 @@ pub mod web; #[derive(Clone)] pub struct HotReloadState { - /// Pending hotreload updates to be sent to all connected clients - pub messages: broadcast::Sender, + /// The receiver for hot reload messages + pub receiver: HotReloadReceiver, /// The file map that tracks the state of the projecta - pub file_map: SharedFileMap, + pub file_map: Option, } -type SharedFileMap = Arc>>; -impl HotReloadState { - pub fn all_templates(&self) -> Vec