Browse Source

Simplify the launch builder (#2967)

* improve documentation for the fullstack server context

* Add a section about axum integration to the crate root docs

* make serve_dioxus_application accept the cfg builder directly

* remove unused server_fn module

* improve fullstack config docs

* improve documentation for the server function macro

* fix axum router extension link

* Fix doc tests

* Fix launch builder

* Simplify the launch builder

* don't re-export launch in the prelude

* refactor fullstack launch

* Fix fullstack launch builder

* Update static generation with the new builder api

* fix some formatting/overly broad launch replacements

* fix custom menu example

* fix fullstack/static generation examples

* Fix static generation launch

* A few small formatting fixes

* Fix a few doc tests

* implement LaunchConfig for serve configs

* fix fullstack launch with separate web and server launch methods

* fix check with all features

* dont expose inner core module

* clippy and check

* fix readme

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
Evan Almloff 8 months ago
parent
commit
20d146d9bd
100 changed files with 448 additions and 872 deletions
  1. 1 0
      example-projects/ecommerce-site/src/components/cart.rs
  2. 1 1
      example-projects/ecommerce-site/src/main.rs
  3. 1 1
      example-projects/file-explorer/src/main.rs
  4. 1 1
      example-projects/fullstack-hackernews/src/main.rs
  5. 1 1
      example-projects/wifi-scanner/src/main.rs
  6. 1 1
      examples/all_events.rs
  7. 1 1
      examples/backgrounded_futures.rs
  8. 1 1
      examples/calculator.rs
  9. 1 1
      examples/calculator_mutable.rs
  10. 1 1
      examples/clock.rs
  11. 1 1
      examples/control_focus.rs
  12. 1 1
      examples/counters.rs
  13. 1 1
      examples/crm.rs
  14. 1 1
      examples/custom_assets.rs
  15. 1 1
      examples/custom_html.rs
  16. 1 1
      examples/custom_menu.rs
  17. 1 1
      examples/disabled.rs
  18. 1 1
      examples/dog_app.rs
  19. 1 1
      examples/dynamic_asset.rs
  20. 1 1
      examples/errors.rs
  21. 1 1
      examples/eval.rs
  22. 1 1
      examples/file_upload.rs
  23. 1 1
      examples/flat_router.rs
  24. 1 1
      examples/form.rs
  25. 1 1
      examples/fullstack-desktop/src/main.rs
  26. 1 1
      examples/fullstack-hello-world/src/main.rs
  27. 1 1
      examples/fullstack-router/src/main.rs
  28. 1 1
      examples/fullstack-streaming/src/main.rs
  29. 1 1
      examples/future.rs
  30. 1 1
      examples/generic_component.rs
  31. 1 1
      examples/global.rs
  32. 1 1
      examples/hash_fragment_state.rs
  33. 1 1
      examples/hello_world.rs
  34. 1 1
      examples/hydration.rs
  35. 1 1
      examples/image_generator_openai.rs
  36. 1 1
      examples/link.rs
  37. 1 1
      examples/login_form.rs
  38. 1 1
      examples/memo_chain.rs
  39. 1 1
      examples/meta.rs
  40. 1 1
      examples/mobile_demo/src/lib.rs
  41. 1 1
      examples/multiwindow.rs
  42. 1 1
      examples/nested_listeners.rs
  43. 1 1
      examples/optional_props.rs
  44. 3 1
      examples/overlay.rs
  45. 1 1
      examples/popup.rs
  46. 1 1
      examples/pwa/src/main.rs
  47. 1 1
      examples/query_segment_search.rs
  48. 1 1
      examples/read_size.rs
  49. 1 1
      examples/readme.rs
  50. 1 1
      examples/reducer.rs
  51. 1 1
      examples/resize.rs
  52. 1 1
      examples/router.rs
  53. 1 1
      examples/router_resource.rs
  54. 1 1
      examples/rsx_usage.rs
  55. 1 1
      examples/scroll_to_top.rs
  56. 1 1
      examples/shortcut.rs
  57. 1 1
      examples/shorthand.rs
  58. 1 1
      examples/signals.rs
  59. 1 1
      examples/simple_list.rs
  60. 2 2
      examples/simple_router.rs
  61. 1 1
      examples/ssg-github-pages/src/main.rs
  62. 1 1
      examples/ssg-router/src/main.rs
  63. 1 1
      examples/ssg-simple/src/main.rs
  64. 1 1
      examples/streams.rs
  65. 1 1
      examples/suspense.rs
  66. 1 1
      examples/svg.rs
  67. 1 1
      examples/tailwind/src/main.rs
  68. 1 1
      examples/title.rs
  69. 1 1
      examples/todomvc.rs
  70. 2 2
      examples/video_stream.rs
  71. 1 1
      examples/weather_app.rs
  72. 1 1
      examples/web_component.rs
  73. 1 1
      examples/window_event.rs
  74. 1 1
      examples/window_focus.rs
  75. 1 1
      examples/window_zoom.rs
  76. 1 1
      examples/xss_safety.rs
  77. 1 1
      packages/autofmt/tests/samples/many_exprs.rsx
  78. 1 1
      packages/autofmt/tests/wrong/multiexpr-many.rsx
  79. 1 1
      packages/autofmt/tests/wrong/multiexpr-many.wrong.rsx
  80. 69 25
      packages/cli/src/builder/mod.rs
  81. 15 2
      packages/cli/src/cli/build.rs
  82. 10 16
      packages/cli/src/serve/mod.rs
  83. 27 8
      packages/cli/src/serve/server.rs
  84. 8 0
      packages/core/src/launch.rs
  85. 6 4
      packages/core/src/lib.rs
  86. 1 1
      packages/desktop/headless_tests/utils.rs
  87. 3 0
      packages/desktop/src/config.rs
  88. 8 3
      packages/desktop/src/launch.rs
  89. 2 2
      packages/desktop/src/readme.md
  90. 2 2
      packages/dioxus-lib/README.md
  91. 2 2
      packages/dioxus/README.md
  92. 127 251
      packages/dioxus/src/launch.rs
  93. 1 5
      packages/dioxus/src/lib.rs
  94. 72 2
      packages/fullstack/README.md
  95. 0 289
      packages/fullstack/src/config.rs
  96. 2 2
      packages/fullstack/src/document/mod.rs
  97. 2 1
      packages/fullstack/src/document/web.rs
  98. 0 171
      packages/fullstack/src/launch.rs
  99. 2 6
      packages/fullstack/src/lib.rs
  100. 6 0
      packages/fullstack/src/serve_config.rs

+ 1 - 0
example-projects/ecommerce-site/src/components/cart.rs

@@ -0,0 +1 @@
+

+ 1 - 1
example-projects/ecommerce-site/src/main.rs

@@ -15,7 +15,7 @@ mod components {
 mod api;
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             head::Link {
                 rel: "stylesheet",

+ 1 - 1
example-projects/file-explorer/src/main.rs

@@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowBuilder};
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true)))
         .launch(app)
 }

+ 1 - 1
example-projects/fullstack-hackernews/src/main.rs

@@ -17,7 +17,7 @@ fn main() {
     #[cfg(feature = "server")]
     tracing_subscriber::fmt::init();
 
-    launch(|| rsx! { Router::<Route> {} });
+    dioxus::launch(|| rsx! { Router::<Route> {} });
 }
 
 #[derive(Clone, Routable)]

