概要
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"
ワークアラウンド
対象コミット
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 を分ける必要があります。