From 8df53df8872681e4d673b1508bc1524c60008cd1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 23 Jun 2025 14:33:25 -0700 Subject: [PATCH] Fix `cargo test --workspace`, associated feature flags, and a number of doctests --- packages/desktop/src/desktop_context.rs | 15 +- packages/fullstack/src/error.rs | 9 + packages/manganis/manganis-core/src/css.rs | 175 ------------------ .../manganis/manganis-core/src/css_module.rs | 168 +++++++++++++++++ packages/manganis/manganis-core/src/lib.rs | 3 + .../manganis/manganis-macro/src/css_module.rs | 2 +- packages/manganis/manganis-macro/src/lib.rs | 4 +- packages/manganis/manganis/src/lib.rs | 5 +- packages/server/Cargo.toml | 1 + packages/server/src/lib.rs | 2 +- packages/server/src/server.rs | 21 ++- 11 files changed, 218 insertions(+), 187 deletions(-) create mode 100644 packages/manganis/manganis-core/src/css_module.rs diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index b3638ef49..5de51ecf6 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -117,12 +117,14 @@ impl DesktopService { /// } /// } /// + /// # async fn app() { /// // Create a new window with a component that will be rendered in the new window. /// let dom = VirtualDom::new(popup); /// // Create and wait for the window /// let window = dioxus::desktop::window().new_window(dom, Default::default()).await; /// // Fullscreen the new window /// window.set_fullscreen(true); + /// # } /// ``` // Note: This method is asynchronous because webview2 does not support creating a new window from // inside of an existing webview callback. Dioxus runs event handlers synchronously inside of a webview @@ -323,12 +325,21 @@ fn is_main_thread() -> bool { /// A [`DesktopContext`] that is pending creation. /// /// # Example -/// ```rust +/// ```rust, no_run +/// # use dioxus::prelude::*; +/// # async fn app() { +/// // Create a new window with a component that will be rendered in the new window. +/// let dom = VirtualDom::new(|| rsx!{ "popup!" }); +/// /// // Create a new window asynchronously -/// let pending_context = desktop_service.new_window(dom, config); +/// let pending_context = dioxus::desktop::window().new_window(dom, Default::default()); +/// /// // Wait for the context to be created /// let window = pending_context.await; +/// +/// // Now control the window /// window.set_fullscreen(true); +/// # } /// ``` pub struct PendingDesktopContext { pub(crate) receiver: tokio::sync::oneshot::Receiver, diff --git a/packages/fullstack/src/error.rs b/packages/fullstack/src/error.rs index bf1fa2b2c..c754db22a 100644 --- a/packages/fullstack/src/error.rs +++ b/packages/fullstack/src/error.rs @@ -135,6 +135,15 @@ impl ServerFnError { /// # Examples /// ```rust /// use dioxus::prelude::*; + /// use serde::{Serialize, Deserialize}; + /// + /// #[derive(Serialize, Deserialize, Debug)] + /// struct MyCustomError; + /// impl std::fmt::Display for MyCustomError { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "My custom error") + /// } + /// } /// /// #[server] /// async fn server_function() -> ServerFnResult { diff --git a/packages/manganis/manganis-core/src/css.rs b/packages/manganis/manganis-core/src/css.rs index bc2dca03e..c391d25aa 100644 --- a/packages/manganis/manganis-core/src/css.rs +++ b/packages/manganis/manganis-core/src/css.rs @@ -1,6 +1,5 @@ use crate::AssetOptions; use const_serialize::SerializeConst; -use std::collections::HashSet; /// Options for a css asset #[derive( @@ -76,177 +75,3 @@ impl CssAssetOptions { AssetOptions::Css(self) } } - -/// Options for a css module asset -#[derive( - Debug, - Eq, - PartialEq, - PartialOrd, - Clone, - Copy, - Hash, - SerializeConst, - serde::Serialize, - serde::Deserialize, -)] -#[non_exhaustive] -pub struct CssModuleAssetOptions { - minify: bool, - preload: bool, -} - -impl Default for CssModuleAssetOptions { - fn default() -> Self { - Self::new() - } -} - -impl CssModuleAssetOptions { - /// Create a new css asset using the builder - pub const fn new() -> Self { - Self { - preload: false, - minify: true, - } - } - - /// Sets whether the css should be minified (default: true) - /// - /// Minifying the css can make your site load faster by loading less data - /// - /// ```rust - /// # use manganis::{styles, CssModuleAssetOptions}; - /// styles!(STYLES, "/assets/style.css", CssModuleAssetOptions::new().with_minify(false)); - /// ``` - #[allow(unused)] - pub const fn with_minify(self, minify: bool) -> Self { - Self { minify, ..self } - } - - /// Check if the asset is minified - pub const fn minified(&self) -> bool { - self.minify - } - - /// Make the asset preloaded - /// - /// Preloading css will make the image start to load as soon as possible. This is useful for css that is used soon after the page loads or css that may not be used immediately, but should start loading sooner - /// - /// ```rust - /// # use manganis::{asset, Asset, CssAssetOptions}; - /// styles!("/assets/style.css", CssAssetOptions::new().with_preload(true)); - /// ``` - #[allow(unused)] - pub const fn with_preload(self, preload: bool) -> Self { - Self { preload, ..self } - } - - /// Check if the asset is preloaded - pub const fn preloaded(&self) -> bool { - self.preload - } - - /// Convert the options into options for a generic asset - pub const fn into_asset_options(self) -> AssetOptions { - AssetOptions::CssModule(self) - } -} - -/// Collect CSS classes & ids. -/// -/// This is a rudementary css classes & ids collector. -/// Idents used only in media queries will not be collected. (not support yet) -/// -/// There are likely a number of edge cases that will show up. -/// -/// Returns `(HashSet, HashSet)` -pub fn collect_css_idents(css: &str) -> (HashSet, HashSet) { - const ALLOWED: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"; - - let mut classes = HashSet::new(); - let mut ids = HashSet::new(); - - // Collected ident name and true for ids. - let mut start: Option<(String, bool)> = None; - - // True if we have the first comment start delimiter `/` - let mut comment_start = false; - // True if we have the first comment end delimiter '*' - let mut comment_end = false; - // True if we're in a comment scope. - let mut in_comment_scope = false; - - // True if we're in a block scope: `#hi { this is block scope }` - let mut in_block_scope = false; - - // If we are currently collecting an ident: - // - Check if the char is allowed, put it into the ident string. - // - If not allowed, finalize the ident string and reset start. - // Otherwise: - // Check if character is a `.` or `#` representing a class or string, and start collecting. - for (_byte_index, c) in css.char_indices() { - if let Some(ident) = start.as_mut() { - if ALLOWED.find(c).is_some() { - // CSS ignore idents that start with a number. - // 1. Difficult to process - // 2. Avoid false positives (transition: 0.5s) - if ident.0.is_empty() && c.is_numeric() { - start = None; - continue; - } - - ident.0.push(c); - } else { - match ident.1 { - true => ids.insert(ident.0.clone()), - false => classes.insert(ident.0.clone()), - }; - - start = None; - } - } else { - // Handle entering an exiting scopede. - match c { - // Mark as comment scope if we have comment start: /* - '*' if comment_start => { - comment_start = false; - in_comment_scope = true; - } - // Mark start of comment end if in comment scope: */ - '*' if in_comment_scope => comment_end = true, - // Mark as comment start if not in comment scope and no comment start, mark comment_start - '/' if !in_comment_scope => { - comment_start = true; - } - // If we get the closing delimiter, mark as non-comment scope. - '/' if comment_end => { - in_comment_scope = false; - comment_start = false; - comment_end = false; - } - // Entering & Exiting block scope. - '{' => in_block_scope = true, - '}' => in_block_scope = false, - // Any other character, reset comment start and end if not in scope. - _ => { - comment_start = false; - comment_end = false; - } - } - - // No need to process this char if in bad scope. - if in_comment_scope || in_block_scope { - continue; - } - - match c { - '.' => start = Some((String::new(), false)), - '#' => start = Some((String::new(), true)), - _ => {} - } - } - } - - (classes, ids) -} diff --git a/packages/manganis/manganis-core/src/css_module.rs b/packages/manganis/manganis-core/src/css_module.rs new file mode 100644 index 000000000..61f91757f --- /dev/null +++ b/packages/manganis/manganis-core/src/css_module.rs @@ -0,0 +1,168 @@ +use crate::AssetOptions; +use const_serialize::SerializeConst; +use std::collections::HashSet; + +/// Options for a css module asset +#[derive( + Debug, + Eq, + PartialEq, + PartialOrd, + Clone, + Copy, + Hash, + SerializeConst, + serde::Serialize, + serde::Deserialize, +)] +#[non_exhaustive] +#[doc(hidden)] +pub struct CssModuleAssetOptions { + minify: bool, + preload: bool, +} + +impl Default for CssModuleAssetOptions { + fn default() -> Self { + Self::new() + } +} + +impl CssModuleAssetOptions { + /// Create a new css asset using the builder + pub const fn new() -> Self { + Self { + preload: false, + minify: true, + } + } + + /// Sets whether the css should be minified (default: true) + /// + /// Minifying the css can make your site load faster by loading less data + #[allow(unused)] + pub const fn with_minify(self, minify: bool) -> Self { + Self { minify, ..self } + } + + /// Check if the asset is minified + pub const fn minified(&self) -> bool { + self.minify + } + + /// Make the asset preloaded + /// + /// Preloading css will make the image start to load as soon as possible. This is useful for css that is used soon after the page loads or css that may not be used immediately, but should start loading sooner + #[allow(unused)] + pub const fn with_preload(self, preload: bool) -> Self { + Self { preload, ..self } + } + + /// Check if the asset is preloaded + pub const fn preloaded(&self) -> bool { + self.preload + } + + /// Convert the options into options for a generic asset + pub const fn into_asset_options(self) -> AssetOptions { + AssetOptions::CssModule(self) + } +} + +/// Collect CSS classes & ids. +/// +/// This is a rudementary css classes & ids collector. +/// Idents used only in media queries will not be collected. (not support yet) +/// +/// There are likely a number of edge cases that will show up. +/// +/// Returns `(HashSet, HashSet)` +pub fn collect_css_idents(css: &str) -> (HashSet, HashSet) { + const ALLOWED: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"; + + let mut classes = HashSet::new(); + let mut ids = HashSet::new(); + + // Collected ident name and true for ids. + let mut start: Option<(String, bool)> = None; + + // True if we have the first comment start delimiter `/` + let mut comment_start = false; + // True if we have the first comment end delimiter '*' + let mut comment_end = false; + // True if we're in a comment scope. + let mut in_comment_scope = false; + + // True if we're in a block scope: `#hi { this is block scope }` + let mut in_block_scope = false; + + // If we are currently collecting an ident: + // - Check if the char is allowed, put it into the ident string. + // - If not allowed, finalize the ident string and reset start. + // Otherwise: + // Check if character is a `.` or `#` representing a class or string, and start collecting. + for (_byte_index, c) in css.char_indices() { + if let Some(ident) = start.as_mut() { + if ALLOWED.find(c).is_some() { + // CSS ignore idents that start with a number. + // 1. Difficult to process + // 2. Avoid false positives (transition: 0.5s) + if ident.0.is_empty() && c.is_numeric() { + start = None; + continue; + } + + ident.0.push(c); + } else { + match ident.1 { + true => ids.insert(ident.0.clone()), + false => classes.insert(ident.0.clone()), + }; + + start = None; + } + } else { + // Handle entering an exiting scopede. + match c { + // Mark as comment scope if we have comment start: /* + '*' if comment_start => { + comment_start = false; + in_comment_scope = true; + } + // Mark start of comment end if in comment scope: */ + '*' if in_comment_scope => comment_end = true, + // Mark as comment start if not in comment scope and no comment start, mark comment_start + '/' if !in_comment_scope => { + comment_start = true; + } + // If we get the closing delimiter, mark as non-comment scope. + '/' if comment_end => { + in_comment_scope = false; + comment_start = false; + comment_end = false; + } + // Entering & Exiting block scope. + '{' => in_block_scope = true, + '}' => in_block_scope = false, + // Any other character, reset comment start and end if not in scope. + _ => { + comment_start = false; + comment_end = false; + } + } + + // No need to process this char if in bad scope. + if in_comment_scope || in_block_scope { + continue; + } + + match c { + '.' => start = Some((String::new(), false)), + '#' => start = Some((String::new(), true)), + _ => {} + } + } + } + + (classes, ids) +} diff --git a/packages/manganis/manganis-core/src/lib.rs b/packages/manganis/manganis-core/src/lib.rs index fc7439fbd..09a73cbdc 100644 --- a/packages/manganis/manganis-core/src/lib.rs +++ b/packages/manganis/manganis-core/src/lib.rs @@ -15,3 +15,6 @@ pub use js::*; mod asset; pub use asset::*; + +mod css_module; +pub use css_module::*; diff --git a/packages/manganis/manganis-macro/src/css_module.rs b/packages/manganis/manganis-macro/src/css_module.rs index 3bc5442f3..7c0439b27 100644 --- a/packages/manganis/manganis-macro/src/css_module.rs +++ b/packages/manganis/manganis-macro/src/css_module.rs @@ -123,7 +123,7 @@ impl ToTokens for CssModuleParser { #[allow(missing_docs, non_snake_case)] mod #mod_name { #[allow(unused_imports)] - use super::manganis::{self, CssModuleAssetOptions}; + use manganis::{self, CssModuleAssetOptions}; #linker_tokens; diff --git a/packages/manganis/manganis-macro/src/lib.rs b/packages/manganis/manganis-macro/src/lib.rs index dcf73a7aa..1c8859c35 100644 --- a/packages/manganis/manganis-macro/src/lib.rs +++ b/packages/manganis/manganis-macro/src/lib.rs @@ -151,7 +151,7 @@ pub fn asset(input: TokenStream) -> TokenStream { /// } /// /// .button { -/// background-color: #373737; +/// background-color: #373737; /// } /// ``` /// Then you can use the `css_module!()` macro in your Rust project: @@ -163,9 +163,9 @@ pub fn asset(input: TokenStream) -> TokenStream { /// println!("{}", Styles::button); /// ``` #[proc_macro] +#[doc(hidden)] pub fn css_module(input: TokenStream) -> TokenStream { let style = parse_macro_input!(input as CssModuleParser); - quote! { #style }.into_token_stream().into() } diff --git a/packages/manganis/manganis/src/lib.rs b/packages/manganis/manganis/src/lib.rs index 4cd01cce5..0add09fb8 100644 --- a/packages/manganis/manganis/src/lib.rs +++ b/packages/manganis/manganis/src/lib.rs @@ -3,7 +3,10 @@ #[doc(hidden)] pub mod macro_helpers; -pub use manganis_macro::{asset, css_module}; +pub use manganis_macro::asset; + +#[doc(hidden)] +pub use manganis_macro::css_module; pub use manganis_core::{ Asset, AssetOptions, BundledAsset, CssAssetOptions, CssModuleAssetOptions, FolderAssetOptions, diff --git a/packages/server/Cargo.toml b/packages/server/Cargo.toml index 487b76515..78d5b200d 100644 --- a/packages/server/Cargo.toml +++ b/packages/server/Cargo.toml @@ -92,6 +92,7 @@ full = [ "dep:tower-layer", "dep:tokio-util", "dep:hyper-util", + "axum/default", ] core = [ "server_fn/axum-no-default", diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 0d87d3dc9..96729eb08 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -34,7 +34,7 @@ //! } //! //! fn app() -> Element { -//! +//! let mut text = use_signal(|| "Click the button to run a server function".to_string()); //! //! rsx! { //! button { diff --git a/packages/server/src/server.rs b/packages/server/src/server.rs index 433c01799..a2455ae4a 100644 --- a/packages/server/src/server.rs +++ b/packages/server/src/server.rs @@ -26,7 +26,9 @@ pub trait DioxusRouterExt: DioxusRouterFnExt { /// ```rust, no_run /// # #![allow(non_snake_case)] /// # use dioxus_lib::prelude::*; - /// # use dioxus_fullstack::prelude::*; + /// # use dioxus_server::prelude::*; + /// use dioxus_server::{DioxusRouterExt, DioxusRouterFnExt}; + /// /// #[tokio::main] /// async fn main() { /// let addr = dioxus::cli_config::fullstack_address_or_localhost(); @@ -51,7 +53,9 @@ pub trait DioxusRouterExt: DioxusRouterFnExt { /// ```rust, no_run /// # #![allow(non_snake_case)] /// # use dioxus_lib::prelude::*; - /// # use dioxus_fullstack::prelude::*; + /// # use dioxus_server::prelude::*; + /// use dioxus_server::{DioxusRouterExt, DioxusRouterFnExt}; + /// /// #[tokio::main] /// async fn main() { /// let addr = dioxus::cli_config::fullstack_address_or_localhost(); @@ -110,7 +114,7 @@ pub trait DioxusRouterFnExt { /// # Example /// ```rust, no_run /// # use dioxus_lib::prelude::*; - /// # use dioxus_fullstack::prelude::*; + /// # use dioxus_server::prelude::*; /// #[tokio::main] /// async fn main() { /// let addr = dioxus::cli_config::fullstack_address_or_localhost(); @@ -135,7 +139,7 @@ pub trait DioxusRouterFnExt { /// # Example /// ```rust, no_run /// # use dioxus_lib::prelude::*; - /// # use dioxus_fullstack::prelude::*; + /// # use dioxus_server::prelude::*; /// # use std::sync::Arc; /// #[tokio::main] /// async fn main() { @@ -159,7 +163,8 @@ pub trait DioxusRouterFnExt { /// # Example /// ```rust, no_run /// # use dioxus_lib::prelude::*; - /// # use dioxus_fullstack::prelude::*; + /// # use dioxus_server::prelude::*; + /// # use dioxus_server::prelude::*; /// #[tokio::main] /// async fn main() { /// let router = axum::Router::new() @@ -167,6 +172,11 @@ pub trait DioxusRouterFnExt { /// .into_make_service(); /// // ... /// } + /// + /// fn app() -> Element { + /// rsx! { "Hello World" } + /// } + /// ``` fn serve_api_application(self, cfg: ServeConfig, app: fn() -> Element) -> Self where Self: Sized; @@ -357,6 +367,7 @@ impl RenderHandleState { /// /// use axum::routing::get; /// use dioxus::prelude::*; +/// use dioxus_server::{RenderHandleState, render_handler, ServeConfig}; /// /// fn app() -> Element { /// rsx! {