+ 1 - 1
example-projects/wifi-scanner/src/main.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use wifiscanner::Wifi;
 
 fn main() {
-    launch(app)
+    dioxus::launch(app)
 }
 
 fn perform_scan() -> Status {

+ 1 - 1
examples/all_events.rs

@@ -9,7 +9,7 @@ use std::{collections::VecDeque, fmt::Debug, rc::Rc};
 const STYLE: &str = asset!("./examples/assets/events.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/backgrounded_futures.rs

@@ -11,7 +11,7 @@ use async_std::task::sleep;
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/calculator.rs

@@ -15,7 +15,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/calculator.css");
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(desktop!({
             use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
             Config::new().with_window(

+ 1 - 1
examples/calculator_mutable.rs

@@ -13,7 +13,7 @@ use dioxus::html::MouseEvent;
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(
             Config::new().with_window(
                 WindowBuilder::new()

+ 1 - 1
examples/clock.rs

@@ -8,7 +8,7 @@ use web_time::Instant;
 const STYLE: &str = asset!("./examples/assets/clock.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/control_focus.rs

@@ -11,7 +11,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/roulette.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/counters.rs

@@ -5,7 +5,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/counter.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/crm.rs

@@ -12,7 +12,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::new()
+    dioxus::LaunchBuilder::new()
         .with_cfg(desktop!({
             use dioxus::desktop::{LogicalSize, WindowBuilder};
             dioxus::desktop::Config::default()

+ 1 - 1
examples/custom_assets.rs

@@ -14,7 +14,7 @@ static ASSET_PATH: &str = "examples/assets/logo.png";
 static ASSET_PATH: &str = asset!("examples/assets/logo.png".format(ImageType::Avif));
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/custom_html.rs

@@ -4,7 +4,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::new()
+    dioxus::LaunchBuilder::new()
         .with_cfg(
             dioxus::desktop::Config::new().with_custom_index(
                 r#"

+ 1 - 1
examples/custom_menu.rs

@@ -28,7 +28,7 @@ fn main() {
     let config = dioxus::desktop::Config::new().with_menu(menu);
 
     // Launch the app with the custom menu
-    LaunchBuilder::new().with_cfg(config).launch(app)
+    dioxus::LaunchBuilder::new().with_cfg(config).launch(app)
 }
 
 fn app() -> Element {

+ 1 - 1
examples/disabled.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/dog_app.rs

@@ -11,7 +11,7 @@ use dioxus::prelude::*;
 use std::collections::HashMap;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/dynamic_asset.rs

@@ -10,7 +10,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/custom_assets.css");
 
 fn main() {
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/errors.rs

@@ -11,7 +11,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(|| rsx! { Router::<Route> {} });
+    dioxus::launch(|| rsx! { Router::<Route> {} });
 }
 
 /// You can use an ErrorBoundary to catch errors in children and display a warning

+ 1 - 1
examples/eval.rs

@@ -7,7 +7,7 @@ use async_std::task::sleep;
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/file_upload.rs

@@ -11,7 +11,7 @@ use dioxus::{html::HasFileData, prelude::dioxus_elements::FileEngine};
 const STYLE: &str = asset!("./examples/assets/file_upload.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 struct UploadedFile {

+ 1 - 1
examples/flat_router.rs

@@ -12,7 +12,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/flat_router.css");
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             head::Link { rel: "stylesheet", href: STYLE }
             Router::<Route> {}

+ 1 - 1
examples/form.rs

@@ -7,7 +7,7 @@ use dioxus::prelude::*;
 use std::collections::HashMap;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/fullstack-desktop/src/main.rs

@@ -5,7 +5,7 @@ fn main() {
     // Set the url of the server where server functions are hosted.
     #[cfg(not(feature = "server"))]
     dioxus::fullstack::prelude::server_fn::client::set_server_url("http://127.0.0.1:8080");
-    launch(app);
+    dioxus::launch(app);
 }
 
 pub fn app() -> Element {

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

@@ -50,5 +50,5 @@ fn main() {
     #[cfg(feature = "server")]
     tracing_subscriber::fmt::init();
 
-    launch(app);
+    dioxus::launch(app);
 }

+ 1 - 1
examples/fullstack-router/src/main.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::fullstack()
+    dioxus::LaunchBuilder::new()
         .with_cfg(server_only!(ServeConfig::builder().incremental(
             IncrementalRendererConfig::default()
                 .invalidate_after(std::time::Duration::from_secs(120)),

+ 1 - 1
examples/fullstack-streaming/src/main.rs

@@ -37,5 +37,5 @@ pub async fn test_stream() -> Result<TextStream, ServerFnError> {
 }
 
 fn main() {
-    launch(app)
+    dioxus::launch(app)
 }

+ 1 - 1
examples/future.rs

@@ -7,7 +7,7 @@ use async_std::task::sleep;
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/generic_component.rs

@@ -7,7 +7,7 @@ use dioxus::prelude::*;
 use std::fmt::Display;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/global.rs

@@ -13,7 +13,7 @@ static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
 static DOUBLED_COUNT: GlobalMemo<i32> = Memo::global(|| COUNT() * 2);
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/hash_fragment_state.rs

@@ -19,7 +19,7 @@ use dioxus::prelude::*;
 use serde::{Deserialize, Serialize};
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             Router::<Route> {}
         }

+ 1 - 1
examples/hello_world.rs

@@ -12,7 +12,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/hydration.rs

@@ -13,7 +13,7 @@ use dioxus::desktop::Config;
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(Config::new().with_prerendered({
             // We build the dom a first time, then pre-render it to HTML
             let pre_rendered_dom = VirtualDom::prebuilt(app);

+ 1 - 1
examples/image_generator_openai.rs

@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
 use serde_json::{json, Error};
 
 fn main() {
-    launch(app)
+    dioxus::launch(app)
 }
 
 fn app() -> Element {

+ 1 - 1
examples/link.rs

@@ -11,7 +11,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/links.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/login_form.rs

@@ -9,7 +9,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/memo_chain.rs

@@ -6,7 +6,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/meta.rs

@@ -4,7 +4,7 @@ use dioxus::prelude::*;
 
 fn main() {
     tracing_subscriber::fmt::init();
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/mobile_demo/src/lib.rs

@@ -50,7 +50,7 @@ pub fn main() -> Result<()> {
 
     // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile
     // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc
-    LaunchBuilder::mobile()
+    dioxus::LaunchBuilder::mobile()
         .with_cfg(
             // Note that we have to disable the viewport goofiness of the browser.
             // Dioxus_mobile should do this for us

+ 1 - 1
examples/multiwindow.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/nested_listeners.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/optional_props.rs

@@ -6,7 +6,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 3 - 1
examples/overlay.rs

@@ -11,7 +11,9 @@ use dioxus::desktop::{
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop().with_cfg(make_config()).launch(app);
+    dioxus::LaunchBuilder::desktop()
+        .with_cfg(make_config())
+        .launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/popup.rs

@@ -5,7 +5,7 @@ use dioxus::prelude::*;
 use std::rc::Rc;
 
 fn main() {
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/pwa/src/main.rs

@@ -5,7 +5,7 @@ fn main() {
     wasm_logger::init(wasm_logger::Config::default());
     console_error_panic_hook::set_once();
 
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/query_segment_search.rs

@@ -14,7 +14,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             Router::<Route> {}
         }

+ 1 - 1
examples/read_size.rs

@@ -9,7 +9,7 @@ use std::rc::Rc;
 use dioxus::{html::geometry::euclid::Rect, prelude::*};
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/readme.rs

@@ -8,7 +8,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/reducer.rs

@@ -10,7 +10,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/radio.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/resize.rs

@@ -8,7 +8,7 @@ use dioxus::prelude::*;
 use dioxus_elements::geometry::euclid::Size2D;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/router.rs

@@ -11,7 +11,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/router.css");
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             head::Link { rel: "stylesheet", href: STYLE }
             Router::<Route> {}

+ 1 - 1
examples/router_resource.rs

@@ -15,7 +15,7 @@ enum Route {
 }
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 #[component]

+ 1 - 1
examples/rsx_usage.rs

@@ -39,7 +39,7 @@
 //! - Allow top-level fragments
 
 fn main() {
-    launch(app)
+    dioxus::launch(app)
 }
 
 use core::{fmt, str::FromStr};

+ 1 - 1
examples/scroll_to_top.rs

@@ -8,7 +8,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/shortcut.rs

@@ -9,7 +9,7 @@ use dioxus::desktop::use_global_shortcut;
 use dioxus::prelude::*;
 
 fn main() {
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/shorthand.rs

@@ -3,7 +3,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/signals.rs

@@ -10,7 +10,7 @@ use async_std::task::sleep;
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/simple_list.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 2 - 2
examples/simple_router.rs

@@ -3,9 +3,9 @@
 use dioxus::prelude::*;
 
 fn main() {
-    // Launch the router, using our `Route` component as the generic type
+    // launch the router, using our `Route` component as the generic type
     // This will automatically boot the app to "/" unless otherwise specified
-    launch(|| rsx! { Router::<Route> {} });
+    dioxus::launch(|| rsx! { Router::<Route> {} });
 }
 
 /// By default, the Routable derive will use the name of the variant as the route

+ 1 - 1
examples/ssg-github-pages/src/main.rs

@@ -6,7 +6,7 @@ use dioxus::prelude::*;
 
 // Generate all routes and output them to the static path
 fn main() {
-    LaunchBuilder::new()
+    dioxus::LaunchBuilder::new()
         .with_cfg(dioxus::static_site_generation::Config::new().github_pages())
         .launch(|| {
             rsx! {

+ 1 - 1
examples/ssg-router/src/main.rs

@@ -5,7 +5,7 @@ use dioxus::prelude::*;
 
 // Generate all routes and output them to the static path
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             Router::<Route> {}
         }

+ 1 - 1
examples/ssg-simple/src/main.rs

@@ -3,7 +3,7 @@ use dioxus::prelude::*;
 
 // Generate all routes and output them to the static path
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/streams.rs

@@ -5,7 +5,7 @@ use dioxus::prelude::*;
 use futures_util::{future, stream, Stream, StreamExt};
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/suspense.rs

@@ -15,7 +15,7 @@ use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::new()
+    dioxus::LaunchBuilder::new()
         .with_cfg(desktop! {
             Config::new().with_window(
                 WindowBuilder::new()

+ 1 - 1
examples/svg.rs

@@ -10,7 +10,7 @@ use dioxus::prelude::*;
 use rand::{thread_rng, Rng};
 
 fn main() {
-    launch(|| {
+    dioxus::launch(|| {
         rsx! {
             div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%",
                 h1 { "Click die to generate a new value" }

+ 1 - 1
examples/tailwind/src/main.rs

@@ -5,7 +5,7 @@ use dioxus::prelude::*;
 const _STYLE: &str = asset!("public/tailwind.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 pub fn app() -> Element {

+ 1 - 1
examples/title.rs

@@ -4,7 +4,7 @@ use dioxus::prelude::*;
 
 fn main() {
     tracing_subscriber::fmt::init();
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/todomvc.rs

@@ -6,7 +6,7 @@ use std::collections::HashMap;
 const STYLE: &str = asset!("./examples/assets/todomvc.css");
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 #[derive(PartialEq, Eq, Clone, Copy)]

+ 2 - 2
examples/video_stream.rs

@@ -16,13 +16,13 @@ fn main() {
     // For the sake of this example, we will download the video file if it doesn't exist
     ensure_video_is_loaded();
 
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {
     // Any request to /videos will be handled by this handler
     use_asset_handler("videos", move |request, responder| {
-        // Using dioxus::spawn works, but is slower than a dedicated thread
+        // Using spawn works, but is slower than a dedicated thread
         tokio::task::spawn(async move {
             let video_file = PathBuf::from(VIDEO_PATH);
             let mut file = tokio::fs::File::open(&video_file).await.unwrap();

+ 1 - 1
examples/weather_app.rs

@@ -4,7 +4,7 @@ use dioxus::prelude::*;
 use serde::{Deserialize, Serialize};
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/web_component.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/window_event.rs

@@ -13,7 +13,7 @@ use dioxus::desktop::{window, Config, WindowBuilder};
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(
             Config::new().with_window(
                 WindowBuilder::new()

+ 1 - 1
examples/window_focus.rs

@@ -12,7 +12,7 @@ use dioxus::desktop::{Config, WindowCloseBehaviour};
 use dioxus::prelude::*;
 
 fn main() {
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow))
         .launch(app)
 }

+ 1 - 1
examples/window_zoom.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch_desktop(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/xss_safety.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
packages/autofmt/tests/samples/many_exprs.rsx

@@ -8,7 +8,7 @@ use std::{
 use tokio::time::sleep;
 
 fn main() {
-    LaunchBuilder::desktop().launch(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 struct WindowPreferences {

+ 1 - 1
packages/autofmt/tests/wrong/multiexpr-many.rsx

@@ -8,7 +8,7 @@ use std::{
 use tokio::time::sleep;
 
 fn main() {
-    LaunchBuilder::desktop().launch(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 struct WindowPreferences {

+ 1 - 1
packages/autofmt/tests/wrong/multiexpr-many.wrong.rsx

@@ -8,7 +8,7 @@ use std::{
 use tokio::time::sleep;
 
 fn main() {
-    LaunchBuilder::desktop().launch(app);
+    dioxus::LaunchBuilder::desktop().launch(app);
 }
 
 struct WindowPreferences {

+ 69 - 25
packages/cli/src/builder/mod.rs

@@ -150,6 +150,49 @@ impl BuildRequest {
     }
 }
 
+#[derive(Debug, Clone, Default)]
+pub(crate) struct OpenArguments {
+    fullstack_address: Option<SocketAddr>,
+    devserver_addr: Option<SocketAddr>,
+    always_on_top: Option<bool>,
+    workspace: PathBuf,
+    asset_root: PathBuf,
+    app_title: String,
+    out_dir: PathBuf,
+    serve: bool,
+}
+
+impl OpenArguments {
+    #[allow(clippy::too_many_arguments)]
+    pub(crate) fn new(
+        serve: &ServeArguments,
+        fullstack_address: Option<SocketAddr>,
+        dioxus_crate: &DioxusCrate,
+    ) -> Self {
+        Self {
+            devserver_addr: Some(serve.address.address()),
+            always_on_top: Some(serve.always_on_top.unwrap_or(true)),
+            serve: true,
+            fullstack_address,
+            workspace: dioxus_crate.workspace_dir().to_path_buf(),
+            asset_root: dioxus_crate.asset_dir().to_path_buf(),
+            app_title: dioxus_crate.dioxus_config.application.name.clone(),
+            out_dir: dioxus_crate.out_dir().to_path_buf(),
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    pub(crate) fn new_for_static_generation_build(dioxus_crate: &DioxusCrate) -> Self {
+        Self {
+            workspace: dioxus_crate.workspace_dir().to_path_buf(),
+            asset_root: dioxus_crate.asset_dir().to_path_buf(),
+            app_title: dioxus_crate.dioxus_config.application.name.clone(),
+            out_dir: dioxus_crate.out_dir().to_path_buf(),
+            ..Default::default()
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub(crate) struct BuildResult {
     pub executable: PathBuf,
@@ -158,20 +201,12 @@ pub(crate) struct BuildResult {
 
 impl BuildResult {
     /// Open the executable if this is a native build
-    #[allow(clippy::too_many_arguments)]
-    pub fn open(
-        &self,
-        serve: &ServeArguments,
-        fullstack_address: Option<SocketAddr>,
-        workspace: &std::path::Path,
-        asset_root: &std::path::Path,
-        devserver_addr: SocketAddr,
-        app_title: String,
-        out_dir: PathBuf,
-    ) -> std::io::Result<Option<Child>> {
+    pub fn open(&self, arguments: OpenArguments) -> std::io::Result<Option<Child>> {
         match self.target_platform {
             TargetPlatform::Web => {
-                tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", serve.address.address());
+                if let Some(address) = arguments.fullstack_address {
+                    tracing::info!(dx_src = ?TraceSrc::Dev, "Serving web app on http://{} 🎉", address);
+                }
                 return Ok(None);
             }
             TargetPlatform::Desktop => {
@@ -181,7 +216,7 @@ impl BuildResult {
                 // shut this up for now - the web app will take priority in logging
             }
             TargetPlatform::Liveview => {
-                if let Some(fullstack_address) = fullstack_address {
+                if let Some(fullstack_address) = arguments.fullstack_address {
                     tracing::info!(
                         dx_src = ?TraceSrc::Dev,
                         "Launching liveview server on http://{:?} 🎉",
@@ -191,7 +226,9 @@ impl BuildResult {
             }
         }
 
-        tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually.");
+        if arguments.serve {
+            tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually.");
+        }
 
         let executable = self.executable.canonicalize()?;
         let mut cmd = Command::new(executable);
@@ -199,29 +236,36 @@ impl BuildResult {
         // Set the env vars that the clients will expect
         // These need to be stable within a release version (ie 0.6.0)
         cmd.env(dioxus_cli_config::CLI_ENABLED_ENV, "true");
-        if let Some(addr) = fullstack_address {
+        if let Some(addr) = arguments.fullstack_address {
             cmd.env(dioxus_cli_config::SERVER_IP_ENV, addr.ip().to_string());
             cmd.env(dioxus_cli_config::SERVER_PORT_ENV, addr.port().to_string());
         }
-        cmd.env(
-            dioxus_cli_config::ALWAYS_ON_TOP_ENV,
-            serve.always_on_top.unwrap_or(true).to_string(),
-        );
+        if let Some(always_on_top) = arguments.always_on_top {
+            cmd.env(
+                dioxus_cli_config::ALWAYS_ON_TOP_ENV,
+                always_on_top.to_string(),
+            );
+        }
         cmd.env(
             dioxus_cli_config::ASSET_ROOT_ENV,
-            asset_root.display().to_string(),
+            arguments.asset_root.display().to_string(),
         );
+        if let Some(devserver_addr) = arguments.devserver_addr {
+            cmd.env(
+                dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
+                devserver_addr.to_string(),
+            );
+        }
+        cmd.env(dioxus_cli_config::APP_TITLE_ENV, arguments.app_title);
         cmd.env(
-            dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
-            devserver_addr.to_string(),
+            dioxus_cli_config::OUT_DIR,
+            arguments.out_dir.display().to_string(),
         );
-        cmd.env(dioxus_cli_config::APP_TITLE_ENV, app_title);
-        cmd.env(dioxus_cli_config::OUT_DIR, out_dir.display().to_string());
 
         cmd.stderr(Stdio::piped())
             .stdout(Stdio::piped())
             .kill_on_drop(true)
-            .current_dir(workspace);
+            .current_dir(arguments.workspace);
 
         Ok(Some(cmd.spawn()?))
     }

+ 15 - 2
packages/cli/src/cli/build.rs

@@ -1,6 +1,6 @@
 use std::str::FromStr;
 
-use crate::config::Platform;
+use crate::{builder::OpenArguments, config::Platform};
 use anyhow::Context;
 
 use crate::{
@@ -115,7 +115,20 @@ impl Build {
     pub async fn build(&mut self, dioxus_crate: &mut DioxusCrate) -> Result<()> {
         self.resolve(dioxus_crate)?;
         let build_requests = BuildRequest::create(false, dioxus_crate, self.clone())?;
-        BuildRequest::build_all_parallel(build_requests).await?;
+        let builds = BuildRequest::build_all_parallel(build_requests).await?;
+
+        // If this is a static generation build, building involves running the server to generate static files
+        if self.platform.unwrap() == Platform::StaticGeneration {
+            println!("Building static site...");
+            for build in builds {
+                if let Some(mut result) =
+                    build.open(OpenArguments::new_for_static_generation_build(dioxus_crate))?
+                {
+                    result.wait().await?;
+                }
+            }
+            println!("Static site built!");
+        }
         Ok(())
     }
 

+ 10 - 16
packages/cli/src/serve/mod.rs

@@ -1,6 +1,7 @@
 use std::future::{poll_fn, Future, IntoFuture};
 use std::task::Poll;
 
+use crate::builder::OpenArguments;
 use crate::cli::serve::Serve;
 use crate::dioxus_crate::DioxusCrate;
 use crate::tracer::CLILogControl;
@@ -156,23 +157,14 @@ pub async fn serve_all(
                             builder.children.clear();
                         }
 
-                        let asset_dir = dioxus_crate
-                            .dioxus_config
-                            .application
-                            .asset_dir
-                            .canonicalize()
-                            .unwrap_or(std::path::PathBuf::from("./assets"));
-
                         // If we have a build result, open it
                         for build_result in results.iter() {
                             let child = build_result.open(
+                                OpenArguments::new(
                                 &serve.server_arguments,
                                 server.fullstack_address(),
-                                &dioxus_crate.workspace_dir(),
-                                &asset_dir,
-                                server.ip,
-                                dioxus_crate.dioxus_config.application.name.clone(),
-                                dioxus_crate.out_dir()
+                                &dioxus_crate
+                            )
                             );
                             match child {
                                 Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)),
@@ -192,12 +184,12 @@ pub async fn serve_all(
                         server.send_reload_command().await;
                     },
 
-                    // If the process exited *cleanly*, we can exit
+                    // If the desktop process exited *cleanly*, we can exit
                     Ok(BuilderUpdate::ProcessExited { status, target_platform }) => {
                         // Then remove the child process
                         builder.children.retain(|(platform, _)| *platform != target_platform);
-                        match status {
-                            Ok(status) => {
+                        match (target_platform, status) {
+                            (TargetPlatform::Desktop, Ok(status)) => {
                                 if status.success() {
                                     break;
                                 }
@@ -205,7 +197,9 @@ pub async fn serve_all(
                                     tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with status: {status}");
                                 }
                             },
-                            Err(e) => {
+                            // Ignore the static generation platform exiting
+                            (_ , Ok(_)) => {},
+                            (_, Err(e)) => {
                                 tracing::error!(dx_src = ?TraceSrc::Dev, "Application exited with error: {e}");
                             }
                         }

+ 27 - 8
packages/cli/src/serve/server.rs

@@ -114,7 +114,7 @@ impl Server {
         // If we're serving a fullstack app, we need to find a port to proxy to
         let fullstack_port = if matches!(
             serve.build_arguments.platform(),
-            Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration
+            Platform::Liveview | Platform::Fullstack
         ) {
             get_available_port(addr.ip())
         } else {
@@ -368,7 +368,7 @@ fn setup_router(
 
     // server the dir if it's web, otherwise let the fullstack server itself handle it
     match platform {
-        Platform::Web => {
+        Platform::Web | Platform::StaticGeneration => {
             // Route file service to output the .wasm and assets if this is a web build
             let base_path = format!(
                 "/{}",
@@ -382,9 +382,9 @@ fn setup_router(
                     .trim_matches('/')
             );
 
-            router = router.nest_service(&base_path, build_serve_dir(serve, config));
+            router = router.nest_service(&base_path, build_serve_dir(serve, config, platform));
         }
-        Platform::Liveview | Platform::Fullstack | Platform::StaticGeneration => {
+        Platform::Liveview | Platform::Fullstack => {
             // For fullstack and static generation, forward all requests to the server
             let address = fullstack_address.unwrap();
 
@@ -449,7 +449,11 @@ fn setup_router(
     Ok(router)
 }
 
-fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRouter {
+fn build_serve_dir(
+    serve: &Serve,
+    cfg: &DioxusCrate,
+    platform: Platform,
+) -> axum::routing::MethodRouter {
     static CORS_UNSAFE: (HeaderValue, HeaderValue) = (
         HeaderValue::from_static("unsafe-none"),
         HeaderValue::from_static("unsafe-none"),
@@ -465,7 +469,11 @@ fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRou
         false => CORS_UNSAFE.clone(),
     };
 
-    let out_dir = cfg.out_dir();
+    let out_dir = match platform {
+        // Static generation only serves files from the public directory
+        Platform::StaticGeneration => cfg.out_dir().join("public"),
+        _ => cfg.out_dir(),
+    };
     let index_on_404 = cfg.dioxus_config.web.watcher.index_on_404;
 
     get_service(
@@ -479,7 +487,7 @@ fn build_serve_dir(serve: &Serve, cfg: &DioxusCrate) -> axum::routing::MethodRou
                 let out_dir = out_dir.clone();
                 move |response| async move { Ok(no_cache(index_on_404, &out_dir, response)) }
             })
-            .service(ServeDir::new(out_dir)),
+            .service(ServeDir::new(&out_dir)),
     )
     .handle_error(|error: Infallible| async move {
         (
@@ -500,7 +508,18 @@ fn no_cache(
     // If there's a 404 and we're supposed to index on 404, upgrade that failed request to the index.html
     // We might want to isnert a header here saying we *did* that but oh well
     if response.status() == StatusCode::NOT_FOUND && index_on_404 {
-        let body = Body::from(std::fs::read_to_string(out_dir.join("index.html")).unwrap());
+        // First try to find a 404.html or 404/index.html file
+        let out_dir_404_html = out_dir.join("404.html");
+        let out_dir_404_index_html = out_dir.join("404").join("index.html");
+        let path = if out_dir_404_html.exists() {
+            out_dir_404_html
+        } else if out_dir_404_index_html.exists() {
+            out_dir_404_index_html
+        } else {
+            // If we can't find a 404.html or 404/index.html, just use the index.html
+            out_dir.join("index.html")
+        };
+        let body = Body::from(std::fs::read_to_string(path).unwrap());
 
         response = Response::builder()
             .status(StatusCode::OK)

+ 8 - 0
packages/core/src/launch.rs

@@ -0,0 +1,8 @@
+//! This module contains utilities renderers use to integrate with the launch function.
+
+/// A marker trait for platform configs. We use this marker to
+/// make sure that the user doesn't accidentally pass in a config
+/// builder instead of the config
+pub trait LaunchConfig: 'static {}
+
+impl LaunchConfig for () {}

+ 6 - 4
packages/core/src/lib.rs

@@ -12,6 +12,7 @@ mod events;
 mod fragment;
 mod generational_box;
 mod global_context;
+mod launch;
 mod mutations;
 mod nodes;
 mod properties;
@@ -51,6 +52,7 @@ pub(crate) mod innerlude {
     pub use crate::fragment::*;
     pub use crate::generational_box::*;
     pub use crate::global_context::*;
+    pub use crate::launch::*;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::properties::*;
@@ -75,10 +77,10 @@ pub(crate) mod innerlude {
 pub use crate::innerlude::{
     fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
     AnyValue, Attribute, AttributeValue, CapturedError, Component, ComponentFunction, DynamicNode,
-    Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, MarkerWrapper, Mutation,
-    Mutations, NoOpMutations, Ok, Properties, Result, Runtime, ScopeId, ScopeState, SpawnIfAsync,
-    Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner, VPlaceholder,
-    VText, VirtualDom, WriteMutations,
+    Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, LaunchConfig, MarkerWrapper,
+    Mutation, Mutations, NoOpMutations, Ok, Properties, Result, Runtime, ScopeId, ScopeState,
+    SpawnIfAsync, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner,
+    VPlaceholder, VText, VirtualDom, WriteMutations,
 };
 
 /// The purpose of this module is to alleviate imports of many common types

+ 1 - 1
packages/desktop/headless_tests/utils.rs

@@ -17,7 +17,7 @@ pub fn check_app_exits(app: fn() -> Element) {
         }
     });
 
-    LaunchBuilder::desktop()
+    dioxus::LaunchBuilder::desktop()
         .with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(false)))
         .launch(app);
 

+ 3 - 0
packages/desktop/src/config.rs

@@ -1,3 +1,4 @@
+use dioxus_core::LaunchConfig;
 use std::borrow::Cow;
 use std::path::PathBuf;
 use tao::window::{Icon, WindowBuilder};
@@ -51,6 +52,8 @@ pub struct Config {
     pub(crate) last_window_close_behavior: WindowCloseBehaviour,
 }
 
+impl LaunchConfig for Config {}
+
 pub(crate) type WryProtocol = (
     String,
     Box<dyn Fn(HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,

+ 8 - 3
packages/desktop/src/launch.rs

@@ -1,4 +1,4 @@
-pub use crate::Config;
+use crate::Config;
 use crate::{
     app::App,
     ipc::{IpcMethod, UserWindowEvent},
@@ -82,8 +82,8 @@ pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> !
 /// Launches the WebView and runs the event loop, with configuration and root props.
 pub fn launch(
     root: fn() -> Element,
-    contexts: Vec<Box<dyn Fn() -> Box<dyn Any>>>,
-    platform_config: Config,
+    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
+    platform_config: Vec<Box<dyn Any>>,
 ) -> ! {
     let mut virtual_dom = VirtualDom::new(root);
 
@@ -91,5 +91,10 @@ pub fn launch(
         virtual_dom.insert_any_root_context(context());
     }
 
+    let platform_config = *platform_config
+        .into_iter()
+        .find_map(|cfg| cfg.downcast::<Config>().ok())
+        .unwrap_or_default();
+
     launch_virtual_dom(virtual_dom, platform_config)
 }

+ 2 - 2
packages/desktop/src/readme.md

@@ -35,11 +35,11 @@ fn main() {
 }
 
 fn app() -> Element {
-    rsx!{
+    rsx! {
         div {
             "hello world!"
         }
-    })
+    }
 }
 ```
 

+ 2 - 2
packages/dioxus-lib/README.md

@@ -44,7 +44,7 @@ To launch an app, we use the `launch` method and use features in `Cargo.toml` to
 use dioxus::prelude::*;
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 // The #[component] attribute streamlines component creation.
@@ -239,7 +239,7 @@ Using components, templates, and hooks, we can build a simple app.
 use dioxus::prelude::*;
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 #[component]

+ 2 - 2
packages/dioxus/README.md

@@ -42,7 +42,7 @@ To launch an app, we use the `launch` method and use features in `Cargo.toml` to
 use dioxus::prelude::*;
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 // The #[component] attribute streamlines component creation.
@@ -184,7 +184,7 @@ Using components, rsx, and hooks, we can build a simple app.
 use dioxus::prelude::*;
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 #[component]

+ 127 - 251
packages/dioxus/src/launch.rs

@@ -2,20 +2,22 @@
 #![allow(clippy::new_without_default)]
 #![allow(unused)]
 use dioxus_config_macro::*;
+use dioxus_core::LaunchConfig;
 use std::any::Any;
 
 use crate::prelude::*;
 
 /// A builder for a fullstack app.
 #[must_use]
-pub struct LaunchBuilder<Cfg: 'static = (), ContextFn: ?Sized = ValidContext> {
-    launch_fn: LaunchFn<Cfg, ContextFn>,
-    contexts: Vec<Box<ContextFn>>,
-
-    platform_config: Option<Cfg>,
+pub struct LaunchBuilder {
+    launch_fn: LaunchFn,
+    contexts: Vec<ContextFn>,
+    configs: Vec<Box<dyn Any>>,
 }
 
-pub type LaunchFn<Cfg, Context> = fn(fn() -> Element, Vec<Box<Context>>, Cfg);
+pub type LaunchFn = fn(fn() -> Element, Vec<ContextFn>, Vec<Box<dyn Any>>);
+/// A context function is a Send and Sync closure that returns a boxed trait object
+pub type ContextFn = Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>;
 
 #[cfg(any(
     feature = "fullstack",
@@ -54,69 +56,74 @@ impl LaunchBuilder {
             note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```"
         )
     )]
-    pub fn new() -> LaunchBuilder<current_platform::Config, ValidContext> {
+    pub fn new() -> LaunchBuilder {
         LaunchBuilder {
+            // We can't use the `current_platform::launch` function directly because it may return ! or ()
             launch_fn: |root, contexts, cfg| current_platform::launch(root, contexts, cfg),
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
     /// Launch your web application.
     #[cfg(feature = "web")]
     #[cfg_attr(docsrs, doc(cfg(feature = "web")))]
-    pub fn web() -> LaunchBuilder<dioxus_web::Config, UnsendContext> {
+    pub fn web() -> LaunchBuilder {
         LaunchBuilder {
-            launch_fn: dioxus_web::launch::launch,
+            launch_fn: web_launch,
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
     /// Launch your desktop application.
     #[cfg(feature = "desktop")]
     #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
-    pub fn desktop() -> LaunchBuilder<dioxus_desktop::Config, UnsendContext> {
+    pub fn desktop() -> LaunchBuilder {
         LaunchBuilder {
             launch_fn: |root, contexts, cfg| dioxus_desktop::launch::launch(root, contexts, cfg),
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
-    /// Launch your fullstack application.
-    #[cfg(feature = "fullstack")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))]
-    pub fn fullstack() -> LaunchBuilder<dioxus_fullstack::Config, SendContext> {
+    /// Launch your fullstack axum server.
+    #[cfg(all(feature = "fullstack", feature = "server"))]
+    #[cfg_attr(docsrs, doc(cfg(all(feature = "fullstack", feature = "server"))))]
+    pub fn server() -> LaunchBuilder {
         LaunchBuilder {
-            launch_fn: |root, contexts, cfg| dioxus_fullstack::launch::launch(root, contexts, cfg),
+            launch_fn: |root, contexts, cfg| {
+                dioxus_fullstack::server::launch::launch(root, contexts, cfg)
+            },
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
     /// Launch your static site generation application.
-    #[cfg(feature = "static-generation")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "static-generation")))]
-    pub fn static_generation() -> LaunchBuilder<dioxus_static_site_generation::Config, SendContext>
-    {
+    #[cfg(all(feature = "static-generation", feature = "server"))]
+    #[cfg_attr(
+        docsrs,
+        doc(cfg(all(feature = "static-generation", feature = "server")))
+    )]
+    pub fn static_generation() -> LaunchBuilder {
         LaunchBuilder {
             launch_fn: |root, contexts, cfg| {
                 dioxus_static_site_generation::launch::launch(root, contexts, cfg)
             },
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
     /// Launch your fullstack application.
     #[cfg(feature = "mobile")]
     #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
-    pub fn mobile() -> LaunchBuilder<dioxus_mobile::Config, UnsendContext> {
+    pub fn mobile() -> LaunchBuilder {
         LaunchBuilder {
             launch_fn: |root, contexts, cfg| dioxus_mobile::launch::launch(root, contexts, cfg),
             contexts: Vec::new(),
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 
@@ -132,7 +139,7 @@ impl LaunchBuilder {
     /// #[derive(Default)]
     /// struct Config;
     ///
-    /// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Any>>, cfg: Config) {
+    /// fn my_custom_launcher(root: fn() -> Element, contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>, cfg: Vec<Box<dyn Any>>) {
     ///     println!("launching with root: {:?}", root());
     ///     loop {
     ///         println!("running...");
@@ -145,42 +152,24 @@ impl LaunchBuilder {
     ///     }
     /// }
     ///
-    /// LaunchBuilder::custom(my_custom_launcher).launch(app);
+    /// dioxus::LaunchBuilder::custom(my_custom_launcher).launch(app);
     /// ```
-    pub fn custom<Config, Context: ?Sized>(
-        launch_fn: LaunchFn<Config, Context>,
-    ) -> LaunchBuilder<Config, Context> {
+    pub fn custom(launch_fn: LaunchFn) -> LaunchBuilder {
         LaunchBuilder {
             launch_fn,
             contexts: vec![],
-            platform_config: None,
+            configs: Vec::new(),
         }
     }
 }
 
-// Fullstack platform builder
-impl<Cfg> LaunchBuilder<Cfg, UnsendContext> {
-    /// Inject state into the root component's context that is created on the thread that the app is launched on.
-    pub fn with_context_provider(mut self, state: impl Fn() -> Box<dyn Any> + 'static) -> Self {
-        self.contexts.push(Box::new(state) as Box<UnsendContext>);
-        self
-    }
-
-    /// Inject state into the root component's context.
-    pub fn with_context(mut self, state: impl Any + Clone + 'static) -> Self {
-        self.contexts
-            .push(Box::new(move || Box::new(state.clone())));
-        self
-    }
-}
-
-impl<Cfg> LaunchBuilder<Cfg, SendContext> {
+impl LaunchBuilder {
     /// Inject state into the root component's context that is created on the thread that the app is launched on.
     pub fn with_context_provider(
         mut self,
-        state: impl Fn() -> Box<dyn Any + Send + Sync> + Send + Sync + 'static,
+        state: impl Fn() -> Box<dyn Any> + Send + Sync + 'static,
     ) -> Self {
-        self.contexts.push(Box::new(state) as Box<SendContext>);
+        self.contexts.push(Box::new(state));
         self
     }
 
@@ -192,38 +181,10 @@ impl<Cfg> LaunchBuilder<Cfg, SendContext> {
     }
 }
 
-/// A trait for converting a type into a platform-specific config:
-/// - A unit value will be converted into `None`
-/// - Any config will be converted into `Some(config)`
-/// - If the config is for another platform, it will be converted into `None`
-pub trait TryIntoConfig<Config = Self> {
-    fn into_config(self, config: &mut Option<Config>);
-}
-
-// A config can always be converted into itself
-impl<Cfg> TryIntoConfig<Cfg> for Cfg {
-    fn into_config(self, config: &mut Option<Cfg>) {
-        *config = Some(self);
-    }
-}
-
-// The unit type can be converted into the current platform config.
-// This makes it possible to use the `desktop!`, `web!`, etc macros with the launch API.
-#[cfg(any(
-    feature = "liveview",
-    feature = "desktop",
-    feature = "mobile",
-    feature = "web",
-    feature = "fullstack"
-))]
-impl TryIntoConfig<current_platform::Config> for () {
-    fn into_config(self, config: &mut Option<current_platform::Config>) {}
-}
-
-impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
+impl LaunchBuilder {
     /// Provide a platform-specific config to the builder.
-    pub fn with_cfg(mut self, config: impl TryIntoConfig<Cfg>) -> Self {
-        config.into_config(&mut self.platform_config);
+    pub fn with_cfg(mut self, config: impl LaunchConfig) -> Self {
+        self.configs.push(Box::new(config));
         self
     }
 
@@ -231,7 +192,7 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
     #[cfg(any(feature = "static-generation", feature = "web"))]
     /// Launch your application.
     pub fn launch(self, app: fn() -> Element) {
-        let cfg = self.platform_config.unwrap_or_default();
+        let cfg = self.configs;
 
         (self.launch_fn)(app, self.contexts, cfg)
     }
@@ -239,7 +200,7 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
     #[cfg(not(any(feature = "static-generation", feature = "web")))]
     /// Launch your application.
     pub fn launch(self, app: fn() -> Element) -> ! {
-        let cfg = self.platform_config.unwrap_or_default();
+        let cfg = self.configs;
 
         (self.launch_fn)(app, self.contexts, cfg);
         unreachable!("Launching an application will never exit")
@@ -256,147 +217,67 @@ impl<Cfg: Default + 'static, ContextFn: ?Sized> LaunchBuilder<Cfg, ContextFn> {
 /// - `web`
 /// - `liveview`
 mod current_platform {
-    macro_rules! if_else_cfg {
-        (if $attr:meta { $($then:item)* } else { $($else:item)* }) => {
-            $(
-                #[cfg($attr)]
-                $then
-            )*
-            $(
-                #[cfg(not($attr))]
-                $else
-            )*
-        };
-    }
-    use crate::prelude::TryIntoConfig;
-
-    #[cfg(feature = "fullstack")]
-    pub use dioxus_fullstack::launch::*;
+    #[cfg(all(feature = "fullstack", feature = "server"))]
+    pub use dioxus_fullstack::server::launch::*;
 
-    #[cfg(all(feature = "fullstack", feature = "axum"))]
-    impl TryIntoConfig<crate::launch::current_platform::Config>
-        for ::dioxus_fullstack::prelude::ServeConfigBuilder
-    {
-        fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
-            match config {
-                Some(config) => config.set_server_config(self),
-                None => {
-                    *config = Some(
-                        crate::launch::current_platform::Config::new().with_server_config(self),
-                    )
-                }
-            }
-        }
-    }
-
-    #[cfg(any(feature = "desktop", feature = "mobile"))]
-    if_else_cfg! {
-        if not(feature = "fullstack") {
-            #[cfg(feature = "desktop")]
-            pub use dioxus_desktop::launch::*;
-            #[cfg(not(feature = "desktop"))]
-            pub use dioxus_mobile::launch::*;
-        } else {
-            if_else_cfg! {
-                if feature = "desktop" {
-                    impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_desktop::Config {
-                        fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
-                            match config {
-                                Some(config) => config.set_desktop_config(self),
-                                None => *config = Some(crate::launch::current_platform::Config::new().with_desktop_config(self)),
-                            }
-                        }
-                    }
-                } else {
-                    impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_mobile::Config {
-                        fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
-                            match config {
-                                Some(config) => config.set_mobile_cfg(self),
-                                None => *config = Some(crate::launch::current_platform::Config::new().with_mobile_cfg(self)),
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    #[cfg(feature = "static-generation")]
-    if_else_cfg! {
-        if all(not(feature = "fullstack"), not(feature = "desktop"), not(feature = "mobile")) {
-            pub use dioxus_static_site_generation::launch::*;
-        } else {
-            impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_static_site_generation::Config {
-                fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
-            }
-        }
-    }
-
-    #[cfg(feature = "web")]
-    if_else_cfg! {
-        if not(any(feature = "desktop", feature = "mobile", feature = "fullstack", feature = "static-generation")) {
-            pub use dioxus_web::launch::*;
-        } else {
-            if_else_cfg! {
-                if feature = "fullstack" {
-                    impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_web::Config {
-                        fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {
-                            match config {
-                                Some(config) => config.set_web_config(self),
-                                None => *config = Some(crate::launch::current_platform::Config::new().with_web_config(self)),
-                            }
-                        }
-                    }
-                } else {
-                    impl TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_web::Config {
-                        fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
-                    }
-                }
-            }
-        }
-    }
+    #[cfg(all(
+        feature = "desktop",
+        not(all(feature = "fullstack", feature = "server"))
+    ))]
+    pub use dioxus_desktop::launch::*;
 
-    #[cfg(feature = "liveview")]
-    if_else_cfg! {
-        if
-            not(any(
-                feature = "web",
-                feature = "desktop",
-                feature = "mobile",
-                feature = "fullstack",
-                feature = "static-generation"
-            ))
-        {
-            pub use dioxus_liveview::launch::*;
-        } else {
-            impl<R: ::dioxus_liveview::LiveviewRouter> TryIntoConfig<crate::launch::current_platform::Config> for ::dioxus_liveview::Config<R> {
-                fn into_config(self, config: &mut Option<crate::launch::current_platform::Config>) {}
-            }
-        }
+    #[cfg(all(
+        feature = "mobile",
+        not(feature = "desktop"),
+        not(all(feature = "fullstack", feature = "server"))
+    ))]
+    pub use dioxus_mobile::launch::*;
+
+    #[cfg(all(
+        all(feature = "static-generation", feature = "server"),
+        not(all(feature = "fullstack", feature = "server")),
+        not(feature = "desktop"),
+        not(feature = "mobile")
+    ))]
+    pub use dioxus_static_site_generation::launch::*;
+
+    #[cfg(all(
+        feature = "web",
+        not(all(feature = "fullstack", feature = "server")),
+        not(all(feature = "static-generation", feature = "server")),
+        not(feature = "desktop"),
+        not(feature = "mobile"),
+    ))]
+    pub fn launch(
+        root: fn() -> dioxus_core::Element,
+        contexts: Vec<super::ContextFn>,
+        platform_config: Vec<Box<dyn std::any::Any>>,
+    ) {
+        super::web_launch(root, contexts, platform_config);
     }
 
-    #[cfg(not(any(
+    #[cfg(all(
         feature = "liveview",
-        feature = "desktop",
-        feature = "mobile",
-        feature = "web",
-        feature = "fullstack",
-        feature = "static-generation"
-    )))]
-    pub type Config = ();
+        not(all(feature = "fullstack", feature = "server")),
+        not(all(feature = "static-generation", feature = "server")),
+        not(feature = "desktop"),
+        not(feature = "mobile"),
+        not(feature = "web"),
+    ))]
+    pub use dioxus_liveview::launch::*;
 
     #[cfg(not(any(
         feature = "liveview",
+        all(feature = "fullstack", feature = "server"),
+        all(feature = "static-generation", feature = "server"),
         feature = "desktop",
         feature = "mobile",
         feature = "web",
-        feature = "fullstack",
-        feature = "static-generation"
     )))]
     pub fn launch(
         root: fn() -> dioxus_core::Element,
-        contexts: Vec<Box<super::ValidContext>>,
-        platform_config: (),
+        contexts: Vec<super::ContextFn>,
+        platform_config: Vec<Box<dyn std::any::Any>>,
     ) -> ! {
         #[cfg(feature = "third-party-renderer")]
         panic!("No first party renderer feature enabled. It looks like you are trying to use a third party renderer. You will need to use the launch function from the third party renderer crate.");
@@ -409,22 +290,6 @@ mod current_platform {
 macro_rules! impl_launch {
     ($($return_type:tt),*) => {
         /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
-        // If you aren't using a third party renderer and this is not a docs.rs build, generate a warning about no renderer being enabled
-        #[cfg_attr(
-            all(not(any(
-                docsrs,
-                feature = "third-party-renderer",
-                feature = "liveview",
-                feature = "desktop",
-                feature = "mobile",
-                feature = "web",
-                feature = "fullstack",
-                feature = "static-generation"
-            ))),
-            deprecated(
-                note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, `fullstack`, or `static-generation` to the `features` of dioxus field in your Cargo.toml.\n# Example\n```toml\n# ...\n[dependencies]\ndioxus = { version = \"0.5.0\", features = [\"web\"] }\n# ...\n```"
-            )
-        )]
         pub fn launch(app: fn() -> Element) -> $($return_type)* {
             #[allow(deprecated)]
             LaunchBuilder::new().launch(app)
@@ -439,29 +304,40 @@ impl_launch!(());
 impl_launch!(!);
 
 #[cfg(feature = "web")]
-#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
-/// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_web(app: fn() -> Element) {
-    LaunchBuilder::web().launch(app)
-}
-
-#[cfg(feature = "desktop")]
-#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
-/// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_desktop(app: fn() -> Element) {
-    LaunchBuilder::desktop().launch(app)
-}
-
-#[cfg(feature = "fullstack")]
-#[cfg_attr(docsrs, doc(cfg(feature = "fullstack")))]
-/// Launch your fullstack application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_fullstack(app: fn() -> Element) {
-    LaunchBuilder::fullstack().launch(app)
-}
+fn web_launch(
+    root: fn() -> dioxus_core::Element,
+    contexts: Vec<super::ContextFn>,
+    platform_config: Vec<Box<dyn std::any::Any>>,
+) {
+    // If the server feature is enabled, launch the client with hydration enabled
+    #[cfg(any(feature = "static-generation", feature = "fullstack"))]
+    {
+        let platform_config = platform_config
+            .into_iter()
+            .find_map(|cfg| cfg.downcast::<dioxus_web::Config>().ok())
+            .unwrap_or_default()
+            .hydrate(true);
+
+        let factory = move || {
+            let mut vdom = dioxus_core::VirtualDom::new(root);
+            for context in contexts {
+                vdom.insert_any_root_context(context());
+            }
+            #[cfg(feature = "document")]
+            {
+                #[cfg(feature = "fullstack")]
+                use dioxus_fullstack::document;
+                #[cfg(all(feature = "static-generation", not(feature = "fullstack")))]
+                use dioxus_static_site_generation::document;
+                let document = std::rc::Rc::new(document::web::FullstackWebDocument)
+                    as std::rc::Rc<dyn crate::prelude::Document>;
+                vdom.provide_root_context(document);
+            }
+            vdom
+        };
 
-#[cfg(feature = "mobile")]
-#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
-/// Launch your mobile application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_mobile(app: fn() -> Element) {
-    LaunchBuilder::mobile().launch(app)
+        dioxus_web::launch::launch_virtual_dom(factory(), platform_config)
+    }
+    #[cfg(not(any(feature = "static-generation", feature = "fullstack")))]
+    dioxus_web::launch::launch(root, contexts, platform_config);
 }

+ 1 - 5
packages/dioxus/src/lib.rs

@@ -34,8 +34,7 @@ mod launch;
 
 #[cfg(feature = "launch")]
 #[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
-#[allow(deprecated)]
-pub use launch::launch;
+pub use crate::launch::*;
 
 #[cfg(feature = "hooks")]
 #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))]
@@ -60,9 +59,6 @@ pub use dioxus_html as html;
 pub use dioxus_core_macro as core_macro;
 
 pub mod prelude {
-    #[cfg(feature = "launch")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "launch")))]
-    pub use crate::launch::*;
 
     #[cfg(feature = "hooks")]
     #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))]

+ 72 - 2
packages/fullstack/README.md

@@ -37,7 +37,77 @@ Full stack Dioxus in under 30 lines of code
 use dioxus::prelude::*;
 
 fn main() {
-    launch(App);
+    dioxus::launch(App);
+}
+
+#[component]
+fn App() -> Element {
+    let mut meaning = use_signal(|| None);
+
+    rsx! {
+        h1 { "Meaning of life: {meaning:?}" }
+        button {
+            onclick: move |_| async move {
+                if let Ok(data) = get_meaning("life the universe and everything".into()).await {
+                    meaning.set(data);
+                }
+            },
+            "Run a server function"
+        }
+    }
+}
+
+#[server]
+async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
+    Ok(of.contains("life").then(|| 42))
+}
+```
+
+## Axum Integration
+
+If you have an existing Axum router or you need more control over the server, you can use the [`DioxusRouterExt`](https://docs.rs/dioxus-fullstack/0.6.0-alpha.2/dioxus_fullstack/prelude/trait.DioxusRouterExt.html) trait to integrate with your existing Axum router.
+
+First, make sure your `axum` dependency is optional and enabled by the server feature flag. Axum cannot be compiled to wasm, so if it is enabled by default, it will cause a compile error:
+
+```toml
+[dependencies]
+dioxus = { version = "*", features = ["fullstack"] }
+axum = { version = "0.7.0", optional = true }
+
+[features]
+server = ["dep:axum", "dioxus/server"]
+web = ["dioxus/web"]
+```
+
+Then we can set up dioxus with the axum server:
+
+```rust, no_run
+#![allow(non_snake_case)]
+use dioxus::prelude::*;
+
+// The entry point for the server
+#[cfg(feature = "server")]
+fn main() {
+    // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
+    // and we use the generated address the CLI gives us
+    let address = dioxus_cli_config::RuntimeCLIArguments::fullstack_address_or_localhost();
+
+    // Set up the axum router
+    let router = axum::Router::new()
+        // You can add a dioxus application to the router with the `serve_dioxus_application` method
+        // This will add a fallback route to the router that will serve your component and server functions
+        .serve_dioxus_application(ServeConfigBuilder::default(), App);
+
+    // Finally, we can launch the server
+    let router = router.into_make_service();
+    let listener = tokio::net::TcpListener::bind(address).await.unwrap();
+    axum::serve(listener, router).await.unwrap();
+}
+
+// For any other platform, we just launch the app
+#[cfg(not(feature = "server"))]
+fn main() {
+    dioxus::launch(App);
 }
 
 #[component]
@@ -111,7 +181,7 @@ fn main() {
 // For any other platform, we just launch the app
 #[cfg(not(feature = "server"))]
 fn main() {
-    launch(App);
+    dioxus::launch(App);
 }
 
 #[component]

+ 0 - 289
packages/fullstack/src/config.rs

@@ -1,289 +0,0 @@
-//! Launch helper macros for fullstack apps
-#![allow(unused)]
-use crate::prelude::*;
-use dioxus_lib::prelude::*;
-use std::sync::Arc;
-
-/// Settings for a fullstack app.
-///
-/// Depending on what features are enabled, you can pass in configurations for each client platform as well as the server:
-/// ```rust, no_run
-/// # fn app() -> Element { todo!() }
-/// use dioxus::prelude::*;
-///
-/// let mut cfg = dioxus::fullstack::Config::new();
-///
-/// // Only set the server config if the server feature is enabled
-/// server_only! {
-///     cfg = cfg.with_server_config(ServeConfigBuilder::default());
-/// }
-///
-/// // Only set the web config if the web feature is enabled
-/// web! {
-///     cfg = cfg.with_web_config(dioxus::web::Config::default());
-/// }
-///
-/// // Only set the desktop config if the desktop feature is enabled
-/// desktop! {
-///     cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
-/// }
-///
-/// // Finally, launch the app with the config
-/// LaunchBuilder::new()
-///     .with_cfg(cfg)
-///     .launch(app);
-/// ```
-pub struct Config {
-    #[cfg(feature = "server")]
-    pub(crate) server_cfg: ServeConfigBuilder,
-
-    #[cfg(feature = "web")]
-    pub(crate) web_cfg: dioxus_web::Config,
-
-    #[cfg(feature = "desktop")]
-    pub(crate) desktop_cfg: dioxus_desktop::Config,
-
-    #[cfg(feature = "mobile")]
-    pub(crate) mobile_cfg: dioxus_mobile::Config,
-}
-
-#[allow(clippy::derivable_impls)]
-impl Default for Config {
-    fn default() -> Self {
-        Self {
-            #[cfg(feature = "server")]
-            server_cfg: ServeConfigBuilder::new(),
-            #[cfg(feature = "web")]
-            web_cfg: dioxus_web::Config::default(),
-            #[cfg(feature = "desktop")]
-            desktop_cfg: dioxus_desktop::Config::default(),
-            #[cfg(feature = "mobile")]
-            mobile_cfg: dioxus_mobile::Config::default(),
-        }
-    }
-}
-
-impl Config {
-    /// Create a new config for a fullstack app.
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    /// Set the incremental renderer config. The incremental config can be used to improve
-    /// performance of heavy routes by caching the rendered html in memory and/or the file system.
-    ///
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the server config if the server feature is enabled
-    /// server_only! {
-    ///     cfg = cfg.incremental(IncrementalRendererConfig::default().with_memory_cache_limit(10000));
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "server")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
-    pub fn incremental(self, cfg: IncrementalRendererConfig) -> Self {
-        Self {
-            server_cfg: self.server_cfg.incremental(cfg),
-            ..self
-        }
-    }
-
-    /// Set the server config
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the server config if the server feature is enabled
-    /// server_only! {
-    ///     cfg = cfg.with_server_config(ServeConfigBuilder::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "server")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
-    pub fn with_server_config(self, server_cfg: ServeConfigBuilder) -> Self {
-        Self { server_cfg, ..self }
-    }
-
-    /// Set the server config by modifying the config in place
-    ///
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the server config if the server feature is enabled
-    /// server_only! {
-    ///     cfg.set_server_config(ServeConfigBuilder::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "server")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
-    pub fn set_server_config(&mut self, server_cfg: ServeConfigBuilder) {
-        self.server_cfg = server_cfg;
-    }
-
-    /// Set the web config
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the web config if the server feature is enabled
-    /// web! {
-    ///     cfg = cfg.with_web_config(dioxus::web::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "web")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "web")))]
-    pub fn with_web_config(self, web_cfg: dioxus_web::Config) -> Self {
-        Self { web_cfg, ..self }
-    }
-
-    /// Set the server config by modifying the config in place
-    ///
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the web config if the server feature is enabled
-    /// web! {
-    ///     cfg.set_web_config(dioxus::web::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "web")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "web")))]
-    pub fn set_web_config(&mut self, web_cfg: dioxus_web::Config) {
-        self.web_cfg = web_cfg;
-    }
-
-    /// Set the desktop config
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the desktop config if the server feature is enabled
-    /// desktop! {
-    ///     cfg = cfg.with_desktop_config(dioxus::desktop::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "desktop")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
-    pub fn with_desktop_config(self, desktop_cfg: dioxus_desktop::Config) -> Self {
-        Self {
-            desktop_cfg,
-            ..self
-        }
-    }
-
-    /// Set the desktop config by modifying the config in place
-    ///
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the desktop config if the server feature is enabled
-    /// desktop! {
-    ///     cfg.set_desktop_config(dioxus::desktop::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// ```
-    #[cfg(feature = "desktop")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
-    pub fn set_desktop_config(&mut self, desktop_cfg: dioxus_desktop::Config) {
-        self.desktop_cfg = desktop_cfg;
-    }
-
-    /// Set the mobile config
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the mobile config if the server feature is enabled
-    /// mobile! {
-    ///     cfg = cfg.with_mobile_cfg(dioxus::mobile::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// Set the mobile config.
-    #[cfg(feature = "mobile")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
-    pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self {
-        Self { mobile_cfg, ..self }
-    }
-
-    /// Set the mobile config by modifying the config in place
-    /// ```rust, no_run
-    /// # fn app() -> Element { todo!() }
-    /// use dioxus::prelude::*;
-    ///
-    /// let mut cfg = dioxus::fullstack::Config::new();
-    ///
-    /// // Only set the mobile config if the server feature is enabled
-    /// mobile! {
-    ///     cfg.set_mobile_cfg(dioxus::mobile::Config::default());
-    /// }
-    ///
-    /// // Finally, launch the app with the config
-    /// LaunchBuilder::new()
-    ///     .with_cfg(cfg)
-    ///     .launch(app);
-    /// Set the mobile config.
-    #[cfg(feature = "mobile")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
-    pub fn set_mobile_cfg(&mut self, mobile_cfg: dioxus_mobile::Config) {
-        self.mobile_cfg = mobile_cfg;
-    }
-}

+ 2 - 2
packages/fullstack/src/document/mod.rs

@@ -1,8 +1,8 @@
 //! This module contains the document providers for the fullstack platform.
 
 #[cfg(feature = "server")]
-pub(crate) mod server;
+pub mod server;
 #[cfg(feature = "server")]
 pub use server::ServerDocument;
 #[cfg(all(feature = "web", feature = "document"))]
-pub(crate) mod web;
+pub mod web;

+ 2 - 1
packages/fullstack/src/document/web.rs

@@ -11,7 +11,8 @@ fn head_element_written_on_server() -> bool {
         .unwrap_or_default()
 }
 
-pub(crate) struct FullstackWebDocument;
+/// A document provider for fullstack web clients
+pub struct FullstackWebDocument;
 
 impl Document for FullstackWebDocument {
     fn new_evaluator(

+ 0 - 171
packages/fullstack/src/launch.rs

@@ -1,171 +0,0 @@
-//! This module contains the `launch` function, which is the main entry point for dioxus fullstack
-
-use std::{any::Any, sync::Arc};
-
-use dioxus_lib::prelude::{Element, VirtualDom};
-
-pub use crate::Config;
-
-#[allow(unused)]
-pub(crate) type ContextProviders = Arc<
-    Vec<Box<dyn Fn() -> Box<dyn std::any::Any + Send + Sync + 'static> + Send + Sync + 'static>>,
->;
-
-#[allow(unused)]
-fn virtual_dom_factory(
-    root: fn() -> Element,
-    contexts: ContextProviders,
-) -> impl Fn() -> VirtualDom + 'static {
-    move || {
-        let mut vdom = VirtualDom::new(root);
-        for context in &*contexts {
-            vdom.insert_any_root_context(context());
-        }
-        vdom
-    }
-}
-
-#[cfg(feature = "server")]
-/// Launch a fullstack app with the given root component, contexts, and config.
-#[allow(unused)]
-pub fn launch(
-    root: fn() -> Element,
-    contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
-    platform_config: Config,
-) -> ! {
-    let contexts = Arc::new(contexts);
-    let factory = virtual_dom_factory(root, contexts.clone());
-    #[cfg(all(feature = "server", not(target_arch = "wasm32")))]
-    tokio::runtime::Runtime::new()
-        .unwrap()
-        .block_on(async move {
-            launch_server(platform_config, factory, contexts).await;
-        });
-
-    unreachable!("Launching a fullstack app should never return")
-}
-
-#[cfg(all(not(feature = "server"), feature = "web"))]
-/// Launch a fullstack app with the given root component, contexts, and config.
-#[allow(unused)]
-pub fn launch(
-    root: fn() -> Element,
-    #[allow(unused_mut)] mut contexts: Vec<
-        Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
-    >,
-    platform_config: Config,
-) {
-    let contexts = Arc::new(contexts);
-    let mut factory = virtual_dom_factory(root, contexts);
-    let cfg = platform_config.web_cfg.hydrate(true);
-
-    #[cfg(feature = "document")]
-    let factory = move || {
-        let mut vdom = factory();
-        let document = std::rc::Rc::new(crate::document::web::FullstackWebDocument)
-            as std::rc::Rc<dyn dioxus_lib::prelude::document::Document>;
-        vdom.provide_root_context(document);
-        vdom
-    };
-
-    dioxus_web::launch::launch_virtual_dom(factory(), cfg)
-}
-
-#[cfg(all(not(any(feature = "server", feature = "web")), feature = "desktop"))]
-/// Launch a fullstack app with the given root component, contexts, and config.
-#[allow(unused)]
-pub fn launch(
-    root: fn() -> Element,
-    contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
-    platform_config: Config,
-) -> ! {
-    let contexts = Arc::new(contexts);
-    let factory = virtual_dom_factory(root, contexts);
-    let cfg = platform_config.desktop_cfg;
-    dioxus_desktop::launch::launch_virtual_dom(factory(), cfg)
-}
-
-#[cfg(all(
-    not(any(feature = "server", feature = "web", feature = "desktop")),
-    feature = "mobile"
-))]
-/// Launch a fullstack app with the given root component, contexts, and config.
-#[allow(unused)]
-pub fn launch(
-    root: fn() -> Element,
-    contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
-    platform_config: Config,
-) -> ! {
-    let contexts = Arc::new(contexts);
-    let factory = virtual_dom_factory(root, contexts.clone());
-    let cfg = platform_config.mobile_cfg;
-    dioxus_mobile::launch::launch_virtual_dom(factory(), cfg)
-}
-
-#[cfg(not(any(
-    feature = "server",
-    feature = "web",
-    feature = "desktop",
-    feature = "mobile"
-)))]
-/// Launch a fullstack app with the given root component, contexts, and config.
-#[allow(unused)]
-pub fn launch(
-    root: fn() -> Element,
-    contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
-    platform_config: Config,
-) -> ! {
-    panic!("No platform feature enabled. Please enable one of the following features: axum, desktop, or web to use the launch API.")
-}
-
-#[cfg(feature = "server")]
-#[allow(unused)]
-/// Launch a server application
-async fn launch_server(
-    platform_config: Config,
-    build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
-    context_providers: ContextProviders,
-) {
-    // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address
-    // and we use the generated address the CLI gives us
-    let address = dioxus_cli_config::fullstack_address_or_localhost();
-
-    #[cfg(feature = "axum")]
-    {
-        use crate::axum_adapter::DioxusRouterExt;
-
-        #[allow(unused_mut)]
-        let mut router =
-            axum::Router::new().register_server_functions_with_context(context_providers);
-
-        #[cfg(not(any(feature = "desktop", feature = "mobile")))]
-        {
-            use crate::prelude::RenderHandleState;
-            use crate::prelude::SSRState;
-
-            match platform_config.server_cfg.build() {
-                Ok(cfg) => {
-                    router = router.serve_static_assets();
-
-                    router = router.fallback(
-                        axum::routing::get(crate::axum_adapter::render_handler).with_state(
-                            RenderHandleState::new_with_virtual_dom_factory(cfg, build_virtual_dom),
-                        ),
-                    );
-                }
-                Err(err) => {
-                    tracing::trace!("Failed to create render handler. This is expected if you are only using fullstack for desktop/mobile server functions: {}", err);
-                }
-            }
-        }
-
-        let router = router.into_make_service();
-        let listener = tokio::net::TcpListener::bind(address).await.unwrap();
-
-        axum::serve(listener, router).await.unwrap();
-    }
-    #[cfg(not(feature = "axum"))]
-    {
-        panic!("Launching with dioxus fullstack requires the axum feature. If you are using a community fullstack adapter, please check the documentation for that adapter to see how to launch the application.");
-    }
-}

+ 2 - 6
packages/fullstack/src/lib.rs

@@ -10,13 +10,9 @@ mod html_storage;
 
 #[cfg(feature = "axum")]
 #[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
-mod axum_adapter;
+pub mod server;
 
-mod config;
 mod hooks;
-pub mod launch;
-
-pub use config::*;
 
 pub mod document;
 #[cfg(feature = "server")]
@@ -39,7 +35,7 @@ pub mod prelude {
 
     #[cfg(feature = "axum")]
     #[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
-    pub use crate::axum_adapter::*;
+    pub use crate::server::*;
 
     #[cfg(feature = "server")]
     #[cfg_attr(docsrs, doc(cfg(feature = "server")))]

+ 6 - 0
packages/fullstack/src/serve_config.rs

@@ -5,6 +5,8 @@ use std::fs::File;
 use std::io::Read;
 use std::path::PathBuf;
 
+use dioxus_lib::prelude::dioxus_core::LaunchConfig;
+
 /// 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, Default)]
 pub struct ServeConfigBuilder {
@@ -14,6 +16,8 @@ pub struct ServeConfigBuilder {
     pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
 }
 
+impl LaunchConfig for ServeConfigBuilder {}
+
 impl ServeConfigBuilder {
     /// Create a new ServeConfigBuilder with incremental static generation disabled and the default index.html settings
     pub fn new() -> Self {
@@ -239,6 +243,8 @@ pub struct ServeConfig {
     pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
 }
 
+impl LaunchConfig for ServeConfig {}
+
 impl ServeConfig {
     /// Create a new ServeConfig
     pub fn new() -> Result<Self, UnableToLoadIndex> {

Some files were not shown because too many files changed in this diff