Jest ESM and ts-jest - how to handle when some dependencies are CJS and some are ESM
Asked Answered
S

0

8

Having some issues importing CJS modules with Jest (ESM, and ts-jest). Minimal reproduction repo.

For example, importing from the @apollo/client module as described in their docs:

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/**/*"
  ]
}
Subfusc answered 4/12, 2021 at 17:42 Comment(2)
Did you find an answer to your question?Waddell
In my opinion, this whole cjs extension for common.js and changing to esm obligating us to use the file extesions on imports are just bad design ideas. You can't control how the other libraries will implement or update their code. This change generated lots of breaking changes in lots of libraries. All of a sudden if you want to update your project to use a esm module you need to update your whole project? If a new library has one interesting new tool, or security patch you can't just upgrade without upgrating your all project. I'd like to hear for others how they are handling this issues.Ratchford

© 2022 - 2024 — McMap. All rights reserved.