diff --git a/Makefile.toml b/Makefile.toml index 64f26bd2f..8d62959e1 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -89,6 +89,7 @@ args = [ "test", "--doc", "--workspace", + "--all-features", ] private = true diff --git a/examples/router.rs b/examples/router.rs index 1952e764e..d831ea246 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -22,6 +22,7 @@ fn main() { // Turn off rustfmt since we're doing layouts and routes in the same enum #[derive(Routable, Clone, Debug, PartialEq)] #[rustfmt::skip] +#[allow(clippy::empty_line_after_outer_attr)] enum Route { // Wrap Home in a Navbar Layout #[layout(NavBar)] diff --git a/packages/fullstack/README.md b/packages/fullstack/README.md index 17439ddbc..0d5d10a2b 100644 --- a/packages/fullstack/README.md +++ b/packages/fullstack/README.md @@ -73,9 +73,11 @@ First, make sure your `axum` dependency is optional and enabled by the server fe [dependencies] dioxus = { version = "*", features = ["fullstack"] } axum = { version = "0.7.0", optional = true } +tokio = { version = "1.0", features = ["full"], optional = true } +dioxus-cli-config = { version = "*", optional = true } [features] -server = ["dioxus/server", "dep:axum"] +server = ["dioxus/server", "dep:axum", "dep:tokio", "dioxus-cli-config"] web = ["dioxus/web"] ``` @@ -87,77 +89,8 @@ 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::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] -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, 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 = ["dioxus/server", "dep:axum"] -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() { +#[tokio::main] +async 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::fullstack_address_or_localhost(); diff --git a/packages/fullstack/docs/request_origin.md b/packages/fullstack/docs/request_origin.md index f0e052ab7..0e946f4b0 100644 --- a/packages/fullstack/docs/request_origin.md +++ b/packages/fullstack/docs/request_origin.md @@ -3,13 +3,16 @@ This method interacts with information from the current request. The request may 1. The initial SSR render if this method called from a [`Component`](dioxus_lib::prelude::component) or a [`server`](crate::prelude::server) function that is called during the initial render ```rust +# use dioxus::prelude::*; #[component] fn PrintHtmlRequestInfo() -> Element { // The server context only exists on the server, so we need to put it behind a server_only! config server_only! { // Since we are calling this from a component, the server context that is returned will be from // the html request for ssr rendering - println!("headers are {:?}", server_context().request_parts().headers); + let context = server_context(); + let request_parts = context.request_parts(); + println!("headers are {:?}", request_parts.headers); } rsx! {} } @@ -18,11 +21,14 @@ fn PrintHtmlRequestInfo() -> Element { 2. A request to a [`server`](crate::prelude::server) function called directly from the client (either on desktop/mobile or on the web frontend after the initial render) ```rust +# use dioxus::prelude::*; #[server] async fn read_headers() -> Result<(), ServerFnError> { // Since we are calling this from a server function, the server context that is may be from the // initial request or a request from the client - println!("headers are {:?}", server_context().request_parts().headers); + let context = server_context(); + let request_parts = context.request_parts(); + println!("headers are {:?}", request_parts.headers); Ok(()) } @@ -32,7 +38,9 @@ fn CallServerFunction() -> Element { button { // If you click the button, the server function will be called and the server context will be // from the client request - onclick: move |_| read_headers().await, + onclick: move |_| async { + _ = read_headers().await + }, "Call server function" } } diff --git a/packages/fullstack/src/serve_config.rs b/packages/fullstack/src/serve_config.rs index 434a89381..dc3b6a770 100644 --- a/packages/fullstack/src/serve_config.rs +++ b/packages/fullstack/src/serve_config.rs @@ -40,16 +40,10 @@ impl ServeConfigBuilder { /// # 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_cfg(ServeConfigBuilder::default().incremental(IncrementalRendererConfig::default())); - /// } - /// /// // Finally, launch the app with the config /// LaunchBuilder::new() - /// .with_cfg(cfg) + /// // Only set the server config if the server feature is enabled + /// .with_cfg(server_only!(ServeConfigBuilder::default().incremental(IncrementalRendererConfig::default()))) /// .launch(app); /// ``` pub fn incremental(mut self, cfg: dioxus_isrg::IncrementalRendererConfig) -> Self { @@ -92,26 +86,20 @@ impl ServeConfigBuilder { /// # 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_cfg(ServeConfigBuilder::default().root_id("app")); - /// } - /// - /// // You also need to set the root id in your web config - /// web! { - /// cfg = cfg.with_web_config(dioxus::web::Config::default().rootname("app")); - /// } - /// - /// // And desktop config - /// desktop! { - /// cfg = cfg.with_desktop_config(dioxus::desktop::Config::default().with_root_name("app")); - /// } - /// /// // Finally, launch the app with the config /// LaunchBuilder::new() - /// .with_cfg(cfg) + /// // Only set the server config if the server feature is enabled + /// .with_cfg(server_only! { + /// ServeConfigBuilder::default().root_id("app") + /// }) + /// // You also need to set the root id in your web config + /// .with_cfg(web! { + /// dioxus::web::Config::default().rootname("app") + /// }) + /// // And desktop config + /// .with_cfg(desktop! { + /// dioxus::desktop::Config::default().with_root_name("app") + /// }) /// .launch(app); /// ``` pub fn root_id(mut self, root_id: &'static str) -> Self { diff --git a/packages/fullstack/src/server/mod.rs b/packages/fullstack/src/server/mod.rs index 4ec452331..a53647dee 100644 --- a/packages/fullstack/src/server/mod.rs +++ b/packages/fullstack/src/server/mod.rs @@ -14,7 +14,10 @@ //! tokio::runtime::Runtime::new() //! .unwrap() //! .block_on(async move { -//! let listener = tokio::net::TcpListener::bind("127.0.0.01:8080") +//! // 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(); +//! let listener = tokio::net::TcpListener::bind(address) //! .await //! .unwrap(); //! axum::serve( @@ -82,7 +85,7 @@ pub trait DioxusRouterExt { /// # use dioxus_fullstack::prelude::*; /// #[tokio::main] /// async fn main() { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// let addr = dioxus_cli_config::fullstack_address_or_localhost(); /// let router = axum::Router::new() /// // Register server functions routes with the default handler /// .register_server_functions() @@ -107,7 +110,7 @@ pub trait DioxusRouterExt { /// # use std::sync::Arc; /// #[tokio::main] /// async fn main() { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// let addr = dioxus_cli_config::fullstack_address_or_localhost(); /// let router = axum::Router::new() /// // Register server functions routes with the default handler /// .register_server_functions_with_context(Arc::new(vec![Box::new(|| Box::new(1234567890u32))])) @@ -127,7 +130,7 @@ pub trait DioxusRouterExt { /// # use dioxus_fullstack::prelude::*; /// #[tokio::main] /// async fn main() { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// let addr = dioxus_cli_config::fullstack_address_or_localhost(); /// let router = axum::Router::new() /// // Server side render the application, serve static assets, and register server functions /// .serve_static_assets() @@ -152,7 +155,7 @@ pub trait DioxusRouterExt { /// # use dioxus_fullstack::prelude::*; /// #[tokio::main] /// async fn main() { - /// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); + /// let addr = dioxus_cli_config::fullstack_address_or_localhost(); /// let router = axum::Router::new() /// // Server side render the application, serve static assets, and register server functions /// .serve_dioxus_application(ServeConfig::new().unwrap(), app) @@ -358,7 +361,7 @@ impl RenderHandleState { /// /// #[tokio::main] /// async fn main() { -/// let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); +/// let addr = dioxus_cli_config::fullstack_address_or_localhost(); /// let router = axum::Router::new() /// // Register server functions, etc. /// // Note you can use `register_server_functions_with_context` diff --git a/packages/fullstack/src/server_context.rs b/packages/fullstack/src/server_context.rs index b81216795..08db028ee 100644 --- a/packages/fullstack/src/server_context.rs +++ b/packages/fullstack/src/server_context.rs @@ -161,10 +161,11 @@ mod server_fn_impl { /// #[server] /// async fn set_headers() -> Result<(), ServerFnError> { /// let server_context = server_context(); - /// let cookies = server_context.response_parts() - /// .headers() + /// let response_parts = server_context.response_parts(); + /// let cookies = response_parts + /// .headers /// .get("Cookie") - /// .ok_or_else(|| ServerFnError::msg("failed to find Cookie header in the response"))?; + /// .ok_or_else(|| ServerFnError::new("failed to find Cookie header in the response"))?; /// println!("{:?}", cookies); /// Ok(()) /// } @@ -185,8 +186,8 @@ mod server_fn_impl { /// async fn set_headers() -> Result<(), ServerFnError> { /// let server_context = server_context(); /// server_context.response_parts_mut() - /// .headers_mut() - /// .insert("Cookie", "dioxus=fullstack"); + /// .headers + /// .insert("Cookie", http::HeaderValue::from_static("dioxus=fullstack")); /// Ok(()) /// } /// ``` @@ -205,10 +206,11 @@ mod server_fn_impl { /// #[server] /// async fn read_headers() -> Result<(), ServerFnError> { /// let server_context = server_context(); - /// let id: &i32 = server_context.request_parts() + /// let request_parts = server_context.request_parts(); + /// let id: &i32 = request_parts /// .extensions /// .get() - /// .ok_or_else(|| ServerFnError::msg("failed to find i32 extension in the request"))?; + /// .ok_or_else(|| ServerFnError::new("failed to find i32 extension in the request"))?; /// println!("{:?}", id); /// Ok(()) /// } @@ -231,7 +233,7 @@ mod server_fn_impl { /// let id: i32 = server_context.request_parts_mut() /// .extensions /// .remove() - /// .ok_or_else(|| ServerFnError::msg("failed to find i32 extension in the request"))?; + /// .ok_or_else(|| ServerFnError::new("failed to find i32 extension in the request"))?; /// println!("{:?}", id); /// Ok(()) /// } diff --git a/packages/manganis/manganis-core/src/images.rs b/packages/manganis/manganis-core/src/images.rs index 825dffb7a..a51b85866 100644 --- a/packages/manganis/manganis-core/src/images.rs +++ b/packages/manganis/manganis-core/src/images.rs @@ -181,7 +181,6 @@ impl ImageAssetOptions { /// # use manganis::{asset, Asset, ImageAssetOptions, ImageSize}; /// const _: Asset = asset!("/assets/image.png", ImageAssetOptions::new().with_size(ImageSize::Manual { width: 512, height: 512 })); /// ``` - pub const fn with_size(self, size: ImageSize) -> Self { Self { size, ..self } }