Skip to content

モジュール: ECMAScript モジュール

[履歴]

バージョン変更点
v23.1.0インポート属性が実験的ではなくなりました。
v22.0.0インポートアサーションのサポートを削除しました。
v21.0.0, v20.10.0, v18.20.0インポート属性の実験的なサポートを追加しました。
v20.0.0, v18.19.0モジュールのカスタマイズフックがメインスレッド外で実行されるようになりました。
v18.6.0, v16.17.0モジュールのカスタマイズフックの連鎖のサポートを追加しました。
v17.1.0, v16.14.0インポートアサーションの実験的なサポートを追加しました。
v17.0.0, v16.12.0カスタマイズフックを統合し、getFormatgetSourcetransformSource、および getGlobalPreloadCode フックを削除し、load および globalPreload フックを追加し、resolve または load フックから format を返すことを許可しました。
v14.8.0トップレベル Await のフラグを解除しました。
v15.3.0, v14.17.0, v12.22.0モジュールの実装を安定化させました。
v14.13.0, v12.20.0CommonJS の名前付きエクスポートの検出をサポートしました。
v14.0.0, v13.14.0, v12.20.0実験的なモジュールの警告を削除しました。
v13.2.0, v12.17.0ECMAScript モジュールのロードにコマンドラインフラグが不要になりました。
v12.0.0package.json"type" フィールドを介して .js ファイル拡張子を使用する ES モジュールのサポートを追加しました。
v8.5.0v8.5.0 で追加されました。

[安定版: 2 - 安定版]

安定版: 2 安定性: 2 - 安定版

はじめに

ECMAScript モジュールは、JavaScript コードを再利用のためにパッケージ化するための公式標準フォーマットです。モジュールは、さまざまなimportおよびexportステートメントを使用して定義されます。

ES モジュールの次の例では、関数をエクスポートします。

js
// addTwo.mjs
function addTwo(num) {
  return num + 2
}

export { addTwo }

ES モジュールの次の例では、addTwo.mjs から関数をインポートします。

js
// app.mjs
import { addTwo } from './addTwo.mjs'

// 出力: 6
console.log(addTwo(4))

Node.js は、現在指定されている ECMAScript モジュールを完全にサポートし、それらと元のモジュール形式であるCommonJSとの間の相互運用性を提供します。

有効化

Node.js には、CommonJSモジュールと ECMAScript モジュールの 2 つのモジュールシステムがあります。

開発者は、.mjsファイル拡張子、package.json"type"フィールドに"module"の値を使用するか、--input-typeフラグに"module"の値を使用することで、JavaScript を ES モジュールとして解釈するように Node.js に指示できます。これらは、コードが ES モジュールとして実行されることを意図している明示的なマーカーです。

逆に、開発者は.cjsファイル拡張子、package.json"type"フィールドに"commonjs"の値を使用するか、--input-typeフラグに"commonjs"の値を使用することで、JavaScript を CommonJS として解釈するように Node.js に明示的に指示できます。

いずれのモジュールシステムについても明示的なマーカーがない場合、Node.js はモジュールのソースコードを調べて ES モジュール構文を探します。そのような構文が見つかった場合、Node.js はコードを ES モジュールとして実行します。それ以外の場合は、モジュールを CommonJS として実行します。詳細については、モジュールシステムの決定を参照してください。

パッケージ

このセクションはモジュール:パッケージに移動しました。

import指定子

用語

importステートメントの指定子は、fromキーワードの後の文字列です。たとえば、import { sep } from 'node:path''node:path'です。指定子は、export fromステートメントや、import()式の引数としても使用されます。

指定子には 3 つのタイプがあります。

  • './startup.js''../config.mjs'のような相対指定子。これらは、インポートするファイルの場所に対する相対パスを参照します。これらのファイル拡張子は常に必要です。
  • 'some-package''some-package/shuffle'のような裸指定子。これらは、パッケージ名によるパッケージのメインエントリポイント、または例のようにパッケージ名をプレフィックスとしたパッケージ内の特定の機能モジュールを参照できます。ファイル拡張子の包含は、"exports"フィールドのないパッケージの場合にのみ必要です。
  • 'file:///opt/nodejs/config.js'のような絶対指定子。これらは、フルパスを直接かつ明示的に参照します。

裸指定子の解決は、Node.js のモジュール解決とローディングアルゴリズムによって処理されます。他のすべての指定子解決は、常に標準の相対URL解決セマンティクスでのみ解決されます。

CommonJS と同様に、パッケージ内のモジュールファイルには、パッケージのpackage.json"exports"フィールドが含まれていない限り、パッケージ名にパスを追加することでアクセスできます。"exports"フィールドが含まれている場合は、"exports"で定義されたパスを介してのみ、パッケージ内のファイルにアクセスできます。

Node.js のモジュール解決における裸指定子に適用されるこれらのパッケージ解決規則の詳細については、パッケージのドキュメントを参照してください。

必須のファイル拡張子

相対または絶対指定子を解決するために import キーワードを使用する場合は、ファイル拡張子を指定する必要があります。ディレクトリインデックス (例: './startup/index.js') も完全に指定する必要があります。

この動作は、通常構成されたサーバーを前提として、ブラウザー環境での import の動作と一致します。

URL

ES モジュールは URL として解決およびキャッシュされます。これは、#%23 で、?%3F で置き換えるなど、特殊文字を パーセントエンコード する必要があることを意味します。

file:, node:, および data: URL スキームがサポートされています。'https://example.com/app.js' のような指定子は、カスタム HTTPS ローダー を使用しない限り、Node.js ではネイティブにはサポートされていません。

file: URL

モジュールを解決するために使用された import 指定子が異なるクエリまたはフラグメントを持つ場合、モジュールは複数回ロードされます。

js
import './foo.mjs?query=1' // "./foo.mjs" をクエリ "?query=1" でロードします。
import './foo.mjs?query=2' // "./foo.mjs" をクエリ "?query=2" でロードします。

ボリュームルートは、/, //, または file:/// を介して参照できます。 (URL とパス解決の違い(パーセントエンコーディングの詳細など)を考えると、パスをインポートするときは、url.pathToFileURL を使用することをお勧めします。

data: インポート

追加: v12.10.0

data: URL は、以下の MIME タイプでのインポートがサポートされています。

  • ES モジュール用の text/javascript
  • JSON 用の application/json
  • Wasm 用の application/wasm
js
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' };

data: URL は、組み込みモジュールと 絶対指定子ベア指定子 のみを解決します。相対指定子 の解決は、data:特別なスキーム ではないため機能しません。たとえば、data:text/javascript,import "./foo"; から ./foo をロードしようとしても、data: URL には相対的な解決の概念がないため、解決に失敗します。

node: インポート

[履歴]

バージョン変更点
v16.0.0, v14.18.0require(...)node: インポートのサポートを追加。
v14.13.1, v12.20.0追加: v14.13.1, v12.20.0

node: URL は、Node.js の組み込みモジュールをロードする代替手段としてサポートされています。この URL スキームにより、組み込みモジュールを有効な絶対 URL 文字列で参照できます。

js
import fs from 'node:fs/promises'

インポート属性

[履歴]

バージョン変更点
v21.0.0, v20.10.0, v18.20.0インポートアサーションからインポート属性に切り替え。
v17.1.0, v16.14.0追加: v17.1.0, v16.14.0

[安定版: 2 - 安定版]

安定版: 2 安定性: 2 - 安定版

インポート属性 は、モジュール指定子とともに詳細な情報を渡すためのモジュールインポート文のインライン構文です。

js
import fooData from './foo.json' with { type: 'json' };

const { default: barData } =
  await import('./bar.json', { with: { type: 'json' } });

Node.js は type 属性のみをサポートしており、次の値をサポートしています。

