Skip to content
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

Wasm es6 modules fail to load with Jest, but work in Node #11011

Closed
modulitos opened this issue Jan 16, 2021 · 24 comments · Fixed by #13505
Closed

Wasm es6 modules fail to load with Jest, but work in Node #11011

modulitos opened this issue Jan 16, 2021 · 24 comments · Fixed by #13505

Comments

@modulitos
Copy link

🐛 Bug Report

I'm able to load an es6 module with Node, but I'm unable to load the same module when running with Jest. When I try loading the module with Jest, I get the following error:

node --experimental-modules --experimental-wasm-modules --experimental-vm-modules node_modules/.bin/jest

(node:72954) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use node --trace-warnings ... to show where the warning was created)
FAIL tests/test_jest.test.mjs
● Test suite failed to run

/Users/me/projects/bin_packing/jest-wasm/node_modules/bin_packer_3d/bin_packer_3d_wasm_bg.wasm:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){


SyntaxError: Invalid or unexpected token

  at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)

To Reproduce

Steps to reproduce the behavior:

  1. Clone this repo: https://github.com/modulitos/jest-wasm-esm-example
  2. run npm run test-jest

Note that running npm run test-node loads the module successfully in Node. So why does it fail with Jest?

Expected behavior

The module loads when running npm run test-jest.

Link to repl or repo (highly encouraged)

https://github.com/modulitos/jest-wasm-esm-example

envinfo

❯ npx envinfo --preset jest
npx: installed 1 in 1.358s

System:
OS: macOS 10.15.7
CPU: (4) x64 Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
Binaries:
Node: 14.15.4 - ~/.asdf/installs/nodejs/14.15.4/bin/node
npm: 6.14.10 - ~/.asdf/installs/nodejs/14.15.4/bin/npm
npmPackages:
jest: ^26.6.3 => 26.6.3


@ahnpnl
Copy link
Contributor

ahnpnl commented Jan 18, 2021

The error is about Jest module resolution tries to resolve bin_packer_3d as bin_packer_3d_wasm_bg.wasm while NodeJS module resolution resolves as bin_packer_3d_wasm_bg.js. Adjust moduleNameMapper to map bin_packer_3d to bin_packer_3d_wasm_bg.js should fix the error.

However, after correcting Jest module resolution, I ran into another error

 RUNS  __tests__/test_jest.test.mjs
Exception in PromiseRejectCallback:
/Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:586
    return module;

RangeError: Maximum call stack size exceeded
    at loadEsmModule (/Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:586:19)
    at linkModules (/Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:616:19)
    at /Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:572:18
    at node:internal/vm/module:320:30
    at SourceTextModule.<computed> (node:internal/vm/module:319:36)
    at link (node:internal/vm/module:197:22)
    at loadEsmModule (/Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:571:12)
    at linkModules (/Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:616:19)
    at /Users/ahn/jest-wasm-esm-example/node_modules/jest-runtime/build/index.js:572:18

which Idk how to solve.

If simply solving your original error, moduleNameMapper is enough.

@modulitos
Copy link
Author

Thank you for the tip! I updated the repo with the correct moduleNameMapper config, and can confirm that I'm getting the same RangeError: Maximum call stack size exceeded problem.

Any suggestions for how I can troubleshoot this issue is appreciated 🙏

@ahnpnl
Copy link
Contributor

ahnpnl commented Feb 6, 2021

Just now I noticed that it’s a combination of wasm + ESM. Maybe you should try jest@next first and if not working then go to the ESM tracking issue to mention this issue there.

@modulitos
Copy link
Author

Ah, thanks again for the insight. Can you link me to the "ESM tracking issue"? I'm not sure what you're referring to, but happy to mention this issue there.

I updated to jest@next (updated the repo linked above as well), but now I'm getting this error:

TypeError: wasm.binpacker_packing_algorithm is not a function

It's a different error, but it seems like we're getting closer! But I'm still not sure how to debug this 🤔

@ahnpnl
Copy link
Contributor

ahnpnl commented Feb 10, 2021

#9430

@SimenB
Copy link
Member

SimenB commented Feb 10, 2021

Jest does not support WASM at this time. I'm not sure how support would be added. Is it enough to do WebAssembly.compile on the source code (sorta like the example here)?

I have zero idea how WASM works, so I can't really do much here. Are there any JS globals available in WASM? If not I guess just executing the file and returning the result should be enough?

@SimenB
Copy link
Member

SimenB commented Feb 10, 2021

I guess reading https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration is a start

(I can almost guarantee I won't have the time or energy to work on this for some time, so if anybody wants to pick it up that'd be great!)

@modulitos
Copy link
Author

Thanks everyone for the clarification! This makes much more sense now.

