Implement the local JS snippets RFC

This commit is an implementation of [RFC 6] which enables crates to
inline local JS snippets into the final output artifact of
`wasm-bindgen`. This is accompanied with a few minor breaking changes
which are intended to be relatively minor in practice:

* The `module` attribute disallows paths starting with `./` and `../`.
  It requires paths starting with `/` to actually exist on the filesystem.
* The `--browser` flag no longer emits bundler-compatible code, but
  rather emits an ES module that can be natively loaded into a browser.

Otherwise be sure to check out [the RFC][RFC 6] for more details, and
otherwise this should implement at least the MVP version of the RFC!
Notably at this time JS snippets with `--nodejs` or `--no-modules` are
not supported and will unconditionally generate an error.

[RFC 6]: https://github.com/rustwasm/rfcs/pull/6

Closes #1311
This commit is contained in:
Alex Crichton
2019-02-25 11:11:30 -08:00
parent f161717afe
commit b762948456
32 changed files with 985 additions and 380 deletions

View File

@ -27,6 +27,7 @@
- [web-sys: A TODO MVC App](./examples/todomvc.md)
- [Reference](./reference/index.md)
- [Deployment](./reference/deployment.md)
- [JS snippets](./reference/js-snippets.md)
- [Passing Rust Closures to JS](./reference/passing-rust-closures-to-js.md)
- [Receiving JS Closures in Rust](./reference/receiving-js-closures-in-rust.md)
- [`Promise`s and `Future`s](./reference/js-promises-and-rust-futures.md)

View File

