├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── deno.json ├── index.html ├── lib ├── rs_lib.generated.js └── rs_lib_bg.wasm ├── main.js ├── rs_lib ├── Cargo.toml └── src │ └── lib.rs ├── rust-toolchain.toml └── test.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | rust: 7 | name: example 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 30 10 | 11 | env: 12 | GH_ACTIONS: 1 13 | 14 | steps: 15 | - name: clone repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Install Rust 19 | uses: dsherret/rust-toolchain-file@v1 20 | 21 | - name: Setup rust-cache 22 | uses: Swatinem/rust-cache@v2 23 | with: 24 | save-if: ${{ github.ref == 'refs/heads/main' }} 25 | 26 | - name: Install deno 27 | uses: denoland/setup-deno@v1 28 | with: 29 | deno-version: 1.x 30 | 31 | - name: Check fmt 32 | if: contains(matrix.os, 'ubuntu') 33 | run: | 34 | deno fmt --check 35 | cargo fmt -- --check 36 | 37 | - name: Test 38 | run: | 39 | deno test --allow-read=. 40 | cargo test 41 | 42 | # This is used to ensure that the Rust source doesn't get 43 | # out of sync with the generated wasmbuild output 44 | - name: Check Wasm up-to-date 45 | run: deno task wasmbuild --check 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | edition = "2021" 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bumpalo" 7 | version = "3.13.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "log" 19 | version = "0.4.17" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 22 | dependencies = [ 23 | "cfg-if", 24 | ] 25 | 26 | [[package]] 27 | name = "once_cell" 28 | version = "1.18.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 31 | 32 | [[package]] 33 | name = "proc-macro2" 34 | version = "1.0.66" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 37 | dependencies = [ 38 | "unicode-ident", 39 | ] 40 | 41 | [[package]] 42 | name = "quote" 43 | version = "1.0.31" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" 46 | dependencies = [ 47 | "proc-macro2", 48 | ] 49 | 50 | [[package]] 51 | name = "rs_lib" 52 | version = "0.0.0" 53 | dependencies = [ 54 | "wasm-bindgen", 55 | ] 56 | 57 | [[package]] 58 | name = "syn" 59 | version = "2.0.26" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" 62 | dependencies = [ 63 | "proc-macro2", 64 | "quote", 65 | "unicode-ident", 66 | ] 67 | 68 | [[package]] 69 | name = "unicode-ident" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" 73 | 74 | [[package]] 75 | name = "wasm-bindgen" 76 | version = "0.2.86" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" 79 | dependencies = [ 80 | "cfg-if", 81 | "wasm-bindgen-macro", 82 | ] 83 | 84 | [[package]] 85 | name = "wasm-bindgen-backend" 86 | version = "0.2.86" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" 89 | dependencies = [ 90 | "bumpalo", 91 | "log", 92 | "once_cell", 93 | "proc-macro2", 94 | "quote", 95 | "syn", 96 | "wasm-bindgen-shared", 97 | ] 98 | 99 | [[package]] 100 | name = "wasm-bindgen-macro" 101 | version = "0.2.86" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" 104 | dependencies = [ 105 | "quote", 106 | "wasm-bindgen-macro-support", 107 | ] 108 | 109 | [[package]] 110 | name = "wasm-bindgen-macro-support" 111 | version = "0.2.86" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" 114 | dependencies = [ 115 | "proc-macro2", 116 | "quote", 117 | "syn", 118 | "wasm-bindgen-backend", 119 | "wasm-bindgen-shared", 120 | ] 121 | 122 | [[package]] 123 | name = "wasm-bindgen-shared" 124 | version = "0.2.86" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" 127 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rs_lib", 4 | ] 5 | 6 | [profile.release] 7 | codegen-units = 1 8 | incremental = true 9 | lto = true 10 | opt-level = "z" 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wasmbuild - Example 2 | 3 | This repo contains an example of using 4 | [wasmbuild](https://github.com/denoland/wasmbuild). 5 | 6 | Main files: 7 | 8 | - `rs_lib/src/lib.rs` - Rust source code. 9 | - `lib/rs_lib_bg.wasm` - Generated .wasm file from the Rust code. 10 | - `lib/rs_lib.generated.js` - Generated JS file that can be used to call into 11 | the Wasm file. 12 | - `main.js` - Module that shows an example of using `lib/rs_lib.generated.js`. 13 | 14 | After making edits to any of the Rust code, you can rebuild by running 15 | `deno task wasmbuild`. This will recreate the `lib/rs_lib_bg.wasm` and 16 | `lib/rs_lib.generated.js` files. 17 | 18 | ## Deno 19 | 20 | To run the example in Deno: 21 | 22 | ```sh 23 | deno run --allow-read=. main.js 24 | ``` 25 | 26 | The output should be `3`. 27 | 28 | ## Browser 29 | 30 | To run the example in the browser, start an http server that serves files in the 31 | current directory as follows: 32 | 33 | ```shellsession 34 | $ deno run --allow-net --allow-read https://deno.land/std@0.144.0/http/file_server.ts 35 | Listening on http://localhost:4507/ 36 | ``` 37 | 38 | Then navigate to the link shown and open the browser console. You should see `3` 39 | logged to the console. 40 | 41 | ## Deno Deploy 42 | 43 | See this playground that imports from this repo: 44 | https://dash.deno.com/playground/wasmbuild-example 45 | 46 | Generally with Deno deploy, you would want to use 47 | [Git integration](https://deno.com/deploy/docs/projects#git-integration) to 48 | Deploy this code though. 49 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "wasmbuild": "deno run -A https://deno.land/x/wasmbuild@0.14.1/main.ts" 4 | }, 5 | "exclude": [ 6 | "target" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/rs_lib.generated.js: -------------------------------------------------------------------------------- 1 | // @generated file from wasmbuild -- do not edit 2 | // deno-lint-ignore-file 3 | // deno-fmt-ignore-file 4 | // source-hash: 79f3907d83c7d620c067399271e3857799f32fb6 5 | let wasm; 6 | let cachedInt32Memory0; 7 | let cachedUint8Memory0; 8 | /** 9 | * @param {number} a 10 | * @param {number} b 11 | * @returns {number} 12 | */ 13 | export function add(a, b) { 14 | const ret = wasm.add(a, b); 15 | return ret; 16 | } 17 | 18 | const imports = { 19 | __wbindgen_placeholder__: {}, 20 | }; 21 | 22 | import { Loader } from "https://deno.land/x/wasmbuild@0.14.1/loader.ts"; 23 | import { cacheToLocalDir } from "https://deno.land/x/wasmbuild@0.14.1/cache.ts"; 24 | 25 | const loader = new Loader({ 26 | imports, 27 | cache: cacheToLocalDir, 28 | }); 29 | /** 30 | * Decompression callback 31 | * 32 | * @callback DecompressCallback 33 | * @param {Uint8Array} compressed 34 | * @return {Uint8Array} decompressed 35 | */ 36 | 37 | /** 38 | * Options for instantiating a Wasm instance. 39 | * @typedef {Object} InstantiateOptions 40 | * @property {URL=} url - Optional url to the Wasm file to instantiate. 41 | * @property {DecompressCallback=} decompress - Callback to decompress the 42 | * raw Wasm file bytes before instantiating. 43 | */ 44 | 45 | /** Instantiates an instance of the Wasm module returning its functions. 46 | * @remarks It is safe to call this multiple times and once successfully 47 | * loaded it will always return a reference to the same object. 48 | * @param {InstantiateOptions=} opts 49 | */ 50 | export async function instantiate(opts) { 51 | return (await instantiateWithInstance(opts)).exports; 52 | } 53 | 54 | /** Instantiates an instance of the Wasm module along with its exports. 55 | * @remarks It is safe to call this multiple times and once successfully 56 | * loaded it will always return a reference to the same object. 57 | * @param {InstantiateOptions=} opts 58 | * @returns {Promise<{ 59 | * instance: WebAssembly.Instance; 60 | * exports: { add: typeof add } 61 | * }>} 62 | */ 63 | export async function instantiateWithInstance(opts) { 64 | const { instance } = await loader.load( 65 | opts?.url ?? new URL("rs_lib_bg.wasm", import.meta.url), 66 | opts?.decompress, 67 | ); 68 | wasm = wasm ?? instance.exports; 69 | cachedInt32Memory0 = cachedInt32Memory0 ?? new Int32Array(wasm.memory.buffer); 70 | cachedUint8Memory0 = cachedUint8Memory0 ?? new Uint8Array(wasm.memory.buffer); 71 | return { 72 | instance, 73 | exports: getWasmInstanceExports(), 74 | }; 75 | } 76 | 77 | function getWasmInstanceExports() { 78 | return { add }; 79 | } 80 | 81 | /** Gets if the Wasm module has been instantiated. */ 82 | export function isInstantiated() { 83 | return loader.instance != null; 84 | } 85 | -------------------------------------------------------------------------------- /lib/rs_lib_bg.wasm: -------------------------------------------------------------------------------- 1 | asm`memoryadd 2 |  j o producerslanguageRust processed-byrustc1.71.0 (8ede3aae2 2023-07-12)walrus0.19.0 wasm-bindgen0.2.86,target_features+mutable-globals+sign-ext -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import { instantiate } from "./lib/rs_lib.generated.js"; 2 | 3 | const { add } = await instantiate(); 4 | 5 | console.log(add(1, 2)); 6 | -------------------------------------------------------------------------------- /rs_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rs_lib" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate_type = ["cdylib"] 8 | 9 | [dependencies] 10 | wasm-bindgen = "=0.2.86" 11 | -------------------------------------------------------------------------------- /rs_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen] 4 | pub fn add(a: i32, b: i32) -> i32 { 5 | return a + b; 6 | } 7 | 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn it_works() { 14 | let result = add(1, 2); 15 | assert_eq!(result, 3); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.71.0" 3 | components = ["clippy", "rustfmt"] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.144.0/testing/asserts.ts"; 2 | import { instantiate } from "./lib/rs_lib.generated.js"; 3 | 4 | const { add } = await instantiate(); 5 | 6 | Deno.test("should add numbers", () => { 7 | assertEquals(add(2, 5), 7); 8 | }); 9 | --------------------------------------------------------------------------------