属性 type必要なもの
'json'JSON モジュール

JSON モジュールをインポートするときは、type: 'json' 属性が必須です。

組み込みモジュール

組み込みモジュールは、パブリック API の名前付きエクスポートを提供します。CommonJS のエクスポートの値であるデフォルトのエクスポートも提供されます。デフォルトのエクスポートは、特に名前付きエクスポートの変更に使用できます。組み込みモジュールの名前付きエクスポートは、module.syncBuiltinESMExports() を呼び出すことによってのみ更新されます。

js
import EventEmitter from 'node:events'
const e = new EventEmitter()
js
import { readFile } from 'node:fs'
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err)
  } else {
    console.log(source)
  }
})
js
import fs, { readFileSync } from 'node:fs'
import { syncBuiltinESMExports } from 'node:module'
import { Buffer } from 'node:buffer'

fs.readFileSync = () => Buffer.from('Hello, ESM')
syncBuiltinESMExports()

fs.readFileSync === readFileSync

import()

動的な import() は、CommonJS と ES モジュールの両方でサポートされています。CommonJS モジュールでは、ES モジュールをロードするために使用できます。

import.meta

import.meta メタプロパティは、次のプロパティを含む Object です。これは ES モジュールでのみサポートされています。

import.meta.dirname

追加バージョン: v21.2.0, v20.11.0

[安定: 1 - 試験的]

安定: 1 安定度: 1.2 - リリース候補

import.meta.filename

追加バージョン: v21.2.0, v20.11.0

[安定: 1 - 試験的]

安定: 1 安定度: 1.2 - リリース候補

import.meta.url

  • <string> モジュールの絶対 file: URL。

これは、現在のモジュールファイルの URL を提供するブラウザでの定義とまったく同じです。

これにより、次のような相対ファイルローディングなどの便利なパターンが可能になります。

js
import { readFileSync } from 'node:fs'
const buffer = readFileSync(new URL('./data.proto', import.meta.url))

import.meta.resolve(specifier)

[履歴]

バージョン変更点
v20.6.0, v18.19.0--experimental-import-meta-resolve CLI フラグの後ろに隠れなくなりました。ただし、非標準の parentURL パラメータは除きます。
v20.6.0, v18.19.0この API は、ローカル FS 上の既存のファイルにマッピングされない file: URL をターゲットとする場合、スローしなくなりました。
v20.0.0, v18.19.0この API は、Promise の代わりに文字列を同期的に返すようになりました。
v16.2.0, v14.18.0WHATWG URL オブジェクトを parentURL パラメータに追加しました。
v13.9.0, v12.16.2追加バージョン: v13.9.0, v12.16.2

[安定: 1 - 試験的]

安定: 1 安定度: 1.2 - リリース候補

  • specifier <string> 現在のモジュールを基準に解決するモジュール指定子。
  • 返り値: <string> 指定子が解決される絶対 URL 文字列。

import.meta.resolve は、各モジュールにスコープされたモジュール相対解決関数であり、URL 文字列を返します。

js
const dependencyAsset = import.meta.resolve('component-lib/asset.css')
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js')
// file:///app/dep.js

Node.js モジュール解決のすべての機能がサポートされています。依存関係の解決は、パッケージ内で許可されたエクスポートの解決の対象となります。

注意点:

  • これにより、同期的なファイルシステム操作が発生する可能性があり、require.resolve と同様にパフォーマンスに影響を与える可能性があります。
  • この機能は、カスタムローダー内では利用できません (デッドロックが発生する可能性があります)。

非標準 API:

--experimental-import-meta-resolve フラグを使用すると、その関数は 2 番目の引数を受け入れます。

  • parent <string> | <URL> 解決の基準となるオプションの絶対親モジュール URL。デフォルト: import.meta.url

CommonJS との相互運用性

import ステートメント

