Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Jest ESM (ts-jest) can't find import from CJS module #12120

Closed
themaskedavenger opened this issue Dec 4, 2021 · 15 comments
Closed

[Bug]: Jest ESM (ts-jest) can't find import from CJS module #12120

themaskedavenger opened this issue Dec 4, 2021 · 15 comments

Comments

@themaskedavenger
Copy link

Version

27.4.3

Steps to reproduce

Minimal repro: https://github.com/themaskedavenger/jest-esm-apollo-client

run yarn and then yarn test:jest

also to see the original issue replace the imports (lines 4-10) with:

import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useQuery,
  gql
} from '@apollo/client';

Expected behavior

Would expect to have Jest be able to properly import those components and see the tests run (and hopefully pass!).

Actual behavior

When using these imports:

import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useQuery,
  gql
} from '@apollo/client';

Each of the imports gives an error like SyntaxError: The requested module '@apollo/client' does not provide an export named 'ApolloProvider'. Looking at the @apollo/client's package.json, the "main" key is set to "./main.cjs", which is re-exporting things in cjs style:

var core = require('./core');
var react = require('./react');

for (var k in core) {
	if (k !== 'default' && !exports.hasOwnProperty(k)) exports[k] = core[k];
}
for (var k in react) {
	if (k !== 'default' && !exports.hasOwnProperty(k)) exports[k] = react[k];
}

For some reason I guess this cjs is not recognized with the recommended jest config. To get around it, can directly import the specific exports directly from esm files in the @apollo/client package like this:

import {
  ApolloClient,
  InMemoryCache,
} from '@apollo/client/core';
import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider';
import { useQuery } from '@apollo/client/react/hooks';
import { gql } from 'graphql-tag';

Then there's a new error for a dependency:

/path/to/project/node_modules/ts-invariant/process/index.js:15
    export function install() {
    ^^^^^^

    SyntaxError: Unexpected token 'export'

Adding transformIgnorePatterns to jest config doesn't seem to help:

    "transformIgnorePatterns": [
      "node_modules/(?!(ts-invariant)/)"
    ],

Any thoughts on how to fix?

Full jest config:

  "jest": {
    "resetMocks": true,
    "testEnvironment": "node",
    "testMatch": [
      "**/src/**/*.(spec|test).[tj]s?(x)"
    ],
    "preset": "ts-jest/presets/default-esm",
    "transform": {},
    "transformIgnorePatterns": [
      "node_modules/(?!(ts-invariant)/)"
    ],
    "extensionsToTreatAsEsm": [
      ".ts",
      ".tsx"
    ],
    "globals": {
      "ts-jest": {
        "useESM": true
      }
    },
    "moduleNameMapper": {
      "^(\\.{1,2}/.*)\\.js$": "$1"
    }
  }

and tsconfig:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "declaration": true,
    "downlevelIteration": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "jsx": "react",
    "outDir": "build",
    "sourceMap": true,
    "strictNullChecks": true,
    "baseUrl": ".",
    "lib": [
      "ESNext",
      "dom"
    ],
    "moduleResolution": "Node",
    "target": "ESNext",
    "module": "ESNext"
  },
  "include": [
    "src/**/*"
  ]
}

Additional context

Also tried using this jest resolver suggested in the docs:

module.exports = (request, options) => {
  // Call the defaultResolver, so we leverage its cache, error handling, etc.
  return options.defaultResolver(request, {
    ...options,
    // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb)
    packageFilter: (pkg) => {
      return {
        ...pkg,
        // Alter the value of `main` before resolving the package
        main: pkg.module || pkg.main,
      };
    },
  });
};

I notice that the @apollo/client package has both "module" and "main" keys. But using this resolver it errors for other modules:

/Users/justin/dev/sandbox/jest-esm-apollo-client/node_modules/@testing-library/react/dist/@testing-library/react.esm.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import _extends from '@babel/runtime/helpers/esm/extends';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

Is it possible to utilize the esm "module" field in package.json with jest ESM? Seemed like the obvious choice for ESM jest but it creates all kinds of errors like this. Is it because cjs modules in node_modules are trying to load the ESM exports?

Environment

npx: installed 1 in 1.509s

  System:
    OS: macOS 11.6
    CPU: (8) x64 Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
  Binaries:
    Node: 14.17.6 - /usr/local/bin/node
    Yarn: 1.22.0 - /usr/local/bin/yarn
    npm: 6.14.15 - /usr/local/bin/npm
  npmPackages:
    jest: ^27.4.3 => 27.4.3 


npx envinfo@latest --preset apollo --clipboard
npx: installed 1 in 1.563s

  System:
    OS: macOS 11.6
  Binaries:
    Node: 14.17.6 - /usr/local/bin/node
    Yarn: 1.22.0 - /usr/local/bin/yarn
    npm: 6.14.15 - /usr/local/bin/npm
  Browsers:
    Chrome: 96.0.4664.55
    Edge: 80.0.361.54
    Firefox: 89.0.2
    Safari: 14.1.2
  npmPackages:
    @apollo/client: ^3.5.5 => 3.5.5
@maxpain
Copy link

maxpain commented Jan 13, 2022

The same problem. Any updates?

@scottdotjs
Copy link

I've also run into this on a minimal project (not TypeScript), after following a suggestion that I should install @babel/plugin-transform-modules-commonjs to transform my ES modules to CommonJS for Jest in the test environment due to a pure ESM dependency.

