From 1c57ae8ff86fb7961c47f11be8044a14d4fedded Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Jan 2019 13:42:02 -0800 Subject: [PATCH 01/10] RFC: Add support for local JS snippets in wasm-bindgen --- text/000-local-js-dependencies.md | 280 ++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 text/000-local-js-dependencies.md diff --git a/text/000-local-js-dependencies.md b/text/000-local-js-dependencies.md new file mode 100644 index 0000000..e18b8de --- /dev/null +++ b/text/000-local-js-dependencies.md @@ -0,0 +1,280 @@ +- Start Date: 2018-01-08 +- RFC PR: (leave this empty) +- Tracking Issue: (leave this empty) + +# Summary +[summary]: #summary + +Add the ability for `#[wasm_bindgen]` to process, load, and handle dependencies +on local JS files. + +* A new attribute for this will be added: + + ```rust + #[wasm_bindgen(file = "foo.js")] + extern "C" { + // ... + } + ``` + +* The `--browser` flag is repurposed to generate an ES module for the browser + and `--no-modules` is deprecated in favor of this flag. + +* The `--nodejs` will not immediately support local JS snippets, but will do so + in the future. + +# Motivation +[motivation]: #motivation + +The goal of `wasm-bindgen` is to enable easy interoperation between Rust and JS. +While it's very easy to write custom Rust code, it's actually pretty difficult +to write custom JS and hook it up with `#[wasm_bindgen]` (see +[rustwasm/wasm-bindgen#224][issue]). The `#[wasm_bindgen]` +attribute currently only supports importing functions from ES modules, but even +then the support is limited and simply assumes that the ES module string exists +in the final application build step. + +[issue]: https://github.com/rustwasm/wasm-bindgen/issues/224 + +Currently there is no composable way for a crate to have some auxiliary JS that +it is built with which ends up seamlessly being included into a final built +application. For example the `rand` crate can't easily include local JS (perhaps +to detect what API for randomness it's supposed to use) without imposing strong +requirements on the final artifact. + +Ergonomically support imports from custom JS files also looks to be required by +frameworks like `stdweb` to build a macro like `js!`. This involves generating +snippets of JS at compile time which need to be included into the final bundle, +which is intended to be powered by this new attribute. + +# Stakeholders +[stakeholders]: #stakeholders + +Some major stakeholders in this RFC are: + +* Users of `#[wasm_bindgen]` +* Crate authors wishing to add wasm support to their crate. +* The `stdweb` authors +* Bundler (webpack) and `wasm-bindgen` integration folks. + +Most of the various folks here will be cc'd onto the RFC, and reaching out to +more is always welcome! + +# Detailed Explanation +[detailed-explanation]: #detailed-explanation + +This proposal involves a number of moving pieces, all of which are intended to +work in concert to provide a streamlined story to including local JS files into +a final `#[wasm_bindgen]` artifact. We'll take a look at each piece at a time +here. + +### New Syntactical Features + +The most user-facing change proposed here is the addition of a new attribute +inside of `#[wasm_bindgen]`, the `file` attribute. This configured like so: + +```rust +#[wasm_bindgen(file = "foo.js")] +extern "C" { + // ... definitions +} +``` + +This declaration says that the block of functions and types and such are all +imported from the `foo.js` file. The `foo.js` file is resolved relative to the +crate root (where this is relative to is also discussed in the drawbacks section +below). For example a procedural macro will simply `File::open` (morally) +with the path provided to the attribute. + +The `file` attribute is mutually exclusive with the `module` attribute. Only one +can be specified. + +### Format of imported JS + +All imported JS is required to written with ES module syntax. Initially the JS +must be hand-written and cannot be postprocessed. For example the JS cannot be +written with TypeScript, nor can it be compiled by Babel or similar. + +As an example, a library may contain: + +```rust +// src/lib.rs +#[wasm_bindgen(file = "js/foo.js")] +extern "C" { + fn call_js(); +} +``` + +accompanied with: + +```js +// js/foo.js + +export function call_js() { + // ... +} +``` + +Note that `js/foo.js` uses ES module syntax to export the function `call_js`. +When `call_js` is called from Rust it will call the `call_js` function in +`foo.js`. + +### Propagation Through Dependencies + +The purpose of the `file` attribute is to work seamlessly with dependencies. +When building a project with `#[wasm_bindgen]` you shouldn't be required to know +whether your dependencies are using local JS snippets or not! + +The `#[wasm_bindgen]` macro, at compile time, will read the contents of the file +provided, if any. This file will be serialized into the wasm-bindgen custom +sections in a wasm-bindgen specific format. The final wasm artifact produced by +rustc will contain all referenced JS file contents in its custom sections. + +The `wasm-bindgen` CLI tool will extract all this JS and write it out to the +filesystem. The wasm file (or the wasm-bindgen-generated shim JS file) emitted +will import all the emitted JS files with relative imports. + +### Updating `wasm-bindgen` output modes + +The `wasm-bindgen` has a few modes of output generation today. This PR +proposes repurposing the existing `--browser` flag, deprecating the +`--no-modules` flag, and canonicalizing the output in three modes. All +modes will operate as follows: + +* **Default** - by default `wasm-bindgen` emits output that assumes the wasm + module itself is an ES module. This will naturally work with custom JS + snippets that are themselves ES modules, as they'll just be more modules in + the graph all found in the local output directory. + +* **`--no-modules`** - the `--no-modules` flag to `wasm-bindgen` is incompatible + with ES modules because it's intended to be included via a `