Back

'Cannot use import statement outside a module' エラーの解決方法

'Cannot use import statement outside a module' エラーの解決方法

きれいな import 文を書いてコードを実行すると、すぐにこのエラーに遭遇します:

SyntaxError: Cannot use import statement outside a module

このJavaScript ESモジュールのエラーは、Node.js、ブラウザ、テスト環境で作業する開発者にとって、最も混乱を招く原因の1つです。解決方法は常に同じではなく、間違った方法を適用すると事態が悪化します。ここでは、状況を診断し、正しく解決する方法を説明します。

重要なポイント

  • このエラーはモジュールシステムの不一致であり、構文ミスではありません — ランタイムはCommonJSを期待していましたが、ESモジュールの import 構文に遭遇しました。
  • Node.jsでは、package.json"type": "module" を設定するか、.mjs ファイル拡張子を使用してESMをオプトインします。
  • ブラウザでは、<script> タグに type="module" を追加して、エンジンがファイルをESモジュールとして扱うようにします。
  • Jestでは、BabelでESMをCommonJSに変換するように設定し、ESM専用のサードパーティパッケージ用に transformIgnorePatterns を調整します。
  • 修正を適用する前に、必ず環境(Node.js、ブラウザ、テストランナー)を診断してください。

このエラーが発生する理由

このエラーは、ランタイムがESモジュール構文(import)に遭遇したが、別のもの(通常はCommonJS(require))を期待していたことを意味します。これはモジュールシステムの不一致であり、コードの構文ミスではありません。

JavaScriptには2つのモジュールシステムがあります:

システム構文デフォルト環境
CommonJS (CJS)require()Node.js(レガシー)
ES Modules (ESM)importブラウザ、モダンNode.js

ランタイムは、コード自体ではなく設定に基づいてどのシステムを使用するかを決定します。そのため、同じ import 文があるプロジェクトでは動作し、別のプロジェクトでは失敗します。

Node.jsでImport Statement Outside Moduleエラーを修正する方法

Node.jsは、package.json"type": "module" が設定されていない限り.js ファイルをCommonJSとして扱います。import を使用するには、明示的にESMをオプトインする必要があります。

オプション1: package.json"type": "module" を設定

{
  "name": "my-app",
  "version": "1.0.0",
  "type": "module"
}

これにより、Node.jsはプロジェクト内のすべての .js ファイルをESモジュールとして扱います。Node.jsがモジュールタイプをどのように決定するかの詳細については、公式のNode.js ESモジュールドキュメントを参照してください。

オプション2: .mjs ファイル拡張子を使用

ファイル名を app.js から app.mjs に変更します。Node.jsは、package.json に関係なく、常に .mjs ファイルをESMとして扱います。

オプション3: CommonJSファイルには .cjs を使用

プロジェクトが "type": "module" を使用しているが、1つのファイルで require() が必要な場合は、.cjs 拡張子を付けます。

重要: 他のファイルを確認せずに "type": "module" を追加しないでください。require()module.exports、または __dirname を使用しているファイルは、ESMの下で動作しなくなります。

ブラウザJavaScriptでエラーを修正する

ブラウザでは、静的な importモジュールスクリプト内でのみ機能します。<script> タグが type="module" を宣言していない場合、ブラウザはそれをクラシックスクリプトとして扱い、import 構文を拒否します。

<!-- これはエラーをスローします -->
<script src="app.js"></script>

<!-- これは動作します -->
<script type="module" src="app.js"></script>

この動作は、モダンブラウザで実装されている標準的なJavaScriptモジュール仕様の一部です。

モジュールスクリプトはスコープされていることに注意してください — 内部で宣言された変数はグローバルに利用できません。そのため、type="module" を追加した後、グローバルであると期待していた変数に対して ReferenceError が表示される場合があります。解決策は、グローバルスコープに依存するのではなく、値を明示的にエクスポートおよびインポートすることです。

Jestおよびその他のテスト環境でエラーを修正する

JestはNode.jsで実行され、ソースコードがESMを使用している場合でも、従来はデフォルトでCommonJSを使用します。これは、テストスイートで「cannot use import statement outside a module」エラーが発生する一般的な原因です。

1つのアプローチは、テスト中にESMをCommonJSに変換するようにBabelを設定することです:

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env'],
}

または、プロジェクトがすでにESモジュールを使用している場合、JestをネイティブESMモードでテストを実行するように設定できます。詳細については、Jest ECMAScript Modulesドキュメントを参照してください。

サードパーティパッケージ(swiperlodash-es、または同様のESM専用ライブラリなど)がエラーを引き起こしている場合は、Jestにそれを無視するのではなく変換するように指示する必要があります:

// jest.config.js
module.exports = {
  transformIgnorePatterns: [
    '/node_modules/(?!(swiper|ssr-window|dom7)/)',
  ],
}

重要な洞察: transformIgnorePatterns否定先読みを使用します。「node_modules 内のすべてを無視するが、これらのパッケージは除外する」と言っているのです。別々の配列エントリにリストしても機能しません — | を使用して単一の正規表現パターンに結合する必要があります。

TypeScript設定

TypeScriptを使用している場合は、tsconfig.json を確認してください。module フィールドは、TypeScriptがコンパイル出力で出力するモジュール構文を制御します:

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext"
  }
}

import 文を書きながら moduleCommonJS に設定することは有効なTypeScriptです — コンパイラは出力で importrequire() に変換します。ただし、moduleESNext に設定しながら、CommonJS Node.js環境(package.json"type": "module" がない)で出力を実行すると、実行時にエラーがトリガーされます。tsconfig.json のモジュール設定が、ランタイムが実際に期待するものと一致していることを確認してください。

修正する前に診断する

同じエラーが異なる環境で異なる理由で表示されます。修正を適用する前に、次のことを確認してください:

  • エラーはどこで発生していますか — Node.js、ブラウザ、またはテストランナー?
  • package.json"type" フィールドには何が書かれていますか?
  • バンドラーを使用していますか、それともファイルを直接実行していますか?

これらの3つの質問に答えることで、毎回正しい解決策が見つかります。

よくある質問

はい、ただしファイル拡張子を使用してそれらを区別する必要があります。.mjs拡張子を持つファイルは常にESモジュールとして扱われ、.cjs拡張子を持つファイルは、package.jsonのtypeフィールドに関係なく、常にCommonJSとして扱われます。これにより、単一のプロジェクトで両方のシステムを並行して使用できます。

いいえ。node_modules内の各パッケージには独自のpackage.jsonがあるため、typeフィールドはプロジェクトファイルにのみ適用されます。依存関係は独自のモジュールシステムを独立して定義します。エラーは通常、自分のファイルから、または適切な変換なしでCommonJSコンテキストでESM専用の依存関係をインポートすることから発生します。

WebpackやViteのようなバンドラーは、ビルドステップ中にimport文を処理し、単一の出力ファイルを生成します。ランタイムがそれを見る前にモジュール構文を解決します。Node.jsまたはブラウザでバンドラーなしでファイルを直接実行する場合、ランタイム自体がモジュール形式を理解する必要があり、不一致がこのエラーを引き起こします。

これらのCommonJSグローバルはESモジュールでは使用できません。代わりに、import.meta.urlを使用して再構築できます。たとえば、new URL(import.meta.url).pathnameを使用してファイルパスを取得するか、pathモジュールとurlモジュールのfileURLToPathと組み合わせて__dirnameの動作を再現します。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay