├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── example ├── Cargo.lock ├── Cargo.toml ├── README.md ├── asmjs.html ├── package.json ├── src │ ├── asm.js │ ├── main.rs │ └── wasm.js ├── wasm.html └── webpack.config.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | *.tgz 37 | 38 | example/bundle 39 | example/target 40 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | *.tgz 37 | 38 | example 39 | .npmignore 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Matt Dziuban 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Webpack loader 2 | 3 | [![npm](https://img.shields.io/npm/v/rust-emscripten-loader.svg)](https://www.npmjs.com/package/rust-emscripten-loader) 4 | 5 | ### Usage 6 | 7 | This is a simple Webpack loader that shells out to cargo to build a Rust project targeting either asm.js or 8 | WebAssembly. See [this post](https://users.rust-lang.org/t/compiling-to-the-web-with-rust-and-emscripten/7627) for 9 | more details on using Rust to target the web. 10 | 11 | To use it, first install the package: 12 | 13 | ```bash 14 | $ npm install --save rust-emscripten-loader 15 | ``` 16 | 17 | then configure the loader in your Webpack config: 18 | 19 | ```js 20 | module.exports = { 21 | // ... 22 | module: { 23 | rules: [ 24 | { test: /\.rs$/, loader: 'rust-emscripten-loader' }, 25 | // ... 26 | ] 27 | } 28 | } 29 | ``` 30 | 31 | Make sure you have the `cargo`, `rustc`, and `emsdk` binaries somewhere in your `PATH`. 32 | 33 | When targeting WebAssembly, the loader will emit two additional files: one `.wasm` and one `.js`. You must specify 34 | the `outName` option to tell the loader how to name these files, and you'll need to load them info your HTML page in 35 | order to run the code. Check out the configuration and example sections below for more details. 36 | 37 | ### Configuration 38 | 39 | The following options can be added to the Webpack loader query: 40 | 41 | | Name | Description | Required | Default | 42 | | ---- | ----------- | -------- | ------- | 43 | | `release` | Whether or not to pass the `--release` flag to cargo | false | false | 44 | | `target` | The output target of the cargo build. Available options are `asmjs` and `wasm`. | false | `asmjs` | 45 | | `outName` | The name of the output file for the `wasm` and `js` files when targeting `wasm` | Only when target is `wasm` | none | 46 | 47 | ### Example 48 | 49 | Check out the [example](example) directory for a simple Hello World example targeting both asm.js and WebAssembly. 50 | -------------------------------------------------------------------------------- /example/Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "rust-emscripten-loader-example" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "webplatform 0.4.2 (git+https://github.com/mrdziuban/rust-webplatform.git)", 6 | ] 7 | 8 | [[package]] 9 | name = "libc" 10 | version = "0.2.21" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "webplatform" 15 | version = "0.4.2" 16 | source = "git+https://github.com/mrdziuban/rust-webplatform.git#9316fd6ef1b143087ce5e41a22f44387c976f2cb" 17 | dependencies = [ 18 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [metadata] 22 | "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" 23 | "checksum webplatform 0.4.2 (git+https://github.com/mrdziuban/rust-webplatform.git)" = "" 24 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Matt Dziuban "] 3 | name = "rust-emscripten-loader-example" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | webplatform = { git = "https://github.com/mrdziuban/rust-webplatform.git" } 8 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Rust Webpack loader example 2 | 3 | This is a simple Hello World example of the [Rust Webpack loader](https://www.npmjs.com/package/rust-emscripten-loader). 4 | 5 | ### Install dependencies 6 | 7 | ```bash 8 | $ npm install 9 | ``` 10 | 11 | ### Compile 12 | 13 | ```bash 14 | $ npm run compile 15 | ``` 16 | 17 | ### Serve 18 | 19 | Run 20 | 21 | ```bash 22 | $ npm run serve 23 | ``` 24 | 25 | then open http://localhost:8080/asmjs.html or http://localhost:8080/wasm.html in your browser. 26 | -------------------------------------------------------------------------------- /example/asmjs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-emscripten-loader-example", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "Matt Dziuban", 6 | "license": "UNLICENSED", 7 | "scripts": { 8 | "compile": "webpack --progress", 9 | "serve": "http-server" 10 | }, 11 | "dependencies": { 12 | "rust-emscripten-loader": "../", 13 | "http-server": "^0.9.0", 14 | "webpack": "^2.2.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/src/asm.js: -------------------------------------------------------------------------------- 1 | require('./main.rs'); 2 | -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate webplatform; 2 | 3 | fn main() { 4 | webplatform::init().element_query("#container").unwrap().html_set("Hello World"); 5 | } 6 | -------------------------------------------------------------------------------- /example/src/wasm.js: -------------------------------------------------------------------------------- 1 | require('rust-emscripten-loader?target=wasm&outName=bundle/out!./main.rs'); 2 | 3 | const script = document.createElement('script'); 4 | script.text = ` 5 | if (typeof WebAssembly === 'object') { 6 | var Module = {}; 7 | var req = new XMLHttpRequest(); 8 | req.open('GET', 'bundle/out.wasm'); 9 | req.responseType = 'arraybuffer'; 10 | req.send(); 11 | 12 | req.onload = function() { 13 | Module.wasmBinary = req.response; 14 | var script = document.createElement('script'); 15 | script.src = 'bundle/out.js'; 16 | document.body.appendChild(script); 17 | }; 18 | } else { 19 | document.getElementById('container').innerHTML = 'Your browser doesn\\'t support WebAssembly!'; 20 | } 21 | `; 22 | document.body.appendChild(script); 23 | -------------------------------------------------------------------------------- /example/wasm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello World 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: { 3 | asmjs: './src/asm.js', 4 | wasm: './src/wasm.js' 5 | }, 6 | output: { filename: 'bundle/[name].js' }, 7 | module: { 8 | rules: [ 9 | { test: /\.rs$/, loader: 'rust-emscripten-loader' } 10 | ] 11 | }, 12 | node: { fs: 'empty' } 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const child_process = require('child_process'); 2 | const fs = require('fs'); 3 | const loaderUtils = require('loader-utils'); 4 | const path = require('path'); 5 | const toml = require('toml'); 6 | 7 | const targets = { 8 | wasm: 'wasm32', 9 | wasm32: 'wasm32', 10 | asm: 'asmjs', 11 | asmjs: 'asmjs' 12 | }; 13 | 14 | module.exports = function(source) { 15 | const callback = this.async(); 16 | const srcDir = path.dirname(path.dirname(this.resourcePath)); 17 | const packageName = toml.parse(fs.readFileSync(path.join(srcDir, 'Cargo.toml'), 'utf8').toString()).package.name; 18 | 19 | const opts = loaderUtils.getOptions(this); 20 | const release = opts ? opts.release : false; 21 | const target = opts && opts.target ? targets[opts.target] : 'asmjs'; 22 | 23 | if (!target) { return callback(new Error(`Unknown target: ${opts.target}`), null); } 24 | if (target === 'wasm32' && !opts.outName) { return callback(new Error('You must specify the `outName` option'), null); } 25 | const rustTarget = `${target}-unknown-emscripten`; 26 | 27 | const outDir = path.join(srcDir, 'target', rustTarget, (release ? 'release' : 'debug')); 28 | const outFile = path.join(outDir, `${packageName}.js`); 29 | const cmd = `cargo build --target=${rustTarget}${release ? ' --release' : ''}`; 30 | 31 | const self = this; 32 | child_process.exec(cmd, { cwd: this.context }, function(error, stdout, stderr) { 33 | if (error) { return callback(error, null); } 34 | 35 | const out = fs.readFileSync(outFile, 'utf8'); 36 | 37 | if (target === 'wasm32') { 38 | const wasmFile = fs.readdirSync(path.join(outDir, 'deps')).find(f => /\.wasm$/.test(f)); 39 | if (!wasmFile) { return callback(new Error('No wasm file found', null)); } 40 | self.emitFile(`${opts.outName}.wasm`, fs.readFileSync(path.join(outDir, 'deps', wasmFile))); 41 | self.emitFile(`${opts.outName}.js`, out); 42 | return callback(null, ''); 43 | } 44 | 45 | callback(null, `(function(){\n${out}\n})();`); 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust-emscripten-loader", 3 | "version": "0.0.1", 4 | "author": "Matt Dziuban", 5 | "description": "Rust loader module for webpack", 6 | "dependencies": { 7 | "loader-utils": "^1.0.3", 8 | "toml": "^2.3.2" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:mrdziuban/rust-emscripten-loader" 13 | }, 14 | "main": "index.js", 15 | "license": "MIT" 16 | } 17 | --------------------------------------------------------------------------------