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
and 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:
#[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:
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!
inline_js
In addition to module = "..."
if you're a macro author you also have the ability to use the inline_js
attribute:
#[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.
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 --target web
and the default bundler output mode are supported. To support --target 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 --target 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.