node-fetch: V3 breaks webpack and jest

Please document how to get things working with jest and webpack like formatJS does for react-intl: https://formatjs.io/docs/react-intl#jest

Right now my webpack blows up with:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: <path>/node_modules/node-fetch/src/index.js
require() of ES modules is not supported.
require() of <path>/node_modules/node-fetch/src/index.js from <path>/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 <path>/node_modules/node-fetch/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.node-fetch (<path>/external "node-fetch":1:1)
    at __webpack_require__ (<path>/build/webpack:/app/webpack/bootstrap:24:1)
    at fn (<path>/build/webpack:/app/webpack/runtime/hot module replacement:61:1)
    at Module../src/server/createNodeWretch.js (<path>/build/server.js:7943:68)
    at __webpack_require__ (<path>/build/webpack:/app/webpack/bootstrap:24:1)

and Jest with:

  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    <path>/node_modules/node-fetch/src/index.js:9
    import http from 'http';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      2 | import url from 'url';
      3 | import FormData from 'form-data';
    > 4 | import fetch from 'node-fetch';
        | ^
      5 |

Expected behavior I’ve have always used the import syntax as described in the migration guide… but the guide is incomplete since webpack/jest now blow up when I try upgrading

Your Environment

software version
node-fetch 3.0.0
node 14.16.0
npm 6.14.11
Operating System macOS big sur

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 10
  • Comments: 23 (4 by maintainers)

Commits related to this issue

Most upvoted comments

I’m using ESM and Rollup in my project but Jest still had the same problem with loading the package. Here’s what worked for me:

jest.config.ts:

export default {
  preset: 'ts-jest/presets/js-with-ts-esm',
  transformIgnorePatterns: [
      'node_modules/(?!(fetch-blob|node-fetch)/)'
  ]
}

tsconfig.json:

{
  "compilerOptions": {             
    "allowJs": true,                       
    "esModuleInterop": true
  }
}

Credit goes to: https://stackoverflow.com/a/66480267/8746088

Thanks @xbreaker that worked great until node-fetch updated to v3.1.0 now. The dependencies changed (https://github.com/node-fetch/node-fetch/blob/v3.1.0/package.json#L65) so the transformIgnorePatterns needs updating to include them

transformIgnorePatterns: [
    'node_modules/(?!(data-uri-to-buffer|formdata-polyfill|fetch-blob|node-fetch)/)',
]

There are other issues with node-fetch@3.1 which causes problems with jest, specifically the usage of node:prefixes. I have added another workaround details here: https://github.com/node-fetch/node-fetch/issues/1367#issuecomment-977473094

Hope this helps somebody

Update: A minimal jest.config.ts example tested worked with node-fetch.

  • Can choose experimental, full ESM support
  • Or choose stable, partial but enough ESM support
  • Tested with pnpm, yarn, npm, yarn@berry

https://github.com/loynoir/example-jest-config-esm/blob/master/jest.config.ts

Good luck, bro! 🎉

@heath-freenome The core tech here is using transformer, exclude all esm-only package from transformIgnorePatterns, which means

  • let transformer convert ALL ESM-only package to CJS on-the-fly of jest

Maybe you need babel-jest or jest-babel something, let babel convert those edge packages.

FYI, ESM-only package node-fetch depends on ESM-only fetch-blob. So, you need to at least node-fetch and fetch-blob, add adjust according to error msg.

Good luck!

@heath-freenome Not false alarm. I saw and tested it! 😄 @Thomasan1999 Awesome! As long as node-fetch not using hard core ESM, like top level await, now I can get rid of experimental flag. 😘

PS: I use pnpm, adjust to 'node_modules/(?!node-fetch|fetch-blob)' if you are not.

[tempDir] $ pnpm i -D jest ts-jest ts-node typescript node-fetch && jest && tail -n +1 jest.config.ts tsconfig.json esmDeps.spec.ts

==> jest.config.ts <==
export default {
  preset: 'ts-jest/presets/js-with-ts-esm',
  transformIgnorePatterns: [
      'node_modules/\\.pnpm/(?!node-fetch|fetch-blob)',
  ]
}

==> tsconfig.json <==
{
  "compilerOptions": {             
    "allowJs": true,                       
    "esModuleInterop": true
  }
}

==> esmDeps.spec.ts <==
import nf from 'node-fetch'

describe("esmDeps", () => {
  it("esmDeps", () => {
    console.log(nf)
  });
});

Can’t we just dual-modulize node-fetch?

this are being discussed here: #1227 (read everything before jumping in to discussions)

Documentation for getting this to work with Jest is here:

https://jestjs.io/docs/ecmascript-modules