mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Streamed SSR Response (#2697)
* yew::platform? * Stream Response. * Migrate example * Remove old implementation. * Remove extra implementation. * Prefer String instead of Cow. * Fix MSRV. * Fix trybuild. * Optimise Memory Allocation. * More optimisation. * BufWriter. * Fix tests. * Optimise BufWriter. * Remove more allocations. * Allow setting of buffer capacity. * Fix capacity size. * Fix capacity size. * Remove unneeded const notation. * Fix macro tests. * Slightly optimises BufWriter committing logic. * Optimise Implementation. * Move BufWriter to a separate file. * Additional Implementation Note. * Adjust API so it matches `std::channel::mpsc::channel`. * Fix feature soundness. * Make a compatibility layer on channels. * Fix clippy. * Fix feature soundness. * Fix CI. * Inlining. * Add documentation. * Punctuation. * Switch to tokio channel. * Remvoe pin-project. * Fix feature soundness. * Typo. * Move io to platform. * Tokio does not compile. * Fix workflow. * Restore wrongly removed docs. * Does tokio work? * Switch back to tokio. * Remove pin-project. * Use cargo resolver 2. * Add panic notice. * Update documentation. * Properties does not have to be send. * Fix capacity checking as pointed in the review. * Implementation order. * Update note.
This commit is contained in:
parent
74f850a7ab
commit
7f5eb3890c
22
.github/workflows/main-checks.yml
vendored
22
.github/workflows/main-checks.yml
vendored
@ -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
|
||||
|
||||
|
||||
@ -45,3 +45,4 @@ members = [
|
||||
"tools/process-benchmark-results",
|
||||
"tools/website-test",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
32
ci/feature-soundness-release.sh
Executable file
32
ci/feature-soundness-release.sh
Executable file
@ -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
|
||||
32
ci/feature-soundness.sh
Executable file
32
ci/feature-soundness.sh
Executable file
@ -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
|
||||
@ -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::*;
|
||||
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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<LocalPoolHandle> = Lazy::new(|| LocalPoolHandle::new(num_cpus::get()));
|
||||
type BoxedError = Box<dyn Error + Send + Sync + 'static>;
|
||||
|
||||
/// 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::<App>::new();
|
||||
async fn render(
|
||||
index_html_before: String,
|
||||
index_html_after: String,
|
||||
) -> Box<dyn Stream<Item = Result<Bytes, BoxedError>> + Send> {
|
||||
let renderer = yew::ServerRenderer::<App>::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("<body>", &format!("<body>{}", content))
|
||||
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 html = warp::path::end().then(move || {
|
||||
let index_html_s = index_html_s.clone();
|
||||
let (index_html_before, index_html_after) = index_html_s.split_once("<body>").unwrap();
|
||||
let mut index_html_before = index_html_before.to_owned();
|
||||
index_html_before.push_str("<body>");
|
||||
let index_html_after = index_html_after.to_owned();
|
||||
|
||||
async move { warp::reply::html(render(&index_html_s).await) }
|
||||
let html = warp::path::end().then(move || {
|
||||
let index_html_before = index_html_before.clone();
|
||||
let index_html_after = index_html_after.clone();
|
||||
|
||||
async move { warp::reply::html(render(index_html_before, index_html_after).await) }
|
||||
});
|
||||
|
||||
let routes = html.or(warp::fs::dir(opts.dir));
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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<LocalPoolHandle> = 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<String>,
|
||||
Extension((index_html_before, index_html_after)): Extension<(String, String)>,
|
||||
url: Request<Body>,
|
||||
Query(queries): Query<HashMap<String, String>>,
|
||||
) -> Html<String> {
|
||||
) -> 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::<ServerApp>::with_props(move || ServerAppProps {
|
||||
url: url.into(),
|
||||
queries,
|
||||
});
|
||||
|
||||
let renderer = yew::ServerRenderer::<ServerApp>::with_props(server_app_props);
|
||||
|
||||
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.
|
||||
Html(index_html_s.replace("<body>", &format!("<body>{}", 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("<body>").unwrap();
|
||||
let mut index_html_before = index_html_before.to_owned();
|
||||
index_html_before.push_str("<body>");
|
||||
|
||||
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 {} }),
|
||||
),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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", "${@}"]
|
||||
|
||||
@ -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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` 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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `{integer}`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` 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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `{float}`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `{integer}`
|
||||
= note: required because of the requirements on the impl of `From<Vec<{integer}>>` for `Classes`
|
||||
= note: 1 redundant requirements hidden
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` 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:
|
||||
<Classes as From<&'static str>>
|
||||
<Classes as From<&Option<T>>>
|
||||
<Classes as From<&String>>
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `{integer}`
|
||||
= note: required because of the requirements on the impl of `From<Vec<{integer}>>` for `Classes`
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `Vec<{integer}>`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&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<Classes>` for `{integer}`
|
||||
= note: required because of the requirements on the impl of `From<Option<{integer}>>` for `Classes`
|
||||
= note: 1 redundant requirements hidden
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `Option<{integer}>`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&mut self, class: T) {
|
||||
| ^^^^^^^^^^ required by this bound in `Classes::push`
|
||||
|
||||
error[E0277]: the trait bound `Classes: From<u32>` is not satisfied
|
||||
--> $DIR/classes-fail.rs:14:14
|
||||
--> tests/classes_macro/classes-fail.rs:14:14
|
||||
|
|
||||
14 | classes!(none);
|
||||
| ^^^^ the trait `From<u32>` is not implemented for `Classes`
|
||||
@ -86,11 +106,16 @@ error[E0277]: the trait bound `Classes: From<u32>` is not satisfied
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `u32`
|
||||
= note: required because of the requirements on the impl of `From<Option<u32>>` for `Classes`
|
||||
= note: 1 redundant requirements hidden
|
||||
= note: 1 redundant requirement hidden
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `Option<u32>`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&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
|
||||
<Classes as From<&[T]>>
|
||||
and 4 others
|
||||
= note: required because of the requirements on the impl of `Into<Classes>` for `{integer}`
|
||||
note: required by a bound in `Classes::push`
|
||||
--> $WORKSPACE/packages/yew/src/html/classes.rs
|
||||
|
|
||||
| pub fn push<T: Into<Self>>(&mut self, class: T) {
|
||||
| ^^^^^^^^^^ required by this bound in `Classes::push`
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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<t3::_Props::value, _>` 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<t3::Props
|
||||
29 | #[derive(Clone, Properties, PartialEq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: required because of the requirements on the impl of `AllPropsFor<t3::PropsBuilder, (_,)>` for `AssertAllProps`
|
||||
note: required by a bound in `html::component::properties::__macro::PreBuild::<Token, B>::build`
|
||||
--> $WORKSPACE/packages/yew/src/html/component/properties.rs
|
||||
|
|
||||
| Token: AllPropsFor<B, How>,
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `html::component::properties::__macro::PreBuild::<Token, B>::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::<T>::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::<T>::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 `<fn() -> i32 {t10::foo} as FnOnce<()>>::O
|
||||
98 | #[prop_or_else(foo)]
|
||||
| ^^^ expected struct `String`, found `i32`
|
||||
|
|
||||
note: required by `Option::<T>::unwrap_or_else`
|
||||
note: required by a bound in `Option::<T>::unwrap_or_else`
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -19,6 +19,11 @@ note: required because of the requirements on the impl of `HasAllProps<Props, (_
|
||||
3 | #[derive(Clone, Properties, PartialEq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: required because of the requirements on the impl of `AllPropsFor<PropsBuilder, (_,)>` for `AssertAllProps`
|
||||
note: required by a bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::build`
|
||||
--> $WORKSPACE/packages/yew/src/html/component/properties.rs
|
||||
|
|
||||
| Token: AllPropsFor<B, How>,
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::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<MissingTypeBounds>: yew::BaseComponent` is not satisfied
|
||||
@ -32,17 +37,28 @@ error[E0277]: the trait bound `Comp<MissingTypeBounds>: 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<Comp<MissingTypeBounds>>`, 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<MissingTypeBounds>: yew::BaseComponent`
|
||||
--> tests/function_component_attr/generic-props-fail.rs:27:14
|
||||
|
|
||||
8 | #[function_component(Comp)]
|
||||
| --------------------------- doesn't satisfy `Comp<MissingTypeBounds>: yew::BaseComponent`
|
||||
...
|
||||
27 | html! { <Comp<MissingTypeBounds> /> };
|
||||
| ^^^^ function or associated item cannot be called on `VChild<Comp<MissingTypeBounds>>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Comp<MissingTypeBounds>: yew::BaseComponent`
|
||||
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
27 | html! { <Comp<MissingTypeBounds> /> };
|
||||
| ^^^^ function or associated item cannot be called on `VChild<Comp<MissingTypeBounds>>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Comp<MissingTypeBounds>: 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<String>;
|
||||
| | }
|
||||
| |_^
|
||||
= 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
|
||||
|
||||
@ -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
|
||||
|
|
||||
|
||||
@ -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
|
||||
|
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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<VNode>` for `()`
|
||||
= note: 2 redundant requirements hidden
|
||||
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` 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<VNode>` for `()`
|
||||
= note: 2 redundant requirements hidden
|
||||
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` 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 | <div>{ not_tree() }</div>
|
||||
| ^^^^^^^^ `()` 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<VNode>` for `()`
|
||||
= note: 2 redundant requirements hidden
|
||||
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` 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 | <div>{ not_tree() }</div>
|
||||
| ^^^^^^^^ `()` 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<VNode>` for `()`
|
||||
= note: 2 redundant requirements hidden
|
||||
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` 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
|
||||
|
||||
@ -160,10 +160,10 @@ error: expected identifier, found keyword `type`
|
||||
71 | html! { <Child type=0 /> };
|
||||
| ^^^^ 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! { <Child r#type=0 /> };
|
||||
| ~~~~~~
|
||||
| ++
|
||||
|
||||
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<String>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:77:33
|
||||
|
|
||||
77 | html! { <Child int=1 string={} /> };
|
||||
| ^^ the trait `IntoPropValue<String>` is not implemented for `()`
|
||||
| ------ ^^ the trait `IntoPropValue<String>` 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<String>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:78:33
|
||||
|
|
||||
78 | html! { <Child int=1 string=3 /> };
|
||||
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||
| ------ ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
|
||||
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
|
||||
<&'static str as IntoPropValue<Classes>>
|
||||
<&'static str as IntoPropValue<Option<String>>>
|
||||
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<String>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:79:34
|
||||
|
|
||||
79 | html! { <Child int=1 string={3} /> };
|
||||
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||
| ------ ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
|
||||
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
|
||||
<&'static str as IntoPropValue<Classes>>
|
||||
<&'static str as IntoPropValue<Option<String>>>
|
||||
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[E0308]: mismatched types
|
||||
--> tests/html_macro/component-fail.rs:80:31
|
||||
@ -374,7 +396,19 @@ error[E0277]: the trait bound `u32: IntoPropValue<i32>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:82:24
|
||||
|
|
||||
82 | html! { <Child int=0u32 /> };
|
||||
| ^^^^ the trait `IntoPropValue<i32>` is not implemented for `u32`
|
||||
| --- ^^^^ the trait `IntoPropValue<i32>` 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<int, _>` 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<ChildProp
|
||||
4 | #[derive(Clone, Properties, PartialEq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: required because of the requirements on the impl of `AllPropsFor<ChildPropertiesBuilder, (_,)>` for `AssertAllProps`
|
||||
note: required by a bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::build`
|
||||
--> $WORKSPACE/packages/yew/src/html/component/properties.rs
|
||||
|
|
||||
| Token: AllPropsFor<B, How>,
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::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<ChildCont
|
||||
24 | #[derive(Clone, Properties, PartialEq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: required because of the requirements on the impl of `AllPropsFor<ChildContainerPropertiesBuilder, (_,)>` for `AssertAllProps`
|
||||
note: required by a bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::build`
|
||||
--> $WORKSPACE/packages/yew/src/html/component/properties.rs
|
||||
|
|
||||
| Token: AllPropsFor<B, How>,
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::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<ChildCont
|
||||
24 | #[derive(Clone, Properties, PartialEq)]
|
||||
| ^^^^^^^^^^
|
||||
= note: required because of the requirements on the impl of `AllPropsFor<ChildContainerPropertiesBuilder, (_,)>` for `AssertAllProps`
|
||||
note: required by a bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::build`
|
||||
--> $WORKSPACE/packages/yew/src/html/component/properties.rs
|
||||
|
|
||||
| Token: AllPropsFor<B, How>,
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `yew::html::component::properties::__macro::PreBuild::<Token, B>::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<Child>: From<yew::virtual_dom::VText>` is not satisfied
|
||||
@ -454,7 +503,6 @@ error[E0277]: the trait bound `VChild<Child>: From<yew::virtual_dom::VText>` is
|
||||
| ^^^^^^^^^^^^^ the trait `From<yew::virtual_dom::VText>` is not implemented for `VChild<Child>`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `yew::virtual_dom::VText`
|
||||
note: required by `into`
|
||||
|
||||
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:102:29
|
||||
@ -463,7 +511,6 @@ error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||
| ^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
||||
note: required by `into`
|
||||
|
||||
error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:103:30
|
||||
@ -472,4 +519,3 @@ error[E0277]: the trait bound `VChild<Child>: From<VNode>` is not satisfied
|
||||
| ^^^^^ the trait `From<VNode>` is not implemented for `VChild<Child>`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `Into<VChild<Child>>` for `VNode`
|
||||
note: required by `into`
|
||||
|
||||
@ -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<Unimplemented>`, 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! { <Unimplemented /> };
|
||||
| ^^^^^^^^^^^^^ function or associated item cannot be called on `VChild<Unimplemented>` 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! { <Unimplemented /> };
|
||||
| ^^^^^^^^^^^^^ function or associated item cannot be called on `VChild<Unimplemented>` 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<String>;
|
||||
| | }
|
||||
| |_^
|
||||
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@ -251,54 +251,33 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::
|
||||
|
|
||||
43 | html! { <input type={()} /> };
|
||||
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` 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<Option<implicit_clone::unsync::IString>>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:44:27
|
||||
|
|
||||
44 | html! { <input value={()} /> };
|
||||
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` 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<Option<implicit_clone::unsync::IString>>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:45:22
|
||||
|
|
||||
45 | html! { <a href={()} /> };
|
||||
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` 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<Option<implicit_clone::unsync::IString>>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:46:28
|
||||
|
|
||||
46 | html! { <input string={NotToString} /> };
|
||||
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` 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<NotToString>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:47:23
|
||||
|
|
||||
47 | html! { <a media={Some(NotToString)} /> };
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<NotToString>`
|
||||
| ----^^^^^^^^^^^^^
|
||||
| |
|
||||
| the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<NotToString>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
@ -306,17 +285,15 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implici
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
|
||||
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<Option<implicit_clone::unsync::IString>>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:48:22
|
||||
|
|
||||
48 | html! { <a href={Some(5)} /> };
|
||||
| ^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<{integer}>`
|
||||
| ----^^^
|
||||
| |
|
||||
| the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<{integer}>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
@ -324,17 +301,15 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
|
||||
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! { <input onclick=1 /> };
|
||||
| ^ 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<MouseEvent>` for `{integer}`
|
||||
@ -355,12 +330,12 @@ error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback<Strin
|
||||
--> tests/html_macro/element-fail.rs:52:29
|
||||
|
|
||||
52 | html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| expected an implementor of trait `IntoEventCallback<MouseEvent>`
|
||||
| help: consider borrowing here: `&Callback::from(|a: String| ())`
|
||||
| ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------
|
||||
| | |
|
||||
| | expected an `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: the trait bound `yew::Callback<String>: IntoEventCallback<MouseEvent>` is not satisfied
|
||||
= help: the trait `Fn<(MouseEvent,)>` is not implemented for `yew::Callback<String>`
|
||||
= note: required because of the requirements on the impl of `IntoEventCallback<MouseEvent>` for `yew::Callback<String>`
|
||||
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<FocusEvent>`
|
||||
--> tests/html_macro/element-fail.rs:53:29
|
||||
|
|
||||
53 | html! { <input onfocus={Some(5)} /> };
|
||||
| ^^^^^^^ the trait `IntoEventCallback<FocusEvent>` is not implemented for `Option<{integer}>`
|
||||
| ------------------------^^^^^^^------
|
||||
| | |
|
||||
| | the trait `IntoEventCallback<FocusEvent>` is not implemented for `Option<{integer}>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<Option<T> as IntoEventCallback<EVENT>>
|
||||
@ -401,19 +379,19 @@ error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:56:25
|
||||
|
|
||||
56 | html! { <input ref={()} /> };
|
||||
| ^^ the trait `IntoPropValue<yew::NodeRef>` 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<yew::NodeRef>` is not implemented for `()`
|
||||
| required by a bound introduced by this call
|
||||
|
||||
error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:57:25
|
||||
|
|
||||
57 | html! { <input ref={Some(NodeRef::default())} /> };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `Option<yew::NodeRef>`
|
||||
| ----^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| the trait `IntoPropValue<yew::NodeRef>` is not implemented for `Option<yew::NodeRef>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
@ -421,22 +399,17 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
|
||||
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<String>`
|
||||
--> tests/html_macro/element-fail.rs:58:29
|
||||
|
|
||||
58 | html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| expected an implementor of trait `IntoEventCallback<MouseEvent>`
|
||||
| help: consider borrowing here: `&Callback::from(|a: String| ())`
|
||||
| ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------
|
||||
| | |
|
||||
| | expected an `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: the trait bound `yew::Callback<String>: IntoEventCallback<MouseEvent>` is not satisfied
|
||||
= help: the trait `Fn<(MouseEvent,)>` is not implemented for `yew::Callback<String>`
|
||||
= note: required because of the requirements on the impl of `IntoEventCallback<MouseEvent>` for `yew::Callback<String>`
|
||||
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<Option<implicit_clone:
|
||||
|
|
||||
60 | html! { <input string={NotToString} /> };
|
||||
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` 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<yew::NodeRef>` is not satisfied
|
||||
--> tests/html_macro/element-fail.rs:62:25
|
||||
|
|
||||
62 | html! { <input ref={()} /> };
|
||||
| ^^ the trait `IntoPropValue<yew::NodeRef>` 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<yew::NodeRef>` 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:
|
||||
<Cow<'a, CStr> as From<&'a CStr>>
|
||||
<Cow<'a, CStr> as From<&'a CString>>
|
||||
<Cow<'a, CStr> as From<CString>>
|
||||
<Cow<'a, OsStr> as From<&'a OsStr>>
|
||||
and 11 others
|
||||
= note: required because of the requirements on the impl of `Into<Cow<'static, str>>` 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:
|
||||
<Cow<'a, CStr> as From<&'a CStr>>
|
||||
<Cow<'a, CStr> as From<&'a CString>>
|
||||
<Cow<'a, CStr> as From<CString>>
|
||||
<Cow<'a, OsStr> as From<&'a OsStr>>
|
||||
and 11 others
|
||||
= note: required because of the requirements on the impl of `Into<Cow<'static, str>>` for `{integer}`
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[tasks.test]
|
||||
clear = true
|
||||
toolchain = "1.56.1"
|
||||
toolchain = "1.60.0"
|
||||
command = "cargo"
|
||||
args = ["test"]
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
'''
|
||||
|
||||
@ -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")]
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -41,7 +41,7 @@ pub(crate) enum ComponentRenderState {
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
Ssr {
|
||||
sender: Option<futures::channel::oneshot::Sender<Html>>,
|
||||
sender: Option<crate::platform::sync::oneshot::Sender<Html>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -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<COMP: BaseComponent> Scope<COMP> {
|
||||
|
||||
#[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<COMP: BaseComponent> Scope<COMP> {
|
||||
pub(crate) async fn render_to_string(
|
||||
self,
|
||||
w: &mut String,
|
||||
pub(crate) async fn render_into_stream(
|
||||
&self,
|
||||
w: &mut BufWriter,
|
||||
props: Rc<COMP::Properties>,
|
||||
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#"<script type="application/x-yew-comp-state">"#);
|
||||
w.push_str(&prepared_state);
|
||||
w.push_str(r#"</script>"#);
|
||||
w.write(r#"<script type="application/x-yew-comp-state">"#.into());
|
||||
w.write(prepared_state.into());
|
||||
w.write(r#"</script>"#.into());
|
||||
}
|
||||
|
||||
if hydratable {
|
||||
|
||||
@ -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: F)
|
||||
where
|
||||
F: Future<Output = ()> + '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::*;
|
||||
@ -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")]
|
||||
|
||||
103
packages/yew/src/platform/io.rs
Normal file
103
packages/yew/src/platform/io.rs
Normal file
@ -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<String>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
/// Creates a Buffer pair.
|
||||
pub(crate) fn buffer(capacity: usize) -> (BufWriter, impl Stream<Item = String>) {
|
||||
let (tx, rx) = mpsc::unbounded_channel::<String>();
|
||||
|
||||
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<Item = Bytes>`. 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
packages/yew/src/platform/mod.rs
Normal file
90
packages/yew/src/platform/mod.rs
Normal file
@ -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: F)
|
||||
where
|
||||
F: Future<Output = ()> + '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<F, Fut>(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
|
||||
}
|
||||
26
packages/yew/src/platform/rt_none.rs
Normal file
26
packages/yew/src/platform/rt_none.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use std::future::Future;
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn spawn_local<F>(_f: F)
|
||||
where
|
||||
F: Future<Output = ()> + '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<F, Fut>(_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."#
|
||||
)
|
||||
}
|
||||
29
packages/yew/src/platform/rt_tokio.rs
Normal file
29
packages/yew/src/platform/rt_tokio.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub(super) async fn run_pinned<F, Fut>(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<LocalPoolHandle> =
|
||||
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: F)
|
||||
where
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
tokio::task::spawn_local(f);
|
||||
}
|
||||
15
packages/yew/src/platform/rt_wasm_bindgen.rs
Normal file
15
packages/yew/src/platform/rt_wasm_bindgen.rs
Normal file
@ -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<F, Fut>(create_task: F) -> Fut::Output
|
||||
where
|
||||
F: FnOnce() -> Fut,
|
||||
F: Send + 'static,
|
||||
Fut: Future + 'static,
|
||||
Fut::Output: Send + 'static,
|
||||
{
|
||||
create_task().await
|
||||
}
|
||||
5
packages/yew/src/platform/sync/mod.rs
Normal file
5
packages/yew/src/platform/sync/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
//! A module that provides task synchronisation primitives.
|
||||
|
||||
#[doc(inline)]
|
||||
pub use tokio::sync::oneshot;
|
||||
pub mod mpsc;
|
||||
6
packages/yew/src/platform/sync/mpsc.rs
Normal file
6
packages/yew/src/platform/sync/mpsc.rs
Normal file
@ -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};
|
||||
@ -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.
|
||||
|
||||
@ -1,17 +1,24 @@
|
||||
use crate::html::{BaseComponent, Scope};
|
||||
use std::fmt;
|
||||
|
||||
/// A Yew Server-side Renderer.
|
||||
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 that renders on the current thread.
|
||||
#[cfg_attr(documenting, doc(cfg(feature = "ssr")))]
|
||||
#[derive(Debug)]
|
||||
pub struct ServerRenderer<COMP>
|
||||
pub struct LocalServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
props: COMP::Properties,
|
||||
hydratable: bool,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<COMP> Default for ServerRenderer<COMP>
|
||||
impl<COMP> Default for LocalServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
@ -21,29 +28,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
impl<COMP> LocalServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with default properties.
|
||||
/// Creates a [LocalServerRenderer] with default properties.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
impl<COMP> LocalServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with custom properties.
|
||||
/// 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`.
|
||||
@ -67,9 +84,155 @@ where
|
||||
|
||||
/// 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<Item = String> {
|
||||
let (mut w, r) = io::buffer(self.capacity);
|
||||
|
||||
let scope = Scope::<COMP>::new(None);
|
||||
scope
|
||||
.render_to_string(w, self.props.into(), self.hydratable)
|
||||
.await;
|
||||
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<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
create_props: Box<dyn Send + FnOnce() -> COMP::Properties>,
|
||||
hydratable: bool,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl<COMP> fmt::Debug for ServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("ServerRenderer<_>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP> Default for ServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::with_props(Default::default)
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
COMP::Properties: Default,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with default properties.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<COMP> ServerRenderer<COMP>
|
||||
where
|
||||
COMP: BaseComponent,
|
||||
{
|
||||
/// Creates a [ServerRenderer] with custom properties.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The properties does not have to implement `Send`.
|
||||
/// However, the function to create properties needs to be `Send`.
|
||||
pub fn with_props<F>(create_props: F) -> Self
|
||||
where
|
||||
F: 'static + Send + FnOnce() -> COMP::Properties,
|
||||
{
|
||||
Self {
|
||||
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`.
|
||||
///
|
||||
/// 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().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<Item = String> {
|
||||
// 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::<COMP>::with_props(props)
|
||||
.hydratable(hydratable)
|
||||
.capacity(capacity)
|
||||
.render_stream()
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -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! {
|
||||
|
||||
@ -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,38 +93,8 @@ mod feat_ssr_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn write_open_tag(&self, w: &mut String) {
|
||||
w.push_str("<!--");
|
||||
w.push_str(self.open_start_mark());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
match self {
|
||||
Self::Component(type_name) => w.push_str(type_name),
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
w.push_str(self.end_mark());
|
||||
w.push_str("-->");
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn write_close_tag(&self, w: &mut String) {
|
||||
w.push_str("<!--");
|
||||
w.push_str(self.close_start_mark());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
match self {
|
||||
Self::Component(type_name) => w.push_str(type_name),
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
w.push_str(self.end_mark());
|
||||
w.push_str("-->");
|
||||
}
|
||||
|
||||
#[cfg(feature = "hydration")]
|
||||
pub fn name(&self) -> std::borrow::Cow<'static, str> {
|
||||
pub fn name(&self) -> Cow<'static, str> {
|
||||
match self {
|
||||
#[cfg(debug_assertions)]
|
||||
Self::Component(m) => format!("Component({})", m).into(),
|
||||
@ -136,6 +109,42 @@ mod feat_ssr_hydration {
|
||||
#[cfg(any(feature = "ssr", feature = "hydration"))]
|
||||
pub(crate) use feat_ssr_hydration::*;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
mod feat_ssr {
|
||||
use super::*;
|
||||
use crate::platform::io::BufWriter;
|
||||
|
||||
impl Collectable {
|
||||
pub(crate) fn write_open_tag(&self, w: &mut BufWriter) {
|
||||
w.write("<!--".into());
|
||||
w.write(self.open_start_mark().into());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
match self {
|
||||
Self::Component(type_name) => w.write((*type_name).into()),
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
w.write(self.end_mark().into());
|
||||
w.write("-->".into());
|
||||
}
|
||||
|
||||
pub(crate) fn write_close_tag(&self, w: &mut BufWriter) {
|
||||
w.write("<!--".into());
|
||||
w.write(self.close_start_mark().into());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
match self {
|
||||
Self::Component(type_name) => w.write((*type_name).into()),
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
w.write(self.end_mark().into());
|
||||
w.write("-->".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of attributes for an element
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Attributes {
|
||||
|
||||
@ -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<Self>, 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<COMP: BaseComponent> Mountable for PropsWrapper<COMP> {
|
||||
}
|
||||
|
||||
#[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<COMP> = Scope::new(Some(parent_scope.clone()));
|
||||
|
||||
async move {
|
||||
let scope: Scope<COMP> = 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
match &self.children[..] {
|
||||
[] => {}
|
||||
[child] => {
|
||||
child.render_into_stream(w, parent_scope, hydratable).await;
|
||||
}
|
||||
_ => {
|
||||
let buf_capacity = w.capacity();
|
||||
|
||||
m.render_to_string(&mut w, parent_scope, hydratable).await;
|
||||
// Concurrently render all children.
|
||||
let mut children: FuturesOrdered<_> = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|m| async move {
|
||||
let (mut w, r) = io::buffer(buf_capacity);
|
||||
|
||||
w
|
||||
}))
|
||||
.await
|
||||
{
|
||||
w.push_str(&fragment)
|
||||
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;
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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("</textarea>");
|
||||
w.write("</textarea>".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("</"));
|
||||
w.write(Cow::Borrowed(tag));
|
||||
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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"] }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user