├── .gitignore ├── LICENSE ├── README.md ├── example ├── package.json ├── src │ ├── fib.c │ └── index.js └── webpack.config.js ├── loader.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | emsdk/ 4 | dist/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sven Sauleau 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 | # @wasm-tool/emscripten 2 | 3 | > Emscripten loader for webpack 4 | 5 | ## Installation 6 | 7 | ```sh 8 | npm i -D @wasm-tool/emscripten 9 | ``` 10 | 11 | ## Usage: webpack 12 | 13 | Add the loader and mock the `fs` module in your webpack configuration: 14 | 15 | ```js 16 | module.exports = { 17 | // ... 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.c$/, 22 | type: "javascript/auto", 23 | loader: "@wasm-tool/emscripten" 24 | } 25 | ] 26 | }, 27 | node: { 28 | fs: "empty" 29 | }, 30 | // ... 31 | }; 32 | ``` 33 | 34 | We need to set the `type` to JavaScript to bypass webpack's wasm support (as a workaround, for now). Which will also prevent the loading to work correclty in non-web environements. 35 | 36 | You can then directly import c files: 37 | 38 | ```js 39 | import("./add.c").then(exports => ...); 40 | ``` 41 | 42 | `emcc` must be available in your `$PATH`, see http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. 43 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@wasm-tool/emscripten": "0.0.1", 4 | "html-webpack-plugin": "^3.2.0", 5 | "https-server": "^0.1.2", 6 | "webpack": "^4.12.0", 7 | "webpack-cli": "^3.3.11" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/src/fib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | EMSCRIPTEN_KEEPALIVE 4 | int fib(int n) { 5 | int l1 = 0; 6 | int l2 = 1; 7 | 8 | int b; 9 | 10 | for (int i = 0; i < n; i++) { 11 | b = l1 + l2; 12 | 13 | l2 = l1; 14 | l1 = b; 15 | } 16 | 17 | return b; 18 | } 19 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import("./fib.c").then(({_fib}) => { 2 | document.body.innerHTML = "fib(10) = " + _fib(10); 3 | }); 4 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 3 | 4 | module.exports = { 5 | entry: "./src/index.js", 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.c$/, 10 | type: "javascript/auto", 11 | loader: "@wasm-tool/emscripten" 12 | } 13 | ] 14 | }, 15 | node: { 16 | fs: "empty" 17 | }, 18 | plugins: [new HtmlWebpackPlugin()] 19 | }; 20 | -------------------------------------------------------------------------------- /loader.js: -------------------------------------------------------------------------------- 1 | const { execFileSync } = require("child_process"); 2 | const { join, basename } = require("path"); 3 | const { 4 | existsSync, 5 | mkdirSync, 6 | writeFileSync, 7 | readFileSync 8 | } = require("fs"); 9 | const { tmpdir } = require("os"); 10 | const loaderUtils = require("loader-utils"); 11 | 12 | const emcc = "emcc"; 13 | 14 | function createTempDir() { 15 | const path = join(tmpdir(), "wasm-tool-emscripten"); 16 | if (!existsSync(path)) { 17 | mkdirSync(path); 18 | } 19 | return path; 20 | } 21 | 22 | function runEmcc(cwd, main) { 23 | const args = [main, "-O3", "-s", "WASM=1", "-s", "MODULARIZE=1"]; 24 | const options = { cwd }; 25 | return execFileSync(emcc, args, options); 26 | } 27 | 28 | module.exports = function(source) { 29 | const tmpdir = createTempDir(); 30 | const filename = basename(this.resource); 31 | 32 | writeFileSync(join(tmpdir, filename), source); 33 | runEmcc(tmpdir, filename); 34 | 35 | const options = loaderUtils.getOptions(this) || {}; 36 | 37 | const publicPath = 38 | typeof options.publicPath === "string" 39 | ? options.publicPath === "" || options.publicPath.endsWith("/") 40 | ? options.publicPath 41 | : `${options.publicPath}/` 42 | : typeof options.publicPath === "function" 43 | ? options.publicPath(this.resourcePath, this.rootContext) 44 | : this._compilation.outputOptions.publicPath || "dist"; 45 | 46 | if (!existsSync(publicPath)) { 47 | mkdirSync(publicPath); 48 | } 49 | 50 | const wasmPath = join(publicPath, filename + ".wasm"); 51 | const wasm = readFileSync(join(tmpdir, "a.out.wasm")); 52 | writeFileSync(wasmPath, wasm); 53 | 54 | return ` 55 | import Load from "${join(tmpdir, "a.out.js")}"; 56 | 57 | export async function then(cb) { 58 | const m = Load({ 59 | locateFile(path) { 60 | if(path.endsWith('.wasm')) { 61 | return "${filename + ".wasm"}"; 62 | } 63 | return path; 64 | } 65 | }); 66 | 67 | m.onRuntimeInitialized = () => { 68 | delete m.then; 69 | cb(m); 70 | } 71 | } 72 | `; 73 | }; 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wasm-tool/emscripten", 3 | "version": "1.0.0", 4 | "description": "Emscripten loader for webpack", 5 | "main": "loader.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/wasm-tool/emscripten.git" 12 | }, 13 | "keywords": [ 14 | "emscripten", 15 | "webpack", 16 | "c", 17 | "c++", 18 | "webassembly", 19 | "loader" 20 | ], 21 | "author": "Sven Sauleau ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/wasm-tool/emscripten/issues" 25 | }, 26 | "homepage": "https://github.com/wasm-tool/emscripten#readme", 27 | "devDependencies": { 28 | "loader-utils": "^1.2.3" 29 | } 30 | } 31 | --------------------------------------------------------------------------------