I didn't realize that WASM isn't supported in Jest - I was a little naive in thinking that if I can load a module in Node, then I should be able to load it in Jest.

I appreciate the help 🙏

@SimenB
Copy link
Member

SimenB commented Feb 12, 2021

I was a little naive in thinking that if I can load a module in Node, then I should be able to load it in Jest.

That's how it's supposed to work, but node has added a bunch of stuff for ESM (including WASM) in the last couple of years we haven't been able to follow (especially since most is still behind flags on the Node side)

@FlatAssembler
Copy link

Are there any JS globals available in WASM?

No, JavaScript global variables are not accessible from WebAssembly by default. The earliest versions of WebAssembly did not have global variables at all. Modern WebAssembly does have global variables, and they can be shared between JavaScript and WebAssembly.

@gthb
Copy link

gthb commented Oct 30, 2021

Is anyone currently working on this?

@knightcode
Copy link

How have our tests been working with the WASM in virgil-crypto for months and just now started to fail?

@kachkaev
Copy link
Contributor

kachkaev commented Oct 13, 2022

I managed to tweak jest-runtime and this allowed me to import *.wasm from native ESM modules 🎉

This is obviously not a production-grade solution, but I hope it can helpful to someone.

@SimenB
Copy link
Member

SimenB commented Oct 13, 2022

@kachkaev happy to take a PR if you can add some tests 🙂 some support is way better than no support

@kachkaev
Copy link
Contributor

kachkaev commented Oct 13, 2022

My knowledge of WebAssembly and SyntheticModule APIs is extremely limited, so I’m not sure if my solution is good enough even for an MVP. It’d be great if someone a bit more experienced could sense-check the patch first.

My _importWasmModule function maps WebAssembly.Module imports and exports to an instance of SyntheticModule quite naively, based on just a couple of wasm file examples I’ve seen. It’s possible that I’m not covering some common cases, but I can’t tell that because I don’t know where to find more wasm files 😅

@SimenB
Copy link
Member

SimenB commented Oct 13, 2022

Fair enough. It looks like you pretty much just mapped https://github.com/nodejs/node/blob/7cdf745fddade8b7fc644f44121fbba041d3ac4c/lib/internal/modules/esm/translators.js#L322-L349 to SyntheticModule, so seems fine to me. Let's try and if people hit cases that don't work adjust?

@kachkaev
Copy link
Contributor

kachkaev commented Oct 13, 2022

Interesting! I did not see this translator so was crafting the patch in the ‘blind watchmaker’ mode 😅

Not sure I’ll have capacity to create a PR in my work hours, so unfortunately I can start it only around 22-23 Oct (the earliest). I’d be happy if someone else makes an attempt before me – feel free to recycle the demo repo.

@kachkaev
Copy link
Contributor

kachkaev commented Oct 24, 2022

@SimenB I’ve started #13505, which essentially copies my experimental patch over to jest-runtime. I have to wrap up for today, but happy to continue the PR later this week if things go as planned. Any feedback welcome!

@SimenB
Copy link
Member

SimenB commented Nov 7, 2022

https://github.com/facebook/jest/releases/tag/v29.3.0

@kachkaev
Copy link
Contributor

kachkaev commented Nov 7, 2022

Nice! Just tested 29.3.0 against the same demo project and it worked without a local patch! 🎉

@knightcode
Copy link

I'm still having a failure with virgil-crypto on node 16, which needs access to a hardware randomness (I assume... it complains about an "entropy source" failing). I'm not sure how it goes about accessing it, but it works when jest isn't involved.

@kachkaev
Copy link
Contributor

kachkaev commented Nov 9, 2022

There is a bug in the initial implementation, to do with imports into the Wasm module. Details here: #13505 (comment)

If you see

TypeError: modulePath.endsWith is not a function
  at isWasm (../../../node_modules/jest-runtime/build/index.js:210:41)

you can use this patch for now:

diff --git a/node_modules/jest-runtime/build/index.js b/node_modules/jest-runtime/build/index.js
index 9906023..d8458dd 100644
--- a/node_modules/jest-runtime/build/index.js
+++ b/node_modules/jest-runtime/build/index.js
@@ -1509,7 +1509,7 @@ class Runtime {
     const moduleLookup = {};
     for (const {module} of imports) {
       if (moduleLookup[module] === undefined) {
-        moduleLookup[module] = await this.loadEsmModule(
+        moduleLookup[module] = await this.linkAndEvaluateModule(
           await this.resolveModule(module, identifier, context)
         );
       }

UPD: Fixed via #13608

@SimenB
Copy link
Member

SimenB commented Nov 10, 2022

@knightcode can you open up a new issue?

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants