Skip to content

Commit b5941b2

Browse files
dan-dajan-ferdinand
authored andcommitted
docs: add README files for wasm32
1 parent 605984e commit b5941b2

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

twenty-first/README-wasm32-dev.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# `twenty-first` wasm32 integration
2+
3+
This document provides a summary of changes that were required to get
4+
the twenty-first crate to build and run tests for wasm32 target.
5+
6+
7+
## 1\. Summary of `Cargo.toml` Changes
8+
9+
To support both native and `wasm32` environments, the `Cargo.toml` file was restructured to use target-specific dependency sections. This allows us to have different configurations and dependencies for each compilation target.
10+
11+
### General Dependencies for Wasm
12+
13+
A new section, `[target.'cfg(target_arch = "wasm32")'.dependencies]`, was added to define dependencies that are only needed when compiling for WebAssembly:
14+
15+
- `getrandom = { ..., features = ["js"] }`: Provides an entropy source required by the `rand` crate by linking to JavaScript's Web Crypto API.
16+
- `wasm-bindgen`: The core library enabling communication between Rust and JavaScript.
17+
18+
```toml
19+
[target.'cfg(target_arch = "wasm32")'.dependencies]
20+
getrandom = { version = "0.3", features = ["wasm_js"] }
21+
wasm-bindgen = "=0.2.100"
22+
```
23+
24+
### Target-Specific Development Dependencies
25+
26+
The `[dev-dependencies]` have been split into two target-specific sections to handle the different needs of testing in native vs. Wasm environments.
27+
28+
- When targeting `wasm32`, we use `[target.'cfg(target_arch = "wasm32")'.dev-dependencies]`:
29+
30+
- `criterion` and `proptest` are included with `default-features = false` to ensure they are `no_std`-compatible.
31+
- `wasm-bindgen-test` is included to provide the test runner for the Wasm environment.
32+
33+
- For all other targets (i.e., native builds), we use `[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]`:
34+
35+
- This section contains the standard versions of `criterion` and `proptest` with all their default features enabled for native benchmarking and testing.
36+
37+
## 2\. Rationale for Forked `proptest-arbitrary-interop`
38+
39+
The `twenty-first` crate uses property-based testing via the `proptest` library. To generate arbitrary instances of external types (like `num_bigint::BigUint`), we rely on the `proptest-arbitrary-interop` crate.
40+
41+
The official version of this crate on `crates.io` lacks `wasm32` support. To enable our property tests to run in a WebAssembly environment, we use a forked version of the crate. This fork specifically adds conditional dependencies for the `wasm32` target.
42+
43+
As shown in the fork's `Cargo.toml`, when compiling for `wasm32` it correctly pulls in `proptest` with its `std` feature enabled and adds `getrandom` with the `js` feature, which is necessary for the tests to compile and run successfully.
44+
45+
```toml
46+
# In the forked proptest-arbitrary-interop/Cargo.toml
47+
[target.'cfg(target_arch = "wasm32")'.dependencies]
48+
proptest = { version = "1.2.0", default-features = false, features = ["std"] }
49+
# convince getrandom to build for wasm target.
50+
# note that there is also a flag in .cargo/config.toml
51+
getrandom = { version = "0.2", features = ["js"] }
52+
```
53+
54+
The dependency is specified in our `Cargo.toml` to point directly to the git repository containing the fork:
55+
56+
```toml
57+
# In twenty-first/Cargo.toml
58+
[dev-dependencies]
59+
proptest-arbitrary-interop = { git = "https://github.com/dan-da/proptest-arbitrary-interop" }
60+
```
61+
62+
### PR Raised to `proptest-arbitrary-interop`
63+
64+
[PR #3](https://github.com/graydon/proptest-arbitrary-interop/pull/3) has been raised to the [proptest-arbitrary-interop repo
65+
](https://github.com/graydon/proptest-arbitrary-interop). So if/when it is included in a new release on crates.io
66+
twenty-first can again use the official version.
67+
68+
## 3\. Code Changes for Wasm Testing
69+
70+
The WebAssembly environment uses its own test harness (`wasm-pack test`) which ignores `#[test]` and looks for tests marked with `#[wasm_bindgen_test]` instead.
71+
Yet our test suite should run in both the regular `cargo test` harness and the `wasm-pack test` harness.
72+
73+
## Dual Test Harness Compatibility
74+
75+
The core change is to conditionally add the `#[wasm_bindgen_test]` attribute to our existing tests, which already have the `#[test]` or `#[proptest]` attributes. This is accomplished using the `#[cfg_attr]` attribute, which applies another attribute only when a certain configuration is met.
76+
77+
This pattern allows a single test function to be recognized by both test harnesses:
78+
79+
1. The native harness sees `#[test]` (or `#[proptest]`) and runs the test.
80+
2. The Wasm harness sees `#[wasm_bindgen_test]` and runs the test.
81+
3. Each harness ignores the attribute it doesn't recognize.
82+
83+
Here is the specific pattern used throughout the codebase:
84+
85+
```rust
86+
// This 'use' statement is only included when compiling Wasm tests.
87+
#[cfg(all(test, target_arch = "wasm32"))]
88+
use wasm_bindgen_test::wasm_bindgen_test;
89+
90+
// The #[wasm_bindgen_test] attribute is conditionally added only when building
91+
// for the wasm32 target. This prevents compilation errors on native builds.
92+
#[cfg_attr(all(test, target_arch = "wasm32"), wasm_bindgen_test)]
93+
// The standard #[test] attribute is always present for the native test runner.
94+
# [test]
95+
fn my_dual_target_test() {
96+
// ... test logic ...
97+
}
98+
99+
// The same pattern applies to proptests:
100+
# [cfg_attr(all(test, target_arch = "wasm32"), wasm_bindgen_test)]
101+
# [proptest]
102+
fn my_dual_target_proptest() {
103+
// ... test logic ...
104+
}
105+
106+
By using `#[cfg_attr(...)]` and a conditional `use` statement, we make our test suite compatible with the Wasm environment without breaking the existing native test workflow.

twenty-first/README-wasm32.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Building and Testing `twenty-first` for wasm32
2+
3+
This document provides instructions on how to build and test the `twenty-first` crate for the `wasm32-unknown-unknown` target, which allows the library to run in WebAssembly environments.
4+
5+
## 1\. What is wasm32?
6+
7+
WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine. The `wasm32-unknown-unknown` target allows Rust code to be compiled into Wasm, enabling high-performance applications to run directly in web browsers and other Wasm-compatible environments. This is ideal for bringing computationally intensive tasks, like the cryptographic operations in `twenty-first`, to the web without sacrificing performance.
8+
9+
For more detailed information, see the official [WebAssembly website](https://webassembly.org/).
10+
11+
## 2\. Required Tools and Setup
12+
13+
To build and test for `wasm32`, you need to set up your Rust environment with the correct target and tooling.
14+
15+
### Add the `wasm32` Target
16+
17+
First, add the `wasm32-unknown-unknown` target to your Rust toolchain using `rustup`:
18+
19+
```shell
20+
rustup target add wasm32-unknown-unknown
21+
```
22+
23+
### Install `wasm-pack`
24+
25+
`wasm-pack` is the primary tool for building, testing, and publishing Rust-generated WebAssembly. It coordinates the build process and handles the interaction with other tools like `wasm-bindgen`.
26+
27+
Install `wasm-pack` using `cargo`:
28+
29+
```shell
30+
cargo install wasm-pack
31+
```
32+
33+
### Install Node.js (for Testing)
34+
35+
Running the `wasm32` test suite requires a JavaScript runtime. `wasm-pack` uses Node.js for this purpose.
36+
37+
- **Requirement**: You must have Node.js v20 (LTS) or later installed. The getrandom crate, a dependency for our tests, requires the Web Crypto API, which is stable and fully supported in all modern LTS releases of Node.js.
38+
39+
- **Installation**: You can download Node.js from the [official Node.js website](https://nodejs.org/) or install it using a version manager like `nvm`.
40+
41+
## 3\. Build and Test Commands
42+
43+
With the environment configured, you can now build and test the crate.
44+
45+
### Build the Crate
46+
47+
To compile the `twenty-first` crate for WebAssembly, run the following command from the root of the repository:
48+
49+
```shell
50+
wasm-pack build --target nodejs
51+
```
52+
53+
This command compiles the crate and generates the necessary JavaScript bindings, placing the output in a `pkg/` directory.
54+
55+
### Run Tests
56+
57+
To run the test suite for the `wasm32` target, use the `test` command from `wasm-pack`. This command will compile the tests and execute them using your installed Node.js runtime.
58+
59+
```shell
60+
wasm-pack test --node
61+
```

0 commit comments

Comments
 (0)