ES Modules のパッケージを使うと ts-jest が動かない問題のワークアラウンド

概要

TypeScript + babel + Jest + ts-jest の環境で、npm で ESM 形式のパッケージを追加したときに Jest が ReferenceError: exports is not defined のエラーを出して動かなくなった現象の対応の記録です。

環境

  • TypeScript を babel でトランスパイルしている
  • テストも TypeScript で書いている
  • テストランナーに Jest を使っている
  • ts-jest を使っている

以下のような構成で、foo.ts の中で ES Module のパッケージを import している状況です。foo.test.ts のテストが実行できません。

  • src
    • foo.ts
    • foo.test.ts
  • package.json
  • jest.config.js
  • .babelrc
  • tsconfig.json

再現リポジトリ https://github.com/ryohey/jest-esm-workaround

エラー

SyntaxError: Unexpected token 'export'

    > 1 | import { groupBy } from "lodash-es"

ワークアラウンド

対象コミット

github.com

jest.config.js の修正

module.exports = {
-  preset: "ts-jest",
+  preset: "ts-jest/presets/js-with-babel-esm",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
+  transformIgnorePatterns: ["/node_modules/(?!lodash-es)"],
}

.babelrc の修正

  • .babelrc を babel.config.json にリネーム
  • modules: false を削除 (true にする)

やっていること

ts-jest では ES Modules を読み込めないので、テストを実行する際、ES Modules で書かれた依存パッケージを babel でトランスパイルさせるように設定しています。

  • .babelrc は jest を使った際に読み込まれないので babel.config.json もしくは babel.config.js にリネームする
  • jest はデフォルトで node_modules 内のソースをトランスパイルしないようになってるので、 transformIgnorePatterns を指定して依存パッケージをトランスパイル対象にする
  • modules: false を削除することで、babel のトランスパイル結果が ES Modules ではなく CommonJS 形式になります

注意点

modules: false の削除により、babel の出力結果も CommonJS に変わっています。ライブラリとして publish するコードなど、最終的な成果物は ES Modules にしたいといった場合には、テスト時とビルド時の babel.config.json を分ける必要があります。