From 5a2ef78f080fb064f89e651c06565865b2a69c5d Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 02:37:38 +0200 Subject: [PATCH 01/10] rework a bunch of feature flags to be more principled --- .github/workflows/main-checks.yml | 6 +- .../src/bin/function_router.rs | 2 + examples/ssr_router/Cargo.toml | 2 +- examples/suspense/Cargo.toml | 2 +- examples/suspense/src/main.rs | 2 +- packages/yew-macro/Cargo.toml | 1 - packages/yew-router/Cargo.toml | 3 - packages/yew/Cargo.toml | 5 +- packages/yew/Makefile.toml | 3 +- packages/yew/src/app_handle.rs | 2 +- packages/yew/src/dom_bundle/bcomp.rs | 4 +- packages/yew/src/dom_bundle/blist.rs | 8 +- packages/yew/src/dom_bundle/bnode.rs | 4 +- packages/yew/src/dom_bundle/bportal.rs | 4 +- packages/yew/src/dom_bundle/btag/listeners.rs | 2 +- packages/yew/src/dom_bundle/btag/mod.rs | 12 +- packages/yew/src/dom_bundle/btext.rs | 8 +- packages/yew/src/dom_bundle/fragment.rs | 4 +- packages/yew/src/dom_bundle/mod.rs | 51 ++-- packages/yew/src/dom_bundle/utils.rs | 4 +- packages/yew/src/html/component/lifecycle.rs | 12 +- packages/yew/src/html/component/mod.rs | 18 +- packages/yew/src/html/component/scope.rs | 192 +++++++-------- packages/yew/src/html/mod.rs | 2 +- packages/yew/src/io_coop.rs | 23 +- packages/yew/src/lib.rs | 1 - packages/yew/src/renderer.rs | 2 +- packages/yew/src/scheduler.rs | 40 +-- packages/yew/src/suspense/component.rs | 7 +- packages/yew/src/suspense/hooks.rs | 231 +++++++++--------- packages/yew/src/suspense/suspension.rs | 34 +-- packages/yew/src/tests/mod.rs | 2 - packages/yew/src/virtual_dom/key.rs | 4 +- packages/yew/src/virtual_dom/mod.rs | 22 +- packages/yew/src/virtual_dom/vcomp.rs | 226 +++++++++-------- packages/yew/src/virtual_dom/vlist.rs | 3 +- packages/yew/src/virtual_dom/vsuspense.rs | 3 +- packages/yew/src/virtual_dom/vtag.rs | 3 +- packages/yew/src/virtual_dom/vtext.rs | 3 +- packages/yew/tests/mod.rs | 2 +- packages/yew/tests/suspense.rs | 2 +- packages/yew/tests/use_callback.rs | 2 +- packages/yew/tests/use_context.rs | 2 +- packages/yew/tests/use_effect.rs | 2 +- packages/yew/tests/use_memo.rs | 2 +- packages/yew/tests/use_reducer.rs | 2 +- packages/yew/tests/use_ref.rs | 2 +- packages/yew/tests/use_state.rs | 2 +- 48 files changed, 482 insertions(+), 493 deletions(-) diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml index d72853ead96..e00cc9a5d48 100644 --- a/.github/workflows/main-checks.yml +++ b/.github/workflows/main-checks.yml @@ -120,7 +120,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: -p yew --doc --features doc_test --target wasm32-unknown-unknown + args: -p yew --doc --features csr,hydration,ssr --target wasm32-unknown-unknown integration_tests: name: Integration Tests on ${{ matrix.toolchain }} @@ -159,8 +159,8 @@ jobs: - name: Run tests - yew run: | cd packages/yew - CHROMEDRIVER=$(which chromedriver) cargo test --features wasm_test --target wasm32-unknown-unknown - GECKODRIVER=$(which geckodriver) cargo test --features wasm_test --target wasm32-unknown-unknown + CHROMEDRIVER=$(which chromedriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown + GECKODRIVER=$(which geckodriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown - name: Run tests - yew-router run: | diff --git a/examples/function_router/src/bin/function_router.rs b/examples/function_router/src/bin/function_router.rs index 5f5017765d8..60571f7ab67 100644 --- a/examples/function_router/src/bin/function_router.rs +++ b/examples/function_router/src/bin/function_router.rs @@ -4,4 +4,6 @@ fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); #[cfg(feature = "csr")] yew::Renderer::::new().render(); + #[cfg(not(feature = "csr"))] + panic!("You must enable the csr feature to run this binary"); } diff --git a/examples/ssr_router/Cargo.toml b/examples/ssr_router/Cargo.toml index 49092a942f0..5b3789e6d2f 100644 --- a/examples/ssr_router/Cargo.toml +++ b/examples/ssr_router/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yew = { path = "../../packages/yew", features = ["ssr", "hydration", "trace_hydration"] } +yew = { path = "../../packages/yew", features = ["ssr", "hydration"] } function_router = { path = "../function_router" } log = "0.4" diff --git a/examples/suspense/Cargo.toml b/examples/suspense/Cargo.toml index 57caf1701ac..67cdd00b790 100644 --- a/examples/suspense/Cargo.toml +++ b/examples/suspense/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yew = { path = "../../packages/yew", features = ["tokio", "csr"] } +yew = { path = "../../packages/yew", features = ["csr"] } gloo-timers = { version = "0.2.2", features = ["futures"] } wasm-bindgen-futures = "0.4" wasm-bindgen = "0.2" diff --git a/examples/suspense/src/main.rs b/examples/suspense/src/main.rs index 496429fd6b8..a51d99266f2 100644 --- a/examples/suspense/src/main.rs +++ b/examples/suspense/src/main.rs @@ -26,7 +26,7 @@ fn app_content() -> HtmlResult { }) }; - let on_take_a_break = Callback::from(move |_| (resleep.clone())()); + let on_take_a_break = Callback::from(move |_| resleep()); Ok(html! {
diff --git a/packages/yew-macro/Cargo.toml b/packages/yew-macro/Cargo.toml index 85fb249eff4..45aa3a314a1 100644 --- a/packages/yew-macro/Cargo.toml +++ b/packages/yew-macro/Cargo.toml @@ -34,5 +34,4 @@ yew = { path = "../yew" } [build-dependencies] [features] -doc_test = [] lints = [] diff --git a/packages/yew-router/Cargo.toml b/packages/yew-router/Cargo.toml index 739f58f53e5..200f02e2d01 100644 --- a/packages/yew-router/Cargo.toml +++ b/packages/yew-router/Cargo.toml @@ -11,9 +11,6 @@ description = "A router implementation for the Yew framework" repository = "https://github.com/yewstack/yew" rust-version = "1.56.0" -[features] -wasm_test = [] - [dependencies] yew = { version = "0.19.3", path = "../yew", default-features= false } yew-router-macro = { version = "0.16.0", path = "../yew-router-macro" } diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index f7f4705cca9..e99933eb2be 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -90,14 +90,11 @@ features = [ ssr = ["futures", "html-escape"] csr = [] hydration = ["csr"] -trace_hydration = ["hydration"] -doc_test = ["csr", "hydration", "ssr"] -wasm_test = ["csr", "hydration", "ssr"] default = [] [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1.15.0", features = ["full"] } [package.metadata.docs.rs] -features = ["doc_test"] +features = ["csr", "hydration", "ssr"] rustdoc-args = ["--cfg", "documenting"] diff --git a/packages/yew/Makefile.toml b/packages/yew/Makefile.toml index 6dc84597086..cd02021592a 100644 --- a/packages/yew/Makefile.toml +++ b/packages/yew/Makefile.toml @@ -10,7 +10,7 @@ args = [ "--headless", "--", "--features", - "wasm_test" + "csr,hydration,ssr" ] [tasks.ssr-test] @@ -18,6 +18,7 @@ command = "cargo" args = ["test", "ssr_tests", "--features", "ssr"] [tasks.test] +args = ["test", "--all-targets", "--all-features"] dependencies = ["native-test", "wasm-test"] [tasks.clippy-feature-soundness] diff --git a/packages/yew/src/app_handle.rs b/packages/yew/src/app_handle.rs index de429705b06..1ed40396c0b 100644 --- a/packages/yew/src/app_handle.rs +++ b/packages/yew/src/app_handle.rs @@ -9,8 +9,8 @@ use crate::dom_bundle::BSubtree; use crate::html::{BaseComponent, NodeRef, Scope, Scoped}; /// An instance of an application. -#[derive(Debug)] #[cfg_attr(documenting, doc(cfg(feature = "csr")))] +#[derive(Debug)] pub struct AppHandle { /// `Scope` holder pub(crate) scope: Scope, diff --git a/packages/yew/src/dom_bundle/bcomp.rs b/packages/yew/src/dom_bundle/bcomp.rs index b1f4502a306..fb7dec3ba97 100644 --- a/packages/yew/src/dom_bundle/bcomp.rs +++ b/packages/yew/src/dom_bundle/bcomp.rs @@ -176,7 +176,7 @@ mod feat_hydration { } } -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod tests { use std::ops::Deref; @@ -481,7 +481,7 @@ mod tests { } } -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod layout_tests { extern crate self as yew; diff --git a/packages/yew/src/dom_bundle/blist.rs b/packages/yew/src/dom_bundle/blist.rs index 721c5423168..1d41e5dd0a2 100644 --- a/packages/yew/src/dom_bundle/blist.rs +++ b/packages/yew/src/dom_bundle/blist.rs @@ -504,11 +504,11 @@ mod feat_hydration { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests { extern crate self as yew; - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use crate::html; @@ -581,11 +581,11 @@ mod layout_tests { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests_keys { extern crate self as yew; - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use web_sys::Node; diff --git a/packages/yew/src/dom_bundle/bnode.rs b/packages/yew/src/dom_bundle/bnode.rs index c5bdd950694..104e803a494 100644 --- a/packages/yew/src/dom_bundle/bnode.rs +++ b/packages/yew/src/dom_bundle/bnode.rs @@ -291,9 +291,9 @@ mod feat_hydration { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests { - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use super::*; diff --git a/packages/yew/src/dom_bundle/bportal.rs b/packages/yew/src/dom_bundle/bportal.rs index 1e53d87371d..669ff6ad2af 100644 --- a/packages/yew/src/dom_bundle/bportal.rs +++ b/packages/yew/src/dom_bundle/bportal.rs @@ -118,11 +118,11 @@ impl BPortal { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests { extern crate self as yew; - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use yew::virtual_dom::VPortal; diff --git a/packages/yew/src/dom_bundle/btag/listeners.rs b/packages/yew/src/dom_bundle/btag/listeners.rs index 1d6dd283a86..55d708af39b 100644 --- a/packages/yew/src/dom_bundle/btag/listeners.rs +++ b/packages/yew/src/dom_bundle/btag/listeners.rs @@ -196,7 +196,7 @@ impl Registry { } } -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod tests { use std::marker::PhantomData; diff --git a/packages/yew/src/dom_bundle/btag/mod.rs b/packages/yew/src/dom_bundle/btag/mod.rs index 9645123029f..d96bb364aeb 100644 --- a/packages/yew/src/dom_bundle/btag/mod.rs +++ b/packages/yew/src/dom_bundle/btag/mod.rs @@ -265,13 +265,13 @@ impl BTag { self.key.as_ref() } - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] #[cfg(test)] fn reference(&self) -> &Element { &self.reference } - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] #[cfg(test)] fn children(&self) -> &[BNode] { match &self.inner { @@ -280,7 +280,7 @@ impl BTag { } } - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] #[cfg(test)] fn tag(&self) -> &str { match &self.inner { @@ -383,7 +383,7 @@ mod feat_hydration { } } -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod tests { use gloo_utils::document; @@ -975,11 +975,11 @@ mod tests { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests { extern crate self as yew; - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use crate::html; diff --git a/packages/yew/src/dom_bundle/btext.rs b/packages/yew/src/dom_bundle/btext.rs index 73b7d6230cc..7e6e0f1a908 100644 --- a/packages/yew/src/dom_bundle/btext.rs +++ b/packages/yew/src/dom_bundle/btext.rs @@ -157,12 +157,12 @@ mod feat_hydration { mod test { extern crate self as yew; - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use crate::html; - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] wasm_bindgen_test_configure!(run_in_browser); #[test] @@ -177,11 +177,11 @@ mod test { } } -#[cfg(all(test, feature = "wasm_test"))] +#[cfg(target_arch = "wasm32")] +#[cfg(test)] mod layout_tests { extern crate self as yew; - #[cfg(feature = "wasm_test")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use crate::html; diff --git a/packages/yew/src/dom_bundle/fragment.rs b/packages/yew/src/dom_bundle/fragment.rs index 6055ecab570..ccc9037044f 100644 --- a/packages/yew/src/dom_bundle/fragment.rs +++ b/packages/yew/src/dom_bundle/fragment.rs @@ -3,13 +3,13 @@ use std::ops::{Deref, DerefMut}; use web_sys::{Element, Node}; -use super::BSubtree; +use crate::dom_bundle::BSubtree; use crate::html::NodeRef; use crate::virtual_dom::Collectable; /// A Hydration Fragment #[derive(Default, Debug, Clone, PartialEq, Eq)] -pub(crate) struct Fragment(VecDeque); +pub struct Fragment(VecDeque); impl Deref for Fragment { type Target = VecDeque; diff --git a/packages/yew/src/dom_bundle/mod.rs b/packages/yew/src/dom_bundle/mod.rs index aff317c277f..3d497cb687a 100644 --- a/packages/yew/src/dom_bundle/mod.rs +++ b/packages/yew/src/dom_bundle/mod.rs @@ -19,9 +19,6 @@ mod btag; mod btext; mod subtree_root; -#[cfg(feature = "hydration")] -mod fragment; - mod traits; mod utils; @@ -32,18 +29,24 @@ use bportal::BPortal; use bsuspense::BSuspense; use btag::{BTag, Registry}; use btext::BText; -#[cfg(feature = "hydration")] -pub(crate) use fragment::Fragment; -pub use subtree_root::set_event_bubbling; -pub(crate) use subtree_root::BSubtree; use subtree_root::EventDescriptor; -#[cfg(feature = "hydration")] -use traits::Hydratable; +pub use subtree_root::{set_event_bubbling, BSubtree}; use traits::{Reconcilable, ReconcileTarget}; -#[cfg(feature = "hydration")] -use utils::node_type_str; use utils::{insert_node, test_log}; +#[cfg(feature = "hydration")] +#[path = "."] +mod feat_hydration { + #[path = "./fragment.rs"] + mod fragment; + pub use fragment::Fragment; + + pub(super) use super::traits::Hydratable; + pub(super) use super::utils::node_type_str; +} +#[cfg(feature = "hydration")] +pub(crate) use feat_hydration::*; + /// A Bundle. /// /// Each component holds a bundle that represents a realised layout, designated by a [VNode]. @@ -84,20 +87,16 @@ impl Bundle { } #[cfg(feature = "hydration")] -mod feat_hydration { - use super::*; - - impl Bundle { - /// Creates a bundle by hydrating a virtual dom layout. - pub fn hydrate( - root: &BSubtree, - parent_scope: &AnyScope, - parent: &Element, - fragment: &mut Fragment, - node: VNode, - ) -> (NodeRef, Self) { - let (node_ref, bundle) = node.hydrate(root, parent_scope, parent, fragment); - (node_ref, Self(bundle)) - } +impl Bundle { + /// Creates a bundle by hydrating a virtual dom layout. + pub fn hydrate( + root: &BSubtree, + parent_scope: &AnyScope, + parent: &Element, + fragment: &mut Fragment, + node: VNode, + ) -> (NodeRef, Self) { + let (node_ref, bundle) = node.hydrate(root, parent_scope, parent, fragment); + (node_ref, Self(bundle)) } } diff --git a/packages/yew/src/dom_bundle/utils.rs b/packages/yew/src/dom_bundle/utils.rs index 8f0eb44f79e..f4973dad37a 100644 --- a/packages/yew/src/dom_bundle/utils.rs +++ b/packages/yew/src/dom_bundle/utils.rs @@ -13,13 +13,13 @@ pub(super) fn insert_node(node: &Node, parent: &Element, next_sibling: Option<&N }; } -#[cfg(all(test, feature = "wasm_test", verbose_tests))] +#[cfg(all(test, target_arch = "wasm32", verbose_tests))] macro_rules! test_log { ($fmt:literal, $($arg:expr),* $(,)?) => { ::wasm_bindgen_test::console_log!(concat!("\t ", $fmt), $($arg),*); }; } -#[cfg(not(all(test, feature = "wasm_test", verbose_tests)))] +#[cfg(not(all(test, target_arch = "wasm32", verbose_tests)))] macro_rules! test_log { ($fmt:literal, $($arg:expr),* $(,)?) => { // Only type-check the format expression, do not run any side effects diff --git a/packages/yew/src/html/component/lifecycle.rs b/packages/yew/src/html/component/lifecycle.rs index 517c123d341..56df09f5781 100644 --- a/packages/yew/src/html/component/lifecycle.rs +++ b/packages/yew/src/html/component/lifecycle.rs @@ -400,8 +400,6 @@ impl Runnable for UpdateRunner { pub(crate) struct DestroyRunner { pub state: Shared>, - - #[cfg(feature = "csr")] pub parent_to_detach: bool, } @@ -629,7 +627,7 @@ mod feat_csr { #[cfg(feature = "csr")] use feat_csr::*; -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod tests { extern crate self as yew; @@ -686,7 +684,7 @@ mod tests { struct Props { lifecycle: Rc>>, #[allow(dead_code)] - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] create_message: Option, update_message: RefCell>, view_message: RefCell>, @@ -703,7 +701,7 @@ mod tests { fn create(ctx: &Context) -> Self { ctx.props().lifecycle.borrow_mut().push("create".into()); - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] if let Some(msg) = ctx.props().create_message { ctx.link().send_message(msg); } @@ -790,7 +788,7 @@ mod tests { test_lifecycle( Props { lifecycle: lifecycle.clone(), - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] create_message: Some(false), ..Props::default() }, @@ -871,7 +869,7 @@ mod tests { test_lifecycle( Props { lifecycle, - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] create_message: Some(true), update_message: RefCell::new(Some(true)), ..Props::default() diff --git a/packages/yew/src/html/component/mod.rs b/packages/yew/src/html/component/mod.rs index edd1809181d..8f19f066de0 100644 --- a/packages/yew/src/html/component/mod.rs +++ b/packages/yew/src/html/component/mod.rs @@ -21,6 +21,9 @@ use super::{Html, HtmlResult, IntoHtmlResult}; #[cfg(debug_assertions)] #[cfg(any(feature = "csr", feature = "ssr"))] mod feat_csr_ssr { + use wasm_bindgen::prelude::wasm_bindgen; + use wasm_bindgen::JsValue; + thread_local! { static EVENT_HISTORY: std::cell::RefCell>> = Default::default(); @@ -37,13 +40,16 @@ mod feat_csr_ssr { } /// Get [Component] event log from lifecycle debugging registry - #[allow(dead_code)] - pub(crate) fn get_event_log(comp_id: usize) -> Vec { + #[wasm_bindgen(js_name = "yewGetEventLog")] + pub fn _get_event_log(comp_id: usize) -> Option> { EVENT_HISTORY.with(|h| { - h.borrow() - .get(&comp_id) - .map(|l| (*l).clone()) - .unwrap_or_default() + Some( + h.borrow() + .get(&comp_id)? + .iter() + .map(|l| (*l).clone().into()) + .collect(), + ) }) } } diff --git a/packages/yew/src/html/component/scope.rs b/packages/yew/src/html/component/scope.rs index dc579b9c219..94362ba9487 100644 --- a/packages/yew/src/html/component/scope.rs +++ b/packages/yew/src/html/component/scope.rs @@ -1,18 +1,18 @@ //! Component scope module use std::any::{Any, TypeId}; -#[cfg(any(feature = "csr", feature = "ssr"))] -use std::cell::RefCell; +use std::future::Future; use std::marker::PhantomData; use std::ops::Deref; use std::rc::Rc; use std::{fmt, iter}; #[cfg(any(feature = "csr", feature = "ssr"))] -use super::lifecycle::{ComponentState, UpdateRunner}; +use super::lifecycle::ComponentState; use super::BaseComponent; use crate::callback::Callback; use crate::context::{ContextHandle, ContextProvider}; +use crate::io_coop::spawn_local; #[cfg(any(feature = "csr", feature = "ssr"))] use crate::scheduler::Shared; @@ -178,6 +178,81 @@ impl Scope { ) -> Option<(T, ContextHandle)> { AnyScope::from(self.clone()).context(callback) } + + /// This method asynchronously awaits a [Future] that returns a message and sends it + /// to the linked component. + /// + /// # Panics + /// If the future panics, then the promise will not resolve, and will leak. + pub fn send_future(&self, future: Fut) + where + Msg: Into, + Fut: Future + 'static, + { + let link = self.clone(); + spawn_local(async move { + let message: COMP::Message = future.await.into(); + link.send_message(message); + }); + } + + /// This method creates a [`Callback`] which, when emitted, asynchronously awaits the + /// message returned from the passed function before sending it to the linked component. + /// + /// # Panics + /// If the future panics, then the promise will not resolve, and will leak. + pub fn callback_future(&self, function: F) -> Callback + where + Msg: Into, + Fut: Future + 'static, + F: Fn(IN) -> Fut + 'static, + { + let link = self.clone(); + + let closure = move |input: IN| { + link.send_future(function(input)); + }; + + closure.into() + } + + /// Asynchronously send a batch of messages to a component. This asynchronously awaits the + /// passed [Future], before sending the message batch to the linked component. + /// + /// # Panics + /// If the future panics, then the promise will not resolve, and will leak. + pub fn send_future_batch(&self, future: Fut) + where + Fut: Future + 'static, + Fut::Output: SendAsMessage, + { + let link = self.clone(); + let js_future = async move { + future.await.send(&link); + }; + spawn_local(js_future); + } + + /// Returns the linked component if available + pub fn get_component(&self) -> Option + '_> { + self.arch_get_component() + } + + /// Send a message to the component. + pub fn send_message(&self, msg: T) + where + T: Into, + { + self.arch_send_message(msg) + } + + /// Send a batch of messages to the component. + /// + /// This is slightly more efficient than calling [`send_message`](Self::send_message) + /// in a loop. + pub fn send_message_batch(&self, messages: Vec) { + self.arch_send_message_batch(messages) + } } #[cfg(feature = "ssr")] @@ -235,8 +310,6 @@ mod feat_ssr { scheduler::push_component_destroy(Box::new(DestroyRunner { state: self.state.clone(), - - #[cfg(feature = "csr")] parent_to_detach: false, })); scheduler::start(); @@ -250,32 +323,27 @@ mod feat_no_csr_ssr { // Skeleton code to provide public methods when no renderer are enabled. impl Scope { - /// Returns the linked component if available - pub fn get_component(&self) -> Option + '_> { + pub(super) fn arch_get_component(&self) -> Option + '_> { Option::<&COMP>::None } - /// Send a message to the component. - pub fn send_message(&self, _msg: T) + pub(super) fn arch_send_message(&self, _msg: T) where T: Into, { } - /// Send a batch of messages to the component. - /// - /// This is slightly more efficient than calling [`send_message`](Self::send_message) - /// in a loop. - pub fn send_message_batch(&self, _messages: Vec) {} + pub(super) fn arch_send_message_batch(&self, _messages: Vec) {} } } #[cfg(any(feature = "ssr", feature = "csr"))] mod feat_csr_ssr { - use std::cell::Ref; + use std::cell::{Ref, RefCell}; use std::sync::atomic::{AtomicUsize, Ordering}; use super::*; + use crate::html::component::lifecycle::UpdateRunner; use crate::scheduler::{self, Shared}; #[derive(Debug)] @@ -339,8 +407,7 @@ mod feat_csr_ssr { } } - /// Returns the linked component if available - pub fn get_component(&self) -> Option + '_> { + pub(super) fn arch_get_component(&self) -> Option + '_> { self.state.try_borrow().ok().and_then(|state_ref| { state_ref.as_ref()?; // TODO: Replace unwrap with Ref::filter_map once it becomes stable. @@ -362,8 +429,7 @@ mod feat_csr_ssr { scheduler::start(); } - /// Send a message to the component. - pub fn send_message(&self, msg: T) + pub(super) fn arch_send_message(&self, msg: T) where T: Into, { @@ -373,11 +439,7 @@ mod feat_csr_ssr { } } - /// Send a batch of messages to the component. - /// - /// This is slightly more efficient than calling [`send_message`](Self::send_message) - /// in a loop. - pub fn send_message_batch(&self, mut messages: Vec) { + pub(super) fn arch_send_message_batch(&self, mut messages: Vec) { let msg_len = messages.len(); // The queue was empty, so we queue the update @@ -526,6 +588,8 @@ mod feat_csr { } } } +#[cfg(feature = "csr")] +pub(crate) use feat_csr::*; #[cfg_attr(documenting, doc(cfg(feature = "hydration")))] #[cfg(feature = "hydration")] @@ -562,12 +626,11 @@ mod feat_hydration { // This is very helpful to see which component is failing during hydration // which means this component may not having a stable layout / differs between // client-side and server-side. - #[cfg(all(debug_assertions, feature = "trace_hydration"))] - gloo::console::trace!(format!( - "queuing hydration of: {}(ID: {:?})", - std::any::type_name::(), - self.id - )); + #[cfg(debug_assertions)] + super::super::log_event( + self.id, + format!("hydration(type = {})", std::any::type_name::()), + ); #[cfg(debug_assertions)] let collectable = Collectable::Component(std::any::type_name::()); @@ -603,75 +666,6 @@ mod feat_hydration { } } } -#[cfg(feature = "csr")] -pub(crate) use feat_csr::*; - -#[cfg_attr(documenting, doc(cfg(any(target_arch = "wasm32", feature = "tokio"))))] -#[cfg(any(target_arch = "wasm32", feature = "tokio"))] -mod feat_io { - use std::future::Future; - - use super::*; - use crate::io_coop::spawn_local; - - impl Scope { - /// This method creates a [`Callback`] which, when emitted, asynchronously awaits the - /// message returned from the passed function before sending it to the linked component. - /// - /// # Panics - /// If the future panics, then the promise will not resolve, and will leak. - pub fn callback_future(&self, function: FN) -> Callback - where - M: Into, - FU: Future + 'static, - FN: Fn(IN) -> FU + 'static, - { - let link = self.clone(); - - let closure = move |input: IN| { - let future: FU = function(input); - link.send_future(future); - }; - - closure.into() - } - - /// This method asynchronously awaits a [Future] that returns a message and sends it - /// to the linked component. - /// - /// # Panics - /// If the future panics, then the promise will not resolve, and will leak. - pub fn send_future(&self, future: F) - where - M: Into, - F: Future + 'static, - { - let link = self.clone(); - let js_future = async move { - let message: COMP::Message = future.await.into(); - link.send_message(message); - }; - spawn_local(js_future); - } - - /// Asynchronously send a batch of messages to a component. This asynchronously awaits the - /// passed [Future], before sending the message batch to the linked component. - /// - /// # Panics - /// If the future panics, then the promise will not resolve, and will leak. - pub fn send_future_batch(&self, future: F) - where - F: Future + 'static, - F::Output: SendAsMessage, - { - let link = self.clone(); - let js_future = async move { - future.await.send(&link); - }; - spawn_local(js_future); - } - } -} /// Defines a message type that can be sent to a component. /// Used for the return value of closure given to diff --git a/packages/yew/src/html/mod.rs b/packages/yew/src/html/mod.rs index 1953b545168..beda1bd3d98 100644 --- a/packages/yew/src/html/mod.rs +++ b/packages/yew/src/html/mod.rs @@ -180,7 +180,7 @@ pub fn create_portal(child: Html, host: Element) -> Html { VNode::VPortal(VPortal::new(child, host)) } -#[cfg(feature = "wasm_test")] +#[cfg(target_arch = "wasm32")] #[cfg(test)] mod tests { use gloo_utils::document; diff --git a/packages/yew/src/io_coop.rs b/packages/yew/src/io_coop.rs index 17df422f3f1..6fdd72ac3d4 100644 --- a/packages/yew/src/io_coop.rs +++ b/packages/yew/src/io_coop.rs @@ -1,15 +1,12 @@ //! module that provides io compatibility over browser tasks and other async io tasks (e.g.: tokio) #[cfg(target_arch = "wasm32")] -mod io_wasm_bindgen { +mod arch { pub use wasm_bindgen_futures::spawn_local; } -#[cfg(target_arch = "wasm32")] -pub(crate) use io_wasm_bindgen::*; - -#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] -mod io_tokio { +#[cfg(not(target_arch = "wasm32"))] +mod arch { use std::future::Future; // spawn_local in tokio is more powerful, but we need to adjust the function signature to match @@ -19,9 +16,17 @@ mod io_tokio { where F: Future + 'static, { - tokio::task::spawn_local(f); + #[cfg(feature = "tokio")] + ::tokio::task::spawn_local(f); + #[cfg(not(feature = "tokio"))] + { + let _ = f; + panic!( + r#"No scheduler configured for this platform, features related to async can't be used. + Either compile with `target_arch = "wasm32", or enable the `tokio` feature."# + ); + } } } -#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] -pub(crate) use io_tokio::*; +pub(crate) use arch::*; diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index 16360a8eee8..1532277021b 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -279,7 +279,6 @@ pub mod macros { pub mod callback; pub mod context; -#[cfg_attr(documenting, doc(cfg(feature = "csr")))] #[cfg(feature = "csr")] mod dom_bundle; pub mod functional; diff --git a/packages/yew/src/renderer.rs b/packages/yew/src/renderer.rs index 620cdbe6e1f..fbf57fd1894 100644 --- a/packages/yew/src/renderer.rs +++ b/packages/yew/src/renderer.rs @@ -29,8 +29,8 @@ fn set_default_panic_hook() { /// The Yew Renderer. /// /// This is the main entry point of a Yew application. -#[derive(Debug)] #[cfg_attr(documenting, doc(cfg(feature = "csr")))] +#[derive(Debug)] #[must_use = "Renderer does nothing unless render() is called."] pub struct Renderer where diff --git a/packages/yew/src/scheduler.rs b/packages/yew/src/scheduler.rs index 225eaf96bef..2216e16c1f7 100644 --- a/packages/yew/src/scheduler.rs +++ b/packages/yew/src/scheduler.rs @@ -34,7 +34,6 @@ struct Scheduler { /// rendering parent first. render: BTreeMap>, render_first: BTreeMap>, - #[cfg(feature = "hydration")] render_priority: BTreeMap>, /// Binary Tree Map to guarantee children rendered are always called before parent calls @@ -165,38 +164,31 @@ pub(crate) fn start_now() { } #[cfg(target_arch = "wasm32")] -mod target_wasm { - use super::*; +mod arch { use crate::io_coop::spawn_local; /// We delay the start of the scheduler to the end of the micro task queue. /// So any messages that needs to be queued can be queued. pub(crate) fn start() { spawn_local(async { - start_now(); + super::start_now(); }); } } -#[cfg(target_arch = "wasm32")] -pub(crate) use target_wasm::*; - #[cfg(not(target_arch = "wasm32"))] -mod target_native { - use super::*; - +mod arch { // Delayed rendering is not very useful in the context of server-side rendering. // There are no event listeners or other high priority events that need to be // processed and we risk of having a future un-finished. // Until scheduler is future-capable which means we can join inside a future, // it can remain synchronous. pub(crate) fn start() { - start_now(); + super::start_now(); } } -#[cfg(not(target_arch = "wasm32"))] -pub(crate) use target_native::*; +pub(crate) use arch::*; impl Scheduler { /// Fill vector with tasks to be executed according to Runnable type execution priority @@ -244,21 +236,15 @@ impl Scheduler { // Priority rendering // // This is needed for hydration susequent render to fix node refs. - #[cfg(feature = "hydration")] + if let Some(r) = self + .render_priority + .keys() + .next() + .cloned() + .and_then(|m| self.render_priority.remove(&m)) { - if let Some(r) = self - .render_priority - .keys() - .next() - .cloned() - .and_then(|m| self.render_priority.remove(&m)) - { - to_run.push(r); - } - - if !to_run.is_empty() { - return; - } + to_run.push(r); + return; } if !self.rendered_first.is_empty() { diff --git a/packages/yew/src/suspense/component.rs b/packages/yew/src/suspense/component.rs index 99fc5eb9899..38f81d25448 100644 --- a/packages/yew/src/suspense/component.rs +++ b/packages/yew/src/suspense/component.rs @@ -15,10 +15,6 @@ pub struct SuspenseProps { #[cfg(any(feature = "csr", feature = "ssr"))] mod feat_csr_ssr { use super::*; - #[cfg(feature = "hydration")] - use crate::callback::Callback; - #[cfg(feature = "hydration")] - use crate::html::RenderMode; use crate::html::{Children, Component, Context, Html, Scope}; use crate::suspense::Suspension; #[cfg(feature = "hydration")] @@ -57,6 +53,9 @@ mod feat_csr_ssr { // We create a suspension to block suspense until its rendered method is notified. #[cfg(feature = "hydration")] let (suspensions, hydration_handle) = { + use crate::callback::Callback; + use crate::html::RenderMode; + match ctx.mode() { RenderMode::Hydration => { let link = ctx.link().clone(); diff --git a/packages/yew/src/suspense/hooks.rs b/packages/yew/src/suspense/hooks.rs index 73f62dd4fac..611f6ea2535 100644 --- a/packages/yew/src/suspense/hooks.rs +++ b/packages/yew/src/suspense/hooks.rs @@ -1,133 +1,126 @@ -#[cfg_attr(documenting, doc(cfg(any(target_arch = "wasm32", feature = "tokio"))))] -#[cfg(any(target_arch = "wasm32", feature = "tokio"))] -mod feat_futures { - use std::cell::Cell; - use std::fmt; - use std::future::Future; - use std::ops::Deref; - use std::rc::Rc; +use std::cell::Cell; +use std::fmt; +use std::future::Future; +use std::ops::Deref; +use std::rc::Rc; - use yew::prelude::*; - use yew::suspense::{Suspension, SuspensionResult}; +use yew::prelude::*; +use yew::suspense::{Suspension, SuspensionResult}; - /// This hook is used to await a future in a suspending context. - /// - /// A [Suspension] is created from the passed future and the result of the future - /// is the output of the suspension. - pub struct UseFutureHandle { - inner: UseStateHandle>, - } +/// This hook is used to await a future in a suspending context. +/// +/// A [Suspension] is created from the passed future and the result of the future +/// is the output of the suspension. +pub struct UseFutureHandle { + inner: UseStateHandle>, +} - impl Deref for UseFutureHandle { - type Target = O; +impl Deref for UseFutureHandle { + type Target = O; - fn deref(&self) -> &Self::Target { - &*self.inner.as_ref().unwrap() - } + fn deref(&self) -> &Self::Target { + &*self.inner.as_ref().unwrap() } +} - impl fmt::Debug for UseFutureHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("UseFutureHandle") - .field("value", &format!("{:?}", self.inner)) - .finish() - } +impl fmt::Debug for UseFutureHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UseFutureHandle") + .field("value", &format!("{:?}", self.inner)) + .finish() } +} - /// Use the result of an async computation, suspending while waiting. - /// - /// Awaits the future returned from the first call to `init_f`, and returns - /// its result in a [`UseFutureHandle`]. Always suspends initially, even if - /// the future is immediately [ready]. - /// - /// [ready]: std::task::Poll::Ready - /// - /// # Example - /// - /// ``` - /// # use yew::prelude::*; - /// # use yew::suspense::use_future; - /// use gloo_net::http::Request; - /// - /// const URL: &str = "https://en.wikipedia.org/w/api.php?\ - /// action=query&origin=*&format=json&generator=search&\ - /// gsrnamespace=0&gsrlimit=5&gsrsearch='New_England_Patriots'"; - /// - /// #[function_component] - /// fn WikipediaSearch() -> HtmlResult { - /// let res = use_future(|| async { Request::new(URL).send().await?.text().await })?; - /// let result_html = match *res { - /// Ok(ref res) => html! { res }, - /// Err(ref failure) => failure.to_string().into(), - /// }; - /// Ok(html! { - ///

- /// {"Wikipedia search result: "} - /// {result_html} - ///

- /// }) - /// } - /// ``` - #[hook] - pub fn use_future(init_f: F) -> SuspensionResult> - where - F: FnOnce() -> T, - T: Future + 'static, - O: 'static, - { - use_future_with_deps(move |_| init_f(), ()) - } +/// Use the result of an async computation, suspending while waiting. +/// +/// Awaits the future returned from the first call to `init_f`, and returns +/// its result in a [`UseFutureHandle`]. Always suspends initially, even if +/// the future is immediately [ready]. +/// +/// [ready]: std::task::Poll::Ready +/// +/// # Example +/// +/// ``` +/// # use yew::prelude::*; +/// # use yew::suspense::use_future; +/// use gloo_net::http::Request; +/// +/// const URL: &str = "https://en.wikipedia.org/w/api.php?\ +/// action=query&origin=*&format=json&generator=search&\ +/// gsrnamespace=0&gsrlimit=5&gsrsearch='New_England_Patriots'"; +/// +/// #[function_component] +/// fn WikipediaSearch() -> HtmlResult { +/// let res = use_future(|| async { Request::new(URL).send().await?.text().await })?; +/// let result_html = match *res { +/// Ok(ref res) => html! { res }, +/// Err(ref failure) => failure.to_string().into(), +/// }; +/// Ok(html! { +///

+/// {"Wikipedia search result: "} +/// {result_html} +///

+/// }) +/// } +/// ``` +#[hook] +pub fn use_future(init_f: F) -> SuspensionResult> +where + F: FnOnce() -> T, + T: Future + 'static, + O: 'static, +{ + use_future_with_deps(move |_| init_f(), ()) +} - /// Use the result of an async computation with dependencies, suspending while waiting. - /// - /// Awaits the future returned from `f` for the latest `deps`. Even if the future is immediately - /// [ready], the hook suspends at least once. If the dependencies - /// change while a future is still pending, the result is never used. This guarantees that your - /// component always sees up-to-date values while it is not suspended. - /// - /// [ready]: std::task::Poll::Ready - #[hook] - pub fn use_future_with_deps(f: F, deps: D) -> SuspensionResult> - where - F: FnOnce(Rc) -> T, - T: Future + 'static, - O: 'static, - D: PartialEq + 'static, - { - let output = use_state(|| None); - // We only commit a result if it comes from the latest spawned future. Otherwise, this - // might trigger pointless updates or even override newer state. - let latest_id = use_state(|| Cell::new(0u32)); +/// Use the result of an async computation with dependencies, suspending while waiting. +/// +/// Awaits the future returned from `f` for the latest `deps`. Even if the future is immediately +/// [ready], the hook suspends at least once. If the dependencies +/// change while a future is still pending, the result is never used. This guarantees that your +/// component always sees up-to-date values while it is not suspended. +/// +/// [ready]: std::task::Poll::Ready +#[hook] +pub fn use_future_with_deps(f: F, deps: D) -> SuspensionResult> +where + F: FnOnce(Rc) -> T, + T: Future + 'static, + O: 'static, + D: PartialEq + 'static, +{ + let output = use_state(|| None); + // We only commit a result if it comes from the latest spawned future. Otherwise, this + // might trigger pointless updates or even override newer state. + let latest_id = use_state(|| Cell::new(0u32)); - let suspension = { - let output = output.clone(); + let suspension = { + let output = output.clone(); - use_memo_base( - move |deps| { - let self_id = latest_id.get().wrapping_add(1); - // As long as less than 2**32 futures are in flight wrapping_add is fine - (*latest_id).set(self_id); - let deps = Rc::new(deps); - let task = f(deps.clone()); - let suspension = Suspension::from_future(async move { - let result = task.await; - if latest_id.get() == self_id { - output.set(Some(result)); - } - }); - (suspension, deps) - }, - deps, - ) - }; + use_memo_base( + move |deps| { + let self_id = latest_id.get().wrapping_add(1); + // As long as less than 2**32 futures are in flight wrapping_add is fine + (*latest_id).set(self_id); + let deps = Rc::new(deps); + let task = f(deps.clone()); + let suspension = Suspension::from_future(async move { + let result = task.await; + if latest_id.get() == self_id { + output.set(Some(result)); + } + }); + (suspension, deps) + }, + deps, + ) + }; - if suspension.resumed() { - Ok(UseFutureHandle { inner: output }) - } else { - Err((*suspension).clone()) - } + if suspension.resumed() { + Ok(UseFutureHandle { inner: output }) + } else { + Err((*suspension).clone()) } } - -#[cfg(any(target_arch = "wasm32", feature = "tokio"))] -pub use feat_futures::*; diff --git a/packages/yew/src/suspense/suspension.rs b/packages/yew/src/suspense/suspension.rs index 501ab5c20f3..01f0dd8849d 100644 --- a/packages/yew/src/suspense/suspension.rs +++ b/packages/yew/src/suspense/suspension.rs @@ -7,6 +7,7 @@ use std::task::{Context, Poll}; use thiserror::Error; +use crate::io_coop::spawn_local; use crate::Callback; thread_local! { @@ -56,6 +57,18 @@ impl Suspension { self.resumed.load(Ordering::Relaxed) } + /// Creates a Suspension that resumes when the [`Future`] resolves. + pub fn from_future(f: impl Future + 'static) -> Self { + let (self_, handle) = Self::new(); + + spawn_local(async move { + f.await; + handle.resume(); + }); + + self_ + } + /// Listens to a suspension and get notified when it resumes. pub(crate) fn listen(&self, cb: Callback) { if self.resumed() { @@ -125,24 +138,3 @@ impl Drop for SuspensionHandle { self.inner.resume_by_ref(); } } - -#[cfg_attr(documenting, doc(cfg(any(target_arch = "wasm32", feature = "tokio"))))] -#[cfg(any(target_arch = "wasm32", feature = "tokio"))] -mod feat_io { - use super::*; - use crate::io_coop::spawn_local; - - impl Suspension { - /// Creates a Suspension that resumes when the [`Future`] resolves. - pub fn from_future(f: impl Future + 'static) -> Self { - let (self_, handle) = Self::new(); - - spawn_local(async move { - f.await; - handle.resume(); - }); - - self_ - } - } -} diff --git a/packages/yew/src/tests/mod.rs b/packages/yew/src/tests/mod.rs index 1c6c0361617..7c8881b072f 100644 --- a/packages/yew/src/tests/mod.rs +++ b/packages/yew/src/tests/mod.rs @@ -1,3 +1 @@ -#[cfg(feature = "csr")] -#[cfg_attr(documenting, doc(cfg(feature = "csr")))] pub mod layout_tests; diff --git a/packages/yew/src/virtual_dom/key.rs b/packages/yew/src/virtual_dom/key.rs index 4a0cadd882a..3c0ab2bbdf3 100644 --- a/packages/yew/src/virtual_dom/key.rs +++ b/packages/yew/src/virtual_dom/key.rs @@ -72,12 +72,12 @@ key_impl_from_to_string!(isize); mod test { use std::rc::Rc; - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use crate::html; - #[cfg(feature = "wasm_test")] + #[cfg(target_arch = "wasm32")] wasm_bindgen_test_configure!(run_in_browser); #[test] diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs index 66fc22cff6c..44064e1f9aa 100644 --- a/packages/yew/src/virtual_dom/mod.rs +++ b/packages/yew/src/virtual_dom/mod.rs @@ -204,44 +204,36 @@ mod tests_attr_value { #[cfg(any(feature = "ssr", feature = "hydration"))] mod feat_ssr_hydration { + #[cfg(debug_assertions)] + type ComponentName = &'static str; + #[cfg(not(debug_assertions))] + type ComponentName = (); /// A collectable. /// /// This indicates a kind that can be collected from fragment to be processed at a later time - pub(crate) enum Collectable { - #[cfg(debug_assertions)] - Component(&'static str), - #[cfg(not(debug_assertions))] - Component, + pub enum Collectable { + Component(ComponentName), Suspense, } impl Collectable { pub fn open_start_mark(&self) -> &'static str { match self { - #[cfg(debug_assertions)] Self::Component(_) => "<[", - #[cfg(not(debug_assertions))] - Self::Component => "<[", Self::Suspense => " &'static str { match self { - #[cfg(debug_assertions)] Self::Component(_) => " " " &'static str { match self { - #[cfg(debug_assertions)] Self::Component(_) => "]>", - #[cfg(not(debug_assertions))] - Self::Component => "]>", Self::Suspense => ">", } } @@ -282,7 +274,7 @@ mod feat_ssr_hydration { #[cfg(debug_assertions)] Self::Component(m) => format!("Component({})", m).into(), #[cfg(not(debug_assertions))] - Self::Component => "Component".into(), + Self::Component(_) => "Component".into(), Self::Suspense => "Suspense".into(), } } diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index c330b12ec8e..e5cf16420a7 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -4,20 +4,7 @@ use std::any::TypeId; use std::fmt; use std::rc::Rc; -#[cfg(feature = "ssr")] -use futures::future::{FutureExt, LocalBoxFuture}; -#[cfg(feature = "csr")] -use web_sys::Element; - use super::Key; -#[cfg(feature = "csr")] -use crate::dom_bundle::BSubtree; -#[cfg(feature = "hydration")] -use crate::dom_bundle::Fragment; -#[cfg(feature = "csr")] -use crate::html::Scoped; -#[cfg(any(feature = "ssr", feature = "csr"))] -use crate::html::{AnyScope, Scope}; use crate::html::{BaseComponent, NodeRef}; /// A virtual component. @@ -50,39 +37,12 @@ impl Clone for VComp { } } -pub(crate) trait Mountable { - fn copy(&self) -> Box; +// Used as a substitute trait when features are not enabled +pub(crate) trait _Empty {} +impl _Empty for T {} - #[cfg(feature = "csr")] - fn mount( - self: Box, - root: &BSubtree, - node_ref: NodeRef, - parent_scope: &AnyScope, - parent: Element, - next_sibling: NodeRef, - ) -> Box; - - #[cfg(feature = "csr")] - fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef); - - #[cfg(feature = "ssr")] - fn render_to_string<'a>( - &'a self, - w: &'a mut String, - parent_scope: &'a AnyScope, - hydratable: bool, - ) -> LocalBoxFuture<'a, ()>; - - #[cfg(feature = "hydration")] - fn hydrate( - self: Box, - root: BSubtree, - parent_scope: &AnyScope, - parent: Element, - fragment: &mut Fragment, - node_ref: NodeRef, - ) -> Box; +pub(crate) trait Mountable: MountableCsr + MountableHydrate + MountableSsr { + fn copy(&self) -> Box; } pub(crate) struct PropsWrapper { @@ -102,58 +62,6 @@ impl Mountable for PropsWrapper { }; Box::new(wrapper) } - - #[cfg(feature = "csr")] - fn mount( - self: Box, - root: &BSubtree, - node_ref: NodeRef, - parent_scope: &AnyScope, - parent: Element, - next_sibling: NodeRef, - ) -> Box { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope.mount_in_place(root.clone(), parent, next_sibling, node_ref, self.props); - - Box::new(scope) - } - - #[cfg(feature = "csr")] - fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef) { - let scope: Scope = scope.to_any().downcast::(); - scope.reuse(self.props, next_sibling); - } - - #[cfg(feature = "ssr")] - fn render_to_string<'a>( - &'a self, - w: &'a mut String, - parent_scope: &'a AnyScope, - hydratable: bool, - ) -> LocalBoxFuture<'a, ()> { - async move { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope - .render_to_string(w, self.props.clone(), hydratable) - .await; - } - .boxed_local() - } - - #[cfg(feature = "hydration")] - fn hydrate( - self: Box, - root: BSubtree, - parent_scope: &AnyScope, - parent: Element, - fragment: &mut Fragment, - node_ref: NodeRef, - ) -> Box { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope.hydrate_in_place(root, parent, fragment, node_ref, self.props); - - Box::new(scope) - } } /// A virtual child component. @@ -234,10 +142,99 @@ impl fmt::Debug for VChild { } } +#[cfg(feature = "csr")] +mod feat_csr { + use web_sys::Element; + + use super::*; + use crate::dom_bundle::BSubtree; + use crate::html::{AnyScope, Scope, Scoped}; + + pub(crate) trait MountableCsr { + fn mount( + self: Box, + root: &BSubtree, + node_ref: NodeRef, + parent_scope: &AnyScope, + parent: Element, + next_sibling: NodeRef, + ) -> Box; + + fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef); + } + + impl MountableCsr for PropsWrapper { + fn mount( + self: Box, + root: &BSubtree, + node_ref: NodeRef, + parent_scope: &AnyScope, + parent: Element, + next_sibling: NodeRef, + ) -> Box { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope.mount_in_place(root.clone(), parent, next_sibling, node_ref, self.props); + + Box::new(scope) + } + + fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef) { + let scope: Scope = scope.to_any().downcast::(); + scope.reuse(self.props, next_sibling); + } + } +} +#[cfg(not(feature = "csr"))] +pub(crate) use _Empty as MountableCsr; +#[cfg(feature = "csr")] +pub(crate) use feat_csr::MountableCsr; + +#[cfg(feature = "hydration")] +mod feat_hydration { + use web_sys::Element; + + use super::*; + use crate::dom_bundle::{BSubtree, Fragment}; + use crate::html::{AnyScope, Scope, Scoped}; + + pub(crate) trait MountableHydrate { + fn hydrate( + self: Box, + root: BSubtree, + parent_scope: &AnyScope, + parent: Element, + fragment: &mut Fragment, + node_ref: NodeRef, + ) -> Box; + } + + impl MountableHydrate for PropsWrapper { + fn hydrate( + self: Box, + root: BSubtree, + parent_scope: &AnyScope, + parent: Element, + fragment: &mut Fragment, + node_ref: NodeRef, + ) -> Box { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope.hydrate_in_place(root, parent, fragment, node_ref, self.props); + + Box::new(scope) + } + } +} +#[cfg(not(feature = "hydration"))] +pub(crate) use _Empty as MountableHydrate; +#[cfg(feature = "hydration")] +pub(crate) use feat_hydration::MountableHydrate; + #[cfg(feature = "ssr")] mod feat_ssr { + use futures::future::{FutureExt, LocalBoxFuture}; + use super::*; - use crate::html::AnyScope; + use crate::html::{AnyScope, Scope}; impl VComp { pub(crate) async fn render_to_string( @@ -252,9 +249,40 @@ mod feat_ssr { .await; } } + + pub(crate) trait MountableSsr { + fn render_to_string<'a>( + &'a self, + w: &'a mut String, + parent_scope: &'a AnyScope, + hydratable: bool, + ) -> LocalBoxFuture<'a, ()>; + } + + impl MountableSsr for PropsWrapper { + fn render_to_string<'a>( + &'a self, + w: &'a mut String, + parent_scope: &'a AnyScope, + hydratable: bool, + ) -> LocalBoxFuture<'a, ()> { + async move { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope + .render_to_string(w, self.props.clone(), hydratable) + .await; + } + .boxed_local() + } + } } +#[cfg(not(feature = "ssr"))] +pub(crate) use _Empty as MountableSsr; +#[cfg(feature = "ssr")] +pub(crate) use feat_ssr::MountableSsr; -#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 6a2249c165f..f9ab588ad76 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -113,7 +113,8 @@ mod feat_ssr { } } -#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/packages/yew/src/virtual_dom/vsuspense.rs b/packages/yew/src/virtual_dom/vsuspense.rs index 8d43ad36080..0d4aaf9bdc5 100644 --- a/packages/yew/src/virtual_dom/vsuspense.rs +++ b/packages/yew/src/virtual_dom/vsuspense.rs @@ -55,7 +55,8 @@ mod feat_ssr { } } -#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] mod ssr_tests { use std::rc::Rc; use std::time::Duration; diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs index 9afdfb384fa..bab201bb8b5 100644 --- a/packages/yew/src/virtual_dom/vtag.rs +++ b/packages/yew/src/virtual_dom/vtag.rs @@ -503,7 +503,8 @@ mod feat_ssr { } } -#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/packages/yew/src/virtual_dom/vtext.rs b/packages/yew/src/virtual_dom/vtext.rs index dff927de7c2..e187d4d5605 100644 --- a/packages/yew/src/virtual_dom/vtext.rs +++ b/packages/yew/src/virtual_dom/vtext.rs @@ -49,7 +49,8 @@ mod feat_ssr { } } -#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/packages/yew/tests/mod.rs b/packages/yew/tests/mod.rs index a211d28e02a..f61f01d24f8 100644 --- a/packages/yew/tests/mod.rs +++ b/packages/yew/tests/mod.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; diff --git a/packages/yew/tests/suspense.rs b/packages/yew/tests/suspense.rs index f996a850ec6..eb045a7c37b 100644 --- a/packages/yew/tests/suspense.rs +++ b/packages/yew/tests/suspense.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; diff --git a/packages/yew/tests/use_callback.rs b/packages/yew/tests/use_callback.rs index 4f4707dfd3a..b7f8a45c1f8 100644 --- a/packages/yew/tests/use_callback.rs +++ b/packages/yew/tests/use_callback.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/packages/yew/tests/use_context.rs b/packages/yew/tests/use_context.rs index 23206fb99fa..19f58f494e1 100644 --- a/packages/yew/tests/use_context.rs +++ b/packages/yew/tests/use_context.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; diff --git a/packages/yew/tests/use_effect.rs b/packages/yew/tests/use_effect.rs index 0d8830cfbac..c7969544be5 100644 --- a/packages/yew/tests/use_effect.rs +++ b/packages/yew/tests/use_effect.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; diff --git a/packages/yew/tests/use_memo.rs b/packages/yew/tests/use_memo.rs index a233c654e30..c62b71852c6 100644 --- a/packages/yew/tests/use_memo.rs +++ b/packages/yew/tests/use_memo.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/packages/yew/tests/use_reducer.rs b/packages/yew/tests/use_reducer.rs index e9cf0e424e4..dddde7c8541 100644 --- a/packages/yew/tests/use_reducer.rs +++ b/packages/yew/tests/use_reducer.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] use std::collections::HashSet; use std::rc::Rc; diff --git a/packages/yew/tests/use_ref.rs b/packages/yew/tests/use_ref.rs index e07d78c2068..d0e34e4dc8a 100644 --- a/packages/yew/tests/use_ref.rs +++ b/packages/yew/tests/use_ref.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; diff --git a/packages/yew/tests/use_state.rs b/packages/yew/tests/use_state.rs index 5f22fea548b..68800dae0f9 100644 --- a/packages/yew/tests/use_state.rs +++ b/packages/yew/tests/use_state.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "wasm_test")] +#![cfg(target_arch = "wasm32")] mod common; From 27e06ba6124289f24b00ea73e7c043a62ee769d7 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 03:05:12 +0200 Subject: [PATCH 02/10] fixup of Collectable --- packages/yew/src/html/component/scope.rs | 11 ++--------- packages/yew/src/virtual_dom/mod.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/yew/src/html/component/scope.rs b/packages/yew/src/html/component/scope.rs index 94362ba9487..fc0865056a5 100644 --- a/packages/yew/src/html/component/scope.rs +++ b/packages/yew/src/html/component/scope.rs @@ -289,11 +289,7 @@ mod feat_ssr { ); scheduler::start(); - #[cfg(debug_assertions)] - let collectable = Collectable::Component(std::any::type_name::()); - - #[cfg(not(debug_assertions))] - let collectable = Collectable::Component; + let collectable = Collectable::for_component::(); if hydratable { collectable.write_open_tag(w); @@ -632,10 +628,7 @@ mod feat_hydration { format!("hydration(type = {})", std::any::type_name::()), ); - #[cfg(debug_assertions)] - let collectable = Collectable::Component(std::any::type_name::()); - #[cfg(not(debug_assertions))] - let collectable = Collectable::Component; + let collectable = Collectable::for_component::(); let fragment = Fragment::collect_between(fragment, &collectable, &parent); node_ref.set(fragment.front().cloned()); diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs index 44064e1f9aa..ff54cfc6f15 100644 --- a/packages/yew/src/virtual_dom/mod.rs +++ b/packages/yew/src/virtual_dom/mod.rs @@ -208,6 +208,7 @@ mod feat_ssr_hydration { type ComponentName = &'static str; #[cfg(not(debug_assertions))] type ComponentName = (); + /// A collectable. /// /// This indicates a kind that can be collected from fragment to be processed at a later time @@ -217,6 +218,14 @@ mod feat_ssr_hydration { } impl Collectable { + pub fn for_component() -> Self { + #[cfg(debug_assertions)] + let comp_name = std::any::type_name::(); + #[cfg(not(debug_assertions))] + let comp_name = (); + Self::Component(comp_name) + } + pub fn open_start_mark(&self) -> &'static str { match self { Self::Component(_) => "<[", From 64d1696255a21cedb4666e6db8f1905b0a90f805 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 03:27:00 +0200 Subject: [PATCH 03/10] silence clippy --- packages/yew/src/html/component/lifecycle.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/yew/src/html/component/lifecycle.rs b/packages/yew/src/html/component/lifecycle.rs index 56df09f5781..991b4cc28dc 100644 --- a/packages/yew/src/html/component/lifecycle.rs +++ b/packages/yew/src/html/component/lifecycle.rs @@ -439,7 +439,9 @@ impl Runnable for DestroyRunner { } #[cfg(feature = "ssr")] - ComponentRenderState::Ssr { .. } => {} + ComponentRenderState::Ssr { .. } => { + let _ = self.parent_to_detach; + } } } } From a6bfef59be34e2c905c0e014ae0fcd89b5577890 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 13:38:57 +0200 Subject: [PATCH 04/10] unfix Mountable trait leads to bundle size increase, so stick to the optimized version --- packages/yew/src/virtual_dom/vcomp.rs | 226 +++++++++++--------------- 1 file changed, 99 insertions(+), 127 deletions(-) diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index e5cf16420a7..c330b12ec8e 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -4,7 +4,20 @@ use std::any::TypeId; use std::fmt; use std::rc::Rc; +#[cfg(feature = "ssr")] +use futures::future::{FutureExt, LocalBoxFuture}; +#[cfg(feature = "csr")] +use web_sys::Element; + use super::Key; +#[cfg(feature = "csr")] +use crate::dom_bundle::BSubtree; +#[cfg(feature = "hydration")] +use crate::dom_bundle::Fragment; +#[cfg(feature = "csr")] +use crate::html::Scoped; +#[cfg(any(feature = "ssr", feature = "csr"))] +use crate::html::{AnyScope, Scope}; use crate::html::{BaseComponent, NodeRef}; /// A virtual component. @@ -37,12 +50,39 @@ impl Clone for VComp { } } -// Used as a substitute trait when features are not enabled -pub(crate) trait _Empty {} -impl _Empty for T {} - -pub(crate) trait Mountable: MountableCsr + MountableHydrate + MountableSsr { +pub(crate) trait Mountable { fn copy(&self) -> Box; + + #[cfg(feature = "csr")] + fn mount( + self: Box, + root: &BSubtree, + node_ref: NodeRef, + parent_scope: &AnyScope, + parent: Element, + next_sibling: NodeRef, + ) -> Box; + + #[cfg(feature = "csr")] + fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef); + + #[cfg(feature = "ssr")] + fn render_to_string<'a>( + &'a self, + w: &'a mut String, + parent_scope: &'a AnyScope, + hydratable: bool, + ) -> LocalBoxFuture<'a, ()>; + + #[cfg(feature = "hydration")] + fn hydrate( + self: Box, + root: BSubtree, + parent_scope: &AnyScope, + parent: Element, + fragment: &mut Fragment, + node_ref: NodeRef, + ) -> Box; } pub(crate) struct PropsWrapper { @@ -62,6 +102,58 @@ impl Mountable for PropsWrapper { }; Box::new(wrapper) } + + #[cfg(feature = "csr")] + fn mount( + self: Box, + root: &BSubtree, + node_ref: NodeRef, + parent_scope: &AnyScope, + parent: Element, + next_sibling: NodeRef, + ) -> Box { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope.mount_in_place(root.clone(), parent, next_sibling, node_ref, self.props); + + Box::new(scope) + } + + #[cfg(feature = "csr")] + fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef) { + let scope: Scope = scope.to_any().downcast::(); + scope.reuse(self.props, next_sibling); + } + + #[cfg(feature = "ssr")] + fn render_to_string<'a>( + &'a self, + w: &'a mut String, + parent_scope: &'a AnyScope, + hydratable: bool, + ) -> LocalBoxFuture<'a, ()> { + async move { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope + .render_to_string(w, self.props.clone(), hydratable) + .await; + } + .boxed_local() + } + + #[cfg(feature = "hydration")] + fn hydrate( + self: Box, + root: BSubtree, + parent_scope: &AnyScope, + parent: Element, + fragment: &mut Fragment, + node_ref: NodeRef, + ) -> Box { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + scope.hydrate_in_place(root, parent, fragment, node_ref, self.props); + + Box::new(scope) + } } /// A virtual child component. @@ -142,99 +234,10 @@ impl fmt::Debug for VChild { } } -#[cfg(feature = "csr")] -mod feat_csr { - use web_sys::Element; - - use super::*; - use crate::dom_bundle::BSubtree; - use crate::html::{AnyScope, Scope, Scoped}; - - pub(crate) trait MountableCsr { - fn mount( - self: Box, - root: &BSubtree, - node_ref: NodeRef, - parent_scope: &AnyScope, - parent: Element, - next_sibling: NodeRef, - ) -> Box; - - fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef); - } - - impl MountableCsr for PropsWrapper { - fn mount( - self: Box, - root: &BSubtree, - node_ref: NodeRef, - parent_scope: &AnyScope, - parent: Element, - next_sibling: NodeRef, - ) -> Box { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope.mount_in_place(root.clone(), parent, next_sibling, node_ref, self.props); - - Box::new(scope) - } - - fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef) { - let scope: Scope = scope.to_any().downcast::(); - scope.reuse(self.props, next_sibling); - } - } -} -#[cfg(not(feature = "csr"))] -pub(crate) use _Empty as MountableCsr; -#[cfg(feature = "csr")] -pub(crate) use feat_csr::MountableCsr; - -#[cfg(feature = "hydration")] -mod feat_hydration { - use web_sys::Element; - - use super::*; - use crate::dom_bundle::{BSubtree, Fragment}; - use crate::html::{AnyScope, Scope, Scoped}; - - pub(crate) trait MountableHydrate { - fn hydrate( - self: Box, - root: BSubtree, - parent_scope: &AnyScope, - parent: Element, - fragment: &mut Fragment, - node_ref: NodeRef, - ) -> Box; - } - - impl MountableHydrate for PropsWrapper { - fn hydrate( - self: Box, - root: BSubtree, - parent_scope: &AnyScope, - parent: Element, - fragment: &mut Fragment, - node_ref: NodeRef, - ) -> Box { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope.hydrate_in_place(root, parent, fragment, node_ref, self.props); - - Box::new(scope) - } - } -} -#[cfg(not(feature = "hydration"))] -pub(crate) use _Empty as MountableHydrate; -#[cfg(feature = "hydration")] -pub(crate) use feat_hydration::MountableHydrate; - #[cfg(feature = "ssr")] mod feat_ssr { - use futures::future::{FutureExt, LocalBoxFuture}; - use super::*; - use crate::html::{AnyScope, Scope}; + use crate::html::AnyScope; impl VComp { pub(crate) async fn render_to_string( @@ -249,40 +252,9 @@ mod feat_ssr { .await; } } - - pub(crate) trait MountableSsr { - fn render_to_string<'a>( - &'a self, - w: &'a mut String, - parent_scope: &'a AnyScope, - hydratable: bool, - ) -> LocalBoxFuture<'a, ()>; - } - - impl MountableSsr for PropsWrapper { - fn render_to_string<'a>( - &'a self, - w: &'a mut String, - parent_scope: &'a AnyScope, - hydratable: bool, - ) -> LocalBoxFuture<'a, ()> { - async move { - let scope: Scope = Scope::new(Some(parent_scope.clone())); - scope - .render_to_string(w, self.props.clone(), hydratable) - .await; - } - .boxed_local() - } - } } -#[cfg(not(feature = "ssr"))] -pub(crate) use _Empty as MountableSsr; -#[cfg(feature = "ssr")] -pub(crate) use feat_ssr::MountableSsr; -#[cfg(not(target_arch = "wasm32"))] -#[cfg(test)] +#[cfg(all(test, not(target_arch = "wasm32"), feature = "ssr"))] mod ssr_tests { use tokio::test; From 23a70e9087674c942e97e3443d859d147b9dd426 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 14:51:50 +0200 Subject: [PATCH 05/10] use required-features for function_router --- examples/function_router/Cargo.toml | 5 +++-- examples/function_router/index.html | 2 +- examples/function_router/src/bin/function_router.rs | 3 --- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/function_router/Cargo.toml b/examples/function_router/Cargo.toml index 9bc73d082da..313d21a7a37 100644 --- a/examples/function_router/Cargo.toml +++ b/examples/function_router/Cargo.toml @@ -19,5 +19,6 @@ instant = { version = "0.1", features = ["wasm-bindgen"] } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } -[features] -csr = ["yew/csr"] +[[bin]] +name = "function_router" +required-features = ["yew/csr"] diff --git a/examples/function_router/index.html b/examples/function_router/index.html index 2a75b730809..94d1b1d121c 100644 --- a/examples/function_router/index.html +++ b/examples/function_router/index.html @@ -11,7 +11,7 @@ href="https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css" /> - + diff --git a/examples/function_router/src/bin/function_router.rs b/examples/function_router/src/bin/function_router.rs index 60571f7ab67..b7c7169331e 100644 --- a/examples/function_router/src/bin/function_router.rs +++ b/examples/function_router/src/bin/function_router.rs @@ -2,8 +2,5 @@ pub use function_router::*; fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); - #[cfg(feature = "csr")] yew::Renderer::::new().render(); - #[cfg(not(feature = "csr"))] - panic!("You must enable the csr feature to run this binary"); } From a1a061e563f8268cfbdaa43864f04d5658bb1b18 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 14:59:40 +0200 Subject: [PATCH 06/10] use --all-features in various places --- .github/workflows/main-checks.yml | 6 +++--- packages/yew/Cargo.toml | 2 +- packages/yew/Makefile.toml | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml index e00cc9a5d48..c96cca1cef6 100644 --- a/.github/workflows/main-checks.yml +++ b/.github/workflows/main-checks.yml @@ -120,7 +120,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: -p yew --doc --features csr,hydration,ssr --target wasm32-unknown-unknown + args: -p yew --doc --all-features --target wasm32-unknown-unknown integration_tests: name: Integration Tests on ${{ matrix.toolchain }} @@ -159,8 +159,8 @@ jobs: - name: Run tests - yew run: | cd packages/yew - CHROMEDRIVER=$(which chromedriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown - GECKODRIVER=$(which geckodriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown + CHROMEDRIVER=$(which chromedriver) cargo test --all-features --target wasm32-unknown-unknown + GECKODRIVER=$(which geckodriver) cargo test --all-features --target wasm32-unknown-unknown - name: Run tests - yew-router run: | diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index e99933eb2be..a5082e23272 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -96,5 +96,5 @@ default = [] tokio = { version = "1.15.0", features = ["full"] } [package.metadata.docs.rs] -features = ["csr", "hydration", "ssr"] +all-features = true rustdoc-args = ["--cfg", "documenting"] diff --git a/packages/yew/Makefile.toml b/packages/yew/Makefile.toml index cd02021592a..6db85102728 100644 --- a/packages/yew/Makefile.toml +++ b/packages/yew/Makefile.toml @@ -9,8 +9,7 @@ args = [ "--firefox", "--headless", "--", - "--features", - "csr,hydration,ssr" + "--all-features", ] [tasks.ssr-test] From 52540625edf93ee5acf6f1b4d47cd7851567b76b Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 16:26:00 +0200 Subject: [PATCH 07/10] disable tokio dependency on wasm tests again --- .github/workflows/main-checks.yml | 4 ++-- packages/yew/Cargo.toml | 4 +++- packages/yew/Makefile.toml | 3 ++- packages/yew/src/lib.rs | 2 -- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml index c96cca1cef6..818d33e47da 100644 --- a/.github/workflows/main-checks.yml +++ b/.github/workflows/main-checks.yml @@ -159,8 +159,8 @@ jobs: - name: Run tests - yew run: | cd packages/yew - CHROMEDRIVER=$(which chromedriver) cargo test --all-features --target wasm32-unknown-unknown - GECKODRIVER=$(which geckodriver) cargo test --all-features --target wasm32-unknown-unknown + CHROMEDRIVER=$(which chromedriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown + GECKODRIVER=$(which geckodriver) cargo test --features csr,hydration,ssr --target wasm32-unknown-unknown - name: Run tests - yew-router run: | diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index a5082e23272..491a44695dd 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -87,7 +87,9 @@ features = [ ] [features] -ssr = ["futures", "html-escape"] +# TODO: `dep:` syntax only supported with MSRV 1.60, would be more precise +# tokio = ["dep:tokio"] +ssr = ["futures", "html-escape"] # dep:html-escape csr = [] hydration = ["csr"] default = [] diff --git a/packages/yew/Makefile.toml b/packages/yew/Makefile.toml index 6db85102728..02cf9062616 100644 --- a/packages/yew/Makefile.toml +++ b/packages/yew/Makefile.toml @@ -9,7 +9,8 @@ args = [ "--firefox", "--headless", "--", - "--all-features", + "--features", + "csr,hydration,ssr", ] [tasks.ssr-test] diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index 1532277021b..80efb03e091 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -28,8 +28,6 @@ //! enable this if your application uses future-based APIs and it does not compile / lint on //! non-wasm32 targets.) //! - `hydration`: Enables Hydration support. -//! - `trace_hydration`: Enables trace logging on hydration. (Implies `hydration`. You may want to -//! enable this if you are //! trying to debug hydration layout mismatch.) //! //! ## Example From d395c07442d19815a33848fbb9f7b5c1ad3e2320 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 16:29:52 +0200 Subject: [PATCH 08/10] add some #[inline] annotations --- packages/yew/src/html/component/scope.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/yew/src/html/component/scope.rs b/packages/yew/src/html/component/scope.rs index fc0865056a5..a35913153a5 100644 --- a/packages/yew/src/html/component/scope.rs +++ b/packages/yew/src/html/component/scope.rs @@ -403,6 +403,7 @@ mod feat_csr_ssr { } } + #[inline] pub(super) fn arch_get_component(&self) -> Option + '_> { self.state.try_borrow().ok().and_then(|state_ref| { state_ref.as_ref()?; @@ -425,6 +426,7 @@ mod feat_csr_ssr { scheduler::start(); } + #[inline] pub(super) fn arch_send_message(&self, msg: T) where T: Into, @@ -435,6 +437,7 @@ mod feat_csr_ssr { } } + #[inline] pub(super) fn arch_send_message_batch(&self, mut messages: Vec) { let msg_len = messages.len(); From 8b1fc870b9b899537bad0a811151287c2f65e84b Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 16:30:16 +0200 Subject: [PATCH 09/10] merge one more feature annotations --- packages/yew/src/dom_bundle/mod.rs | 47 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/yew/src/dom_bundle/mod.rs b/packages/yew/src/dom_bundle/mod.rs index 3d497cb687a..a8cac1c900d 100644 --- a/packages/yew/src/dom_bundle/mod.rs +++ b/packages/yew/src/dom_bundle/mod.rs @@ -34,19 +34,6 @@ pub use subtree_root::{set_event_bubbling, BSubtree}; use traits::{Reconcilable, ReconcileTarget}; use utils::{insert_node, test_log}; -#[cfg(feature = "hydration")] -#[path = "."] -mod feat_hydration { - #[path = "./fragment.rs"] - mod fragment; - pub use fragment::Fragment; - - pub(super) use super::traits::Hydratable; - pub(super) use super::utils::node_type_str; -} -#[cfg(feature = "hydration")] -pub(crate) use feat_hydration::*; - /// A Bundle. /// /// Each component holds a bundle that represents a realised layout, designated by a [VNode]. @@ -87,16 +74,28 @@ impl Bundle { } #[cfg(feature = "hydration")] -impl Bundle { - /// Creates a bundle by hydrating a virtual dom layout. - pub fn hydrate( - root: &BSubtree, - parent_scope: &AnyScope, - parent: &Element, - fragment: &mut Fragment, - node: VNode, - ) -> (NodeRef, Self) { - let (node_ref, bundle) = node.hydrate(root, parent_scope, parent, fragment); - (node_ref, Self(bundle)) +#[path = "."] +mod feat_hydration { + pub(super) use super::traits::Hydratable; + pub(super) use super::utils::node_type_str; + #[path = "./fragment.rs"] + mod fragment; + pub use fragment::Fragment; + + use super::*; + impl Bundle { + /// Creates a bundle by hydrating a virtual dom layout. + pub fn hydrate( + root: &BSubtree, + parent_scope: &AnyScope, + parent: &Element, + fragment: &mut Fragment, + node: VNode, + ) -> (NodeRef, Self) { + let (node_ref, bundle) = node.hydrate(root, parent_scope, parent, fragment); + (node_ref, Self(bundle)) + } } } +#[cfg(feature = "hydration")] +pub(crate) use feat_hydration::*; From 371a43496cc96b9ee96edf9b34ebad6a3a626053 Mon Sep 17 00:00:00 2001 From: Martin Molzer Date: Sat, 7 May 2022 16:52:11 +0200 Subject: [PATCH 10/10] fixup: remove a line connected to trace_hydration --- packages/yew/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index 80efb03e091..b9bdd9eb257 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -28,7 +28,6 @@ //! enable this if your application uses future-based APIs and it does not compile / lint on //! non-wasm32 targets.) //! - `hydration`: Enables Hydration support. -//! trying to debug hydration layout mismatch.) //! //! ## Example //!