-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Description
Version
29.7.0
Steps to reproduce
- Clone https://github.com/danfuzz/lactoserv. Note: This is a server-side project, using Node.
- Build and run tests, using this command which will run the two tests that seem to conspire to trigger the problem:
./scripts/ubik run-tests --do=build CertUtil RequestDelay
Expected behavior
Both tests pass.
Actual behavior
With some regularity — but not 100% consistently — the RequestDelay test file will fail to load, with Jest writing this to the console:
● Test suite failed to run
ReferenceError: Export 'emit' is not defined in module
at ../../tester/lib/node_modules/jest-runtime/build/index.js:1567:22
at Array.forEach (<anonymous>)
Additional context
Hi! First and foremost, I'm sorry that I haven't been able to distill this to a tiny example. But I hope I can make up for that lack by providing a lot of information about what's going on:
It turns out that that message is being reported by _importCoreModule() here
jest/packages/jest-runtime/src/index.ts
Line 1771 in 559449e
| private _importCoreModule(moduleName: string, context: VMContext) { |
setExport(), which is on a vm.SyntheticModule (a Node core library class). The complaint amounts to this: When the module is constructed, the name emit was not included in the list of exports, but when the callback came to provide all the exports, the code in fact tried to setExport('emit', ...).
The call to _importCoreModule() is being passed node:process for the moduleName, and indeed the core Node process module does not directly define emit. However, it indirectly defines it by inheriting from EventEmitter. This isn't quite the crux of the problem, though it's related.
The set of pre-declared exports is based on a call to Object.keys() on the module being wrapped, and the calls to setExport() are based on a call to Object.entries() on the same object. These two methods behave identically with respect to inherited properties, skipping them. So, if the module being wrapped didn't change between the two calls, then everything would be okay.
But it turns out that between the call to Object.keys() and the call to Object.entries(), something is directly setting a new emit property directly on the process module, and that's the most-direct cause of the exception, because now Object.entries() is reporting a property that the earlier Object.keys() didn't see. Near as I can tell, the actual value which was set is a transpiled version of the process.emit() wrapper in source-map-support, but I haven't yet been able to catch the system in the act of setting it.
In the runs where the problem doesn't appear, I think process.emit gets set sometime after testing has started but simply (and fortuitously) not when a test is in the middle of loading/wrapping node:process specifically. The notable "feature" of the first test in the repro case (CertUtil) is that it runs pretty slowly, which I'm guessing is significant; it runs slowly but doesn't tend to fail. I'm not sure why the second one (RequestDelay) ends up being the consistent victim.
I figured out that I can work around the problem by explicitly setting required.emit = required.emit in _importCoreModule() when it's given node:process to import. However, I suspect that the right solution is to make sure that the patching of the real global process object happens before any test suites are even loaded.
I should add: The failure often — but not always — gets accompanied by a second ReferenceError:
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From code/node_modules/@this/webapp-builtins/tests/RequestDelay.test.js.
Note that in this case the test suite in question never actually ran, and I believe that this additional error is happening because the main problem left the test suite in a bit of an inconsistent state.
I hope this helps!
PS: Before I filed this report, I tried asking on the suggested Discord server, but nobody responded.
Environment
System:
OS: macOS 14.3.1
CPU: (8) arm64 Apple M1
Binaries:
Node: 22.1.0 - /opt/homebrew/bin/node
Yarn: 1.22.22 - /opt/homebrew/bin/yarn
npm: 10.7.0 - /opt/homebrew/bin/npm