jasmine / jasmine-npm

A jasmine runner for node projects.
MIT License
376 stars 145 forks source link

Jasmine loads test dependency as CommonJS module instead of ESM #191

Closed mdreier-sap closed 2 years ago

mdreier-sap commented 2 years ago

I have a project which uses ES modules and tests using Jasmine. In my tests, I am using fetch-mock to mock web requests. When running the tests, I get the following error:

PS C:\dev\git\jasmine-esm-bug> npm test

> jasmine-esm-bug@1.0.0 test
> node ./spec/support/jasmine.js

C:\dev\git\jasmine-esm-bug\node_modules\fetch-mock\cjs\server.js:9
        fetch = require('node-fetch');
         ^

Error [ERR_REQUIRE_ESM]: require() of ES Module C:\dev\git\jasmine-esm-bug\node_modules\node-fetch\src\index.js from C:\dev\git\jasmine-esm-bug\node_modules\fetch-mock\cjs\server.js not supported.
Instead change the require of index.js in C:\dev\git\jasmine-esm-bug\node_modules\fetch-mock\cjs\server.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (C:\dev\git\jasmine-esm-bug\node_modules\fetch-mock\cjs\server.js:9:10)
    at async Promise.all (index 0)
    at async Jasmine._loadFiles (C:\dev\git\jasmine-esm-bug\node_modules\jasmine\lib\jasmine.js:215:5)
    at async Jasmine.loadSpecs (C:\dev\git\jasmine-esm-bug\node_modules\jasmine\lib\jasmine.js:206:3)
    at async Jasmine.execute (C:\dev\git\jasmine-esm-bug\node_modules\jasmine\lib\jasmine.js:502:3) {
  code: 'ERR_REQUIRE_ESM'
}

It seems that instead of loading fetch-mock as an ES module, it is loaded as a CommonJS module (it contains both). For running the binary, I could understand that (since Jasmine does not set "type": "module" in package.json). However I would expect it to work when running from my own script. And of course best case also running the binary should work directly.

Versions:

To help with analysis, I created a small project which reproduces the issue: https://github.com/mdreier-work/jasmine-esm-bug

You can clone and npm install, then reproduce the issue with either of the following commands:

sgravrock commented 2 years ago

Thanks for the minimal reproduction. That was very helpful.

I think this is a problem with fetch-mock. The first things that jumped out at me are that it's your code rather than Jasmine that imports fetch-mock, and that your code looks fine. I was able to reproduce the problem with a simple test file I added to your repo, without using Jasmine at all:

% cat test.js 
import 'fetch-mock';
console.log('ok');

% node test.js 
internal/process/esm_loader.js:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /private/tmp/jasmine-esm-bug/node_modules/node-fetch/src/index.js
require() of ES modules is not supported.
require() of /private/tmp/jasmine-esm-bug/node_modules/node-fetch/src/index.js from /private/tmp/jasmine-esm-bug/node_modules/fetch-mock/cjs/server.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /private/tmp/jasmine-esm-bug/node_modules/node-fetch/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1015:13)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/private/tmp/jasmine-esm-bug/node_modules/fetch-mock/cjs/server.js:10:10)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
    at Module.load (internal/modules/cjs/loader.js:863:32)
    at Function.Module._load (internal/modules/cjs/loader.js:708:14) {
  code: 'ERR_REQUIRE_ESM'
}

I was also able to reproduce the error in a pure CommonJS project.

I think there are two things going on here:

  1. Whatever fetch-mock is doing to support separate ESM and CommonJS entry points isn't working. The CommonJS entry point loads even in an ESM environment.
  2. The direct cause of the error is that fetch-mock isn't compatible with the version of node-fetch that you're using.

It looks like you'll have to downgrade node-fetch to a compatible version if you want to use fetch-mock.

mdreier-sap commented 2 years ago

Good catch on the unsupported version, I totally missed that.

Downgrading node-fetch to version 2.6.6 (and @types/node-fetch to 2.5.12) solves the issue. I will therefor close the issue (and hope that fetch-mock will add support for newer node-fetch versions sometime in the future).