Skip to content

[Bug]: ReferenceError: Export 'emit' is not defined in module from Jest internals during suite setup. #15077

@danfuzz

Description

@danfuzz

Version

29.7.0

Steps to reproduce

  1. Clone https://github.com/danfuzz/lactoserv. Note: This is a server-side project, using Node.
  2. 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

private _importCoreModule(moduleName: string, context: VMContext) {
. Specifically, it's the call to 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions