From e8491d5cf4a4323248e979bcd3712debf40927d7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 27 Mar 2024 20:12:35 -0500 Subject: [PATCH] Require every field in router variants to be present in the route in the web history (#2159) * require every field to be present in the URL in the web history * remove desktop only extra field from the simple routes example --- packages/router-macro/Cargo.toml | 1 + packages/router-macro/src/lib.rs | 39 ++++++++++++++++++++++- packages/router/Cargo.toml | 3 +- packages/router/examples/simple_routes.rs | 6 ++-- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/router-macro/Cargo.toml b/packages/router-macro/Cargo.toml index 8334c276b..923169906 100644 --- a/packages/router-macro/Cargo.toml +++ b/packages/router-macro/Cargo.toml @@ -22,3 +22,4 @@ slab = { workspace = true } [features] default = [] +web = [] diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 0a0c2c8dc..c6fd70bfd 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -62,7 +62,6 @@ mod segment; /// user_id: usize, /// dynamic: usize, /// query: String, -/// extra: String, /// }, /// #[route("/hello_world")] /// // You can opt out of the layout by using the `!` prefix @@ -437,9 +436,47 @@ impl RouteEnum { site_map, }; + // If we're on the web, only the URL history is preserved between navigation. We need to warn the user that the segment is not present in the URL. + if cfg!(feature = "web") { + for variant in &data.variants { + for field in &variant.fields { + if !myself.field_present_in_url(field.ident.as_ref().unwrap()) { + return Err(syn::Error::new_spanned( + field.ident.as_ref().unwrap(), + format!("The `{}` field must be present in the url for the web history. You can include the field in the url by using the `#[route(\"/:{}\")]` attribute on the enum variant.", field.ident.as_ref().unwrap(), field.ident.as_ref().unwrap()), + )); + } + } + } + } + Ok(myself) } + fn field_present_in_url(&self, field: &Ident) -> bool { + let mut from_route = false; + + for nest in &self.nests { + if nest.dynamic_segments_names().any(|i| &i == field) { + from_route = true + } + } + for route in &self.routes { + for segment in &route.segments { + if segment.name().as_ref() == Some(field) { + from_route = true + } + } + if let Some(query) = &route.query { + if query.contains_ident(field) { + from_route = true + } + } + } + + from_route + } + fn impl_display(&self) -> TokenStream2 { let mut display_match = Vec::new(); diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index df4ee9d09..88041a741 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -35,7 +35,7 @@ default = [] ssr = ["dioxus-ssr/incremental", "tokio", "dioxus-fullstack?/server"] liveview = ["dioxus-liveview", "tokio", "dep:serde", "serde_json"] wasm_test = [] -web = ["gloo", "web-sys", "wasm-bindgen", "gloo-utils", "js-sys"] +web = ["gloo", "web-sys", "wasm-bindgen", "gloo-utils", "js-sys", "dioxus-router-macro/web"] fullstack = ["dioxus-fullstack"] [dev-dependencies] @@ -54,7 +54,6 @@ name = "incremental" required-features = ["ssr"] harness = false - # you need to comment this out when publishing since cargo workspaces is not smart enough to wipe this when dropping # dev-dependncey crates [target.'cfg(target_family = "wasm")'.dev-dependencies] diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 904b7d8e0..cb120deda 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -65,17 +65,16 @@ fn UserFrame(user_id: usize) -> Element { } #[component] -fn Route1(user_id: usize, dynamic: usize, query: String, extra: String) -> Element { +fn Route1(user_id: usize, dynamic: usize, query: String) -> Element { rsx! { pre { - "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query},\n\textra:{extra}\n}}" + "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query}\n}}" } Link { to: Route::Route1 { user_id, dynamic, query: String::new(), - extra: extra.clone() + ".", }, "Route1 with extra+\".\"" } @@ -191,7 +190,6 @@ enum Route { user_id: usize, dynamic: usize, query: String, - extra: String, }, #[route("/hello_world")] // You can opt out of the layout by using the `!` prefix