Skip to content

Commit 90bf3cc

Browse files
langyoranileWorldSEnder
authored andcommitted
Add WASI support for server-side rendering. (yewstack#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. bytecodealliance/wasmtime#4312 * Remove patchs. * Use LocalServerRenderer instead of ServerRenderer. yewstack/tokise#11 (comment) * 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 rustwasm/gloo#423 (comment) * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> Co-authored-by: Martin Molzer <[email protected]>
1 parent 432226c commit 90bf3cc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+395
-79
lines changed

.cargo/config.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
[target.'cfg(target_arch = "wasm32")']
1+
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))']
22
runner = 'wasm-bindgen-test-runner'
33

4+
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))']
5+
runner = 'wasmtime -W unknown-imports-trap=y'
6+
47
# This section needs to be last.
58
# GitHub Actions modifies this section.
69
[unstable]

.github/workflows/main-checks.yml

+69
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,72 @@ jobs:
170170
env:
171171
RUSTFLAGS: --cfg nightly_yew --cfg yew_lints
172172
run: cargo test -p yew-macro test_html_lints
173+
174+
unit_tests_wasi:
175+
name: Unit Tests (WASI) on ${{ matrix.toolchain }}
176+
runs-on: ubuntu-latest
177+
strategy:
178+
fail-fast: false
179+
matrix:
180+
toolchain:
181+
- stable
182+
- nightly
183+
184+
steps:
185+
- uses: actions/checkout@v4
186+
- uses: dtolnay/rust-toolchain@master
187+
with:
188+
toolchain: ${{ matrix.toolchain }}
189+
target: wasm32-wasip1
190+
191+
- name: Install wasmtime
192+
run: |
193+
wget https://github.com/bytecodealliance/wasmtime/releases/download/v24.0.0/wasmtime-v24.0.0-x86_64-linux.tar.xz
194+
tar xf wasmtime-v24.0.0-x86_64-linux.tar.xz
195+
mv wasmtime-v24.0.0-x86_64-linux/wasmtime ~/wasmtime
196+
rm -rf wasmtime-v24.0.0-x86_64-linux.tar.xz wasmtime-v24.0.0-x86_64-linux
197+
chmod +x ~/wasmtime
198+
mv ~/wasmtime /usr/local/bin
199+
source ~/.bashrc
200+
201+
- uses: Swatinem/rust-cache@v2
202+
203+
- name: Run WASI tests for yew
204+
run: |
205+
RUST_LOG=info
206+
cargo test --features ssr,hydration --target wasm32-wasip1 -p yew
207+
208+
example-runnable-tests-on-wasi:
209+
name: Example Runnable Tests on WASI
210+
runs-on: ubuntu-latest
211+
strategy:
212+
fail-fast: false
213+
matrix:
214+
package:
215+
- wasi_ssr_module
216+
toolchain:
217+
- stable
218+
- nightly
219+
steps:
220+
- uses: actions/checkout@v4
221+
- uses: dtolnay/rust-toolchain@master
222+
with:
223+
toolchain: ${{ matrix.toolchain }}
224+
target: wasm32-wasip1
225+
226+
- name: Install wasmtime
227+
run: |
228+
wget https://github.com/bytecodealliance/wasmtime/releases/download/v24.0.0/wasmtime-v24.0.0-x86_64-linux.tar.xz
229+
tar xf wasmtime-v24.0.0-x86_64-linux.tar.xz
230+
mv wasmtime-v24.0.0-x86_64-linux/wasmtime ~/wasmtime
231+
rm -rf wasmtime-v24.0.0-x86_64-linux.tar.xz wasmtime-v24.0.0-x86_64-linux
232+
chmod +x ~/wasmtime
233+
mv ~/wasmtime /usr/local/bin
234+
source ~/.bashrc
235+
236+
- uses: Swatinem/rust-cache@v2
237+
238+
- name: Build and run ${{ matrix.package }}
239+
run: |
240+
cargo build --target wasm32-wasip1 -p ${{ matrix.package }}
241+
wasmtime -W unknown-imports-trap=y target/wasm32-wasip1/debug/${{ matrix.package }}.wasm

