mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Add WASI support for server-side rendering. (#3534)
* Try to add wasi feature to avoid browser's ABI. * Add async render for single-threaded env. * Temporarily enable my own patch branch. It would be modified later after the corresponding library branches are merged. * add example for WASI SSR. * Ready to run WASI on wasmtime. * complete the example * fix fmt * fix fmt * I made a mistake..sry * add yew-router suites for demo * fix typo * Make the async render stream function public * Use target_os instead of feature. * Renew gloo-history's patch. * Exclude WASI example to avoid web-sys. * Try to add CI for WASI example. * Fix CI. * Fix CI that requires compiler 1.67 or newer. * Use CLI's flag instead of exclude example. https://github.com/bytecodealliance/wasmtime/pull/4312 * Remove patchs. * Use LocalServerRenderer instead of ServerRenderer. https://github.com/yewstack/prokio/pull/11#issuecomment-1847979933 * Remove unused exports. * Add description about `LocalServerRenderer`. * fix fmt * fix fmt * Update Cargo.lock * Bump rust compiler's version to 1.67... * Exclude WASI on yew-router browser interfaces. * fix fmt * Wait for gloo's PR dealed. * Rollback to rust compiler 1.64. cc https://github.com/rustwasm/gloo/pull/423#issuecomment-1848353295 * Fix lock file. * Downgrade `toml_datetime` version. * Fix enum for `gloo-history`. * Well, it seems there is no way to avoid the MSRV upgrade.... * fix: Replace feature = "wasi" to target_os = "wasi". * Remove tips for rust version. * Bump `gloo` to 0.11. * Try to test yew-macro on compiler 1.67. * Try to use compiler 1.68 instead. * Try to use compiler 1.69 instead...... * Revert MSRV back * Pin the oldest Cargo.lock. * Downgrade deps for MSRV. * Bump benchmark tool's tokio to 1.35 * Try to write WASI CI. * Rollback the quotes * Combine CI files... * Rollback the use that gloo-history has fixed it. * fix * Bump gloo-history version. * Block raw html update tests on WASI. * Rollback indexmap's version. * fix CI * fix CI * Update some SSR test suites that replace ServerRender instead of LocalServerRender. * Remove yew-router's cfg macro * Fix fmt * Try to fix CI * Update examples/wasi_ssr_module/README.md Co-authored-by: Elina <imelina@elina.website> * Revert back some unnecessary changes. * Clippy * fmt * Fix CI. * Fix CI. * Try to fix clippy. * Fix `ToString` trait. * Remove pin version of WASI CI test. * Pin the newer version. * Fix typo. * Bump `wasm-bindgen`. * Fix SSR example. * Fix typo. * Try to support non-browser environments. * Update wasm-bindgen-test to 0.3.43 refer to rustwasm/wasm-bindgen#4083 * fix doc test running on nightly * Update website/docs/advanced-topics/server-side-rendering.md Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com> * Update WASI CI. * Remove WASI test for rustc 1.76. * Try to let `wasmtime` CLI can be executed. * Limit the function `decode_base64` that it shouldn't runnable in non-browser environment. * Remove WASI example test for rustc 1.76. * Revert changes. * Fix CI * Fix Cargo.lock * Remove unused deps * Undo the formatting changes. * Undo the formatting changes. --------- Co-authored-by: Elina <imelina@elina.website> Co-authored-by: Martin Molzer <WorldSEnder@users.noreply.github.com>
This commit is contained in:
parent
4025fa75f9
commit
84b7548bf7
@ -1,6 +1,9 @@
|
||||
[target.'cfg(target_arch = "wasm32")']
|
||||
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))']
|
||||
runner = 'wasm-bindgen-test-runner'
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))']
|
||||
runner = 'wasmtime -W unknown-imports-trap=y'
|
||||
|
||||
# This section needs to be last.
|
||||
# GitHub Actions modifies this section.
|
||||
[unstable]
|
||||
|
||||
69
.github/workflows/main-checks.yml
vendored
69
.github/workflows/main-checks.yml
vendored
@ -170,3 +170,72 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: --cfg nightly_yew --cfg yew_lints
|
||||
run: cargo test -p yew-macro test_html_lints
|
||||
|
||||
unit_tests_wasi:
|
||||
name: Unit Tests (WASI) on ${{ matrix.toolchain }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
target: wasm32-wasip1
|
||||
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
wget https://github.com/bytecodealliance/wasmtime/releases/download/v24.0.0/wasmtime-v24.0.0-x86_64-linux.tar.xz
|
||||
tar xf wasmtime-v24.0.0-x86_64-linux.tar.xz
|
||||
mv wasmtime-v24.0.0-x86_64-linux/wasmtime ~/wasmtime
|
||||
rm -rf wasmtime-v24.0.0-x86_64-linux.tar.xz wasmtime-v24.0.0-x86_64-linux
|
||||
chmod +x ~/wasmtime
|
||||
mv ~/wasmtime /usr/local/bin
|
||||
source ~/.bashrc
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run WASI tests for yew
|
||||
run: |
|
||||
RUST_LOG=info
|
||||
cargo test --features ssr,hydration --target wasm32-wasip1 -p yew
|
||||
|
||||
example-runnable-tests-on-wasi:
|
||||
name: Example Runnable Tests on WASI
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- wasi_ssr_module
|
||||
toolchain:
|
||||
- stable
|
||||
- nightly
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
target: wasm32-wasip1
|
||||
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
wget https://github.com/bytecodealliance/wasmtime/releases/download/v24.0.0/wasmtime-v24.0.0-x86_64-linux.tar.xz
|
||||
tar xf wasmtime-v24.0.0-x86_64-linux.tar.xz
|
||||
mv wasmtime-v24.0.0-x86_64-linux/wasmtime ~/wasmtime
|
||||
rm -rf wasmtime-v24.0.0-x86_64-linux.tar.xz wasmtime-v24.0.0-x86_64-linux
|
||||
chmod +x ~/wasmtime
|
||||
mv ~/wasmtime /usr/local/bin
|
||||
source ~/.bashrc
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build and run ${{ matrix.package }}
|
||||
run: |
|
||||
cargo build --target wasm32-wasip1 -p ${{ matrix.package }}
|
||||
wasmtime -W unknown-imports-trap=y target/wasm32-wasip1/debug/${{ matrix.package }}.wasm
|
||||
|
||||
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -3674,6 +3674,20 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi_ssr_module"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"yew",
|
||||
"yew-router",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.93"
|
||||
|
||||
@ -60,6 +60,7 @@ As an example, check out the TodoMVC example here: <https://examples.yew.rs/todo
|
||||
| [web_worker_fib](web_worker_fib) | [F] | Calculate Fibonacci numbers in a web worker thread using [`yew-agent`](https://docs.rs/yew-agent/latest/yew_agent/). |
|
||||
| [web_worker_prime](web_worker_prime) | [F] | Calculate Prime numbers in a web worker thread using [`yew-agent`](https://docs.rs/yew-agent/latest/yew_agent/). |
|
||||
| [webgl](webgl) | [S] | Controls a [WebGL canvas](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL) from Yew. |
|
||||
| [wasi_ssr_module](wasi_ssr_module) | [F] | Demonstrates server-side rendering using WASI. |
|
||||
|
||||
[CT]: ## "Component Type"
|
||||
[S]: ## "Struct Components"
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Yew SSR Example</title>
|
||||
|
||||
<link data-trunk rel="rust" data-bin="simple_ssr_hydrate" data-cargo-features="hydration" />
|
||||
</head>
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Yew SSR Example</title>
|
||||
|
||||
<link data-trunk rel="rust" data-bin="simple_ssr_hydrate" data-cargo-features="hydration" />
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
|
||||
</html>
|
||||
17
examples/wasi_ssr_module/Cargo.toml
Normal file
17
examples/wasi_ssr_module/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "wasi_ssr_module"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["langyo <langyo.china@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
yew = { path = "../../packages/yew", features = ["ssr"] }
|
||||
yew-router = { path = "../../packages/yew-router" }
|
||||
|
||||
anyhow = "^1"
|
||||
bytes = "^1"
|
||||
serde = { version = "^1", features = ["derive"] }
|
||||
serde_json = "^1"
|
||||
lazy_static = "^1"
|
||||
|
||||
tokio = { version = "^1", features = ["macros", "rt", "time"] }
|
||||
23
examples/wasi_ssr_module/README.md
Normal file
23
examples/wasi_ssr_module/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# WASI SSR Module Example
|
||||
|
||||
This example demonstrates how to use the WASI target to run a simple server-side rendering application.
|
||||
|
||||
It depends on [wasmtime](https://wasmtime.dev)'s WASI preview2.
|
||||
|
||||
## Building
|
||||
|
||||
To build the example, run the following command from the root of the repository:
|
||||
|
||||
```bash
|
||||
cargo build --manifest-path examples/wasi_ssr_module/Cargo.toml --target wasm32-wasi --release
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
> Note: This example requires the wasmtime CLI to be installed. See [wasmtime's installation instructions](https://docs.wasmtime.dev/cli-install.html) for more information.
|
||||
|
||||
```bash
|
||||
wasmtime target/wasm32-wasi/release/wasi_ssr_module.wasm
|
||||
```
|
||||
|
||||
> Note: If your wasmtime CLI throws an error that it says some imports like `__wbindgen_placeholder__::__wbindgen_xxx` is invalid, try to run `cargo update`. See issue [rustwasm/gloo#411](https://github.com/rustwasm/gloo/pull/411#discussion_r1421219033).
|
||||
60
examples/wasi_ssr_module/src/main.rs
Normal file
60
examples/wasi_ssr_module/src/main.rs
Normal file
@ -0,0 +1,60 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod router;
|
||||
|
||||
use anyhow::Result;
|
||||
use router::{switch, Route};
|
||||
use yew::prelude::*;
|
||||
use yew::LocalServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn Content() -> Html {
|
||||
use yew_router::prelude::*;
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{"Yew WASI SSR demo"}</h1>
|
||||
<Switch<Route> render={switch} />
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
use yew_router::history::{AnyHistory, History, MemoryHistory};
|
||||
use yew_router::prelude::*;
|
||||
|
||||
let history = AnyHistory::from(MemoryHistory::new());
|
||||
history.push("/");
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<Router history={history}>
|
||||
<Content />
|
||||
</Router>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render() -> Result<String> {
|
||||
let renderer = LocalServerRenderer::<App>::new();
|
||||
let html_raw = renderer.render().await;
|
||||
|
||||
let mut body = String::new();
|
||||
body.push_str("<body>");
|
||||
body.push_str("<div id='app' style='width: 100vw; height: 100vh; position: fixed;'>");
|
||||
body.push_str(&html_raw);
|
||||
body.push_str("</div>");
|
||||
body.push_str("</body>");
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let ret = render().await?;
|
||||
println!("{}", ret);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
29
examples/wasi_ssr_module/src/router.rs
Normal file
29
examples/wasi_ssr_module/src/router.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[derive(Routable, PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Route {
|
||||
#[at("/")]
|
||||
Portal,
|
||||
|
||||
#[at("/t/:id")]
|
||||
Thread { id: String },
|
||||
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
pub fn switch(routes: Route) -> Html {
|
||||
match routes {
|
||||
Route::Portal => {
|
||||
html! { <h1>{"Hello"}</h1> }
|
||||
}
|
||||
Route::Thread { id } => {
|
||||
html! { <h1>{format!("Thread id {}", id)}</h1> }
|
||||
}
|
||||
Route::NotFound => {
|
||||
html! { <h1>{"Not found"}</h1> }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,7 +42,7 @@ pub fn fetch_base_url() -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
|
||||
gloo::utils::window()
|
||||
.location()
|
||||
@ -55,7 +55,7 @@ pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
|
||||
let query = query.trim();
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![cfg(not(target_os = "wasi"))]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![cfg(not(target_os = "wasi"))]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![cfg(not(target_os = "wasi"))]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![cfg(not(target_os = "wasi"))]
|
||||
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![cfg(not(target_os = "wasi"))]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use yew::platform::time::sleep;
|
||||
|
||||
@ -81,6 +81,9 @@ features = [
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
tokio = { version = "1.40", features = ["full"] }
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies]
|
||||
tokio = { version = "1.40", features = ["macros", "rt", "time"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
gloo = { version = "0.11", features = ["futures"] }
|
||||
@ -95,6 +98,7 @@ features = ["ShadowRootInit", "ShadowRootMode", "HtmlButtonElement"]
|
||||
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]
|
||||
csr = []
|
||||
hydration = ["csr", "dep:bincode"]
|
||||
not_browser_env = []
|
||||
default = []
|
||||
test = []
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@ mod feat_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gloo::utils::document;
|
||||
@ -391,7 +391,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
extern crate self as yew;
|
||||
|
||||
@ -483,7 +483,7 @@ mod feat_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
extern crate self as yew;
|
||||
@ -560,7 +560,7 @@ mod layout_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests_keys {
|
||||
extern crate self as yew;
|
||||
|
||||
@ -311,7 +311,7 @@ mod feat_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
|
||||
|
||||
@ -118,7 +118,7 @@ impl BPortal {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
extern crate self as yew;
|
||||
|
||||
@ -170,7 +170,7 @@ mod feat_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gloo::utils::document;
|
||||
|
||||
@ -272,7 +272,7 @@ impl Apply for Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -196,7 +196,7 @@ impl Registry {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@ -288,13 +288,13 @@ impl BTag {
|
||||
self.key.as_ref()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
fn reference(&self) -> &Element {
|
||||
&self.reference
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
fn children(&self) -> Option<&BNode> {
|
||||
match &self.inner {
|
||||
@ -303,7 +303,7 @@ impl BTag {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
fn tag(&self) -> &str {
|
||||
match &self.inner {
|
||||
@ -403,7 +403,7 @@ mod feat_hydration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rc::Rc;
|
||||
@ -1000,7 +1000,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
extern crate self as yew;
|
||||
|
||||
@ -146,12 +146,12 @@ mod feat_hydration {
|
||||
mod test {
|
||||
extern crate self as yew;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
|
||||
|
||||
use crate::html;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[test]
|
||||
@ -166,7 +166,7 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
extern crate self as yew;
|
||||
|
||||
@ -124,7 +124,7 @@ impl DomSlot {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
fn get(&self) -> Option<Node> {
|
||||
self.with_next_sibling(|n| n.cloned())
|
||||
@ -180,7 +180,7 @@ impl DynamicDomSlot {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod layout_tests {
|
||||
use gloo::utils::document;
|
||||
|
||||
@ -12,7 +12,11 @@ use crate::functional::{use_state, Hook, HookContext};
|
||||
use crate::platform::spawn_local;
|
||||
use crate::suspense::{Suspension, SuspensionResult};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(target_os = "wasi"),
|
||||
not(feature = "not_browser_env")
|
||||
))]
|
||||
async fn decode_base64(s: &str) -> Result<Vec<u8>, JsValue> {
|
||||
use gloo::utils::window;
|
||||
use js_sys::Uint8Array;
|
||||
@ -34,7 +38,11 @@ async fn decode_base64(s: &str) -> Result<Vec<u8>, JsValue> {
|
||||
Ok(content_array.to_vec())
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(
|
||||
not(target_arch = "wasm32"),
|
||||
target_os = "wasi",
|
||||
feature = "not_browser_env"
|
||||
))]
|
||||
async fn decode_base64(_s: &str) -> Result<Vec<u8>, JsValue> {
|
||||
unreachable!("this function is not callable under non-wasm targets!");
|
||||
}
|
||||
|
||||
@ -741,7 +741,7 @@ mod feat_csr {
|
||||
#[cfg(feature = "csr")]
|
||||
pub(super) use feat_csr::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate self as yew;
|
||||
@ -798,7 +798,7 @@ mod tests {
|
||||
struct Props {
|
||||
lifecycle: Rc<RefCell<Vec<String>>>,
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_message: Option<bool>,
|
||||
update_message: RefCell<Option<bool>>,
|
||||
view_message: RefCell<Option<bool>>,
|
||||
@ -815,7 +815,7 @@ mod tests {
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
ctx.props().lifecycle.borrow_mut().push("create".into());
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
if let Some(msg) = ctx.props().create_message {
|
||||
ctx.link().send_message(msg);
|
||||
}
|
||||
@ -902,7 +902,7 @@ mod tests {
|
||||
test_lifecycle(
|
||||
Props {
|
||||
lifecycle: lifecycle.clone(),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_message: Some(false),
|
||||
..Props::default()
|
||||
},
|
||||
@ -983,7 +983,7 @@ mod tests {
|
||||
test_lifecycle(
|
||||
Props {
|
||||
lifecycle,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_message: Some(true),
|
||||
update_message: RefCell::new(Some(true)),
|
||||
..Props::default()
|
||||
|
||||
@ -211,7 +211,11 @@ pub(crate) fn start_now() {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(target_os = "wasi"),
|
||||
not(feature = "not_browser_env")
|
||||
))]
|
||||
mod arch {
|
||||
use crate::platform::spawn_local;
|
||||
|
||||
@ -224,7 +228,11 @@ mod arch {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(
|
||||
not(target_arch = "wasm32"),
|
||||
target_os = "wasi",
|
||||
feature = "not_browser_env"
|
||||
))]
|
||||
mod arch {
|
||||
// Delayed rendering is not very useful in the context of server-side rendering.
|
||||
// There are no event listeners or other high priority events that need to be
|
||||
|
||||
@ -68,16 +68,15 @@ key_impl_from_to_string!(i64);
|
||||
key_impl_from_to_string!(i128);
|
||||
key_impl_from_to_string!(isize);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
|
||||
|
||||
use crate::html;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[test]
|
||||
|
||||
@ -274,16 +274,17 @@ mod feat_ssr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
#[cfg(feature = "ssr")]
|
||||
#[cfg(test)]
|
||||
mod ssr_tests {
|
||||
use tokio::test;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::ServerRenderer;
|
||||
use crate::LocalServerRenderer as ServerRenderer;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_text_back_to_back() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -300,7 +301,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, "<div>Hello world!</div>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_fragment() {
|
||||
#[derive(PartialEq, Properties, Debug)]
|
||||
struct ChildProps {
|
||||
|
||||
@ -61,7 +61,7 @@ mod feat_ssr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
#[cfg(feature = "ssr")]
|
||||
#[cfg(test)]
|
||||
mod ssr_tests {
|
||||
@ -76,6 +76,7 @@ mod ssr_tests {
|
||||
use crate::suspense::{Suspension, SuspensionResult};
|
||||
use crate::ServerRenderer;
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
#[test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_suspense() {
|
||||
#[derive(PartialEq)]
|
||||
|
||||
@ -555,16 +555,17 @@ mod feat_ssr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
#[cfg(feature = "ssr")]
|
||||
#[cfg(test)]
|
||||
mod ssr_tests {
|
||||
use tokio::test;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::ServerRenderer;
|
||||
use crate::LocalServerRenderer as ServerRenderer;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_simple_tag() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -579,7 +580,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, "<div></div>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_simple_tag_with_attr() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -594,7 +596,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<div class="abc"></div>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_simple_tag_with_content() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -609,7 +612,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<div>Hello!</div>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_simple_tag_with_nested_tag_and_input() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -624,7 +628,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<div>Hello!<input value="abc" type="text"></div>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_textarea() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -639,7 +644,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<textarea>teststring</textarea>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_escaping_in_style_tag() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -654,7 +660,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<style>body > a {color: #cc0;}</style>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_escaping_in_script_tag() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
@ -669,7 +676,8 @@ mod ssr_tests {
|
||||
assert_eq!(s, r#"<script>foo.bar = x < y;</script>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_multiple_vtext_in_style_tag() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
|
||||
@ -68,16 +68,17 @@ mod feat_ssr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
#[cfg(feature = "ssr")]
|
||||
#[cfg(test)]
|
||||
mod ssr_tests {
|
||||
use tokio::test;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::ServerRenderer;
|
||||
use crate::LocalServerRenderer as ServerRenderer;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(target_os = "wasi"), test)]
|
||||
#[cfg_attr(target_os = "wasi", test(flavor = "current_thread"))]
|
||||
async fn test_simple_str() {
|
||||
#[function_component]
|
||||
fn Comp() -> Html {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#![cfg(feature = "hydration")]
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
mod common;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
use wasm_bindgen::JsCast;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
use wasm_bindgen_test::wasm_bindgen_test as test;
|
||||
use yew::prelude::*;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
use tokio::test;
|
||||
|
||||
macro_rules! create_test {
|
||||
@ -27,7 +27,7 @@ macro_rules! create_test {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
{
|
||||
use std::time::Duration;
|
||||
|
||||
@ -46,9 +46,9 @@ macro_rules! create_test {
|
||||
.unwrap();
|
||||
assert_eq!(e.inner_html(), $expected);
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
|
||||
{
|
||||
let actual = yew::ServerRenderer::<App>::new()
|
||||
let actual = yew::LocalServerRenderer::<App>::new()
|
||||
.hydratable(false)
|
||||
.render()
|
||||
.await;
|
||||
@ -72,9 +72,10 @@ create_test!(
|
||||
r#"<p>paragraph</p><a href="https://yew.rs">link</a>"#
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
macro_rules! create_update_html_test {
|
||||
($name:ident, $initial:expr, $updated:expr) => {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[test]
|
||||
async fn $name() {
|
||||
#[function_component]
|
||||
@ -127,30 +128,35 @@ macro_rules! create_update_html_test {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_update_html_test!(
|
||||
set_new_html_string,
|
||||
"<span>first</span>",
|
||||
"<span>second</span>"
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_update_html_test!(
|
||||
set_new_html_string_multiple_children,
|
||||
"<span>first</span><span>second</span>",
|
||||
"<span>second</span>"
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_update_html_test!(
|
||||
clear_html_string_multiple_children,
|
||||
"<span>first</span><span>second</span>",
|
||||
""
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
create_update_html_test!(
|
||||
nothing_changes,
|
||||
"<span>first</span><span>second</span>",
|
||||
"<span>first</span><span>second</span>"
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[test]
|
||||
async fn change_vnode_types_from_other_to_vraw() {
|
||||
#[function_component]
|
||||
@ -202,7 +208,7 @@ async fn change_vnode_types_from_other_to_vraw() {
|
||||
assert_eq!(e.inner_html(), "<span>second</span>");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#[test]
|
||||
async fn change_vnode_types_from_vraw_to_other() {
|
||||
#[function_component]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
#![cfg(feature = "hydration")]
|
||||
#![cfg_attr(nightly_yew, feature(async_closure))]
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
#![cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
@ -195,6 +195,55 @@ fn main() {
|
||||
Example: [simple_ssr](https://github.com/yewstack/yew/tree/master/examples/simple_ssr)
|
||||
Example: [ssr_router](https://github.com/yewstack/yew/tree/master/examples/ssr_router)
|
||||
|
||||
## Single thread mode
|
||||
|
||||
Yew supports single thread mode for server-side rendering by `yew::LocalServerRenderer`. This mode would work in a single thread environment like WASI.
|
||||
|
||||
```rust
|
||||
// Build it by `wasm32-wasip1` target or `wasm32-wasip2` target (after rustc 1.78).
|
||||
// You can still use `wasm32-wasi` target to build if you are using older version of rustc (before 1.84).
|
||||
// See https://blog.rust-lang.org/2024/04/09/updates-to-rusts-wasi-targets.html for more information.
|
||||
|
||||
use yew::prelude::*;
|
||||
use yew::LocalServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
use yew_router::prelude::*;
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{"Yew WASI SSR demo"}</h1>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render() -> String {
|
||||
let renderer = LocalServerRenderer::<App>::new();
|
||||
let html_raw = renderer.render().await;
|
||||
|
||||
let mut body = String::new();
|
||||
body.push_str("<body>");
|
||||
body.push_str("<div id='app'>");
|
||||
body.push_str(&html_raw);
|
||||
body.push_str("</div>");
|
||||
body.push_str("</body>");
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
println!("{}", render().await);
|
||||
}
|
||||
```
|
||||
|
||||
Example: [wasi_ssr_module](https://github.com/yewstack/yew/tree/master/examples/wasi_ssr_module)
|
||||
|
||||
:::note
|
||||
If you are using the `wasm32-unknown-unknown` target to build a SSR application, you can use the `not_browser_env` feature flag to disable access of browser-specific APIs inside of Yew. This would be useful on serverless platforms like Cloudflare Worker.
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
Server-side rendering is currently experimental. If you find a bug, please file
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user