はじめに
Vite + React + TypeScriptのプロジェクトでJestを導入したとき、こんなエラーに遭遇しませんでしたか?
The 'import.meta' meta-property is only allowed when the '--module' option is...ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'
これらのエラーの根本原因は、Vite(ESM)とJest(CommonJS)のモジュールシステムの違いにあります。この記事では、CommonJSとESMの違いを整理し、なぜエラーが起きるのか・どう解決するのかを解説します。
そもそもモジュールシステムとは?
JavaScriptにおける「ファイルの読み込み方式」のことです。主に2つの方式があります。
CommonJS(CJS)
Node.jsで昔から使われてきた方式です。
// エクスポート
module.exports = { hello: "world" };
// インポート
const { hello } = require("./module");
- Node.jsの標準として長年使われてきた
- 同期的に読み込む(上から順にファイルを読む)
- ブラウザでは直接使えない
- Jestはデフォルトでこの方式で動く
ESNext(ESM / ES Modules)
JavaScriptの公式仕様として策定された方式です。
// エクスポート
export const hello = "world";
// インポート
import { hello } from "./module.js";
- ブラウザがネイティブで対応している
- 非同期的に読み込める(パフォーマンスが良い)
- Viteはこの方式を使っている
-
import.meta.envはESMの機能
なぜVite + Jestでエラーが起きるのか
Vite(アプリ本体)はESNextで動き、Jest(テスト)はCommonJSで動きます。
| アプリ本体(Vite) | テスト(Jest) | |
|---|---|---|
| モジュール方式 | ESNext(ESM) | CommonJS |
| import文 | import / export |
require / module.exports |
import.meta.env |
使える | 使えない |
この違いがあるため、以下のようなエラーが発生します。
エラー①:import.meta が使えない
import.meta.env.VITE_SUPABASE_URL のような記述は、ESMの機能です。CommonJSで動くJestの環境には存在しないため、エラーになります。
エラー②:verbatimModuleSyntax との衝突
tsconfig の verbatimModuleSyntax: true は「ESMの書き方を厳密にチェックする」設定ですが、Jest(CommonJS)で実行されると矛盾が生じてエラーになります。
解決方法:Jest用のtsconfigを分ける
アプリ本体とテストで異なるモジュール方式を使うため、tsconfig自体を分けるのが一番きれいな方法です。
1. tsconfig.jest.json をプロジェクトルートに作成
tsconfig.app.json を継承しつつ、Jest用にモジュール設定を上書きします。
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"verbatimModuleSyntax": false
}
}
2. jest.config.js で tsconfig.jest.json を指定
export default {
preset: "ts-jest",
testEnvironment: "jsdom",
setupFilesAfterSetup: ["./jest.setup.ts"],
transform: {
"^.+\\.(ts|tsx)$": ["ts-jest", {
tsconfig: "tsconfig.jest.json", //ここに追加
}],
},
moduleNameMapper: {
"\\.(css|less)$": "identity-obj-proxy",
},
};
import.meta.env の対応は下記の記事を参考にしました。
https://qiita.com/shunii/items/b00d556f3730e6b88708
まとめ
-
CommonJS → Node.jsの昔からの方式。
require/module.exports。Jestはこっち -
ESNext(ESM) → JavaScriptの公式方式。
import/export。Viteはこっち - この2つの世界が混ざるとエラーになりやすい
-
Jest用に
tsconfig.jest.jsonを分けて対応するのが一般的なパターン
Vite + Jestの環境構築でハマったとき、「モジュールシステムの違い」を意識するとエラーの原因が見えてくるはずです。