-
-
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 export
s, 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