Cargo.lock

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ As an example, check out the TodoMVC example here: <https://examples.yew.rs/todo
6060
| [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/). |
6161
| [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/). |
6262
| [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. |
63+
| [wasi_ssr_module](wasi_ssr_module) | [F] | Demonstrates server-side rendering using WASI. |
6364

6465
[CT]: ## "Component Type"
6566
[S]: ## "Struct Components"

examples/simple_ssr/index.html

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
<!DOCTYPE html>
22
<html lang="en">
3-
<head>
4-
<meta charset="utf-8" />
5-
<title>Yew SSR Example</title>
63

7-
<link data-trunk rel="rust" data-bin="simple_ssr_hydrate" data-cargo-features="hydration" />
8-
</head>
9-
</html>
4+
<head>
5+
<meta charset="utf-8" />
6+
<title>Yew SSR Example</title>
7+
8+
<link data-trunk rel="rust" data-bin="simple_ssr_hydrate" data-cargo-features="hydration" />
9+
</head>
10+
11+
<body></body>
12+
13+
</html>

examples/wasi_ssr_module/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "wasi_ssr_module"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["langyo <[email protected]>"]
6+
7+
[dependencies]
8+
yew = { path = "../../packages/yew", features = ["ssr"] }
9+
yew-router = { path = "../../packages/yew-router" }
10+
11+
anyhow = "^1"
12+
bytes = "^1"
13+
serde = { version = "^1", features = ["derive"] }
14+
serde_json = "^1"
15+
lazy_static = "^1"
16+
17+
tokio = { version = "^1", features = ["macros", "rt", "time"] }

examples/wasi_ssr_module/README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# WASI SSR Module Example
2+
3+
This example demonstrates how to use the WASI target to run a simple server-side rendering application.
4+
5+
It depends on [wasmtime](https://wasmtime.dev)'s WASI preview2.
6+
7+
## Building
8+
9+
To build the example, run the following command from the root of the repository:
10+
11+
```bash
12+
cargo build --manifest-path examples/wasi_ssr_module/Cargo.toml --target wasm32-wasi --release
13+
```
14+
15+
## Running
16+
17+
> 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.
18+
19+
```bash
20+
wasmtime target/wasm32-wasi/release/wasi_ssr_module.wasm
21+
```
22+
23+
> 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).

examples/wasi_ssr_module/src/main.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![allow(unused_imports)]
2+
#![allow(non_snake_case)]
3+
4+
mod router;
5+
6+
use anyhow::Result;
7+
use router::{switch, Route};
8+
use yew::prelude::*;
9+
use yew::LocalServerRenderer;
10+
11+
#[function_component]
12+
fn Content() -> Html {
13+
use yew_router::prelude::*;
14+
15+
html! {
16+
<>
17+
<h1>{"Yew WASI SSR demo"}</h1>
18+
<Switch<Route> render={switch} />
19+
</>
20+
}
21+
}
22+
23+
#[function_component]
24+
fn App() -> Html {
25+
use yew_router::history::{AnyHistory, History, MemoryHistory};
26+
use yew_router::prelude::*;
27+
28+
let history = AnyHistory::from(MemoryHistory::new());
29+
history.push("/");
30+
31+
html! {
32+
<div>
33+
<Router history={history}>
34+
<Content />
35+
</Router>
36+
</div>
37+
}
38+
}
39+
40+
pub async fn render() -> Result<String> {
41+
let renderer = LocalServerRenderer::<App>::new();
42+
let html_raw = renderer.render().await;
43+
44+
let mut body = String::new();
45+
body.push_str("<body>");
46+
body.push_str("<div id='app' style='width: 100vw; height: 100vh; position: fixed;'>");
47+
body.push_str(&html_raw);
48+
body.push_str("</div>");
49+
body.push_str("</body>");
50+
51+
Ok(body)
52+
}
53+
54+
#[tokio::main(flavor = "current_thread")]
55+
async fn main() -> Result<()> {
56+
let ret = render().await?;
57+
println!("{}", ret);
58+
59+
Ok(())
60+
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use yew::prelude::*;
2+
use yew_router::prelude::*;
3+
4+
#[derive(Routable, PartialEq, Eq, Clone, Debug)]
5+
pub enum Route {
6+
#[at("/")]
7+
Portal,
8+
9+
#[at("/t/:id")]
10+
Thread { id: String },
11+
12+
#[not_found]
13+
#[at("/404")]
14+
NotFound,
15+
}
16+
17+
pub fn switch(routes: Route) -> Html {
18+
match routes {
19+
Route::Portal => {
20+
html! { <h1>{"Hello"}</h1> }
21+
}
22+
Route::Thread { id } => {
23+
html! { <h1>{format!("Thread id {}", id)}</h1> }
24+
}
25+
Route::NotFound => {
26+
html! { <h1>{"Not found"}</h1> }
27+
}
28+
}
29+
}

packages/yew-router/src/utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub fn fetch_base_url() -> Option<String> {
4242
}
4343
}
4444

45-
#[cfg(target_arch = "wasm32")]
45+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
4646
pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
4747
gloo::utils::window()
4848
.location()
@@ -55,7 +55,7 @@ pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
5555
})
5656
}
5757

58-
#[cfg(not(target_arch = "wasm32"))]
58+
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
5959
pub fn compose_path(pathname: &str, query: &str) -> Option<String> {
6060
let query = query.trim();
6161

packages/yew-router/tests/basename.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg(not(target_os = "wasi"))]
2+
13
use std::time::Duration;
24

35
use serde::{Deserialize, Serialize};

packages/yew-router/tests/browser_router.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg(not(target_os = "wasi"))]
2+
13
use std::time::Duration;
24

35
use serde::{Deserialize, Serialize};

packages/yew-router/tests/hash_router.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg(not(target_os = "wasi"))]
2+
13
use std::time::Duration;
24

35
use serde::{Deserialize, Serialize};

packages/yew-router/tests/link.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg(not(target_os = "wasi"))]
2+
13
use std::sync::atomic::{AtomicU8, Ordering};
24
use std::time::Duration;
35

packages/yew-router/tests/url_encoded_routes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg(not(target_os = "wasi"))]
2+
13
use std::time::Duration;
24

35
use yew::platform::time::sleep;

packages/yew/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ features = [
8181
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
8282
tokio = { version = "1.40", features = ["full"] }
8383

84+
[target.'cfg(all(target_arch = "wasm32", target_os = "wasi"))'.dependencies]
85+
tokio = { version = "1.40", features = ["macros", "rt", "time"] }
86+
8487
[dev-dependencies]
8588
wasm-bindgen-test = "0.3"
8689
gloo = { version = "0.11", features = ["futures"] }
@@ -95,6 +98,7 @@ features = ["ShadowRootInit", "ShadowRootMode", "HtmlButtonElement"]
9598
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]
9699
csr = []
97100
hydration = ["csr", "dep:bincode"]
101+
not_browser_env = []
98102
default = []
99103
test = []
100104

packages/yew/src/dom_bundle/bcomp.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ mod feat_hydration {
158158
}
159159
}
160160

161-
#[cfg(target_arch = "wasm32")]
161+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
162162
#[cfg(test)]
163163
mod tests {
164164
use gloo::utils::document;
@@ -391,7 +391,7 @@ mod tests {
391391
}
392392
}
393393

394-
#[cfg(target_arch = "wasm32")]
394+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
395395
#[cfg(test)]
396396
mod layout_tests {
397397
extern crate self as yew;

packages/yew/src/dom_bundle/blist.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ mod feat_hydration {
483483
}
484484
}
485485

486-
#[cfg(target_arch = "wasm32")]
486+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
487487
#[cfg(test)]
488488
mod layout_tests {
489489
extern crate self as yew;
@@ -560,7 +560,7 @@ mod layout_tests {
560560
}
561561
}
562562

563-
#[cfg(target_arch = "wasm32")]
563+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
564564
#[cfg(test)]
565565
mod layout_tests_keys {
566566
extern crate self as yew;

packages/yew/src/dom_bundle/bnode.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ mod feat_hydration {
311311
}
312312
}
313313

314-
#[cfg(target_arch = "wasm32")]
314+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
315315
#[cfg(test)]
316316
mod layout_tests {
317317
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};

packages/yew/src/dom_bundle/bportal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl BPortal {
118118
}
119119
}
120120

121-
#[cfg(target_arch = "wasm32")]
121+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
122122
#[cfg(test)]
123123
mod layout_tests {
124124
extern crate self as yew;

packages/yew/src/dom_bundle/braw.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ mod feat_hydration {
170170
}
171171
}
172172

173-
#[cfg(target_arch = "wasm32")]
173+
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
174174
#[cfg(test)]
175175
mod tests {
176176
use gloo::utils::document;

0 commit comments

Comments
 (0)