Jestで「SyntaxError: Cannot use import statement outside a module」が発生する場合の解決方法
問題の概要
Jestを使用してテストを実行する際に、以下のエラーが発生することがあります:
SyntaxError: Cannot use import statement outside a module
このエラーは、JestがESモジュール(import
/export
構文)を適切に処理できない場合に発生します。特に、TypeScript、React、Webpackを使用したプロジェクトで頻繁に見られます。
エラーは通常、以下のような形で表示されます:
/Users/me/dev/Project/project/node_modules/variables/src/variables.js:12
import './main.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
根本的な原因
JestはデフォルトでCommonJSモジュールシステムを使用しますが、現代のJavaScriptプロジェクトではESモジュール構文が広く使用されています。また、Jestはデフォルトでnode_modules
ディレクトリ内のファイルをトランスパイルしないため、ESモジュール形式で提供されているサードパーティライブラリをインポートすると問題が発生します。
解決方法
方法1: transformとtransformIgnorePatternsの組み合わせ
最も一般的な解決策は、Jest設定でtransform
とtransformIgnorePatterns
を組み合わせて使用することです:
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'node_modules/variables/.+\\.(j|t)sx?$': 'ts-jest'
},
transformIgnorePatterns: [
'node_modules/(?!variables/.*)'
]
}
この設定では:
transformIgnorePatterns
でvariables
パッケージを無視しないように指定transform
で特定のパッケージをts-jest
で変換するよう設定
WARNING
注意点:トランスフォーム対象を過度に増やすとテストの実行速度が低下する可能性があります。必要最小限のパッケージのみを指定しましょう。
方法2: Babel設定の変更
Babelを使用している場合、.babelrc.js
をbabel.config.json
に変更し、以下の設定を追加します:
// babel.config.json
{
"presets": [
["@babel/preset-env", {
"modules": "commonjs",
"targets": { "node": "current" }
}],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
// 必要なプラグイン
]
}
// jest.config.js
module.exports = {
transform: {
'^.+\\.jsx?$': 'babel-jest',
'^.+\\.tsx?$': 'ts-jest'
},
transformIgnorePatterns: [
'node_modules/(?!variables/.*)'
]
}
方法3: ts-jestの適切なプリセットを使用
TypeScriptプロジェクトでは、ts-jest
の適切なプリセットを使用することが重要です:
// jest.config.js
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
}
},
transformIgnorePatterns: [
'node_modules/(?!troublesome-dependency/.*)'
]
}
tsconfig.spec.json
ではallowJs: true
を設定する必要があります。
方法4: モジュールマッピングの使用
特定のライブラリがESモジュールとCommonJSの両方を提供している場合、モジュールマッピングを使用できます:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^variables$': 'variables/dist/cjs',
'^antd/es/(.*)$': 'antd/lib/$1'
}
}
方法5: ESMモードの有効化(Node.js対応の場合)
Node.jsの実験的なESMサポートを有効にすることも可能です:
// package.json
{
"type": "module",
"scripts": {
"test": "node --experimental-vm-modules ./node_modules/.bin/jest"
}
}
// jest.config.js
export default { transform: {} };
DANGER
この方法は実験的な機能を使用するため、プロダクション環境では注意が必要です。Node.jsのバージョンやJestの設定によっては安定しない可能性があります。
デバッグのヒント
キャッシュのクリア: 設定を変更した後は、Jestのキャッシュをクリアしましょう:
bashnpx jest --clearCache
詳細な出力: 詳細なログを有効にして問題の特定を容易に:
bashnpx jest --verbose
TSConfigの確認:
allowJs
オプションが有効になっているか確認:json{ "compilerOptions": { "allowJs": true } }
代替方案: Vitestの使用
Jestの設定に煩わされたくない場合、現代的なテストフレームワークであるVitestの使用を検討してください。VitestはESモジュールをネイティブでサポートし、Viteベースのプロジェクトとシームレスに連携します。
# Jest関連パッケージの削除
npm remove jest ts-jest @types/jest
# Vitestのインストール
npm install -D vite vitest
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
test: {
// テスト設定
}
})
まとめ
Jestで「Cannot use import statement outside a module」エラーが発生した場合、以下の点を確認しましょう:
- 変換設定:
transform
とtransformIgnorePatterns
の組み合わせが適切か - Babel/TypeScript設定: 適切なプリセットと設定が使用されているか
- モジュール解決:
moduleNameMapper
でパスが正しく解決されているか - キャッシュ: 古いキャッシュが問題を引き起こしていないか
これらの方法を試しても解決しない場合、プロジェクトの特定の状況に応じた解決策が必要な可能性があります。その場合は、使用しているライブラリのドキュメントやIssueを確認することをお勧めします。