import ステートメントは、ES モジュールまたは CommonJS モジュールを参照できます。import ステートメントは ES モジュールでのみ許可されていますが、動的な import() 式は、ES モジュールをロードするために CommonJS でサポートされています。

CommonJS モジュールをインポートする場合、module.exports オブジェクトがデフォルトのエクスポートとして提供されます。名前付きエクスポートは、エコシステムの互換性を向上させるための便宜として静的解析によって提供される場合があります。

require

CommonJS モジュール require は現在、同期的な ES モジュール(つまり、トップレベルの await を使用しない ES モジュール)のロードのみをサポートしています。

詳細については、require() を使用した ECMAScript モジュールのロードを参照してください。

CommonJS 名前空間

[履歴]

バージョン変更点
v23.0.0CJS 名前空間に 'module.exports' エクスポートマーカーを追加しました。
v14.13.0v14.13.0 で追加されました。

CommonJS モジュールは、任意の型を取りうる module.exports オブジェクトで構成されています。

これをサポートするために、ECMAScript モジュールから CommonJS をインポートする場合、CommonJS モジュールの名前空間ラッパーが構築され、CommonJS の module.exports 値を指す default エクスポートキーが常に提供されます。

さらに、CommonJS モジュールのソーステキストに対してヒューリスティックな静的解析が実行され、module.exports の値から名前空間で提供されるエクスポートの最善の静的リストを取得します。これらの名前空間は、CJS モジュールの評価の前に構築する必要があるため、これは必要です。

これらの CommonJS 名前空間オブジェクトは、default エクスポートを 'module.exports' という名前のエクスポートとしても提供し、CommonJS での表現が名前空間の値ではなくこの値を使用していることを明確に示すために使用されます。これは、require(esm) interop サポートにおける 'module.exports' エクスポート名の処理のセマンティクスを反映しています。

CommonJS モジュールをインポートする場合、ES モジュールのデフォルトインポートまたは対応する糖衣構文を使用して確実にインポートできます。

js
import { default as cjs } from 'cjs'
// 上記と同じ
import cjsSugar from 'cjs'

console.log(cjs)
console.log(cjs === cjsSugar)
// 出力:
//   <module.exports>
//   true

このモジュール名前空間エキゾチックオブジェクトは、import * as m from 'cjs' または動的インポートを使用するときに直接観察できます。

js
import * as m from 'cjs'
console.log(m)
console.log(m === (await import('cjs')))
// 出力:
//   [Module] { default: <module.exports>, 'module.exports': <module.exports> }
//   true

JS エコシステムにおける既存の使用法との互換性を高めるために、Node.js はさらに、すべてのインポートされた CommonJS モジュールの CommonJS 名前付きエクスポートを特定しようと試み、静的解析プロセスを使用してそれらを個別の ES モジュールエクスポートとして提供します。

たとえば、次のように記述された CommonJS モジュールを考えてみましょう。

js
// cjs.cjs
exports.name = 'exported'

上記のモジュールは、ES モジュールでの名前付きインポートをサポートします。

js
import { name } from './cjs.cjs'
console.log(name)
// 出力: 'exported'

import cjs from './cjs.cjs'
console.log(cjs)
// 出力: { name: 'exported' }

import * as m from './cjs.cjs'
console.log(m)
// 出力:
//   [Module] {
//     default: { name: 'exported' },
//     'module.exports': { name: 'exported' },
//     name: 'exported'
//   }

モジュール名前空間エキゾチックオブジェクトの最後の例をログ出力からわかるように、name エクスポートは module.exports オブジェクトからコピーされ、モジュールがインポートされるときに ES モジュール名前空間に直接設定されます。

ライブバインディングの更新や module.exports に追加された新しいエクスポートは、これらの名前付きエクスポートでは検出されません。

名前付きエクスポートの検出は、一般的な構文パターンに基づいていますが、名前付きエクスポートを常に正しく検出するとは限りません。これらの場合、上記で説明したデフォルトインポート形式を使用する方が良い場合があります。

名前付きエクスポートの検出は、多くの一般的なエクスポートパターン、再エクスポートパターン、およびビルドツールとトランスパイラーの出力をカバーしています。正確なセマンティクスの実装については、cjs-module-lexerを参照してください。

ES モジュールと CommonJS の違い

requireexportsmodule.exports がない

ほとんどの場合、ES モジュールの import を使用して CommonJS モジュールをロードできます。

必要な場合、module.createRequire()を使用して ES モジュール内に require 関数を構築できます。

__filename__dirname がない

これらの CommonJS 変数は ES モジュールでは利用できません。

__filename__dirname のユースケースは、import.meta.filename および import.meta.dirname を介して再現できます。

アドオンのロードがない

アドオンは現在、ES モジュールのインポートではサポートされていません。

代わりに、module.createRequire() または process.dlopen でロードできます。

require.resolve がない

相対的な解決は、new URL('./local', import.meta.url) を介して処理できます。

完全な require.resolve の代替として、import.meta.resolve API があります。

あるいは、module.createRequire() を使用することもできます。

NODE_PATH がない

NODE_PATHimport 指定子の解決の一部ではありません。この動作が必要な場合は、シンボリックリンクを使用してください。

require.extensions がない

require.extensionsimport では使用されません。モジュールのカスタマイズフックで代替を提供できます。

require.cache がない

require.cache は、ES モジュールローダーが独自の分離されたキャッシュを持っているため、import では使用されません。

JSON モジュール

[履歴]

バージョン変更
v23.1.0JSON モジュールは実験的ではなくなりました。

[安定版: 2 - 安定版]

安定版: 2 安定性: 2 - 安定版

JSON ファイルは import で参照できます。

js
import packageConfig from './package.json' with { type: 'json' };

with { type: 'json' } 構文は必須です。Import Attributes を参照してください。

インポートされた JSON は default エクスポートのみを公開します。名前付きエクスポートはサポートされていません。重複を避けるために、CommonJS キャッシュにキャッシュエントリが作成されます。JSON モジュールが同じパスからすでにインポートされている場合、CommonJS で同じオブジェクトが返されます。

Wasm モジュール

[Stable: 1 - 実験的]

Stable: 1 Stability: 1 - 実験的

WebAssembly モジュールのインポートは --experimental-wasm-modules フラグの下でサポートされており、任意の .wasm ファイルを通常のモジュールとしてインポートできるだけでなく、それらのモジュールインポートもサポートします。

この統合は、WebAssembly 用の ES モジュール統合提案に沿ったものです。

例えば、次のような index.mjs があるとします。

js
import * as M from './module.wasm'
console.log(M)

これを次のコマンドで実行すると:

bash
node --experimental-wasm-modules index.mjs

module.wasm のインスタンス化のためのエクスポートインターフェースが提供されます。

トップレベル await

追加: v14.8.0

await キーワードは、ECMAScript モジュールのトップレベルの本体で使用できます。

例えば、次のような a.mjs があるとします。

js
export const five = await Promise.resolve(5)

そして、次のような b.mjs があるとします。

js
import { five } from './a.mjs'

console.log(five) // `5` をログ出力
bash
node b.mjs # 動作します

トップレベルの await 式が解決されない場合、node プロセスは 13ステータスコードで終了します。

js
import { spawn } from 'node:child_process'
import { execPath } from 'node:process'

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // 決して解決しない Promise:
  'await new Promise(() => {})',
]).once('exit', code => {
  console.log(code) // `13` をログ出力
})

ローダー

以前のローダーに関するドキュメントは、モジュール: カスタマイズフックに移動しました。

解決と読み込みアルゴリズム

特徴

デフォルトのリゾルバーには、以下の特性があります。

  • ES モジュールで使用される FileURL ベースの解決
  • 相対および絶対 URL の解決
  • デフォルトの拡張子なし
  • フォルダのメインなし
  • node_modules を通じたベア指定子パッケージ解決ルックアップ
  • 不明な拡張子またはプロトコルでは失敗しない
  • オプションで読み込みフェーズに形式のヒントを提供できる