@nika-d
Copy link

nika-d commented Jan 25, 2022

we had a similar issue. here is described, what we did to keep hopefully everything in esm-mode and not having any cjs produced on the way. : svelteness/svelte-jester#72 (comment)
the stack is a bit different that yours, but probably most important are the jest and node settings.

@cakeinpanic
Copy link

Hi, I had the same issue, spent a lot time on it but eventually fixed by changing jest config like described here
#11783 (comment)

@boubou158
Copy link

I am facing the same issue. Does anyone have a solution? If I modify manually a package.json from my node_modules adding type: "module" and modifying main to point to the esm dist, it will work. If I do it through the resolver.js, it is not working.

@unional
Copy link
Contributor

unional commented Dec 6, 2022

@themaskedavenger I'm able to get it working on your repro (here is my fork of your repo: https://github.com/cyberuni/jest-esm-apollo-client)
What you need is to compile the ESM dependency to CJS (using jest-esm-transformer-2).
I test it with latest jest and ts-jest.

In my repo I'm using @repobuddy/jest which I'm working on, trying to simplify all these jest config issues.

@castarco
Copy link

castarco commented Jul 18, 2023

I'm having a similar issue, I think it fits in here.

My problem is that, even though the packages from node_modules being imported have both CJS and ESM modules, and that in "my" (well, my company's) package.json and tsconfig.files we have it to work with CJS... When Jest runs, it tries to load the ESM variants instead, and then it complains about the export keyword because it does not understand it.

Of course I could try to apply some of the transformations referred in this thread, but what confuses me is why is it even trying to load the ESM files when we have CJS ones available (and they are properly declared in the respective package.json files of the packages we have in node_modules, via the "exports" section).

@mrazauskas
Copy link
Contributor

Better open new issue with minimal reproduction. It is impossible to guess what is going on from your description.

@NiklasPor
Copy link

I'm having a similar issue, I think it fits in here.

My problem is that, even though the packages from node_modules being imported have both CJS and ESM modules, and that in "my" (well, my company's) package.json and tsconfig.files we have it to work with CJS... When Jest runs, it tries to load the ESM variants instead, and then it complains about the export keyword because it does not understand it.

Of course I could try to apply some of the transformations referred in this thread, but what confuses me is why is it even trying to load the ESM files when we have CJS ones available (and they are properly declared in the respective package.json files of the packages we have in node_modules, via the "exports" section).

I'm also experiencing this issue, did you create an issue or managed to resolve it?

@unional
Copy link
Contributor

unional commented Jul 20, 2023

What you need is to compile the ESM dependency to CJS (using jest-esm-transformer-2).
I test it with latest jest and ts-jest.
You can try this.

#12120 (comment)

@castarco
Copy link

I'm also experiencing this issue, did you create an issue or managed to resolve it?

@NiklasPor I didn't had time to prepare a minimum reproducible example for this problem and to write a detailed issue yet. Will do.

@forgetso
Copy link

forgetso commented Nov 22, 2023

@themaskedavenger I'm able to get it working on your repro (here is my fork of your repo: https://github.com/cyberuni/jest-esm-apollo-client) What you need is to compile the ESM dependency to CJS (using jest-esm-transformer-2). I test it with latest jest and ts-jest.

In my repo I'm using @repobuddy/jest which I'm working on, trying to simplify all these jest config issues.

Why do we need to compile the ESM to CJS when there is already a CJS compiled version in the node_modules directory?

For example, package.json contains:

    },
    "exports": {
        ".": {
            "import": "./dist/index.js",
            "require": "./dist/cjs/index.cjs" <-------------this tells Jest where to get the CJS version
        }
    },

And the node_module itself contains both ESM and CJS:

├── dist
│   ├── cjs
│   │   ├── config
│   │   │   ├── config.cjs
...
...
│   ├── config
│   │   ├── config.d.ts
│   │   ├── config.d.ts.map
│   │   ├── config.js
│   │   ├── config.js.map
...

Despite this layour, Jest complains about exports coming from the dist directory in .js files and ignores the .cjs files.

@SimenB
Copy link
Member

SimenB commented Nov 22, 2023

If you run Jest in ESM mode, then it will use the import condition, not require.


As for the OP - you cannot import @apollo/client using native ESM with node either, so I think in this case Jest is just following that.

// file.mjs
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useQuery,
  gql
} from '@apollo/client';
$ node file.mjs
file:///Users/simen/wat/file.mjs:2
  ApolloClient,
  ^^^^^^^^^^^^
SyntaxError: Named export 'ApolloClient' not found. The requested module '@apollo/client' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@apollo/client';

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:131:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:213:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
    at async loadESM (node:internal/process/esm_loader:34:7)
    at async handleMainPromise (node:internal/modules/run_main:66:12)

Node.js v20.9.0
{
  "devDependencies": {
    "@apollo/client": "^3.8.7",
    "graphql": "^16.8.1"
  }
}

With that I'll close this issue. If people are having other issues, feel free to open a new one with a minimal reproduction

@SimenB SimenB closed this as not planned Won't fix, can't repro, duplicate, stale Nov 22, 2023
@SebastienGllmt
Copy link

Possibly related issue that also mentions a temporary workaround as well (that worked for me): #11563

Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests