Skip to content

Jestで「SyntaxError: Cannot use import statement outside a module」が発生する場合の解決方法

問題の概要

Jestを使用してテストを実行する際に、以下のエラーが発生することがあります:

bash
SyntaxError: Cannot use import statement outside a module

このエラーは、JestがESモジュール(import/export構文)を適切に処理できない場合に発生します。特に、TypeScript、React、Webpackを使用したプロジェクトで頻繁に見られます。

エラーは通常、以下のような形で表示されます:

javascript
/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設定でtransformtransformIgnorePatternsを組み合わせて使用することです:

javascript
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  transform: {
    'node_modules/variables/.+\\.(j|t)sx?$': 'ts-jest'
  },
  transformIgnorePatterns: [
    'node_modules/(?!variables/.*)'
  ]
}

この設定では:

  1. transformIgnorePatternsvariablesパッケージを無視しないように指定
  2. transformで特定のパッケージをts-jestで変換するよう設定

WARNING

注意点:トランスフォーム対象を過度に増やすとテストの実行速度が低下する可能性があります。必要最小限のパッケージのみを指定しましょう。

方法2: Babel設定の変更

Babelを使用している場合、.babelrc.jsbabel.config.jsonに変更し、以下の設定を追加します:

json
// babel.config.json
{
  "presets": [
    ["@babel/preset-env", {
      "modules": "commonjs",
      "targets": { "node": "current" }
    }],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    // 必要なプラグイン
  ]
}
javascript
// jest.config.js
module.exports = {
  transform: {
    '^.+\\.jsx?$': 'babel-jest',
    '^.+\\.tsx?$': 'ts-jest'
  },
  transformIgnorePatterns: [
    'node_modules/(?!variables/.*)'
  ]
}

方法3: ts-jestの適切なプリセットを使用

TypeScriptプロジェクトでは、ts-jestの適切なプリセットを使用することが重要です:

javascript
// 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の両方を提供している場合、モジュールマッピングを使用できます:

javascript
// jest.config.js
module.exports = {
  moduleNameMapper: {
    '^variables$': 'variables/dist/cjs',
    '^antd/es/(.*)$': 'antd/lib/$1'
  }
}

方法5: ESMモードの有効化(Node.js対応の場合)

Node.jsの実験的なESMサポートを有効にすることも可能です:

json
// package.json
{
  "type": "module",
  "scripts": {
    "test": "node --experimental-vm-modules ./node_modules/.bin/jest"
  }
}
javascript
// jest.config.js
export default { transform: {} };

DANGER

この方法は実験的な機能を使用するため、プロダクション環境では注意が必要です。Node.jsのバージョンやJestの設定によっては安定しない可能性があります。

デバッグのヒント

  1. キャッシュのクリア: 設定を変更した後は、Jestのキャッシュをクリアしましょう:

    bash
    npx jest --clearCache
  2. 詳細な出力: 詳細なログを有効にして問題の特定を容易に:

    bash
    npx jest --verbose
  3. TSConfigの確認: allowJsオプションが有効になっているか確認:

    json
    {
      "compilerOptions": {
        "allowJs": true
      }
    }

代替方案: Vitestの使用

Jestの設定に煩わされたくない場合、現代的なテストフレームワークであるVitestの使用を検討してください。VitestはESモジュールをネイティブでサポートし、Viteベースのプロジェクトとシームレスに連携します。

bash
# Jest関連パッケージの削除
npm remove jest ts-jest @types/jest

# Vitestのインストール
npm install -D vite vitest
javascript
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    // テスト設定
  }
})

まとめ

Jestで「Cannot use import statement outside a module」エラーが発生した場合、以下の点を確認しましょう:

  1. 変換設定: transformtransformIgnorePatternsの組み合わせが適切か
  2. Babel/TypeScript設定: 適切なプリセットと設定が使用されているか
  3. モジュール解決: moduleNameMapperでパスが正しく解決されているか
  4. キャッシュ: 古いキャッシュが問題を引き起こしていないか

これらの方法を試しても解決しない場合、プロジェクトの特定の状況に応じた解決策が必要な可能性があります。その場合は、使用しているライブラリのドキュメントやIssueを確認することをお勧めします。