Skip to content

Commit

Permalink
Add WASI support for server-side rendering. (#3534)
Browse files Browse the repository at this point in the history
* 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/prokio#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]>
  • Loading branch information
3 people authored Oct 21, 2024
1 parent 4025fa7 commit 84b7548
Show file tree
Hide file tree
Showing 48 changed files with 395 additions and 79 deletions.
5 changes: 4 additions & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/main-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 10 additions & 6 deletions examples/simple_ssr/index.html
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions examples/wasi_ssr_module/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "wasi_ssr_module"
version = "0.1.0"
edition = "2021"
authors = ["langyo <[email protected]>"]

[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 changes: 23 additions & 0 deletions examples/wasi_ssr_module/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 60 additions & 0 deletions examples/wasi_ssr_module/src/main.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions examples/wasi_ssr_module/src/router.rs
Original file line number Diff line number Diff line change
@@ -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> }
}
}
}
4 changes: 2 additions & 2 deletions packages/yew-router/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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();

Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/tests/basename.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_os = "wasi"))]

use std::time::Duration;

use serde::{Deserialize, Serialize};
Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/tests/browser_router.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_os = "wasi"))]

use std::time::Duration;

use serde::{Deserialize, Serialize};
Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/tests/hash_router.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_os = "wasi"))]

use std::time::Duration;

use serde::{Deserialize, Serialize};
Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/tests/link.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_os = "wasi"))]

use std::sync::atomic::{AtomicU8, Ordering};
use std::time::Duration;

Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/tests/url_encoded_routes.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_os = "wasi"))]

use std::time::Duration;

use yew::platform::time::sleep;
Expand Down
4 changes: 4 additions & 0 deletions packages/yew/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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 = []

Expand Down
4 changes: 2 additions & 2 deletions packages/yew/src/dom_bundle/bcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions packages/yew/src/dom_bundle/blist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/dom_bundle/bnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/dom_bundle/bportal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/dom_bundle/braw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

1 comment on commit 84b7548

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yew master branch benchmarks (Lower is better)

Benchmark suite Current: 84b7548 Previous: b89609c Ratio
yew-hooks-v0.21.0-keyed 01_run1k 187 193.8 0.96
yew-hooks-v0.21.0-keyed 02_replace1k 212.3 214.9 0.99
yew-hooks-v0.21.0-keyed 03_update10th1k_x16 97.2 88.7 1.10
yew-hooks-v0.21.0-keyed 04_select1k 45.1 41.3 1.09
yew-hooks-v0.21.0-keyed 05_swap1k 109.7 101.9 1.08
yew-hooks-v0.21.0-keyed 06_remove-one-1k 78.9 81.9 0.96
yew-hooks-v0.21.0-keyed 07_create10k 2158.4 2208.8 0.98
yew-hooks-v0.21.0-keyed 08_create1k-after1k_x2 211.6 215.6 0.98
yew-hooks-v0.21.0-keyed 09_clear1k_x8 86 88.4 0.97
yew-hooks-v0.21.0-keyed 21_ready-memory 2.1491613388061523 2.1487531661987305 1.00
yew-hooks-v0.21.0-keyed 22_run-memory 6.310652732849121 6.277518272399902 1.01
yew-hooks-v0.21.0-keyed 23_update5-memory 6.6225128173828125 6.604434967041016 1.00
yew-hooks-v0.21.0-keyed 25_run-clear-memory 5.134346008300781 5.16943359375 0.99
yew-hooks-v0.21.0-keyed 26_run-10k-memory 42.794864654541016 42.79558944702149 1.00
yew-hooks-v0.21.0-keyed 41_size-uncompressed 166.6 168 0.99
yew-hooks-v0.21.0-keyed 42_size-compressed 54.4 54.6 1.00
yew-hooks-v0.21.0-keyed 43_first-paint 442.8 471.8 0.94
yew-v0.21.0-keyed 01_run1k 194.8 199 0.98
yew-v0.21.0-keyed 02_replace1k 211.4 226.8 0.93
yew-v0.21.0-keyed 03_update10th1k_x16 73.9 71.2 1.04
yew-v0.21.0-keyed 04_select1k 24.9 16.5 1.51
yew-v0.21.0-keyed 05_swap1k 86.3 80.1 1.08
yew-v0.21.0-keyed 06_remove-one-1k 67 66.7 1.00
yew-v0.21.0-keyed 07_create10k 2173.9 2204.3 0.99
yew-v0.21.0-keyed 08_create1k-after1k_x2 208.7 214.2 0.97
yew-v0.21.0-keyed 09_clear1k_x8 89.5 84.4 1.06
yew-v0.21.0-keyed 21_ready-memory 1.8421964645385744 2.0839223861694336 0.88
yew-v0.21.0-keyed 22_run-memory 6.215226173400879 6.196225166320801 1.00
yew-v0.21.0-keyed 23_update5-memory 6.2975311279296875 6.37141227722168 0.99
yew-v0.21.0-keyed 25_run-clear-memory 4.978641510009766 4.997003555297852 1.00
yew-v0.21.0-keyed 26_run-10k-memory 41.54443550109863 41.52542304992676 1.00
yew-v0.21.0-keyed 41_size-uncompressed 164.2 166 0.99
yew-v0.21.0-keyed 42_size-compressed 54 54.4 0.99
yew-v0.21.0-keyed 43_first-paint 454.6 446.8 1.02

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.