デフォルトのローダーには、以下の特性があります。

  • node: URL を介した組み込みモジュールの読み込みのサポート
  • data: URL を介した「インライン」モジュールの読み込みのサポート
  • file: モジュールの読み込みのサポート
  • その他の URL プロトコルでは失敗する
  • file: 読み込みの不明な拡張子で失敗する (.cjs.js.mjs のみをサポート)

解決アルゴリズム

ES モジュール指定子をロードするアルゴリズムは、以下のESM_RESOLVEメソッドによって与えられます。これは、親 URL に対するモジュール指定子の解決済み URL を返します。

解決アルゴリズムは、モジュールのロードのための完全な解決済み URL と、推奨されるモジュール形式を決定します。解決アルゴリズムは、解決された URL プロトコルがロード可能かどうか、またはファイル拡張子が許可されているかどうかを判断しません。代わりに、これらの検証はロードフェーズ中に Node.js によって適用されます(たとえば、file:data:、またはnode:ではないプロトコルを持つ URL をロードするように要求された場合)。

このアルゴリズムは、拡張子に基づいてファイルの形式を判別しようともします(以下のESM_FILE_FORMATアルゴリズムを参照)。ファイル拡張子を認識しない場合(例えば、.mjs.cjs、または.jsonではない場合)、undefinedの形式が返され、ロードフェーズ中にエラーが発生します。

解決された URL のモジュール形式を決定するアルゴリズムは、ESM_FILE_FORMATによって提供され、任意のファイルに対して一意のモジュール形式を返します。「module」形式は ECMAScript モジュールに対して返され、「commonjs」形式は従来の CommonJS ローダーを介したロードを示すために使用されます。「addon」などの追加形式は、将来のアップデートで拡張できます。

以下のアルゴリズムでは、特に明記されていない限り、すべてのサブルーチンエラーはこれらのトップレベルルーチンのエラーとして伝播されます。

defaultConditionsは、条件付き環境名配列["node", "import"]です。

リゾルバーは、次のエラーをスローする可能性があります。

  • 無効なモジュール指定子: モジュール指定子は、無効な URL、パッケージ名、またはパッケージサブパス指定子です。
  • 無効なパッケージ構成: package.json の構成が無効であるか、無効な構成が含まれています。
  • 無効なパッケージターゲット: パッケージのエクスポートまたはインポートは、パッケージのターゲットモジュールとして、無効な型または文字列ターゲットを定義しています。
  • パッケージパスがエクスポートされていません: パッケージのエクスポートは、指定されたモジュールのパッケージ内のターゲットサブパスを定義または許可していません。
  • パッケージインポートが定義されていません: パッケージのインポートは、指定子を定義していません。
  • モジュールが見つかりません: 要求されたパッケージまたはモジュールが存在しません。
  • サポートされていないディレクトリインポート: 解決されたパスがディレクトリに対応しており、モジュールインポートのサポート対象ターゲットではありません。

解決アルゴリズムの仕様

ESM_RESOLVE(specifier, parentURL)

PACKAGE_RESOLVE(packageSpecifier, parentURL)

PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)

PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)

PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)

PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)

PATTERN_KEY_COMPARE(keyA, keyB)

PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)

ESM_FILE_FORMAT(url)

LOOKUP_PACKAGE_SCOPE(url)

READ_PACKAGE_JSON(packageURL)

DETECT_MODULE_SYNTAX(source)

ESM 指定子解決アルゴリズムのカスタマイズ

モジュールカスタマイズフックは、ESM 指定子解決アルゴリズムをカスタマイズするためのメカニズムを提供します。ESM 指定子に対して CommonJS スタイルの解決を提供する例として、commonjs-extension-resolution-loaderがあります。