-
-
Notifications
You must be signed in to change notification settings - Fork 8.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Webpack doesn’t work well with wasm modules created with Emscripten #7352
Comments
Here's my suggestion: #7264 (comment) We don't need to explicitly support emscripten, that would be generic enough. Note that wasn-bindgen already does that. |
It would be great to see a wasm-bindgen creates two files That works great but exposes all internal functions too. I think a 3 file output would be better:
Even better if repeated runtime code could be moved into a npm package: This way the wasm could import it directly from this package. This could get handy if you got many wasm modules in an application. |
For ref emscripten-core/emscripten#6483 Also note that the same works for a module on npm. I think it would be more practical. |
The workaround is described in webpack/webpack#7352 Note that now the webserver cannot load our wasm: "TypeError: _myelin_visualization_bg__WEBPACK_IMPORTED_MODULE_0__.init is not a function" This might be caused by a transcient dependency on stdweb: https://github.com/sebcrozet/nphysics/blob/master/build/nphysics2d/Cargo.toml#L31 Which is incompatible with wasm-bindgen: koute/stdweb#263
- this is what I was having trouble with when I first created this repo - c.f. kripken/ammo.js#109 (comment) , webpack/webpack#7352 , and emscripten-core/emscripten#6542 - can't just upgrade to a new ammo as that might very well break physijs, so just make the Node environment check false - in this case I changed `var qa="object"===typeof process,` to `var qa=false,` in the minified code - could also remove the `if(qa){`... parts now, though that'll probably get re-minified too and nbd
- this is what I was having trouble with when I first created this repo - c.f. kripken/ammo.js#109 (comment) , webpack/webpack#7352 , and emscripten-core/emscripten#6542 - can't just upgrade to a new ammo as that might very well break physijs, so just make the Node environment check false - in this case I changed `var qa="object"===typeof process,` to `var qa=false,` in the minified code - and also remove thed `if(qa){`...`}` parts - this was the part that had `require` statements, so now webpack etc should be able to parse it without a problem - there was a `require("fs")` and a `require("path")` in there - alternatively, could build and replace stuff with webpack, but I'd need to provide that anyway for auto-config - downside is it might no longer work on Node, but not the target audience so w/e
- use physijs-webpack, a fork I created a few years ago and finally took some time to debug and get working - use via github since someone took the NPM name a few years ago and referenced my repo in it even though it never worked until today... - maybe will update once I get that resolved - now we don't have to vendor in all those scripts anymore and don't need to manually configure Physijs either! - remove the vendored physijs and three scripts - had to debug and modify ammo to get that version working with webpack / bundlers in general - newer versions of Emscripten can target specific envs - c.f. kripken/ammo.js#109 (comment) , webpack/webpack#7352 , and emscripten-core/emscripten#6542 - and then made the worker config more flexible - add worker-loader as a devDep per physijs-webpack instructions - fix publicPath location as this is now actually used - the worker is loaded based on the publicPath - add three-window-resize and three-trackballcontrols as deps as well - since three's examples/js/ folder doesn't quite work with bundlers - c.f. mrdoob/three.js#9562 - maybe if I made some modifications, updated to newer Three revision, and used imports-loader it might work 🤷 TBD - upgrade physijs to latest and Three from r60 to r73 - latest physijs uses r73, so remain consistent - also physijs-webpack has a peerDep for that specific version - three-trackballcontrols@0.0.3 also requires r73 as dep - had to refactor a bit due to upgrade - WebGLRenderer() -> WebGLRenderer({alpha: true}) - the canvas now defaults to black without this, which was extremely disorienting - shadowMapEnabled -> shadowMap.enabled - CubeGeometry -> BoxGeometry - quaternion._euler -> rotation - I probably could've just used this before, couldn't have I...? . . - TODO: improve webpack perf (CPU + Memory) and build speed - this change slows down initial build times quite a bit (~20s), since Three, Physijs, and Ammo are all parsed by Webpack now - will want to update webpack to get a dev-server (wepback-serve) running - webpack itself is faster in later versions as well - and perhaps add HardSource for caching otherwise or split out vendored stuff into a DLL - probably can't update until decaffeinate'd since I believe the loaders used here are no longer maintained :/ - and then would need to figure out the literate programming and its sourcemaps - will want to output a separate vendor bundle per best practice - may also want to output HTML via webpack too while at it - the templates don't do any templating so would be nbd - and would allow for hashing and therefore cache-busting - no need to manually clean cache then - and prod build / uglification is even _slower_ (~80s) - may want to exclude some files from Uglify - i.e. Ammo and use three.min so it can be excluded too
Trying to get webpack 4 to work by hacking on the code until it stops complaining. Done with input from: webpack/webpack#8412 webpack/webpack#7352
I also came across this problem when using a JS library I made, that uses WebAssembly, in a React web project. I solved it by adding some hacky After that I combine the generated JS glue code with the JS API interface code bundled with Rollup. The entry of this can be found in Then finally when using the library I need to provide it an {
test: /\.wasm$/,
type: 'javascript/auto',
loader: 'file-loader',
} Some implementation references: So my conclusion being... // node.js
import { initializer } from './wasm-glue-code'
import { fileToArrayBuffer } from 'emscripten-helpers'
const myLibrary = initializer(fileToArrayBuffer('./wasm-file.wasm'))
// web
import { initializer } from './wasm-glue-code'
const myLibrary = initializer(fetch('./wasm-file.wasm')) Hope this was helpful in some way. |
* currently webpack throws error because of missing 'env' * see: webpack/webpack#7352
The default webpack options don't play with wasm32-emscripten. Refer to the open issue in: webpack/webpack#7352
You can specify |
The default webpack options don't play with wasm32-emscripten. Refer to the open issue in: webpack/webpack#7352
For Webpack 5, disabling the node-specific stuff is relatively easy: resolve: {
fallback: {
crypto: false,
fs: false,
path: false
},
}, To include the wasm files, one solution is just to use copy-webpack-plugin, which is annoying since it makes it non-transparent for people to use your module, but then again, they're already adding the magic const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{ from: "node_modules/MYPACKAGE/MYPACKAGE.wasm*",
to: "[name][ext]" },
],
}),
],
}; It would sure be swell if someone could write a webpack plugin or loader that would automate this so people could, like, just use Emscripten-compiled modules transparently. It seems like it should be relatively easy to recognize an Emscripten .js/.wasm file pair? |
@dhdaines Though this seemed to work, it printed some - to me - unintelligible, and unsilenceable warning on every build, so ultimately I used Angular's configuration options to bring in the JS file as a regular script and the WASM as a resource/asset. |
Hi guys, I felt extremely lost while I was digging into this issue, so after I got everything working, I made a example repo containing detailed walkthroughs:
Hope it helps someone. |
Note also that you still need to disable Webpack's "mocking" of __filename, at the very least, or you will not be able to load your WASM unless it is in the root directory (Webpack sets __filename = "/index.js" and then Emscripten' s preamble takes this as the path of the current script in order to find the WASM, which is, obviously, wrong): node: {
global: false,
__filename: false,
__dirname: false,
}, |
Can confirm that this works now, and it produces something like this: https://github.com/dhdaines/soundswallower-demo/tree/main/docs ...as long as you don't use Angular, because Angular doesn't include whatever magical Webpack configuration makes Webpack use the appropriate loader for the WASM file. If you try to fix this by treating the WASM as a normal asset, Angular will use Webpack's default broken behaviour of munging the So, you can use a custom Webpack config to disable this behaviour, but this breaks testing among other things, and probably means you can't target non-ES6 browsers. (see https://stackoverflow.com/questions/74038161/angular-cli-and-loading-assets-with-import-meta-url-causing-not-allowed-to-load) Use ES6 modules, they said. It'll be great, they said. |
Just to follow up on this. The problem is actually Angular and its mysterious built-in webpack configuration, which disables The correct (as far as I can tell) workaround is to enable |
@dhdaines Thank you for the deep dive! I recently tried to reproduce my own previously reported success and failed. Now I can try again... |
Feature request
What is the current behavior?
The modularized JS emitted by Emscripten registers a global with a given name that loads the wasm file on invocation, initializes the wasm runtime and returns a
Module
.Making it work with Webpack is quite hard as there seems to be interference with Webpack 4 defaults.
This is the
webpack.config.js
that I came up with:(Here is a minimal test project in a gist that you can clone and build with
npm start
. Docker required!)edit:
In the meantime, @sokra informed that that I can simplify the configuration a bit (and make it less like a sledgehammer):
Unexpected things I had to do
defaultRules
as otherwise some sort of default rule will run in addition to the ones I specified and making webpack error “Module parse failed: magic header not detected” (try it!)file-loader
for the wasm file as otherwise webpack tries to resolve the names of the wasm module’s import object likeenv
, which are provided by the JS file.locateFile()
function as webpack changes the file (and potentially path) of the wasm file and Emscripten hardcodes that name (not visible here but in the gist)I am not sure what the right course of action here is, but considering that most wasm projects are going to be built with Emscripten, I feel like it’s worth making it easier.
Happy to answer Qs to give y’all a clearer picture.
What is the expected behavior?
Ideally, Webpack would recognize the typical Emscripten JS files and automatically bundle the accomodating wasm module and make paths work.
Other relevant information:
webpack version: 4.8.3
Node.js version: 10
Operating System: Mac OS 10.13.4
Additional tools:
The text was updated successfully, but these errors were encountered: