diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml index e4b3445e237..57ea4c92fa5 100644 --- a/.github/workflows/main-checks.yml +++ b/.github/workflows/main-checks.yml @@ -25,15 +25,10 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --features "csr,ssr,hydration" -- -D warnings + args: --all-targets --features "csr,ssr,hydration,tokio" -- -D warnings - name: Lint feature soundness - run: | - cargo clippy -- --deny=warnings - cargo clippy --features=ssr -- --deny=warnings - cargo clippy --features=csr -- --deny=warnings - cargo clippy --features=hydration -- --deny=warnings - cargo clippy --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings + run: bash ../../ci/feature-soundness.sh working-directory: packages/yew @@ -55,15 +50,10 @@ jobs: uses: actions-rs/cargo@v1 with: command: clippy - args: --all-targets --features "csr,ssr,hydration" --release -- -D warnings + args: --all-targets --features "csr,ssr,hydration,tokio" --release -- -D warnings - name: Lint feature soundness - run: | - cargo clippy --release -- --deny=warnings - cargo clippy --release --features=ssr -- --deny=warnings - cargo clippy --release --features=csr -- --deny=warnings - cargo clippy --release --features=hydration -- --deny=warnings - cargo clippy --release --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings + run: bash ../../ci/feature-soundness-release.sh working-directory: packages/yew spell_check: @@ -129,7 +119,7 @@ jobs: matrix: toolchain: # anyway to dynamically grep the MSRV from Cargo.toml? - - 1.56.1 # MSRV + - 1.60.0 # MSRV - stable steps: @@ -180,7 +170,7 @@ jobs: matrix: toolchain: # anyway to dynamically grep the MSRV from Cargo.toml? - - 1.56.1 # MSRV + - 1.60.0 # MSRV - stable - nightly diff --git a/Cargo.toml b/Cargo.toml index bc3ed7f88e0..2b716ffd3b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,4 @@ members = [ "tools/process-benchmark-results", "tools/website-test", ] +resolver = "2" diff --git a/ci/feature-soundness-release.sh b/ci/feature-soundness-release.sh new file mode 100755 index 00000000000..6de18c3ab23 --- /dev/null +++ b/ci/feature-soundness-release.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -xe + +# You can extract the feature list with the following command: +# cargo hack check --feature-powerset --exclude-features nightly + +# You need to run this script in packages/yew + +cargo clippy --release --no-default-features -- --deny=warnings +cargo clippy --release --no-default-features --features csr -- --deny=warnings +cargo clippy --release --no-default-features --features default -- --deny=warnings +cargo clippy --release --no-default-features --features csr,default -- --deny=warnings +cargo clippy --release --no-default-features --features hydration -- --deny=warnings +cargo clippy --release --no-default-features --features default,hydration -- --deny=warnings +cargo clippy --release --no-default-features --features ssr -- --deny=warnings +cargo clippy --release --no-default-features --features csr,ssr -- --deny=warnings +cargo clippy --release --no-default-features --features default,ssr -- --deny=warnings +cargo clippy --release --no-default-features --features csr,default,ssr -- --deny=warnings +cargo clippy --release --no-default-features --features hydration,ssr -- --deny=warnings +cargo clippy --release --no-default-features --features default,hydration,ssr -- --deny=warnings +cargo clippy --release --no-default-features --features tokio -- --deny=warnings +cargo clippy --release --no-default-features --features csr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features default,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features csr,default,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features hydration,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features default,hydration,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features ssr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features csr,ssr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features default,ssr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features csr,default,ssr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features hydration,ssr,tokio -- --deny=warnings +cargo clippy --release --no-default-features --features default,hydration,ssr,tokio -- --deny=warnings diff --git a/ci/feature-soundness.sh b/ci/feature-soundness.sh new file mode 100755 index 00000000000..5686b2fa392 --- /dev/null +++ b/ci/feature-soundness.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -xe + +# You can extract the feature list with the following command: +# cargo hack check --feature-powerset --exclude-features nightly + +# You need to run this script in packages/yew + +cargo clippy --no-default-features -- --deny=warnings +cargo clippy --no-default-features --features csr -- --deny=warnings +cargo clippy --no-default-features --features default -- --deny=warnings +cargo clippy --no-default-features --features csr,default -- --deny=warnings +cargo clippy --no-default-features --features hydration -- --deny=warnings +cargo clippy --no-default-features --features default,hydration -- --deny=warnings +cargo clippy --no-default-features --features ssr -- --deny=warnings +cargo clippy --no-default-features --features csr,ssr -- --deny=warnings +cargo clippy --no-default-features --features default,ssr -- --deny=warnings +cargo clippy --no-default-features --features csr,default,ssr -- --deny=warnings +cargo clippy --no-default-features --features hydration,ssr -- --deny=warnings +cargo clippy --no-default-features --features default,hydration,ssr -- --deny=warnings +cargo clippy --no-default-features --features tokio -- --deny=warnings +cargo clippy --no-default-features --features csr,tokio -- --deny=warnings +cargo clippy --no-default-features --features default,tokio -- --deny=warnings +cargo clippy --no-default-features --features csr,default,tokio -- --deny=warnings +cargo clippy --no-default-features --features hydration,tokio -- --deny=warnings +cargo clippy --no-default-features --features default,hydration,tokio -- --deny=warnings +cargo clippy --no-default-features --features ssr,tokio -- --deny=warnings +cargo clippy --no-default-features --features csr,ssr,tokio -- --deny=warnings +cargo clippy --no-default-features --features default,ssr,tokio -- --deny=warnings +cargo clippy --no-default-features --features csr,default,ssr,tokio -- --deny=warnings +cargo clippy --no-default-features --features hydration,ssr,tokio -- --deny=warnings +cargo clippy --no-default-features --features default,hydration,ssr,tokio -- --deny=warnings diff --git a/examples/function_router/src/app.rs b/examples/function_router/src/app.rs index 6f2140e5e77..87408e8123b 100644 --- a/examples/function_router/src/app.rs +++ b/examples/function_router/src/app.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use yew::prelude::*; -use yew::virtual_dom::AttrValue; use yew_router::history::{AnyHistory, History, MemoryHistory}; use yew_router::prelude::*; diff --git a/examples/simple_ssr/Cargo.toml b/examples/simple_ssr/Cargo.toml index 0e1fc0aad6d..1f796fdba61 100644 --- a/examples/simple_ssr/Cargo.toml +++ b/examples/simple_ssr/Cargo.toml @@ -10,6 +10,8 @@ yew = { path = "../../packages/yew" } reqwest = { version = "0.11.8", features = ["json"] } serde = { version = "1.0.132", features = ["derive"] } uuid = { version = "1.0.0", features = ["serde"] } +futures = "0.3" +bytes = "1.0" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" @@ -19,9 +21,6 @@ log = "0.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "1.15.0", features = ["full"] } warp = "0.3" -num_cpus = "1.13" -tokio-util = { version = "0.7", features = ["rt"] } -once_cell = "1.5" clap = { version = "3.1.7", features = ["derive"] } [features] diff --git a/examples/simple_ssr/src/bin/simple_ssr_server.rs b/examples/simple_ssr/src/bin/simple_ssr_server.rs index bf86e7cf0dc..a6127c68bb9 100644 --- a/examples/simple_ssr/src/bin/simple_ssr_server.rs +++ b/examples/simple_ssr/src/bin/simple_ssr_server.rs @@ -1,13 +1,13 @@ +use std::error::Error; use std::path::PathBuf; +use bytes::Bytes; use clap::Parser; -use once_cell::sync::Lazy; +use futures::stream::{self, Stream, StreamExt}; use simple_ssr::App; -use tokio_util::task::LocalPoolHandle; use warp::Filter; -// We spawn a local pool that is as big as the number of cpu threads. -static LOCAL_POOL: Lazy = Lazy::new(|| LocalPoolHandle::new(num_cpus::get())); +type BoxedError = Box; /// A basic example #[derive(Parser, Debug)] @@ -17,19 +17,18 @@ struct Opt { dir: PathBuf, } -async fn render(index_html_s: &str) -> String { - let content = LOCAL_POOL - .spawn_pinned(move || async move { - let renderer = yew::ServerRenderer::::new(); - - renderer.render().await - }) - .await - .expect("the task has failed."); - - // Good enough for an example, but developers should avoid the replace and extra allocation - // here in an actual app. - index_html_s.replace("", &format!("{}", content)) +async fn render( + index_html_before: String, + index_html_after: String, +) -> Box> + Send> { + let renderer = yew::ServerRenderer::::new(); + + Box::new( + stream::once(async move { index_html_before }) + .chain(renderer.render_stream().await) + .chain(stream::once(async move { index_html_after })) + .map(|m| Result::<_, BoxedError>::Ok(m.into())), + ) } #[tokio::main] @@ -40,10 +39,16 @@ async fn main() { .await .expect("failed to read index.html"); + let (index_html_before, index_html_after) = index_html_s.split_once("").unwrap(); + let mut index_html_before = index_html_before.to_owned(); + index_html_before.push_str(""); + let index_html_after = index_html_after.to_owned(); + let html = warp::path::end().then(move || { - let index_html_s = index_html_s.clone(); + let index_html_before = index_html_before.clone(); + let index_html_after = index_html_after.clone(); - async move { warp::reply::html(render(&index_html_s).await) } + async move { warp::reply::html(render(index_html_before, index_html_after).await) } }); let routes = html.or(warp::fs::dir(opts.dir)); diff --git a/examples/ssr_router/Cargo.toml b/examples/ssr_router/Cargo.toml index 6cc7c73fda6..8c64e440c2a 100644 --- a/examples/ssr_router/Cargo.toml +++ b/examples/ssr_router/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" yew = { path = "../../packages/yew" } function_router = { path = "../function_router" } log = "0.4" +futures = "0.3" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4" @@ -20,9 +21,6 @@ axum = "0.5" tower = { version = "0.4", features = ["make"] } tower-http = { version = "0.3", features = ["fs"] } env_logger = "0.9" -num_cpus = "1.13" -tokio-util = { version = "0.7", features = ["rt"] } -once_cell = "1.5" clap = { version = "3.1.7", features = ["derive"] } [features] diff --git a/examples/ssr_router/src/bin/ssr_router_server.rs b/examples/ssr_router/src/bin/ssr_router_server.rs index b52a58cb3f1..5aa8c1c86ea 100644 --- a/examples/ssr_router/src/bin/ssr_router_server.rs +++ b/examples/ssr_router/src/bin/ssr_router_server.rs @@ -1,24 +1,21 @@ use std::collections::HashMap; +use std::convert::Infallible; use std::path::PathBuf; -use axum::body::Body; +use axum::body::{Body, StreamBody}; use axum::error_handling::HandleError; use axum::extract::Query; use axum::handler::Handler; use axum::http::{Request, StatusCode}; -use axum::response::Html; +use axum::response::IntoResponse; use axum::routing::get; use axum::{Extension, Router}; use clap::Parser; use function_router::{ServerApp, ServerAppProps}; -use once_cell::sync::Lazy; -use tokio_util::task::LocalPoolHandle; +use futures::stream::{self, StreamExt}; use tower::ServiceExt; use tower_http::services::ServeDir; -// We spawn a local pool that is as big as the number of cpu threads. -static LOCAL_POOL: Lazy = Lazy::new(|| LocalPoolHandle::new(num_cpus::get())); - /// A basic example #[derive(Parser, Debug)] struct Opt { @@ -28,29 +25,23 @@ struct Opt { } async fn render( - Extension(index_html_s): Extension, + Extension((index_html_before, index_html_after)): Extension<(String, String)>, url: Request, Query(queries): Query>, -) -> Html { +) -> impl IntoResponse { let url = url.uri().to_string(); - let content = LOCAL_POOL - .spawn_pinned(move || async move { - let server_app_props = ServerAppProps { - url: url.into(), - queries, - }; - - let renderer = yew::ServerRenderer::::with_props(server_app_props); - - renderer.render().await - }) - .await - .expect("the task has failed."); + let renderer = yew::ServerRenderer::::with_props(move || ServerAppProps { + url: url.into(), + queries, + }); - // Good enough for an example, but developers should avoid the replace and extra allocation - // here in an actual app. - Html(index_html_s.replace("", &format!("{}", content))) + StreamBody::new( + stream::once(async move { index_html_before }) + .chain(renderer.render_stream().await) + .chain(stream::once(async move { index_html_after })) + .map(Result::<_, Infallible>::Ok), + ) } #[tokio::main] @@ -63,6 +54,12 @@ async fn main() { .await .expect("failed to read index.html"); + let (index_html_before, index_html_after) = index_html_s.split_once("").unwrap(); + let mut index_html_before = index_html_before.to_owned(); + index_html_before.push_str(""); + + let index_html_after = index_html_after.to_owned(); + let handle_error = |e| async move { ( StatusCode::INTERNAL_SERVER_ERROR, @@ -77,7 +74,10 @@ async fn main() { .append_index_html_on_directories(false) .fallback( render - .layer(Extension(index_html_s)) + .layer(Extension(( + index_html_before.clone(), + index_html_after.clone(), + ))) .into_service() .map_err(|err| -> std::io::Error { match err {} }), ), diff --git a/packages/yew-macro/Cargo.toml b/packages/yew-macro/Cargo.toml index 837db121069..875fb88cde6 100644 --- a/packages/yew-macro/Cargo.toml +++ b/packages/yew-macro/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" keywords = ["web", "wasm", "frontend", "webasm", "webassembly"] categories = ["gui", "web-programming", "wasm"] description = "A framework for making client-side single-page apps" -rust-version = "1.56.1" +rust-version = "1.60.0" [lib] proc-macro = true diff --git a/packages/yew-macro/Makefile.toml b/packages/yew-macro/Makefile.toml index 5c890bbdee1..999a235920b 100644 --- a/packages/yew-macro/Makefile.toml +++ b/packages/yew-macro/Makefile.toml @@ -1,6 +1,6 @@ [tasks.test] clear = true -toolchain = "1.56.1" +toolchain = "1.60.0" command = "cargo" # test target can be optionally specified like `cargo make test html_macro`, args = ["test", "${@}"] diff --git a/packages/yew-macro/tests/classes_macro/classes-fail.stderr b/packages/yew-macro/tests/classes_macro/classes-fail.stderr index 4ae13819a9a..9e1ec462e91 100644 --- a/packages/yew-macro/tests/classes_macro/classes-fail.stderr +++ b/packages/yew-macro/tests/classes_macro/classes-fail.stderr @@ -1,62 +1,77 @@ error: expected `,` - --> $DIR/classes-fail.rs:7:20 + --> tests/classes_macro/classes-fail.rs:7:20 | 7 | classes!("one" "two"); | ^^^^^ error: string literals must not contain more than one class (hint: use `"two", "three"`) - --> $DIR/classes-fail.rs:18:21 + --> tests/classes_macro/classes-fail.rs:18:21 | 18 | classes!("one", "two three", "four"); | ^^^^^^^^^^^ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied - --> $DIR/classes-fail.rs:4:14 - | -4 | classes!(42); - | ^^ the trait `From<{integer}>` is not implemented for `Classes` - | - = help: the following implementations were found: - > - >> - > - > - and 4 others - = note: required because of the requirements on the impl of `Into` for `{integer}` + --> tests/classes_macro/classes-fail.rs:4:14 + | +4 | classes!(42); + | ^^ the trait `From<{integer}>` is not implemented for `Classes` + | + = help: the following implementations were found: + > + >> + > + > + and 4 others + = note: required because of the requirements on the impl of `Into` for `{integer}` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` error[E0277]: the trait bound `Classes: From<{float}>` is not satisfied - --> $DIR/classes-fail.rs:5:14 - | -5 | classes!(42.0); - | ^^^^ the trait `From<{float}>` is not implemented for `Classes` - | - = help: the following implementations were found: - > - >> - > - > - and 4 others - = note: required because of the requirements on the impl of `Into` for `{float}` + --> tests/classes_macro/classes-fail.rs:5:14 + | +5 | classes!(42.0); + | ^^^^ the trait `From<{float}>` is not implemented for `Classes` + | + = help: the following implementations were found: + > + >> + > + > + and 4 others + = note: required because of the requirements on the impl of `Into` for `{float}` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied - --> $DIR/classes-fail.rs:9:14 - | -9 | classes!(vec![42]); - | ^^^ the trait `From<{integer}>` is not implemented for `Classes` - | - = help: the following implementations were found: - > - >> - > - > - and 4 others - = note: required because of the requirements on the impl of `Into` for `{integer}` - = note: required because of the requirements on the impl of `From>` for `Classes` - = note: 1 redundant requirements hidden - = note: required because of the requirements on the impl of `Into` for `Vec<{integer}>` + --> tests/classes_macro/classes-fail.rs:9:14 + | +9 | classes!(vec![42]); + | ^^^ the trait `From<{integer}>` is not implemented for `Classes` + | + = help: the following implementations were found: + > + >> + > + > + and 4 others + = note: required because of the requirements on the impl of `Into` for `{integer}` + = note: required because of the requirements on the impl of `From>` for `Classes` + = note: 1 redundant requirement hidden + = note: required because of the requirements on the impl of `Into` for `Vec<{integer}>` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied - --> $DIR/classes-fail.rs:13:14 + --> tests/classes_macro/classes-fail.rs:13:14 | 13 | classes!(some); | ^^^^ the trait `From<{integer}>` is not implemented for `Classes` @@ -69,11 +84,16 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied and 4 others = note: required because of the requirements on the impl of `Into` for `{integer}` = note: required because of the requirements on the impl of `From>` for `Classes` - = note: 1 redundant requirements hidden + = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Into` for `Option<{integer}>` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` error[E0277]: the trait bound `Classes: From` is not satisfied - --> $DIR/classes-fail.rs:14:14 + --> tests/classes_macro/classes-fail.rs:14:14 | 14 | classes!(none); | ^^^^ the trait `From` is not implemented for `Classes` @@ -86,11 +106,16 @@ error[E0277]: the trait bound `Classes: From` is not satisfied and 4 others = note: required because of the requirements on the impl of `Into` for `u32` = note: required because of the requirements on the impl of `From>` for `Classes` - = note: 1 redundant requirements hidden + = note: 1 redundant requirement hidden = note: required because of the requirements on the impl of `Into` for `Option` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied - --> $DIR/classes-fail.rs:16:21 + --> tests/classes_macro/classes-fail.rs:16:21 | 16 | classes!("one", 42); | ^^ the trait `From<{integer}>` is not implemented for `Classes` @@ -102,3 +127,8 @@ error[E0277]: the trait bound `Classes: From<{integer}>` is not satisfied > and 4 others = note: required because of the requirements on the impl of `Into` for `{integer}` +note: required by a bound in `Classes::push` + --> $WORKSPACE/packages/yew/src/html/classes.rs + | + | pub fn push>(&mut self, class: T) { + | ^^^^^^^^^^ required by this bound in `Classes::push` diff --git a/packages/yew-macro/tests/classes_macro_test.rs b/packages/yew-macro/tests/classes_macro_test.rs index 01ea7f34d9c..84b1b5fda48 100644 --- a/packages/yew-macro/tests/classes_macro_test.rs +++ b/packages/yew-macro/tests/classes_macro_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn classes_macro() { let t = trybuild::TestCases::new(); t.pass("tests/classes_macro/*-pass.rs"); diff --git a/packages/yew-macro/tests/derive_props/fail.stderr b/packages/yew-macro/tests/derive_props/fail.stderr index 46b3d9f7d70..35c88342d29 100644 --- a/packages/yew-macro/tests/derive_props/fail.stderr +++ b/packages/yew-macro/tests/derive_props/fail.stderr @@ -11,17 +11,19 @@ error: cannot find attribute `props` in this scope | ^^^^^ error[E0425]: cannot find value `foo` in this scope - --> tests/derive_props/fail.rs:74:24 - | -74 | #[prop_or_else(foo)] - | ^^^ not found in this scope - | -help: consider importing one of these items - | -70 | use crate::t10::foo; - | -70 | use crate::t9::foo; - | + --> tests/derive_props/fail.rs:74:24 + | +74 | #[prop_or_else(foo)] + | ^^^ not found in this scope + | +note: these functions exist but are inaccessible + --> tests/derive_props/fail.rs:88:5 + | +88 | fn foo(bar: i32) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `crate::t9::foo`: not accessible +... +102 | fn foo() -> i32 { + | ^^^^^^^^^^^^^^^ `crate::t10::foo`: not accessible error[E0277]: the trait bound `AssertAllProps: HasProp` is not satisfied --> tests/derive_props/fail.rs:35:24 @@ -35,16 +37,21 @@ note: required because of the requirements on the impl of `HasAllProps` for `AssertAllProps` +note: required by a bound in `html::component::properties::__macro::PreBuild::::build` + --> $WORKSPACE/packages/yew/src/html/component/properties.rs + | + | Token: AllPropsFor, + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `html::component::properties::__macro::PreBuild::::build` = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Value: Default` is not satisfied - --> tests/derive_props/fail.rs:9:21 - | -9 | #[derive(Clone, Properties, PartialEq)] - | ^^^^^^^^^^ the trait `Default` is not implemented for `Value` - | -note: required by `Option::::unwrap_or_default` - = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) + --> tests/derive_props/fail.rs:9:21 + | +9 | #[derive(Clone, Properties, PartialEq)] + | ^^^^^^^^^^ the trait `Default` is not implemented for `Value` + | +note: required by a bound in `Option::::unwrap_or_default` + = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0369]: binary operation `==` cannot be applied to type `Value` --> tests/derive_props/fail.rs:13:9 @@ -55,8 +62,16 @@ error[E0369]: binary operation `==` cannot be applied to type `Value` 13 | value: Value, | ^^^^^^^^^^^^ | - = note: an implementation of `std::cmp::PartialEq` might be missing for `Value` +note: an implementation of `PartialEq<_>` might be missing for `Value` + --> tests/derive_props/fail.rs:8:5 + | +8 | struct Value; + | ^^^^^^^^^^^^^ must implement `PartialEq<_>` = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `Value` with `#[derive(PartialEq)]` + | +8 | #[derive(PartialEq)] + | error[E0369]: binary operation `!=` cannot be applied to type `Value` --> tests/derive_props/fail.rs:13:9 @@ -67,8 +82,16 @@ error[E0369]: binary operation `!=` cannot be applied to type `Value` 13 | value: Value, | ^^^^^^^^^^^^ | - = note: an implementation of `std::cmp::PartialEq` might be missing for `Value` +note: an implementation of `PartialEq<_>` might be missing for `Value` + --> tests/derive_props/fail.rs:8:5 + | +8 | struct Value; + | ^^^^^^^^^^^^^ must implement `PartialEq<_>` = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `Value` with `#[derive(PartialEq)]` + | +8 | #[derive(PartialEq)] + | error[E0308]: mismatched types --> tests/derive_props/fail.rs:54:19 @@ -110,4 +133,4 @@ error[E0271]: type mismatch resolving ` i32 {t10::foo} as FnOnce<()>>::O 98 | #[prop_or_else(foo)] | ^^^ expected struct `String`, found `i32` | -note: required by `Option::::unwrap_or_else` +note: required by a bound in `Option::::unwrap_or_else` diff --git a/packages/yew-macro/tests/derive_props_test.rs b/packages/yew-macro/tests/derive_props_test.rs index bf6569f20e4..c4d8892e03c 100644 --- a/packages/yew-macro/tests/derive_props_test.rs +++ b/packages/yew-macro/tests/derive_props_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn derive_props() { let t = trybuild::TestCases::new(); t.pass("tests/derive_props/pass.rs"); diff --git a/packages/yew-macro/tests/function_attr_test.rs b/packages/yew-macro/tests/function_attr_test.rs index 9d943680f6e..d9409490389 100644 --- a/packages/yew-macro/tests/function_attr_test.rs +++ b/packages/yew-macro/tests/function_attr_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/function_component_attr/*-pass.rs"); diff --git a/packages/yew-macro/tests/function_component_attr/bad-return-type-fail.stderr b/packages/yew-macro/tests/function_component_attr/bad-return-type-fail.stderr index 2e1fff24f1c..6e99e7d6473 100644 --- a/packages/yew-macro/tests/function_component_attr/bad-return-type-fail.stderr +++ b/packages/yew-macro/tests/function_component_attr/bad-return-type-fail.stderr @@ -10,9 +10,4 @@ error[E0277]: the trait bound `u32: IntoHtmlResult` is not satisfied 11 | #[function_component(Comp)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoHtmlResult` is not implemented for `u32` | -note: required by `into_html_result` - --> $WORKSPACE/packages/yew/src/html/mod.rs - | - | fn into_html_result(self) -> HtmlResult; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the attribute macro `function_component` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/packages/yew-macro/tests/function_component_attr/generic-props-fail.stderr b/packages/yew-macro/tests/function_component_attr/generic-props-fail.stderr index c331269bbe6..e298c0cc8ac 100644 --- a/packages/yew-macro/tests/function_component_attr/generic-props-fail.stderr +++ b/packages/yew-macro/tests/function_component_attr/generic-props-fail.stderr @@ -19,6 +19,11 @@ note: required because of the requirements on the impl of `HasAllProps` for `AssertAllProps` +note: required by a bound in `yew::html::component::properties::__macro::PreBuild::::build` + --> $WORKSPACE/packages/yew/src/html/component/properties.rs + | + | Token: AllPropsFor, + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::::build` = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Comp: yew::BaseComponent` is not satisfied @@ -32,17 +37,28 @@ error[E0277]: the trait bound `Comp: yew::BaseComponent` is n = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: the function or associated item `new` exists for struct `VChild>`, but its trait bounds were not satisfied - --> tests/function_component_attr/generic-props-fail.rs:27:14 - | -8 | #[function_component(Comp)] - | --------------------------- doesn't satisfy `Comp: yew::BaseComponent` + --> tests/function_component_attr/generic-props-fail.rs:27:14 + | +8 | #[function_component(Comp)] + | --------------------------- doesn't satisfy `Comp: yew::BaseComponent` ... -27 | html! { /> }; - | ^^^^ function or associated item cannot be called on `VChild>` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `Comp: yew::BaseComponent` - = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) +27 | html! { /> }; + | ^^^^ function or associated item cannot be called on `VChild>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Comp: yew::BaseComponent` +note: the following trait must be implemented + --> $WORKSPACE/packages/yew/src/html/component/mod.rs + | + | / pub trait BaseComponent: Sized + 'static { + | | /// The Component's Message. + | | type Message: 'static; + | | +... | + | | fn prepare_state(&self) -> Option; + | | } + | |_^ + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `MissingTypeBounds: yew::Properties` is not satisfied --> tests/function_component_attr/generic-props-fail.rs:27:14 diff --git a/packages/yew-macro/tests/function_component_attr/hook_location-fail.stderr b/packages/yew-macro/tests/function_component_attr/hook_location-fail.stderr index d40b4cfef08..7c65da37aaa 100644 --- a/packages/yew-macro/tests/function_component_attr/hook_location-fail.stderr +++ b/packages/yew-macro/tests/function_component_attr/hook_location-fail.stderr @@ -1,7 +1,7 @@ error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:9:9 | @@ -10,8 +10,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:14:9 | @@ -20,8 +20,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:19:9 | @@ -30,8 +30,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:22:26 | @@ -40,8 +40,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:23:9 | @@ -50,8 +50,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:27:20 | @@ -60,8 +60,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/function_component_attr/hook_location-fail.rs:34:9 | diff --git a/packages/yew-macro/tests/hook_attr/hook_location-fail.stderr b/packages/yew-macro/tests/hook_attr/hook_location-fail.stderr index 1f9d4697c6b..0836a6a1ac7 100644 --- a/packages/yew-macro/tests/hook_attr/hook_location-fail.stderr +++ b/packages/yew-macro/tests/hook_attr/hook_location-fail.stderr @@ -1,7 +1,7 @@ error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:9:9 | @@ -10,8 +10,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:14:9 | @@ -20,8 +20,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:19:9 | @@ -30,8 +30,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:22:26 | @@ -40,8 +40,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:23:9 | @@ -50,8 +50,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:27:20 | @@ -60,8 +60,8 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_location-fail.rs:34:9 | diff --git a/packages/yew-macro/tests/hook_attr/hook_macro-fail.stderr b/packages/yew-macro/tests/hook_attr/hook_macro-fail.stderr index ca7561ea43c..13e8c3ba574 100644 --- a/packages/yew-macro/tests/hook_attr/hook_macro-fail.stderr +++ b/packages/yew-macro/tests/hook_attr/hook_macro-fail.stderr @@ -1,7 +1,7 @@ error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_macro-fail.rs:20:9 | @@ -10,24 +10,18 @@ error: hooks cannot be called at this position. error: hooks cannot be called at this position. - = help: move hooks to the top-level of your function. - = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks + = help: move hooks to the top-level of your function. + = note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks --> tests/hook_attr/hook_macro-fail.rs:22:9 | 22 | use_some_macro!("b") | ^^^^^^^^^^^^^^ -warning: unused macro definition - --> tests/hook_attr/hook_macro-fail.rs:8:1 - | -8 | / macro_rules! use_some_macro { -9 | | () => { -10 | | use_some_macro_inner("default str") -11 | | }; -... | -14 | | }; -15 | | } - | |_^ - | - = note: `#[warn(unused_macros)]` on by default +warning: unused macro definition: `use_some_macro` + --> tests/hook_attr/hook_macro-fail.rs:8:14 + | +8 | macro_rules! use_some_macro { + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(unused_macros)]` on by default diff --git a/packages/yew-macro/tests/hook_attr_test.rs b/packages/yew-macro/tests/hook_attr_test.rs index e632dfceea0..dae90940ee7 100644 --- a/packages/yew-macro/tests/hook_attr_test.rs +++ b/packages/yew-macro/tests/hook_attr_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/hook_attr/*-pass.rs"); diff --git a/packages/yew-macro/tests/html_macro/block-fail.stderr b/packages/yew-macro/tests/html_macro/block-fail.stderr index 9ad328c38e4..2ef39785479 100644 --- a/packages/yew-macro/tests/html_macro/block-fail.stderr +++ b/packages/yew-macro/tests/html_macro/block-fail.stderr @@ -1,34 +1,32 @@ error[E0277]: `()` doesn't implement `std::fmt::Display` - --> tests/html_macro/block-fail.rs:6:15 - | -6 | { () } - | ^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `ToString` for `()` - = note: required because of the requirements on the impl of `From<()>` for `VNode` - = note: required because of the requirements on the impl of `Into` for `()` - = note: 2 redundant requirements hidden - = note: required because of the requirements on the impl of `Into>` for `()` -note: required by `into` - = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) + --> tests/html_macro/block-fail.rs:6:15 + | +6 | { () } + | ^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `ToString` for `()` + = note: required because of the requirements on the impl of `From<()>` for `VNode` + = note: required because of the requirements on the impl of `Into` for `()` + = note: 2 redundant requirements hidden + = note: required because of the requirements on the impl of `Into>` for `()` + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` - --> tests/html_macro/block-fail.rs:12:16 - | -12 |
{ not_tree() }
- | ^^^^^^^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `ToString` for `()` - = note: required because of the requirements on the impl of `From<()>` for `VNode` - = note: required because of the requirements on the impl of `Into` for `()` - = note: 2 redundant requirements hidden - = note: required because of the requirements on the impl of `Into>` for `()` -note: required by `into` - = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) + --> tests/html_macro/block-fail.rs:12:16 + | +12 |
{ not_tree() }
+ | ^^^^^^^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `ToString` for `()` + = note: required because of the requirements on the impl of `From<()>` for `VNode` + = note: required because of the requirements on the impl of `Into` for `()` + = note: 2 redundant requirements hidden + = note: required because of the requirements on the impl of `Into>` for `()` + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` --> tests/html_macro/block-fail.rs:15:17 diff --git a/packages/yew-macro/tests/html_macro/component-fail.stderr b/packages/yew-macro/tests/html_macro/component-fail.stderr index e1e62501041..3774f0a0182 100644 --- a/packages/yew-macro/tests/html_macro/component-fail.stderr +++ b/packages/yew-macro/tests/html_macro/component-fail.stderr @@ -160,10 +160,10 @@ error: expected identifier, found keyword `type` 71 | html! { }; | ^^^^ expected identifier, found keyword | -help: you can escape reserved keywords to use them as identifiers +help: escape `type` to use it as an identifier | 71 | html! { }; - | ~~~~~~ + | ++ error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression. --> tests/html_macro/component-fail.rs:72:24 @@ -336,33 +336,55 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:77:33 | 77 | html! { }; - | ^^ the trait `IntoPropValue` is not implemented for `()` + | ------ ^^ the trait `IntoPropValue` is not implemented for `()` + | | + | required by a bound introduced by this call + | +note: required by a bound in `ChildPropertiesBuilder::string` + --> tests/html_macro/component-fail.rs:4:17 + | +4 | #[derive(Clone, Properties, PartialEq)] + | ^^^^^^^^^^ required by this bound in `ChildPropertiesBuilder::string` +... +7 | pub string: String, + | ------ required by a bound in this + = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `{integer}: IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:78:33 | 78 | html! { }; - | ^ the trait `IntoPropValue` is not implemented for `{integer}` + | ------ ^ the trait `IntoPropValue` is not implemented for `{integer}` + | | + | required by a bound introduced by this call | - = help: the following implementations were found: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - and 31 others +note: required by a bound in `ChildPropertiesBuilder::string` + --> tests/html_macro/component-fail.rs:4:17 + | +4 | #[derive(Clone, Properties, PartialEq)] + | ^^^^^^^^^^ required by this bound in `ChildPropertiesBuilder::string` +... +7 | pub string: String, + | ------ required by a bound in this + = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `{integer}: IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:79:34 | 79 | html! { }; - | ^ the trait `IntoPropValue` is not implemented for `{integer}` + | ------ ^ the trait `IntoPropValue` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `ChildPropertiesBuilder::string` + --> tests/html_macro/component-fail.rs:4:17 | - = help: the following implementations were found: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - and 31 others +4 | #[derive(Clone, Properties, PartialEq)] + | ^^^^^^^^^^ required by this bound in `ChildPropertiesBuilder::string` +... +7 | pub string: String, + | ------ required by a bound in this + = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> tests/html_macro/component-fail.rs:80:31 @@ -374,7 +396,19 @@ error[E0277]: the trait bound `u32: IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:82:24 | 82 | html! { }; - | ^^^^ the trait `IntoPropValue` is not implemented for `u32` + | --- ^^^^ the trait `IntoPropValue` is not implemented for `u32` + | | + | required by a bound introduced by this call + | +note: required by a bound in `ChildPropertiesBuilder::int` + --> tests/html_macro/component-fail.rs:4:17 + | +4 | #[derive(Clone, Properties, PartialEq)] + | ^^^^^^^^^^ required by this bound in `ChildPropertiesBuilder::int` +... +8 | pub int: i32, + | --- required by a bound in this + = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `AssertAllProps: HasProp` is not satisfied --> tests/html_macro/component-fail.rs:83:14 @@ -388,6 +422,11 @@ note: required because of the requirements on the impl of `HasAllProps` for `AssertAllProps` +note: required by a bound in `yew::html::component::properties::__macro::PreBuild::::build` + --> $WORKSPACE/packages/yew/src/html/component/properties.rs + | + | Token: AllPropsFor, + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::::build` = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0609]: no field `children` on type `ChildProperties` @@ -431,6 +470,11 @@ note: required because of the requirements on the impl of `HasAllProps` for `AssertAllProps` +note: required by a bound in `yew::html::component::properties::__macro::PreBuild::::build` + --> $WORKSPACE/packages/yew/src/html/component/properties.rs + | + | Token: AllPropsFor, + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::::build` = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `AssertAllProps: HasProp<_ChildContainerProperties::children, _>` is not satisfied @@ -445,6 +489,11 @@ note: required because of the requirements on the impl of `HasAllProps` for `AssertAllProps` +note: required by a bound in `yew::html::component::properties::__macro::PreBuild::::build` + --> $WORKSPACE/packages/yew/src/html/component/properties.rs + | + | Token: AllPropsFor, + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::::build` = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `VChild: From` is not satisfied @@ -454,7 +503,6 @@ error[E0277]: the trait bound `VChild: From` is | ^^^^^^^^^^^^^ the trait `From` is not implemented for `VChild` | = note: required because of the requirements on the impl of `Into>` for `yew::virtual_dom::VText` -note: required by `into` error[E0277]: the trait bound `VChild: From` is not satisfied --> tests/html_macro/component-fail.rs:102:29 @@ -463,7 +511,6 @@ error[E0277]: the trait bound `VChild: From` is not satisfied | ^ the trait `From` is not implemented for `VChild` | = note: required because of the requirements on the impl of `Into>` for `VNode` -note: required by `into` error[E0277]: the trait bound `VChild: From` is not satisfied --> tests/html_macro/component-fail.rs:103:30 @@ -472,4 +519,3 @@ error[E0277]: the trait bound `VChild: From` is not satisfied | ^^^^^ the trait `From` is not implemented for `VChild` | = note: required because of the requirements on the impl of `Into>` for `VNode` -note: required by `into` diff --git a/packages/yew-macro/tests/html_macro/component-unimplemented-fail.stderr b/packages/yew-macro/tests/html_macro/component-unimplemented-fail.stderr index 7804e5f6d05..480c0693b39 100644 --- a/packages/yew-macro/tests/html_macro/component-unimplemented-fail.stderr +++ b/packages/yew-macro/tests/html_macro/component-unimplemented-fail.stderr @@ -8,14 +8,25 @@ error[E0277]: the trait bound `Unimplemented: yew::Component` is not satisfied = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: the function or associated item `new` exists for struct `VChild`, but its trait bounds were not satisfied - --> tests/html_macro/component-unimplemented-fail.rs:6:14 - | -3 | struct Unimplemented; - | --------------------- doesn't satisfy `Unimplemented: BaseComponent` + --> tests/html_macro/component-unimplemented-fail.rs:6:14 + | +3 | struct Unimplemented; + | --------------------- doesn't satisfy `Unimplemented: BaseComponent` ... -6 | html! { }; - | ^^^^^^^^^^^^^ function or associated item cannot be called on `VChild` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `Unimplemented: BaseComponent` - = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) +6 | html! { }; + | ^^^^^^^^^^^^^ function or associated item cannot be called on `VChild` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Unimplemented: BaseComponent` +note: the following trait must be implemented + --> $WORKSPACE/packages/yew/src/html/component/mod.rs + | + | / pub trait BaseComponent: Sized + 'static { + | | /// The Component's Message. + | | type Message: 'static; + | | +... | + | | fn prepare_state(&self) -> Option; + | | } + | |_^ + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr index de86b65d6d9..089444ed8ba 100644 --- a/packages/yew-macro/tests/html_macro/element-fail.stderr +++ b/packages/yew-macro/tests/html_macro/element-fail.stderr @@ -251,54 +251,33 @@ error[E0277]: the trait bound `(): IntoPropValue }; | ^^ the trait `IntoPropValue>` is not implemented for `()` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:44:27 | 44 | html! { }; | ^^ the trait `IntoPropValue>` is not implemented for `()` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:45:22 | 45 | html! { }; | ^^ the trait `IntoPropValue>` is not implemented for `()` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:46:28 | 46 | html! { }; | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Option: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:47:23 | 47 | html! { }; - | ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `Option` + | ----^^^^^^^^^^^^^ + | | + | the trait `IntoPropValue>` is not implemented for `Option` + | required by a bound introduced by this call | = help: the following implementations were found: as IntoPropValue>> @@ -306,17 +285,15 @@ error[E0277]: the trait bound `Option: IntoPropValue as IntoPropValue>>> > as IntoPropValue>> and 4 others -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:48:22 | 48 | html! { }; - | ^^^^^^^ the trait `IntoPropValue>` is not implemented for `Option<{integer}>` + | ----^^^ + | | + | the trait `IntoPropValue>` is not implemented for `Option<{integer}>` + | required by a bound introduced by this call | = help: the following implementations were found: as IntoPropValue>> @@ -324,17 +301,15 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue as IntoPropValue>>> > as IntoPropValue>> and 4 others -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `{integer}` --> tests/html_macro/element-fail.rs:51:28 | 51 | html! { }; - | ^ expected an `Fn<(MouseEvent,)>` closure, found `{integer}` + | -----------------------^----- + | | | + | | expected an `Fn<(MouseEvent,)>` closure, found `{integer}` + | required by a bound introduced by this call | = help: the trait `Fn<(MouseEvent,)>` is not implemented for `{integer}` = note: required because of the requirements on the impl of `IntoEventCallback` for `{integer}` @@ -355,12 +330,12 @@ error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback tests/html_macro/element-fail.rs:52:29 | 52 | html! { }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected an implementor of trait `IntoEventCallback` - | help: consider borrowing here: `&Callback::from(|a: String| ())` + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ + | | | + | | expected an `Fn<(MouseEvent,)>` closure, found `yew::Callback` + | required by a bound introduced by this call | - = note: the trait bound `yew::Callback: IntoEventCallback` is not satisfied + = help: the trait `Fn<(MouseEvent,)>` is not implemented for `yew::Callback` = note: required because of the requirements on the impl of `IntoEventCallback` for `yew::Callback` note: required by a bound in `yew::html::onclick::Wrapper::__macro_new` --> $WORKSPACE/packages/yew/src/html/listener/events.rs @@ -379,7 +354,10 @@ error[E0277]: the trait bound `Option<{integer}>: IntoEventCallback` --> tests/html_macro/element-fail.rs:53:29 | 53 | html! { }; - | ^^^^^^^ the trait `IntoEventCallback` is not implemented for `Option<{integer}>` + | ------------------------^^^^^^^------ + | | | + | | the trait `IntoEventCallback` is not implemented for `Option<{integer}>` + | required by a bound introduced by this call | = help: the following implementations were found: as IntoEventCallback> @@ -401,19 +379,19 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied --> tests/html_macro/element-fail.rs:56:25 | 56 | html! { }; - | ^^ the trait `IntoPropValue` is not implemented for `()` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^ + | | + | the trait `IntoPropValue` is not implemented for `()` + | required by a bound introduced by this call error[E0277]: the trait bound `Option: IntoPropValue` is not satisfied --> tests/html_macro/element-fail.rs:57:25 | 57 | html! { }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue` is not implemented for `Option` + | ----^^^^^^^^^^^^^^^^^^^^ + | | + | the trait `IntoPropValue` is not implemented for `Option` + | required by a bound introduced by this call | = help: the following implementations were found: as IntoPropValue>> @@ -421,22 +399,17 @@ error[E0277]: the trait bound `Option: IntoPropValue as IntoPropValue>>> > as IntoPropValue>> and 4 others -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback` --> tests/html_macro/element-fail.rs:58:29 | 58 | html! { }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected an implementor of trait `IntoEventCallback` - | help: consider borrowing here: `&Callback::from(|a: String| ())` + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ + | | | + | | expected an `Fn<(MouseEvent,)>` closure, found `yew::Callback` + | required by a bound introduced by this call | - = note: the trait bound `yew::Callback: IntoEventCallback` is not satisfied + = help: the trait `Fn<(MouseEvent,)>` is not implemented for `yew::Callback` = note: required because of the requirements on the impl of `IntoEventCallback` for `yew::Callback` note: required by a bound in `yew::html::onclick::Wrapper::__macro_new` --> $WORKSPACE/packages/yew/src/html/listener/events.rs @@ -456,36 +429,29 @@ error[E0277]: the trait bound `NotToString: IntoPropValue }; | ^^^^^^^^^^^ the trait `IntoPropValue>` is not implemented for `NotToString` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied --> tests/html_macro/element-fail.rs:62:25 | 62 | html! { }; - | ^^ the trait `IntoPropValue` is not implemented for `()` - | -note: required by `into_prop_value` - --> $WORKSPACE/packages/yew/src/html/conversion.rs - | - | fn into_prop_value(self) -> T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^ + | | + | the trait `IntoPropValue` is not implemented for `()` + | required by a bound introduced by this call error[E0277]: the trait bound `Cow<'static, str>: From<{integer}>` is not satisfied - --> tests/html_macro/element-fail.rs:77:15 - | -77 | html! { <@{55}> }; - | ^^^^ the trait `From<{integer}>` is not implemented for `Cow<'static, str>` - | - = help: the following implementations were found: - as From<&'a CStr>> - as From<&'a CString>> - as From> - as From<&'a OsStr>> - and 11 others - = note: required because of the requirements on the impl of `Into>` for `{integer}` -note: required by `into` + --> tests/html_macro/element-fail.rs:77:15 + | +77 | html! { <@{55}> }; + | ^--^ + | || + | |this tail expression is of type `_` + | the trait `From<{integer}>` is not implemented for `Cow<'static, str>` + | + = help: the following implementations were found: + as From<&'a CStr>> + as From<&'a CString>> + as From> + as From<&'a OsStr>> + and 11 others + = note: required because of the requirements on the impl of `Into>` for `{integer}` diff --git a/packages/yew-macro/tests/html_macro/iterable-fail.stderr b/packages/yew-macro/tests/html_macro/iterable-fail.stderr index b1702551117..f2ebfc0daac 100644 --- a/packages/yew-macro/tests/html_macro/iterable-fail.stderr +++ b/packages/yew-macro/tests/html_macro/iterable-fail.stderr @@ -5,24 +5,25 @@ error: expected an expression after the keyword `for` | ^^^ error[E0277]: `()` is not an iterator - --> tests/html_macro/iterable-fail.rs:5:17 - | -5 | html! { for () }; - | ^^ `()` is not an iterator - | - = help: the trait `Iterator` is not implemented for `()` - = note: required because of the requirements on the impl of `IntoIterator` for `()` -note: required by `into_iter` + --> tests/html_macro/iterable-fail.rs:5:17 + | +5 | html! { for () }; + | ^^ `()` is not an iterator + | + = help: the trait `Iterator` is not implemented for `()` + = note: required because of the requirements on the impl of `IntoIterator` for `()` error[E0277]: `()` is not an iterator - --> tests/html_macro/iterable-fail.rs:6:17 - | -6 | html! { for {()} }; - | ^^^^ `()` is not an iterator - | - = help: the trait `Iterator` is not implemented for `()` - = note: required because of the requirements on the impl of `IntoIterator` for `()` -note: required by `into_iter` + --> tests/html_macro/iterable-fail.rs:6:17 + | +6 | html! { for {()} }; + | ^--^ + | || + | |this tail expression is of type `_` + | `()` is not an iterator + | + = help: the trait `Iterator` is not implemented for `()` + = note: required because of the requirements on the impl of `IntoIterator` for `()` error[E0277]: `()` doesn't implement `std::fmt::Display` --> tests/html_macro/iterable-fail.rs:7:17 diff --git a/packages/yew-macro/tests/html_macro/node-fail.stderr b/packages/yew-macro/tests/html_macro/node-fail.stderr index 5fb050c2ba1..013688f83bd 100644 --- a/packages/yew-macro/tests/html_macro/node-fail.stderr +++ b/packages/yew-macro/tests/html_macro/node-fail.stderr @@ -41,25 +41,25 @@ error[E0425]: cannot find value `invalid` in this scope | ^^^^^^^ not found in this scope error[E0277]: `()` doesn't implement `std::fmt::Display` - --> tests/html_macro/node-fail.rs:6:13 - | -6 | html! { () }; - | ^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `ToString` for `()` - = note: required because of the requirements on the impl of `From<()>` for `VNode` -note: required by `from` + --> tests/html_macro/node-fail.rs:6:13 + | +6 | html! { () }; + | ^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `ToString` for `()` + = note: required because of the requirements on the impl of `From<()>` for `VNode` + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` - --> tests/html_macro/node-fail.rs:17:9 - | -17 | not_node() - | ^^^^^^^^^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `ToString` for `()` - = note: required because of the requirements on the impl of `From<()>` for `VNode` -note: required by `from` + --> tests/html_macro/node-fail.rs:17:9 + | +17 | not_node() + | ^^^^^^^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `ToString` for `()` + = note: required because of the requirements on the impl of `From<()>` for `VNode` + = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/packages/yew-macro/tests/html_macro_test.rs b/packages/yew-macro/tests/html_macro_test.rs index e242ebb87e4..b802002897c 100644 --- a/packages/yew-macro/tests/html_macro_test.rs +++ b/packages/yew-macro/tests/html_macro_test.rs @@ -1,7 +1,7 @@ use yew::{html, html_nested}; #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn html_macro() { let t = trybuild::TestCases::new(); diff --git a/packages/yew-macro/tests/props_macro_test.rs b/packages/yew-macro/tests/props_macro_test.rs index e36eef63d0d..83234005a47 100644 --- a/packages/yew-macro/tests/props_macro_test.rs +++ b/packages/yew-macro/tests/props_macro_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn props_macro() { let t = trybuild::TestCases::new(); t.pass("tests/props_macro/*-pass.rs"); diff --git a/packages/yew-router-macro/Cargo.toml b/packages/yew-router-macro/Cargo.toml index e1beb4323ed..e0f5662e354 100644 --- a/packages/yew-router-macro/Cargo.toml +++ b/packages/yew-router-macro/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "MIT OR Apache-2.0" description = "Contains macros used with yew-router" repository = "https://github.com/yewstack/yew" -rust-version = "1.56.1" +rust-version = "1.60.0" [lib] proc-macro = true diff --git a/packages/yew-router-macro/Makefile.toml b/packages/yew-router-macro/Makefile.toml index 7d9b6645a71..7b2d8fcba8a 100644 --- a/packages/yew-router-macro/Makefile.toml +++ b/packages/yew-router-macro/Makefile.toml @@ -1,6 +1,6 @@ [tasks.test] clear = true -toolchain = "1.56.1" +toolchain = "1.60.0" command = "cargo" args = ["test"] diff --git a/packages/yew-router-macro/tests/routable_derive_test.rs b/packages/yew-router-macro/tests/routable_derive_test.rs index 1cb913dce17..5c2ef260863 100644 --- a/packages/yew-router-macro/tests/routable_derive_test.rs +++ b/packages/yew-router-macro/tests/routable_derive_test.rs @@ -1,5 +1,5 @@ #[allow(dead_code)] -#[rustversion::attr(stable(1.56), test)] +#[rustversion::attr(stable(1.60), test)] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/routable_derive/*-pass.rs"); diff --git a/packages/yew-router/Cargo.toml b/packages/yew-router/Cargo.toml index d22c6d51cbe..cade55b1e70 100644 --- a/packages/yew-router/Cargo.toml +++ b/packages/yew-router/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["web", "yew", "router"] categories = ["gui", "web-programming"] description = "A router implementation for the Yew framework" repository = "https://github.com/yewstack/yew" -rust-version = "1.56.1" +rust-version = "1.60.0" [dependencies] yew = { version = "0.19.3", path = "../yew", default-features= false } diff --git a/packages/yew/Cargo.toml b/packages/yew/Cargo.toml index a29b116acc3..bb1a2a98311 100644 --- a/packages/yew/Cargo.toml +++ b/packages/yew/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["web", "webasm", "javascript"] categories = ["gui", "wasm", "web-programming"] description = "A framework for making client-side single-page apps" readme = "../../README.md" -rust-version = "1.56.1" +rust-version = "1.60.0" [dependencies] console_error_panic_hook = "0.1" @@ -26,13 +26,14 @@ slab = "0.4" wasm-bindgen = "0.2" yew-macro = { version = "^0.19.0", path = "../yew-macro" } thiserror = "1.0" - futures = { version = "0.3", optional = true } html-escape = { version = "0.2.9", optional = true } implicit-clone = { version = "0.2", features = ["map"] } base64ct = { version = "1.5.0", features = ["std"], optional = true } bincode = { version = "1.3.3", optional = true } serde = { version = "1", features = ["derive"] } +tokio = { version = "1.19", features = ["sync"] } +tokio-stream = { version = "0.1.9", features = ["sync"] } [dependencies.web-sys] version = "0.3" @@ -74,7 +75,9 @@ features = [ wasm-bindgen-futures = "0.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { version = "1.15.0", features = ["rt"], optional = true } +num_cpus = { version = "1.13", optional = true } +tokio-util = { version = "0.7", features = ["rt"], optional = true } +once_cell = "1" [dev-dependencies] wasm-bindgen-test = "0.3" @@ -92,16 +95,15 @@ features = [ ] [features] -# TODO: `dep:` syntax only supported with MSRV 1.60, would be more precise -# tokio = ["dep:tokio"] -ssr = ["futures", "html-escape", "base64ct", "bincode"] # dep:html-escape +tokio = ["tokio/rt", "dep:num_cpus", "dep:tokio-util"] +ssr = ["dep:futures", "dep:html-escape", "dep:base64ct", "dep:bincode"] csr = [] -hydration = ["csr", "bincode"] +hydration = ["csr", "dep:bincode"] nightly = ["yew-macro/nightly"] default = [] [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.15.0", features = ["full"] } +tokio = { version = "1.19", features = ["full"] } [package.metadata.docs.rs] all-features = true diff --git a/packages/yew/Makefile.toml b/packages/yew/Makefile.toml index 02cf9062616..da4903e1b63 100644 --- a/packages/yew/Makefile.toml +++ b/packages/yew/Makefile.toml @@ -1,6 +1,6 @@ [tasks.native-test] command = "cargo" -args = ["test", "--features", "csr,ssr,hydration"] +args = ["test", "--features", "csr,ssr,hydration,tokio"] [tasks.wasm-test] command = "wasm-pack" @@ -25,15 +25,6 @@ dependencies = ["native-test", "wasm-test"] script = ''' #!/usr/bin/env bash set -ex -cargo clippy -- --deny=warnings -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 --release -- --deny=warnings -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 +bash ../../ci/feature-soundness.sh +bash ../../ci/feature-soundness-release.sh ''' diff --git a/packages/yew/src/functional/hooks/use_prepared_state/feat_hydration.rs b/packages/yew/src/functional/hooks/use_prepared_state/feat_hydration.rs index 597c0c4c790..c6e6fde3ccf 100644 --- a/packages/yew/src/functional/hooks/use_prepared_state/feat_hydration.rs +++ b/packages/yew/src/functional/hooks/use_prepared_state/feat_hydration.rs @@ -9,7 +9,7 @@ use wasm_bindgen::JsValue; use super::PreparedStateBase; use crate::functional::{use_state, Hook, HookContext}; -use crate::io_coop::spawn_local; +use crate::platform::spawn_local; use crate::suspense::{Suspension, SuspensionResult}; #[cfg(target_arch = "wasm32")] diff --git a/packages/yew/src/functional/hooks/use_prepared_state/feat_ssr.rs b/packages/yew/src/functional/hooks/use_prepared_state/feat_ssr.rs index 47d08179e4c..fab2331e0ae 100644 --- a/packages/yew/src/functional/hooks/use_prepared_state/feat_ssr.rs +++ b/packages/yew/src/functional/hooks/use_prepared_state/feat_ssr.rs @@ -9,7 +9,7 @@ use serde::Serialize; use super::PreparedStateBase; use crate::functional::{use_memo, use_state, Hook, HookContext}; -use crate::io_coop::spawn_local; +use crate::platform::spawn_local; use crate::suspense::{Suspension, SuspensionResult}; #[doc(hidden)] diff --git a/packages/yew/src/html/component/lifecycle.rs b/packages/yew/src/html/component/lifecycle.rs index c12c7a2e1b6..75841b65904 100644 --- a/packages/yew/src/html/component/lifecycle.rs +++ b/packages/yew/src/html/component/lifecycle.rs @@ -41,7 +41,7 @@ pub(crate) enum ComponentRenderState { #[cfg(feature = "ssr")] Ssr { - sender: Option>, + sender: Option>, }, } diff --git a/packages/yew/src/html/component/scope.rs b/packages/yew/src/html/component/scope.rs index 68cdb7fc65c..519d7e5a397 100644 --- a/packages/yew/src/html/component/scope.rs +++ b/packages/yew/src/html/component/scope.rs @@ -12,7 +12,7 @@ use super::lifecycle::ComponentState; use super::BaseComponent; use crate::callback::Callback; use crate::context::{ContextHandle, ContextProvider}; -use crate::io_coop::spawn_local; +use crate::platform::spawn_local; #[cfg(any(feature = "csr", feature = "ssr"))] use crate::scheduler::Shared; @@ -260,22 +260,26 @@ impl Scope { #[cfg(feature = "ssr")] mod feat_ssr { - use futures::channel::oneshot; - use super::*; use crate::html::component::lifecycle::{ ComponentRenderState, CreateRunner, DestroyRunner, RenderRunner, }; + use crate::platform::io::BufWriter; + use crate::platform::sync::oneshot; use crate::scheduler; use crate::virtual_dom::Collectable; impl Scope { - pub(crate) async fn render_to_string( - self, - w: &mut String, + pub(crate) async fn render_into_stream( + &self, + w: &mut BufWriter, props: Rc, hydratable: bool, ) { + // Rust's Future implementation is stack-allocated and incurs zero runtime-cost. + // + // If the content of this channel is ready before it is awaited, it is + // similar to taking the value from a mutex lock. let (tx, rx) = oneshot::channel(); let state = ComponentRenderState::Ssr { sender: Some(tx) }; @@ -303,12 +307,13 @@ mod feat_ssr { let html = rx.await.unwrap(); let self_any_scope = AnyScope::from(self.clone()); - html.render_to_string(w, &self_any_scope, hydratable).await; + html.render_into_stream(w, &self_any_scope, hydratable) + .await; if let Some(prepared_state) = self.get_component().unwrap().prepare_state() { - w.push_str(r#""#); + w.write(r#""#.into()); } if hydratable { diff --git a/packages/yew/src/io_coop.rs b/packages/yew/src/io_coop.rs deleted file mode 100644 index 6fdd72ac3d4..00000000000 --- a/packages/yew/src/io_coop.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! module that provides io compatibility over browser tasks and other async io tasks (e.g.: tokio) - -#[cfg(target_arch = "wasm32")] -mod arch { - pub use wasm_bindgen_futures::spawn_local; -} - -#[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 - // wasm_bindgen_futures. - #[inline(always)] - pub(crate) fn spawn_local(f: F) - where - F: Future + 'static, - { - #[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."# - ); - } - } -} - -pub(crate) use arch::*; diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index a8b30c966c2..5371cda33ee 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -28,9 +28,7 @@ //! - `csr`: Enables Client-side Rendering support and [`Renderer`]. Only enable this feature if you //! are making a Yew application (not a library). //! - `ssr`: Enables Server-side Rendering support and [`ServerRenderer`]. -//! - `tokio`: Enables future-based APIs on non-wasm32 targets with tokio runtime. (You may want to -//! enable this if your application uses future-based APIs and it does not compile / lint on -//! non-wasm32 targets.) +//! - `tokio`: Enables future-based APIs on non-wasm32 targets with tokio runtime. //! - `hydration`: Enables Hydration support. //! //! ## Example @@ -284,7 +282,7 @@ pub mod context; mod dom_bundle; pub mod functional; pub mod html; -mod io_coop; +pub mod platform; pub mod scheduler; mod sealed; #[cfg(feature = "ssr")] diff --git a/packages/yew/src/platform/io.rs b/packages/yew/src/platform/io.rs new file mode 100644 index 00000000000..14e0e11d56e --- /dev/null +++ b/packages/yew/src/platform/io.rs @@ -0,0 +1,103 @@ +//! This module contains types for I/O functionality. + +// This module should remain private until impl trait type alias becomes available so +// `BufReader` can be produced with an existential type. + +use std::borrow::Cow; + +use futures::stream::Stream; + +use crate::platform::sync::mpsc::{self, UnboundedReceiverStream, UnboundedSender}; + +// Same as std::io::BufWriter and futures::io::BufWriter. +pub(crate) const DEFAULT_BUF_SIZE: usize = 8 * 1024; + +/// A [`futures::io::BufWriter`], but operates over string and yields into a Stream. +pub(crate) struct BufWriter { + buf: String, + tx: UnboundedSender, + capacity: usize, +} + +/// Creates a Buffer pair. +pub(crate) fn buffer(capacity: usize) -> (BufWriter, impl Stream) { + let (tx, rx) = mpsc::unbounded_channel::(); + + let tx = BufWriter { + buf: String::with_capacity(capacity), + tx, + capacity, + }; + + (tx, UnboundedReceiverStream::new(rx)) +} + +// Implementation Notes: +// +// When jemalloc is used and a reasonable buffer length is chosen, +// performance of this buffer is related to the number of allocations +// instead of the amount of memory that is allocated. +// +// A Bytes-based implementation is also tested, and yielded a similar performance to String-based +// buffer. +// +// Having a String-based buffer avoids unsafe / cost of conversion between String and Bytes +// when text based content is needed (e.g.: post-processing). +// +// `Bytes::from` can be used to convert a `String` to `Bytes` if web server asks for an +// `impl Stream`. This conversion incurs no memory allocation. +// +// Yielding the output with a Stream provides a couple advantages: +// +// 1. All child components of a VList can have their own buffer and be rendered concurrently. +// 2. If a fixed buffer is used, the rendering process can become blocked if the buffer is filled. +// Using a stream avoids this side effect and allows the renderer to finish rendering +// without being actively polled. +impl BufWriter { + #[inline] + pub fn capacity(&self) -> usize { + self.capacity + } + + fn drain(&mut self) { + let _ = self.tx.send(self.buf.drain(..).collect()); + self.buf.reserve(self.capacity); + } + + /// Returns `True` if the internal buffer has capacity to fit a string of certain length. + #[inline] + fn has_capacity_of(&self, next_part_len: usize) -> bool { + self.buf.capacity() >= self.buf.len() + next_part_len + } + + /// Writes a string into the buffer, optionally drains the buffer. + pub fn write(&mut self, s: Cow<'_, str>) { + if !self.has_capacity_of(s.len()) { + // There isn't enough capacity, we drain the buffer. + self.drain(); + } + + if self.has_capacity_of(s.len()) { + // The next part is going to fit into the buffer, we push it onto the buffer. + self.buf.push_str(&s); + } else { + // if the next part is more than buffer size, we send the next part. + + // We don't need to drain the buffer here as the result of self.has_capacity_of() only + // changes if the buffer was drained. If the buffer capacity didn't change, + // then it means self.has_capacity_of() has returned true the first time which will be + // guaranteed to be matched by the left hand side of this implementation. + let _ = self.tx.send(s.into_owned()); + } + } +} + +impl Drop for BufWriter { + fn drop(&mut self) { + if !self.buf.is_empty() { + let mut buf = String::new(); + std::mem::swap(&mut buf, &mut self.buf); + let _ = self.tx.send(buf); + } + } +} diff --git a/packages/yew/src/platform/mod.rs b/packages/yew/src/platform/mod.rs new file mode 100644 index 00000000000..48b35ddd4b0 --- /dev/null +++ b/packages/yew/src/platform/mod.rs @@ -0,0 +1,90 @@ +//! Compatibility between JavaScript Runtime and Native Runtimes. +//! +//! When designing components and libraries that works on both WebAssembly targets backed by +//! JavaScript Runtime and non-WebAssembly targets with Native Runtimes. Developers usually face +//! challenges that requires applying multiple feature flags throughout their application: +//! +//! 1. Select I/O and timers that works with the target runtime. +//! 2. Native Runtimes usually require `Send` futures and WebAssembly usually use `!Send` +//! primitives for better performance during Client-side Rendering. +//! +//! To alleviate these issues, Yew implements a single-threaded runtime that executes `?Send` +//! (`Send` or `!Send`) futures. When your application starts with `yew::Renderer` or is rendered by +//! `yew::ServerRenderer`, it is executed within the Yew runtime. On systems with multi-threading +//! support, it spawns multiple independent runtimes in a worker pool proportional to the CPU +//! core number. The renderer will randomly select a worker thread from the internal pool. All tasks +//! spawned with `spawn_local` in the application will run on the same thread as the +//! rendering thread the renderer has selected. When the renderer runs in a WebAssembly target, all +//! tasks will be scheduled on the main thread. +//! +//! This runtime is designed in favour of IO-bounded workload with similar runtime cost. It produces +//! better performance by pinning tasks to a single worker thread. However, this means that if a +//! worker thread is back-logged, other threads will not be able to "help" by running tasks +//! scheduled on the busy thread. When you have a CPU-bounded task where CPU time is significantly +//! more expensive than rendering tasks, it should be spawned with a dedicated thread or +//! `yew-agent` and communicates with the application using channels or agent bridges. +//! +//! # Runtime Backend +//! +//! Yew runtime is implemented with different runtimes depending on the target platform and can use +//! all features (timers / IO / task synchronisation) from the selected native runtime: +//! +//! - `wasm-bindgen-futures` (WebAssembly targets) +//! - `tokio` (non-WebAssembly targets) +//! +//! # Compatibility with other async runtimes +//! +//! Yew's ServerRenderer can also be executed in applications using other async runtimes(e.g.: +//! `async-std`). Rendering tasks will enter Yew runtime and be executed with `tokio`. When the +//! rendering task finishes, the result is returned to the original runtime. This process is +//! transparent to the future that executes the renderer. The Yew application still needs to use +//! `tokio`'s timer, IO and task synchronisation primitives. + +use std::future::Future; + +#[cfg(feature = "ssr")] +pub(crate) mod io; + +pub mod sync; + +#[cfg(target_arch = "wasm32")] +#[path = "rt_wasm_bindgen.rs"] +mod imp; +#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))] +#[path = "rt_tokio.rs"] +mod imp; +#[cfg(all(not(target_arch = "wasm32"), not(feature = "tokio")))] +#[path = "rt_none.rs"] +mod imp; + +/// Spawns a task on current thread. +/// +/// # Panics +/// +/// This function will panic when not being executed from within a Yew Application. +#[inline(always)] +pub fn spawn_local(f: F) +where + F: Future + 'static, +{ + imp::spawn_local(f); +} + +/// Runs a task with it pinned onto a local worker thread. +/// +/// This can be used to execute non-Send futures without blocking the current thread. +/// +/// It maintains an internal thread pool dedicated to executing local futures. +/// +/// [`spawn_local`] is available with tasks executed with `run_pinned`. +#[inline(always)] +#[cfg(feature = "ssr")] +pub(crate) async fn run_pinned(create_task: F) -> Fut::Output +where + F: FnOnce() -> Fut, + F: Send + 'static, + Fut: Future + 'static, + Fut::Output: Send + 'static, +{ + imp::run_pinned(create_task).await +} diff --git a/packages/yew/src/platform/rt_none.rs b/packages/yew/src/platform/rt_none.rs new file mode 100644 index 00000000000..3a8dc6a671f --- /dev/null +++ b/packages/yew/src/platform/rt_none.rs @@ -0,0 +1,26 @@ +use std::future::Future; + +#[inline(always)] +pub(super) fn spawn_local(_f: F) +where + F: Future + 'static, +{ + panic!( + r#"No runtime configured for this platform, features that requires task spawning can't be used. + Either compile with `target_arch = "wasm32", or enable the `tokio` feature."# + ); +} + +#[cfg(feature = "ssr")] +pub(crate) async fn run_pinned(_create_task: F) -> Fut::Output +where + F: FnOnce() -> Fut, + F: Send + 'static, + Fut: Future + 'static, + Fut::Output: Send + 'static, +{ + panic!( + r#"No runtime configured for this platform, features that requires task spawning can't be used. + Either compile with `target_arch = "wasm32", or enable the `tokio` feature."# + ) +} diff --git a/packages/yew/src/platform/rt_tokio.rs b/packages/yew/src/platform/rt_tokio.rs new file mode 100644 index 00000000000..ee8e2251a71 --- /dev/null +++ b/packages/yew/src/platform/rt_tokio.rs @@ -0,0 +1,29 @@ +use std::future::Future; + +#[cfg(feature = "ssr")] +pub(super) async fn run_pinned(create_task: F) -> Fut::Output +where + F: FnOnce() -> Fut, + F: Send + 'static, + Fut: Future + 'static, + Fut::Output: Send + 'static, +{ + use once_cell::sync::Lazy; + use tokio_util::task::LocalPoolHandle; + + static POOL_HANDLE: Lazy = + Lazy::new(|| LocalPoolHandle::new(num_cpus::get() * 2)); + + POOL_HANDLE + .spawn_pinned(create_task) + .await + .expect("future has panicked!") +} + +#[inline(always)] +pub(super) fn spawn_local(f: F) +where + F: Future + 'static, +{ + tokio::task::spawn_local(f); +} diff --git a/packages/yew/src/platform/rt_wasm_bindgen.rs b/packages/yew/src/platform/rt_wasm_bindgen.rs new file mode 100644 index 00000000000..8307bfd4350 --- /dev/null +++ b/packages/yew/src/platform/rt_wasm_bindgen.rs @@ -0,0 +1,15 @@ +#[cfg(feature = "ssr")] +use std::future::Future; + +pub(super) use wasm_bindgen_futures::spawn_local; + +#[cfg(feature = "ssr")] +pub(crate) async fn run_pinned(create_task: F) -> Fut::Output +where + F: FnOnce() -> Fut, + F: Send + 'static, + Fut: Future + 'static, + Fut::Output: Send + 'static, +{ + create_task().await +} diff --git a/packages/yew/src/platform/sync/mod.rs b/packages/yew/src/platform/sync/mod.rs new file mode 100644 index 00000000000..63c99dec41a --- /dev/null +++ b/packages/yew/src/platform/sync/mod.rs @@ -0,0 +1,5 @@ +//! A module that provides task synchronisation primitives. + +#[doc(inline)] +pub use tokio::sync::oneshot; +pub mod mpsc; diff --git a/packages/yew/src/platform/sync/mpsc.rs b/packages/yew/src/platform/sync/mpsc.rs new file mode 100644 index 00000000000..de09d342bc9 --- /dev/null +++ b/packages/yew/src/platform/sync/mpsc.rs @@ -0,0 +1,6 @@ +//! A multi-producer, single-receiver channel. + +#[doc(inline)] +pub use tokio::sync::mpsc::*; +#[doc(inline)] +pub use tokio_stream::wrappers::{ReceiverStream, UnboundedReceiverStream}; diff --git a/packages/yew/src/scheduler.rs b/packages/yew/src/scheduler.rs index 2216e16c1f7..3fc57f94dcf 100644 --- a/packages/yew/src/scheduler.rs +++ b/packages/yew/src/scheduler.rs @@ -165,7 +165,7 @@ pub(crate) fn start_now() { #[cfg(target_arch = "wasm32")] mod arch { - use crate::io_coop::spawn_local; + use crate::platform::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. diff --git a/packages/yew/src/server_renderer.rs b/packages/yew/src/server_renderer.rs index 9574cdbc623..45c6c79c2e9 100644 --- a/packages/yew/src/server_renderer.rs +++ b/packages/yew/src/server_renderer.rs @@ -1,17 +1,24 @@ +use std::fmt; + +use futures::stream::{Stream, StreamExt}; + use crate::html::{BaseComponent, Scope}; +use crate::platform::io::{self, DEFAULT_BUF_SIZE}; +use crate::platform::{run_pinned, spawn_local}; -/// A Yew Server-side Renderer. +/// A Yew Server-side Renderer that renders on the current thread. #[cfg_attr(documenting, doc(cfg(feature = "ssr")))] #[derive(Debug)] -pub struct ServerRenderer +pub struct LocalServerRenderer where COMP: BaseComponent, { props: COMP::Properties, hydratable: bool, + capacity: usize, } -impl Default for ServerRenderer +impl Default for LocalServerRenderer where COMP: BaseComponent, COMP::Properties: Default, @@ -21,6 +28,119 @@ where } } +impl LocalServerRenderer +where + COMP: BaseComponent, + COMP::Properties: Default, +{ + /// Creates a [LocalServerRenderer] with default properties. + pub fn new() -> Self { + Self::default() + } +} + +impl LocalServerRenderer +where + COMP: BaseComponent, +{ + /// Creates a [LocalServerRenderer] with custom properties. + pub fn with_props(props: COMP::Properties) -> Self { + Self { + props, + hydratable: true, + capacity: DEFAULT_BUF_SIZE, + } + } + + /// Sets the capacity of renderer buffer. + /// + /// Default: `8192` + pub fn capacity(mut self, capacity: usize) -> Self { + self.capacity = capacity; + + self + } + + /// Sets whether an the rendered result is hydratable. + /// + /// Defaults to `true`. + /// + /// When this is sets to `true`, the rendered artifact will include additional information + /// to assist with the hydration process. + pub fn hydratable(mut self, val: bool) -> Self { + self.hydratable = val; + + self + } + + /// Renders Yew Application. + pub async fn render(self) -> String { + let mut s = String::new(); + + self.render_to_string(&mut s).await; + + s + } + + /// Renders Yew Application to a String. + pub async fn render_to_string(self, w: &mut String) { + let mut s = self.render_stream(); + + while let Some(m) = s.next().await { + w.push_str(&m); + } + } + + /// Renders Yew Applications into a string Stream + pub fn render_stream(self) -> impl Stream { + let (mut w, r) = io::buffer(self.capacity); + + let scope = Scope::::new(None); + spawn_local(async move { + scope + .render_into_stream(&mut w, self.props.into(), self.hydratable) + .await; + }); + + r + } +} + +/// A Yew Server-side Renderer. +/// +/// This renderer spawns the rendering task to an internal worker pool and receives result when +/// the rendering process has finished. +/// +/// See [`yew::platform`] for more information. +#[cfg_attr(documenting, doc(cfg(feature = "ssr")))] +pub struct ServerRenderer +where + COMP: BaseComponent, +{ + create_props: Box COMP::Properties>, + hydratable: bool, + capacity: usize, +} + +impl fmt::Debug for ServerRenderer +where + COMP: BaseComponent, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ServerRenderer<_>") + } +} + +impl Default for ServerRenderer +where + COMP: BaseComponent, + COMP::Properties: Default, +{ + fn default() -> Self { + Self::with_props(Default::default) + } +} + impl ServerRenderer where COMP: BaseComponent, @@ -37,13 +157,31 @@ where COMP: BaseComponent, { /// Creates a [ServerRenderer] with custom properties. - pub fn with_props(props: COMP::Properties) -> Self { + /// + /// # Note + /// + /// The properties does not have to implement `Send`. + /// However, the function to create properties needs to be `Send`. + pub fn with_props(create_props: F) -> Self + where + F: 'static + Send + FnOnce() -> COMP::Properties, + { Self { - props, + create_props: Box::new(create_props), hydratable: true, + capacity: DEFAULT_BUF_SIZE, } } + /// Sets the capacity of renderer buffer. + /// + /// Default: `8192` + pub fn capacity(mut self, capacity: usize) -> Self { + self.capacity = capacity; + + self + } + /// Sets whether an the rendered result is hydratable. /// /// Defaults to `true`. @@ -67,9 +205,34 @@ where /// Renders Yew Application to a String. pub async fn render_to_string(self, w: &mut String) { - let scope = Scope::::new(None); - scope - .render_to_string(w, self.props.into(), self.hydratable) - .await; + let mut s = self.render_stream().await; + + while let Some(m) = s.next().await { + w.push_str(&m); + } + } + + /// Renders Yew Applications into a string Stream. + /// + /// # Note + /// + /// Unlike [`LocalServerRenderer::render_stream`], this method is `async fn`. + pub async fn render_stream(self) -> impl Stream { + // We use run_pinned to switch to our runtime. + run_pinned(move || async move { + let Self { + create_props, + hydratable, + capacity, + } = self; + + let props = create_props(); + + LocalServerRenderer::::with_props(props) + .hydratable(hydratable) + .capacity(capacity) + .render_stream() + }) + .await } } diff --git a/packages/yew/src/suspense/suspension.rs b/packages/yew/src/suspense/suspension.rs index 01f0dd8849d..23663f974ff 100644 --- a/packages/yew/src/suspense/suspension.rs +++ b/packages/yew/src/suspense/suspension.rs @@ -7,7 +7,7 @@ use std::task::{Context, Poll}; use thiserror::Error; -use crate::io_coop::spawn_local; +use crate::platform::spawn_local; use crate::Callback; thread_local! { diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs index 331f19a1dfb..d97e3184b22 100644 --- a/packages/yew/src/virtual_dom/mod.rs +++ b/packages/yew/src/virtual_dom/mod.rs @@ -52,6 +52,9 @@ mod feat_ssr_hydration { #[cfg(not(debug_assertions))] type ComponentName = (); + #[cfg(feature = "hydration")] + use std::borrow::Cow; + /// A collectable. /// /// This indicates a kind that can be collected from fragment to be processed at a later time @@ -90,52 +93,58 @@ mod feat_ssr_hydration { } } - #[cfg(feature = "ssr")] - pub fn write_open_tag(&self, w: &mut String) { - w.push_str(""); } + } +} + +#[cfg(any(feature = "ssr", feature = "hydration"))] +pub(crate) use feat_ssr_hydration::*; - #[cfg(feature = "ssr")] - pub fn write_close_tag(&self, w: &mut String) { - w.push_str(""); + w.write(self.end_mark().into()); + w.write("-->".into()); } - #[cfg(feature = "hydration")] - pub fn name(&self) -> std::borrow::Cow<'static, str> { + pub(crate) fn write_close_tag(&self, w: &mut BufWriter) { + w.write("".into()); } } } -#[cfg(any(feature = "ssr", feature = "hydration"))] -pub(crate) use feat_ssr_hydration::*; - /// A collection of attributes for an element #[derive(PartialEq, Eq, Clone, Debug)] pub enum Attributes { diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index 6311eb1b278..55a5603bd88 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -19,6 +19,8 @@ use crate::html::Scoped; #[cfg(any(feature = "ssr", feature = "csr"))] use crate::html::{AnyScope, Scope}; use crate::html::{BaseComponent, NodeRef}; +#[cfg(feature = "ssr")] +use crate::platform::io::BufWriter; /// A virtual component. pub struct VComp { @@ -67,9 +69,9 @@ pub(crate) trait Mountable { fn reuse(self: Box, scope: &dyn Scoped, next_sibling: NodeRef); #[cfg(feature = "ssr")] - fn render_to_string<'a>( + fn render_into_stream<'a>( &'a self, - w: &'a mut String, + w: &'a mut BufWriter, parent_scope: &'a AnyScope, hydratable: bool, ) -> LocalBoxFuture<'a, ()>; @@ -125,16 +127,17 @@ impl Mountable for PropsWrapper { } #[cfg(feature = "ssr")] - fn render_to_string<'a>( + fn render_into_stream<'a>( &'a self, - w: &'a mut String, + w: &'a mut BufWriter, parent_scope: &'a AnyScope, hydratable: bool, ) -> LocalBoxFuture<'a, ()> { + let scope: Scope = Scope::new(Some(parent_scope.clone())); + async move { - let scope: Scope = Scope::new(Some(parent_scope.clone())); scope - .render_to_string(w, self.props.clone(), hydratable) + .render_into_stream(w, self.props.clone(), hydratable) .await; } .boxed_local() @@ -240,15 +243,16 @@ mod feat_ssr { use crate::html::AnyScope; impl VComp { - pub(crate) async fn render_to_string( + #[inline] + pub(crate) async fn render_into_stream( &self, - w: &mut String, + w: &mut BufWriter, parent_scope: &AnyScope, hydratable: bool, ) { self.mountable .as_ref() - .render_to_string(w, parent_scope, hydratable) + .render_into_stream(w, parent_scope, hydratable) .await; } } diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs index 468b032d6d3..5b9c64b93f0 100644 --- a/packages/yew/src/virtual_dom/vlist.rs +++ b/packages/yew/src/virtual_dom/vlist.rs @@ -156,33 +156,54 @@ mod test { #[cfg(feature = "ssr")] mod feat_ssr { + use futures::stream::{FuturesOrdered, StreamExt}; + use super::*; use crate::html::AnyScope; + use crate::platform::io::{self, BufWriter}; impl VList { - pub(crate) async fn render_to_string( + pub(crate) async fn render_into_stream( &self, - w: &mut String, + w: &mut BufWriter, parent_scope: &AnyScope, hydratable: bool, ) { - // Concurrently render all children. - for fragment in futures::future::join_all(self.children.iter().map(|m| async move { - let mut w = String::new(); - - m.render_to_string(&mut w, parent_scope, hydratable).await; - - w - })) - .await - { - w.push_str(&fragment) + match &self.children[..] { + [] => {} + [child] => { + child.render_into_stream(w, parent_scope, hydratable).await; + } + _ => { + let buf_capacity = w.capacity(); + + // Concurrently render all children. + let mut children: FuturesOrdered<_> = self + .children + .iter() + .map(|m| async move { + let (mut w, r) = io::buffer(buf_capacity); + + m.render_into_stream(&mut w, parent_scope, hydratable).await; + drop(w); + + r + }) + .collect(); + + while let Some(mut r) = children.next().await { + while let Some(next_chunk) = r.next().await { + w.write(next_chunk.into()); + } + } + } } } } } #[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "ssr")] #[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs index fbfb6ffdf17..2624e4a772d 100644 --- a/packages/yew/src/virtual_dom/vnode.rs +++ b/packages/yew/src/virtual_dom/vnode.rs @@ -153,26 +153,31 @@ mod feat_ssr { use super::*; use crate::html::AnyScope; + use crate::platform::io::BufWriter; impl VNode { - // Boxing is needed here, due to: https://rust-lang.github.io/async-book/07_workarounds/04_recursion.html - pub(crate) fn render_to_string<'a>( + pub(crate) fn render_into_stream<'a>( &'a self, - w: &'a mut String, + w: &'a mut BufWriter, parent_scope: &'a AnyScope, hydratable: bool, ) -> LocalBoxFuture<'a, ()> { - async move { - match self { - VNode::VTag(vtag) => vtag.render_to_string(w, parent_scope, hydratable).await, + async fn render_into_stream_( + this: &VNode, + w: &mut BufWriter, + parent_scope: &AnyScope, + hydratable: bool, + ) { + match this { + VNode::VTag(vtag) => vtag.render_into_stream(w, parent_scope, hydratable).await, VNode::VText(vtext) => { - vtext.render_to_string(w, parent_scope, hydratable).await + vtext.render_into_stream(w, parent_scope, hydratable).await } VNode::VComp(vcomp) => { - vcomp.render_to_string(w, parent_scope, hydratable).await + vcomp.render_into_stream(w, parent_scope, hydratable).await } VNode::VList(vlist) => { - vlist.render_to_string(w, parent_scope, hydratable).await + vlist.render_into_stream(w, parent_scope, hydratable).await } // We are pretty safe here as it's not possible to get a web_sys::Node without // DOM support in the first place. @@ -186,12 +191,14 @@ mod feat_ssr { VNode::VPortal(_) => {} VNode::VSuspense(vsuspense) => { vsuspense - .render_to_string(w, parent_scope, hydratable) + .render_into_stream(w, parent_scope, hydratable) .await } } } - .boxed_local() + + async move { render_into_stream_(self, w, parent_scope, hydratable).await } + .boxed_local() } } } diff --git a/packages/yew/src/virtual_dom/vsuspense.rs b/packages/yew/src/virtual_dom/vsuspense.rs index 0d4aaf9bdc5..35dd30253a1 100644 --- a/packages/yew/src/virtual_dom/vsuspense.rs +++ b/packages/yew/src/virtual_dom/vsuspense.rs @@ -28,12 +28,13 @@ impl VSuspense { mod feat_ssr { use super::*; use crate::html::AnyScope; + use crate::platform::io::BufWriter; use crate::virtual_dom::Collectable; impl VSuspense { - pub(crate) async fn render_to_string( + pub(crate) async fn render_into_stream( &self, - w: &mut String, + w: &mut BufWriter, parent_scope: &AnyScope, hydratable: bool, ) { @@ -45,7 +46,7 @@ mod feat_ssr { // always render children on the server side. self.children - .render_to_string(w, parent_scope, hydratable) + .render_into_stream(w, parent_scope, hydratable) .await; if hydratable { @@ -56,6 +57,7 @@ mod feat_ssr { } #[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "ssr")] #[cfg(test)] mod ssr_tests { use std::rc::Rc; diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs index bab201bb8b5..cf69128ec31 100644 --- a/packages/yew/src/virtual_dom/vtag.rs +++ b/packages/yew/src/virtual_dom/vtag.rs @@ -428,10 +428,9 @@ impl PartialEq for VTag { #[cfg(feature = "ssr")] mod feat_ssr { - use std::fmt::Write; - use super::*; use crate::html::AnyScope; + use crate::platform::io::BufWriter; use crate::virtual_dom::VText; // Elements that cannot have any child elements. @@ -441,19 +440,23 @@ mod feat_ssr { ]; impl VTag { - pub(crate) async fn render_to_string( + pub(crate) async fn render_into_stream( &self, - w: &mut String, + w: &mut BufWriter, parent_scope: &AnyScope, hydratable: bool, ) { - write!(w, "<{}", self.tag()).unwrap(); + w.write("<".into()); + w.write(self.tag().into()); - let write_attr = |w: &mut String, name: &str, val: Option<&str>| { - write!(w, " {}", name).unwrap(); + let write_attr = |w: &mut BufWriter, name: &str, val: Option<&str>| { + w.write(" ".into()); + w.write(name.into()); if let Some(m) = val { - write!(w, "=\"{}\"", html_escape::encode_double_quoted_attribute(m)).unwrap(); + w.write("=\"".into()); + w.write(html_escape::encode_double_quoted_attribute(m)); + w.write("\"".into()); } }; @@ -471,18 +474,18 @@ mod feat_ssr { write_attr(w, k, Some(v)); } - write!(w, ">").unwrap(); + w.write(">".into()); match self.inner { VTagInner::Input(_) => {} VTagInner::Textarea { .. } => { if let Some(m) = self.value() { VText::new(m.to_owned()) - .render_to_string(w, parent_scope, hydratable) + .render_into_stream(w, parent_scope, hydratable) .await; } - w.push_str(""); + w.write("".into()); } VTagInner::Other { ref tag, @@ -490,9 +493,13 @@ mod feat_ssr { .. } => { if !VOID_ELEMENTS.contains(&tag.as_ref()) { - children.render_to_string(w, parent_scope, hydratable).await; + children + .render_into_stream(w, parent_scope, hydratable) + .await; - write!(w, "", tag).unwrap(); + w.write(Cow::Borrowed("")); } else { // We don't write children of void elements nor closing tags. debug_assert!(children.is_empty(), "{} cannot have any children!", tag); @@ -504,6 +511,7 @@ mod feat_ssr { } #[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "ssr")] #[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 e187d4d5605..5ee6df06bfe 100644 --- a/packages/yew/src/virtual_dom/vtext.rs +++ b/packages/yew/src/virtual_dom/vtext.rs @@ -34,22 +34,26 @@ impl PartialEq for VText { #[cfg(feature = "ssr")] mod feat_ssr { + use super::*; use crate::html::AnyScope; + use crate::platform::io::BufWriter; impl VText { - pub(crate) async fn render_to_string( + pub(crate) async fn render_into_stream( &self, - w: &mut String, + w: &mut BufWriter, _parent_scope: &AnyScope, _hydratable: bool, ) { - html_escape::encode_text_to_string(&self.text, w); + let s = html_escape::encode_text(&self.text); + w.write(s); } } } #[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "ssr")] #[cfg(test)] mod ssr_tests { use tokio::test; diff --git a/tools/changelog/Cargo.toml b/tools/changelog/Cargo.toml index 56a6ae172dc..1f2afa64bff 100644 --- a/tools/changelog/Cargo.toml +++ b/tools/changelog/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] anyhow = "1" chrono = "0.4" -git2 = "=0.14.2" # see https://github.com/rust-lang/git2-rs/issues/838 fixed with MSRV 1.60 +git2 = "0.14" regex = "1" reqwest = { version = "0.11", features = ["blocking", "json"] } serde = { version = "1", features = ["derive"] }