-
Notifications
You must be signed in to change notification settings - Fork 143
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
ESM support #337
base: master
Are you sure you want to change the base?
ESM support #337
Conversation
So, the intent would be to have a JS-only build, that would be slow, but would hopefully be sometimes smaller (after tree shaking) that the webassembly module. Did I get that right? I'm not too familiar with this, but my quick experiments were not very successful, and tree shaking didn't do much with emscripten-generated code. Hopefully you'll be more successful! Bun/node/deno/etc. support WebAssembly, so a WebAssembly-only build that only contains the required functions (using e.g. https://www.npmjs.com/package/@webassemblyjs/dce) would be the best way to reduce the file size. But does that really matter for server runtimes? |
With your example (and after having compiled libsodium with $ bun build --target bun index.ts --minify --outfile minified.js
minified.js 546.43 KB Unfortunately no significant size reduction. |
With
No size reduction, but that's expected. |
When targeting the browser, this is worse: WebAssembly build: $ bun build --target browser index.ts --minify --outfile minified.js
minified.js 1016.09 KB
import sodium from "libsodium-esm-wrappers"
await sodium.ready |
I may have confused things by mentioning file size. Some people at #263 seem to be hoping ESM support will reduce file size. For the moment, I just needed ESM support so I could properly import libsodium.js in Deno. There's a libsodium.js wrapper for Deno at https://github.com/denosaurs/sodium, but when I used that in my tests, it caused stack overflow exceptions, I believe because Deno only likes ES Modules (https://docs.deno.com/runtime/manual/node/migrate#module-imports-and-exports). So, this first pass was just to get the library built as an ES Module. However, I think I do see how to get some tree-shaking benefit. The wrapper file is dynamically assigning its wrapper functions to the exports object, which I suspect blocks the minifier from trying to strip any unused code. I'll try directly exporting those functions and see what happens. |
In the latest code, I'm directly exporting the wrapper functions by prefixing each import consts, * as sodium from "./dist/modules/libsodium-esm-wrappers"
await sodium.ready
console.log(consts.crypto_hash_BYTES)
console.log(sodium.crypto_secretbox_keygen()) To get emccLibsodium "${PREFIX}/lib/libsodium.esm.js" -O3 -s WASM=1 -s ENVIRONMENT=web -s EXPORT_ES6=1 -s EVAL_CTORS=1 -s INITIAL_MEMORY=${WASM_INITIAL_MEMORY} Here's what I get with the browser build.
Targeting bun:
|
The file size savings is probably from the bundler eliminating unused wrapper functions. Like you mentioned, really minimizing the size would require eliminating unused functions from the WASM output itself. The ultimate form of that would be something like compiling a separate WASM asset for each symbol, then individually exposing them. That would entail making everything async, which would also allow for loading their WASM as binary rather than embedding it as base64 in JS code. Clearly that would be very invasive. I won't do any of that here. But is this current change to expose the lib as an ES Module something you'd like to include in this project, @jedisct1? (Either with or without the latest couple commits directly exporting the wrapper funcs.) I'm happy to do some more tidying up if so. |
I realized that I was already doing a top-level await, so there was no reason not to just do the module initialization immediately after that, rather than waiting for the consumer to await the import * as sodium from "./dist/modules/libsodium-esm-wrappers.js"
console.log(sodium.crypto_hash_BYTES)
console.log(sodium.crypto_secretbox_keygen()) TIL this use case of WASM initialization is one of the motivations for top-level await (https://github.com/tc39/proposal-top-level-await/blob/HEAD/README.md#webassembly-modules). |
Just tried this, nice work. ESM and the top level await are both useful for us |
Here's a proof-of-concept of an ES Modules build.
I haven't tested if this achieves any sort of file-size reduction through tree-shaking. I'm just doing this to facilitate use in the Deno runtime (https://deno.com).
To test, with Deno installed:
emscripten.sh
from ESM build libsodium#1382 tolibsodium/dist-build/emscripten.sh
make esm
deno
> import sodium from "./dist/modules/libsodium-esm-wrappers.js"
> await sodium.ready
> sodium.crypto_secretbox_keygen()
Any feedback on the approach so far? If not, I'll just plan to add Make targets for the sumo builds and get the automated tests running against this (I think there are some—haven't looked deeply).