Rewrite the README using Webpack

Right now Webpack probably has the most mature support for loading wasm modules,
so let's show off how to do that! Additionally this commits hello world as an
example to the repository.
This commit is contained in:
Alex Crichton 2018-03-02 20:11:30 -08:00
parent 30986dacad
commit 4aa6793b9e
14 changed files with 180 additions and 36 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
**/*.rs.bk
Cargo.lock
node_modules
package-lock.json

View File

@ -15,6 +15,7 @@ script:
- rustup target add wasm32-unknown-unknown
- cargo test
- cargo install --debug --path crates/wasm-bindgen-cli
- (cd examples/hello_world && ./build.sh)
notifications:
email:

View File

@ -14,4 +14,7 @@ wasm-bindgen-macro = { path = "crates/wasm-bindgen-macro" }
test-support = { path = "crates/test-support" }
[workspace]
members = ["crates/wasm-bindgen-cli"]
members = [
"crates/wasm-bindgen-cli",
"examples/hello_world",
]

103
README.md
View File

@ -49,10 +49,19 @@ Let's implement the equivalent of "Hello, world!" for this crate.
[rustup]: https://rustup.rs
First up, let's add the wasm target and generate a Rust project:
First up, let's install the tools we need
```
$ rustup target add wasm32-unknown-unknown
$ cargo install --git https://github.com/alexcrichton/wasm-bindgen
```
The first command here installs the wasm target so you can compile to it, and
the latter will install the `wasm-bindgen` CLI tool we'll be using later.
Next up let's make our project
```
$ cargo new js-hello-world
```
@ -110,15 +119,8 @@ can use [wasm-gc] to make this file a little smaller
[wasm-gc]: https://github.com/alexcrichton/wasm-gc
Now that we've generated the wasm module it's time to run the bindgen tool
itself! Let's install it:
```
$ cargo install --git https://github.com/alexcrichton/wasm-bindgen
```
This'll install a `wasm-bindgen` binary next to your `cargo` binary. This tool
will postprocess the wasm file rustc generated, generating a new wasm file and a
set of JS bindings as well. Let's invoke it!
itself! This tool will postprocess the wasm file rustc generated, generating a
new wasm file and a set of JS bindings as well. Let's invoke it!
```
$ wasm-bindgen target/wasm32-unknown-unknown/release/js_hello_world.wasm \
@ -135,36 +137,71 @@ module. The `js_hello_world.js` file emitted by `wasm-bindgen` should have the
intended interface of the wasm file, notably with rich types like strings,
classes, etc.
The `wasm-bindgen` tool also emits a secondary file, `js_hello_world_wasm.wasm`.
This is the original wasm file but postprocessed a bit. It's intended that the
`js_hello_world_wasm.wasm` file, like before, acts like an ES6 module. The
`js_hello_world.wasm` file, for example, uses `import` to import functionality
from the wasm.
The `wasm-bindgen` tool also emits a few other files needed to implement this
module. For example `js_hello_world_wasm.wasm` is the original wasm file but
postprocessed a bit. It's intended that the `js_hello_world_wasm.wasm` file,
like before, acts like an ES6 module. The `js_hello_world.wasm` file, for
example, uses `import` to import functionality from the other `*_shims` file
generated (an internal implementation detail here).
Note that you can also pass a `--nodejs` argument to `wasm-bindgen` for emitting
Node-compatible JS as well as a `--typescript` argument to emit a `*.d.ts` file
describing the exported contents.
At this point you'll typically plug these files into a larger build system. Both
files emitted by `wasm-bindgen` act like normal ES6 modules (one just happens to
At this point you'll probably plug these files into a larger build system.
Files emitted by `wasm-bindgen` act like normal ES6 modules (one just happens to
be wasm). As of the time of this writing there's unfortunately not a lot of
tools that natively do this (but they're coming!). In the meantime we can use
the `wasm2es6js` utility (aka "hack") from the `wasm-bindgen` tool we previously
installed along with the `parcel-bundler` packager. Note that these steps will
differ depending on your build system.
tools that natively do this, but Webpack's 4.0 beta release has native wasm
support!. Let's take a look at that and see how it works.
Alright first create an `index.js` file:
First create an `index.js` file:
```js
import { greet } from "./js_hello_world";
import { booted } from "./js_hello_world_wasm";
const js = import("./js_hello_world");
booted.then(() => {
greet("World!");
js.then(js => {
js.greet("World!");
});
```
Then a corresponding `index.html`:
Note that we're using `import(..)` here because Webpack [doesn't
support][webpack-issue] synchronously importing modules from the main chunk just
yet.
[webpack-issue]: https://github.com/webpack/webpack/issues/6615
Next our JS dependencies by creating a `package.json`:
```json
{
"scripts": {
"serve": "webpack-dev-server"
},
"devDependencies": {
"webpack": "^4.0.1",
"webpack-cli": "^2.0.10",
"webpack-dev-server": "^3.1.0"
}
}
```
and our webpack configuration
```js
// webpack.config.js
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development"
};
```
Our corresponding `index.html`:
```html
<html>
@ -177,18 +214,14 @@ Then a corresponding `index.html`:
</html>
```
And run a local server with these files:
And finally:
```
# Convert `*.wasm` to `*.js` where the JS internally instantiates the wasm
$ wasm2es6js js_hello_world_wasm.wasm -o js_hello_world_wasm.js --base64
# Install parcel and run it against the index files we use below.
$ npm install -g parcel-bundler
$ parcel index.html
$ npm run serve
```
If you open that in a browser you should see a `Hello, world!` dialog pop up!
If you open https://localhost:8080 in a browser you should see a `Hello, world!`
dialog pop up!
## What just happened?

11
examples/README.md Normal file
View File

@ -0,0 +1,11 @@
# Examples
This directory contains a number of examples of the `#[wasm_bindgen]` macro and
how to display them in the browser. Each directory should contain a `build.sh`
which assembles all the relevant files, and then if you open up `index.html` in
a web browser you should be able to see everything in action!
The examples here are:
* `hello_world` - the "hello world" of `#[wasm_bindgen]`, aka throwing up a
dialog greeting you

3
examples/hello_world/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
package-lock.json
hello_world.js
hello_world_wasm.wasm

View File

@ -0,0 +1,14 @@
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
# Here we're using a path dependency to use what's already in this repository,
# but you'd use the commented out version below if you're copying this into your
# project.
wasm-bindgen = { path = "../.." }
#wasm-bindgen = { git = "https://github.com/alexcrichton/wasm-bindgen" }

View File

@ -0,0 +1,14 @@
# Hello, World!
This directory is an example of using the `#[wasm_bindgen]` macro to create an
entry point that's called from the browser and then displays a dialog.
You can build the example with:
```
$ ./build.sh
```
(or running the two commands on Windows manually)
and then opening up `index.html` in a web browser should show a dialog!

15
examples/hello_world/build.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
set -ex
cargo +nightly build --target wasm32-unknown-unknown --release
# Here we're using the version of the CLI in this repository, but for external
# usage you'd use the commented out version below
cargo +nightly run --manifest-path ../../crates/wasm-bindgen-cli/Cargo.toml \
--bin wasm-bindgen -- \
../../target/wasm32-unknown-unknown/release/hello_world.wasm --out-dir .
# wasm-bindgen ../../target/wasm32-unknown-unknown/hello_world.wasm --out-dir .
npm install
npm run serve

View File

@ -0,0 +1,8 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<script src='./index.js'></script>
</body>
</html>

View File

@ -0,0 +1,5 @@
const js = import("./hello_world");
js.then(js => {
js.greet("World!");
});

View File

@ -0,0 +1,10 @@
{
"scripts": {
"serve": "webpack-dev-server"
},
"devDependencies": {
"webpack": "^4.0.1",
"webpack-cli": "^2.0.10",
"webpack-dev-server": "^3.1.0"
}
}

View File

@ -0,0 +1,16 @@
#![feature(proc_macro)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
#[no_mangle]
pub extern fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}

View File

@ -0,0 +1,10 @@
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development"
};