diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml index 434d9047ee8..e4b3445e237 100644 --- a/.github/workflows/main-checks.yml +++ b/.github/workflows/main-checks.yml @@ -25,7 +25,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --all-features -- -D warnings + args: --all-targets --features "csr,ssr,hydration" -- -D warnings - name: Lint feature soundness run: | @@ -33,7 +33,7 @@ jobs: cargo clippy --features=ssr -- --deny=warnings cargo clippy --features=csr -- --deny=warnings cargo clippy --features=hydration -- --deny=warnings - cargo clippy --all-features --all-targets -- --deny=warnings + cargo clippy --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings working-directory: packages/yew @@ -55,7 +55,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --all-features --release -- -D warnings + args: --all-targets --features "csr,ssr,hydration" --release -- -D warnings - name: Lint feature soundness run: | @@ -63,7 +63,7 @@ jobs: cargo clippy --release --features=ssr -- --deny=warnings cargo clippy --release --features=csr -- --deny=warnings cargo clippy --release --features=hydration -- --deny=warnings - cargo clippy --release --all-features --all-targets -- --deny=warnings + cargo clippy --release --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings working-directory: packages/yew spell_check: diff --git a/examples/simple_ssr/src/lib.rs b/examples/simple_ssr/src/lib.rs index dd291b644a8..1204af904ec 100644 --- a/examples/simple_ssr/src/lib.rs +++ b/examples/simple_ssr/src/lib.rs @@ -18,7 +18,7 @@ async fn fetch_uuid() -> Uuid { #[function_component] fn Content() -> HtmlResult { - let uuid = use_prepared_state!(async |_| -> Uuid { fetch_uuid().await }, ())?.unwrap(); + let uuid = use_prepared_state!(async move |_| -> Uuid { fetch_uuid().await }, ())?.unwrap(); Ok(html! {
{"Random UUID: "}{uuid}
diff --git a/packages/yew-macro/Cargo.toml b/packages/yew-macro/Cargo.toml index ae17548fbb0..5cd5679ad2a 100644 --- a/packages/yew-macro/Cargo.toml +++ b/packages/yew-macro/Cargo.toml @@ -35,3 +35,4 @@ yew = { path = "../yew" } [features] lints = [] +nightly = [] diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index a6e9e9e179a..8b981a10d1c 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -387,6 +387,18 @@ impl ToTokens for HtmlElement { }} }); + #[cfg(feature = "nightly")] + let invalid_void_tag_msg_start = { + let span = vtag.span().unwrap(); + let source_file = span.source_file().path(); + let source_file = source_file.display(); + let start = span.start(); + format!("[{}:{}:{}] ", source_file, start.line, start.column) + }; + + #[cfg(not(feature = "nightly"))] + let invalid_void_tag_msg_start = ""; + // this way we get a nice error message (with the correct span) when the expression // doesn't return a valid value quote_spanned! {expr.span()=> { @@ -442,10 +454,6 @@ impl ToTokens for HtmlElement { // These are the runtime-checks exclusive to dynamic tags. // For literal tags this is already done at compile-time. // - // When Span::source_file Span::start get stabilised or yew-macro introduces a - // nightly feature flag we should expand the panic message to contain the exact - // location of the dynamic tag. - // // check void element if !#vtag.children().is_empty() { ::std::debug_assert!( @@ -453,7 +461,7 @@ impl ToTokens for HtmlElement { "area" | "base" | "br" | "col" | "embed" | "hr" | "img" | "input" | "link" | "meta" | "param" | "source" | "track" | "wbr" ), - "a dynamic tag tried to create a `<{0}>` tag with children. `<{0}>` is a void element which can't have any children.", + concat!(#invalid_void_tag_msg_start, "a dynamic tag tried to create a `<{0}>` tag with children. `<{0}>` is a void element which can't have any children."), #vtag.tag(), ); } diff --git a/packages/yew-macro/src/lib.rs b/packages/yew-macro/src/lib.rs index e5a50ab2250..5822f0325ff 100644 --- a/packages/yew-macro/src/lib.rs +++ b/packages/yew-macro/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "nightly", feature(proc_macro_span))] + //! This crate provides Yew's procedural macro `html!` which allows using JSX-like syntax //! for generating html and the `Properties` derive macro for deriving the `Properties` trait //! for components. diff --git a/packages/yew-macro/src/use_prepared_state.rs b/packages/yew-macro/src/use_prepared_state.rs index c94240aab1c..e860277a9f8 100644 --- a/packages/yew-macro/src/use_prepared_state.rs +++ b/packages/yew-macro/src/use_prepared_state.rs @@ -1,7 +1,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::parse::{Parse, ParseStream}; -use syn::{parse_quote, Expr, ExprClosure, ReturnType, Token, Type}; +use syn::{Expr, ExprClosure, ReturnType, Token, Type}; #[derive(Debug)] pub struct PreparedState { @@ -58,8 +58,11 @@ impl Parse for PreparedState { } impl PreparedState { - // Async closure is not stable, so we rewrite it to clsoure + async block + // Async closure is not stable, so we rewrite it to closure + async block + #[cfg(not(feature = "nightly"))] pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure { + use syn::parse_quote; + let async_token = match &self.closure.asyncness { Some(m) => m, None => return self.closure.clone(), @@ -87,6 +90,11 @@ impl PreparedState { closure } + #[cfg(feature = "nightly")] + pub fn rewrite_to_closure_with_async_block(&self) -> ExprClosure { + self.closure.clone() + } + pub fn to_token_stream_with_closure(&self) -> TokenStream { let deps = &self.deps; let rt = &self.return_type; diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index 74d8234090f..a6e6f650d53 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -96,6 +96,7 @@ features = [ ssr = ["futures", "html-escape", "base64ct", "bincode"] # dep:html-escape csr = [] hydration = ["csr", "bincode"] +nightly = ["yew-macro/nightly"] default = [] [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] diff --git a/packages/yew/src/functional/hooks/use_force_update.rs b/packages/yew/src/functional/hooks/use_force_update.rs index 4f05f6d0abc..417273a015b 100644 --- a/packages/yew/src/functional/hooks/use_force_update.rs +++ b/packages/yew/src/functional/hooks/use_force_update.rs @@ -6,29 +6,47 @@ use crate::functional::ReRender; /// A handle which can be used to force a re-render of the associated /// function component. #[derive(Clone)] -pub struct UseForceUpdate { +pub struct UseForceUpdateHandle { trigger: ReRender, } -impl fmt::Debug for UseForceUpdate { +impl fmt::Debug for UseForceUpdateHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("UseForceUpdate").finish() } } -impl UseForceUpdate { +impl UseForceUpdateHandle { /// Trigger an unconditional re-render of the associated function component pub fn force_update(&self) { (self.trigger)() } } -// #![feature(fn_traits)] // required nightly feature to make UseForceUpdate callable directly -// impl Fn<()> for UseForceUpdate { -// extern "rust-call" fn call(&self, _args: ()) { -// self.force_update() -// } -// } +#[cfg(feature = "nightly")] +mod feat_nightly { + use super::*; + + impl FnOnce<()> for UseForceUpdateHandle { + type Output = (); + + extern "rust-call" fn call_once(self, _args: ()) -> Self::Output { + self.force_update() + } + } + + impl FnMut<()> for UseForceUpdateHandle { + extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output { + self.force_update() + } + } + + impl Fn<()> for UseForceUpdateHandle { + extern "rust-call" fn call(&self, _args: ()) -> Self::Output { + self.force_update() + } + } +} /// This hook is used to manually force a function component to re-render. /// @@ -78,14 +96,14 @@ impl UseForceUpdate { /// /// [`use_state`]: super::use_state() /// [`use_reducer`]: super::use_reducer() -pub fn use_force_update() -> impl Hook { +pub fn use_force_update() -> impl Hook { struct UseRerenderHook; impl Hook for UseRerenderHook { - type Output = UseForceUpdate; + type Output = UseForceUpdateHandle; fn run(self, ctx: &mut HookContext) -> Self::Output { - UseForceUpdate { + UseForceUpdateHandle { trigger: ctx.re_render.clone(), } } @@ -93,3 +111,15 @@ pub fn use_force_update() -> impl Hook { UseRerenderHook } + +#[cfg(all(test, feature = "nightly"))] +mod nightly_test { + use yew::prelude::*; + + #[function_component] + fn ManuallyUpdatedDate() -> Html { + let trigger = use_force_update(); + let _ = move || trigger(); + html! {} + } +} diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index b9bdd9eb257..a8b30c966c2 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -1,6 +1,10 @@ #![allow(clippy::needless_doctest_main)] #![doc(html_logo_url = "https://yew.rs/img/logo.png")] #![cfg_attr(documenting, feature(doc_cfg))] +#![cfg_attr( + feature = "nightly", + feature(fn_traits, async_closure, unboxed_closures) +)] //! # Yew Framework - API Documentation //! diff --git a/packages/yew/tests/use_prepared_state.rs b/packages/yew/tests/use_prepared_state.rs index 93c52e71b06..2263aff4f46 100644 --- a/packages/yew/tests/use_prepared_state.rs +++ b/packages/yew/tests/use_prepared_state.rs @@ -1,5 +1,6 @@ #![cfg(target_arch = "wasm32")] #![cfg(feature = "hydration")] +#![cfg_attr(feature = "nightly", feature(async_closure))] use std::time::Duration; @@ -67,7 +68,7 @@ async fn use_prepared_state_works() { async fn use_prepared_state_with_suspension_works() { #[function_component] fn Comp() -> HtmlResult { - let ctr = use_prepared_state!(async |_| -> u32 { 12345 }, ())?.unwrap_or_default(); + let ctr = use_prepared_state!(async move |_| -> u32 { 12345 }, ())?.unwrap_or_default(); Ok(html! {