Forward cli serve settings and make it easier to provide platform config to fullstack (#2521)

* assert that launch never returns for better compiler errors

* fix static generation launch function

* fix web renderer

* pass context providers into server functions

* add an example for FromContext

* clean up DioxusRouterExt

* fix server function context

* fix fullstack desktop example

* forward CLI serve settings to fullstack

* re-export serve config at the root of fullstack

* forward env directly instead of using a guard

* just set the port in the CLI for fullstack playwright tests

* fix fullstack dioxus-cli-config feature

* fix launch server merge conflicts

* fix fullstack launch context

* run web playwright tests on 9999 to avoid port conflicts with other local servers

* fix playwright tests
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-06-28 14:40:41 -07:00
1 parent 9dd4427cb5
commit ffa36a67c3
18 files changed
+403 -244

No files matched your search

+4 -1
View File
@@ -56,6 +56,7 @@ tower-layer = { version = "0.3.2", optional = true }
web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] }
dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true }
clap = { version = "4.5.7", optional = true, features = ["derive"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { workspace = true, features = ["rt", "sync"], optional = true }
@@ -92,7 +93,9 @@ server = [
"dep:tracing-futures",
"dep:pin-project",
"dep:thiserror",
"dep:dioxus-cli-config"
"dep:dioxus-cli-config",
"dep:clap",
"dioxus-cli-config/cli"
]
[package.metadata.docs.rs]
@@ -7,11 +7,12 @@
use dioxus::prelude::*;
fn main() {
let cfg = server_only!(dioxus::fullstack::Config::new().incremental(
IncrementalRendererConfig::default().invalidate_after(std::time::Duration::from_secs(120)),
));
LaunchBuilder::fullstack().with_cfg(cfg).launch(app);
LaunchBuilder::fullstack()
.with_cfg(server_only!(ServeConfig::builder().incremental(
IncrementalRendererConfig::default()
.invalidate_after(std::time::Duration::from_secs(120)),
)))
.launch(app);
}
fn app() -> Element {
+26 -79
View File
@@ -6,15 +6,9 @@ use std::sync::Arc;
/// Settings for a fullstack app.
pub struct Config {
#[cfg(feature = "server")]
pub(crate) server_fn_route: &'static str,
#[cfg(feature = "server")]
pub(crate) server_cfg: ServeConfigBuilder,
#[cfg(feature = "server")]
pub(crate) addr: std::net::SocketAddr,
#[cfg(feature = "web")]
pub(crate) web_cfg: dioxus_web::Config,
@@ -29,10 +23,6 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
#[cfg(feature = "server")]
server_fn_route: "",
#[cfg(feature = "server")]
addr: std::net::SocketAddr::from(([0, 0, 0, 0], 8080)),
#[cfg(feature = "server")]
server_cfg: ServeConfigBuilder::new(),
#[cfg(feature = "web")]
@@ -51,24 +41,6 @@ impl Config {
Self::default()
}
/// Set the address to serve the app on.
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn addr(self, addr: impl Into<std::net::SocketAddr>) -> Self {
let addr = addr.into();
Self { addr, ..self }
}
/// Set the route to the server functions.
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn server_fn_route(self, server_fn_route: &'static str) -> Self {
Self {
server_fn_route,
..self
}
}
/// Set the incremental renderer config.
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
@@ -82,77 +54,52 @@ impl Config {
/// Set the server config.
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn server_cfg(self, server_cfg: ServeConfigBuilder) -> Self {
pub fn with_server_cfg(self, server_cfg: ServeConfigBuilder) -> Self {
Self { server_cfg, ..self }
}
/// Set the server config.
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
pub fn set_server_cfg(&mut self, server_cfg: ServeConfigBuilder) {
self.server_cfg = server_cfg;
}
/// Set the web config.
#[cfg(feature = "web")]
#[cfg_attr(docsrs, doc(cfg(feature = "web")))]
pub fn web_cfg(self, web_cfg: dioxus_web::Config) -> Self {
pub fn with_web_config(self, web_cfg: dioxus_web::Config) -> Self {
Self { web_cfg, ..self }
}
/// Set the desktop config.
/// Set the web config.
#[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
#[cfg(feature = "desktop")]
#[cfg_attr(docsrs, doc(cfg(feature = "desktop")))]
pub fn desktop_cfg(self, desktop_cfg: dioxus_desktop::Config) -> Self {
pub fn with_desktop_config(self, desktop_cfg: dioxus_desktop::Config) -> Self {
Self {
desktop_cfg,
..self
}
}
/// Set the desktop config.
#[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.
#[cfg(feature = "mobile")]
#[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
pub fn mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self {
pub fn with_mobile_cfg(self, mobile_cfg: dioxus_mobile::Config) -> Self {
Self { mobile_cfg, ..self }
}
#[cfg(feature = "server")]
#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
/// Launch a server application
pub async fn launch_server(
self,
build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static,
context_providers: crate::launch::ContextProviders,
) {
use std::any::Any;
let addr = self.addr;
println!("Listening on http://{}", addr);
let cfg = self.server_cfg.build();
let server_fn_route = self.server_fn_route;
#[cfg(feature = "axum")]
{
use crate::axum_adapter::{render_handler, DioxusRouterExt};
use axum::routing::get;
use tower::ServiceBuilder;
let ssr_state = SSRState::new(&cfg);
let router =
axum::Router::new().register_server_functions_with_context(context_providers);
#[cfg(not(any(feature = "desktop", feature = "mobile")))]
let router = {
let mut router = router.serve_static_assets(cfg.assets_path.clone()).await;
#[cfg(all(feature = "hot-reload", debug_assertions))]
{
use dioxus_hot_reload::HotReloadRouterExt;
router = router.forward_cli_hot_reloading();
}
router.fallback(get(render_handler).with_state((
cfg,
Arc::new(build_virtual_dom),
ssr_state,
)))
};
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, router).await.unwrap();
}
}
}
+65 -6
View File
@@ -36,7 +36,7 @@ pub fn launch(
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move {
platform_config.launch_server(factory, contexts).await;
launch_server(platform_config, factory, contexts).await;
});
unreachable!("Launching a fullstack app should never return")
@@ -50,7 +50,8 @@ pub fn launch(
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) {
let factory = virtual_dom_factory(root, Arc::new(contexts));
let contexts = Arc::new(contexts);
let factory = virtual_dom_factory(root, contexts);
let cfg = platform_config.web_cfg.hydrate(true);
dioxus_web::launch::launch_virtual_dom(factory(), cfg)
}
@@ -63,7 +64,8 @@ pub fn launch(
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
platform_config: Config,
) -> ! {
let factory = virtual_dom_factory(root, Arc::new(contexts));
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)
}
@@ -76,10 +78,67 @@ pub fn launch(
#[allow(unused)]
pub fn launch(
root: fn() -> Element,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>>,
platform_config: Config,
) {
let factory = virtual_dom_factory(root, Arc::new(contexts));
) -> ! {
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(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,
) {
use clap::Parser;
let args = dioxus_cli_config::ServeArguments::from_cli()
.unwrap_or_else(dioxus_cli_config::ServeArguments::parse);
let addr = args
.addr
.unwrap_or(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)));
let addr = std::net::SocketAddr::new(addr, args.port);
println!("Listening on http://{}", addr);
#[cfg(feature = "axum")]
{
use crate::axum_adapter::DioxusRouterExt;
let router = axum::Router::new().register_server_functions_with_context(context_providers);
#[cfg(not(any(feature = "desktop", feature = "mobile")))]
let router = {
use crate::prelude::SSRState;
let cfg = platform_config.server_cfg.build();
let ssr_state = SSRState::new(&cfg);
let mut router = router.serve_static_assets(cfg.assets_path.clone()).await;
#[cfg(all(feature = "hot-reload", debug_assertions))]
{
use dioxus_hot_reload::HotReloadRouterExt;
router = router.forward_cli_hot_reloading();
}
router.fallback(
axum::routing::get(crate::axum_adapter::render_handler).with_state((
cfg,
Arc::new(build_virtual_dom),
ssr_state,
)),
)
};
let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(addr).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
View File
@@ -23,6 +23,8 @@ mod render;
#[cfg(feature = "server")]
mod serve_config;
#[cfg(feature = "server")]
pub use serve_config::*;
#[cfg(feature = "server")]
mod server_context;