@ -4,12 +4,15 @@
[code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/without-a-bundler
This example shows how the `--no-modules` flag can be used load code in a
This example shows how the `--browser` flag can be used load code in a
browser directly. For this deployment strategy bundlers like Webpack are not
required. For more information on deployment see the [dedicated
documentation][deployment].
First let's take a look at the code and see how when we're using `--no-modules`
> **Note**: the `--browser` flag is quite new to `wasm-bindgen`, and does not
> currently have support in `wasm-pack` yet. Support will be added soon though!
First let's take a look at the code and see how when we're using `--browser`
we're not actually losing any functionality!
```rust
@ -22,7 +25,33 @@ Otherwise the rest of the deployment magic happens in `index.html`:
{{#include ../../../examples/without-a-bundler/index.html}}
```
And that's it! Be sure to read up on the [deployment options][deployment] to see what it
means to deploy without a bundler.
And that's it! Be sure to read up on the [deployment options][deployment] to see
what it means to deploy without a bundler.
[deployment]: ../reference/deployment.html
## Using the older `--no-modules`
[View full source code][code]
[code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/without-a-bundler-no-modules
The older version of using `wasm-bindgen` without a bundler is to use the
`--no-modules` flag to the `wasm-bindgen` CLI. This corresponds to `--target
no-modules` in `wasm-pack`.
While similar to the newer `--browser`, the `--no-modules` flag has a few
caveats:
* It does not support [local JS snippets][snippets]
* It does not generate an ES module
With that in mind the main difference is how the wasm/JS code is loaded, and
here's an example of loading the output of `wasm-pack` for the same module as
above.
```html
{{#include ../../../examples/without-a-bundler-no-modules/index.html}}
```
[snippets]: ../reference/js-snippets.html

View File

@ -29,26 +29,29 @@ necessary.
If you're not using a bundler but you're still running code in a web browser,
`wasm-bindgen` still supports this! For this use case you'll want to use the
`--no-modules` flag. You can check out a [full example][nomex] in the
`--browser` flag. You can check out a [full example][nomex] in the
documentation, but the highlights of this output are:
* When using `wasm-pack` you'll pass `--target no-modules`, and when using
`wasm-bindgen` directly you'll pass `--no-modules`.
* When using `wasm-bindgen` directly you'll pass `--browser`.
* The output can natively be included on a web page, and doesn't require any
further postprocessing.
* The `--no-modules` mode is not able to use NPM dependencies nor local JS
snippets (both currently [proposed][rfc1] [features][rfc2])
further postprocessing. The output is included as an ES module.
* The `--browser` mode is not able to use NPM dependencies.
* You'll want to review the [browser requirements] for `wasm-bindgen` because
no polyfills will be available.
> **Note**: currently `--browser` is not supported in `wasm-pack` because it is
> a very recent addition to `wasm-bindgen`, but support will be added soon!
[nomex]: ../examples/without-a-bundler.html
[rfc1]: https://github.com/rustwasm/rfcs/pull/6
[rfc2]: https://github.com/rustwasm/rfcs/pull/8
[browser requirements]: browser-support.html
Despite these limitations almost all code today is compatible with
`--no-modules`, but this area is actively being worked on to improve the
experience so the experience here may be tweaked over time!
The `wasm-bindgen` CLI also supports an output mode called `--no-modules` which
is similar to `--browser` in that it requires manual initialization of the wasm
and is intended to be included in web pages without any further postprocessing.
See the [without a bundler example][nomex] for some more information about
`--no-modules`, which corresponds to `--target no-modules` in `wasm-pack`.
## Node.js

View File

@ -0,0 +1,78 @@
# JS Snippets
Often when developing a crate you want to run on the web you'll want to include
some JS code here and there. While [`js-sys`](https://docs.rs/js-sys) and
[`web-sys`](https://docs.rs/web-sys) cover many needs they don't cover
everything, so `wasm-bindgen` supports the ability to write JS code next to your
Rust code and have it included in the final output artifact.
To include a local JS file, you'll use the `#[wasm_bindgen(module)]` macro:
```rust
#[wasm_bindgen(module = "/js/foo.js")]
extern "C" {
fn add(a: u32, b: u32) -> u32;
}
```
This declaration indicates that all the functions contained in the `extern`
block are imported from the file `/js/foo.js`, where the root is relative to the
crate root (where `Cargo.toml` is located).
The `/js/foo.js` file will make its way to the final output when `wasm-bindgen`
executes, so you can use the `module` annotation in a library without having to
worry users of your library!
The JS file itself must be written with ES module syntax:
```js
export function add(a, b) {
return a + b;
}
```
A full design of this feature can be found in [RFC 6] as well if you're
interested!
[RFC 6]: https://github.com/rustwasm/rfcs/pull/6
### Using `inline_js`
In addition to `module = "..."` if you're a macro author you also have the
ability to use the `inline_js` attribute:
```rust
#[wasm_bindgen(inline_js = "export function add(a, b) { return a + b; }")]
extern "C" {
fn add(a: u32, b: u32) -> u32;
}
```
Using `inline_js` indicates that the JS module is specified inline in the
attribute itself, and no files are loaded from the filesystem. They have the
same limitations and caveats as when using `module`, but can sometimes be easier
to generate for macros themselves. It's not recommended for hand-written code to
make use of `inline_js` but instead to leverage `module` where possible.
### Caveats
While quite useful local JS snippets currently suffer from a few caveats which
are important to be aware of. Many of these are temporary though!
* Currently `import` statements are not supported in the JS file. This is a
restriction we may lift in the future once we settle on a good way to support
this. For now, though, js snippets must be standalone modules and can't import
from anything else.
* Only `--browser` and the default bundler output mode are supported. To support
`--nodejs` we'd need to translate ES module syntax to CommonJS (this is
planned to be done, just hasn't been done yet). Additionally to support
`--no-modules` we'd have to similarly translate from ES modules to something
else.
* Paths in `module = "..."` must currently start with `/`, or be rooted at the
crate root. It is intended to eventually support relative paths like `./` and
`../`, but it's currently believed that this requires more support in
the Rust `proc_macro` crate.
As above, more detail about caveats can be found in [RFC 6].