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トップレベルアウェイトのフラグを解除しました。
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.0追加: v8.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';

// Prints: 6
console.log(addTwo(4));

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

有効化

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

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

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

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

パッケージ

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

import Specifier

用語

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

Specifierには、次の3つの種類があります。

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

ベアSpecifierの解決は、Node.jsモジュール解決およびロードアルゴリズムによって処理されます。他のすべてのSpecifierの解決は、常に標準の相対URL解決セマンティクスでのみ解決されます。

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

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

必須のファイル拡張子

相対または絶対指定子を解決するために 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'; // クエリ "?query=1" で ./foo.mjs をロードします
import './foo.mjs?query=2'; // クエリ "?query=2" で ./foo.mjs をロードします

ボリュームルートは、///、または 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.0Import Assertions から Import Attributes に切り替え。
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()

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

import.meta

import.meta メタプロパティは、以下のプロパティを含む Object です。ES modules でのみサポートされています。

import.meta.dirname

Added in: v21.2.0, v20.11.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.2 - Release candidate

import.meta.filename

Added in: v21.2.0, v20.11.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.2 - Release candidate

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)

[History]

VersionChanges
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.2Added in: v13.9.0, v12.16.2

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.2 - Release candidate

  • specifier <string> 現在のモジュールを基準に解決するモジュール指定子。
  • Returns: <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。 Default: 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.0'module.exports' エクスポートマーカーを CJS 名前空間に追加しました。
v14.13.0追加: v14.13.0

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

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

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

これらの CommonJS 名前空間オブジェクトは、'module.exports' 名前付きエクスポートとして default エクスポートも提供し、CommonJS での表現がこの値を使用し、名前空間の値を使用しないことを明確に示すために提供します。これは、require(esm) 相互運用サポートにおける '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.filenameimport.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 がない

ESモジュールローダーには独自のキャッシュがあるため、require.cacheimport では使用されません。

JSONモジュール

[履歴]

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

[安定版: 2 - 安定]

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

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

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

with { type: 'json' } 構文は必須です。インポート属性 を参照してください。

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

Wasm モジュール

[Stable: 1 - Experimental]

Stable: 1 Stability: 1 - 実験的

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

この統合は、WebAssembly の ES Module Integration Proposal に沿ったものです。

たとえば、index.mjs に以下が含まれているとします。

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

以下のように実行した場合:

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

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

トップレベルの await

Added in: 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); // Logs `5`
bash
node b.mjs # works

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

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

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // Never-resolving Promise:
  'await new Promise(() => {})',
]).once('exit', (code) => {
  console.log(code); // Logs `13`
});

ローダー

以前のローダーのドキュメントは、モジュール: カスタマイズフック にあります。

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

機能

デフォルトの解決器には、次のプロパティがあります。

  • ES モジュールで使用されている FileURL ベースの解決
  • 相対 URL および絶対 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"]です。

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

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

解決アルゴリズムの仕様

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があります。