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:
1 parent
9dd4427cb5
commit
ffa36a67c3
18 files changed
+403
-244
No files matched your search
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user