モジュール: 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 | カスタマイズフックを統合し、getFormat 、getSource 、transformSource 、および getGlobalPreloadCode フックを削除し、load および globalPreload フックを追加し、resolve または load フックのいずれかから format を返すことを許可しました。 |
v14.8.0 | トップレベルアウェイトのフラグを解除しました。 |
v15.3.0, v14.17.0, v12.22.0 | モジュールの実装を安定化させます。 |
v14.13.0, v12.20.0 | CommonJS の名前付きエクスポートの検出をサポートします。 |
v14.0.0, v13.14.0, v12.20.0 | 実験的モジュールの警告を削除しました。 |
v13.2.0, v12.17.0 | ECMAScript モジュールをロードするのにコマンドラインフラグは不要になりました。 |
v12.0.0 | package.json の "type" フィールドを介して .js ファイル拡張子を使用する ES モジュールのサポートを追加しました。 |
v8.5.0 | 追加: v8.5.0 |
導入
ECMAScript モジュールは、JavaScript コードを再利用のためにパッケージ化する公式の標準形式です。 モジュールは、さまざまな import
および export
ステートメントを使用して定義されます。
ES モジュールの次の例では、関数をエクスポートします。
// addTwo.mjs
function addTwo(num) {
return num + 2;
}
export { addTwo };
ES モジュールの次の例では、addTwo.mjs
から関数をインポートします。
// 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
指定子が異なるクエリまたはフラグメントを持っている場合、複数回ロードされます。
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
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.0 | require(...) への node: インポートのサポートを追加。 |
v14.13.1, v12.20.0 | 追加: v14.13.1, v12.20.0 |
node:
URL は、Node.js 組み込みモジュールをロードする代替手段としてサポートされています。この URL スキームにより、組み込みモジュールを有効な絶対 URL 文字列で参照できます。
import fs from 'node:fs/promises';
インポート属性
[歴史]
バージョン | 変更点 |
---|---|
v21.0.0, v20.10.0, v18.20.0 | Import Assertions から Import Attributes に切り替え。 |
v17.1.0, v16.14.0 | 追加: v17.1.0, v16.14.0 |
インポート属性 は、モジュール指定子とともにモジュールインポートステートメントにさらに多くの情報を渡すためのインライン構文です。
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()
を呼び出すことによってのみ更新されます。
import EventEmitter from 'node:events';
const e = new EventEmitter();
import { readFile } from 'node:fs';
readFile('./foo.txt', (err, source) => {
if (err) {
console.error(err);
} else {
console.log(source);
}
});
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
- <string> 現在のモジュールのディレクトリ名。これは、
import.meta.filename
のpath.dirname()
と同じです。
import.meta.filename
Added in: v21.2.0, v20.11.0
[Stable: 1 - Experimental]
Stable: 1 Stability: 1.2 - Release candidate
- <string> 現在のモジュールの完全な絶対パスとファイル名。シンボリックリンクは解決されます。
- これは、
import.meta.url
のurl.fileURLToPath()
と同じです。
import.meta.url
- <string> モジュールの絶対
file:
URL。
これは、現在のモジュールファイルのURLを提供するブラウザと同じように定義されます。
これにより、相対ファイル読み込みなどの便利なパターンが可能になります。
import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
import.meta.resolve(specifier)
[History]
Version | Changes |
---|---|
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.0 | WHATWG URL オブジェクトの parentURL パラメーターへのサポートを追加します。 |
v13.9.0, v12.16.2 | Added in: v13.9.0, v12.16.2 |
[Stable: 1 - Experimental]
Stable: 1 Stability: 1.2 - Release candidate
import.meta.resolve
は、各モジュールにスコープされたモジュール相対解決関数で、URL 文字列を返します。
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番目の引数を受け入れます。
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 モジュールのデフォルトのインポートまたは対応する構文シュガーを使用して確実にインポートできます。
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'
または動的なインポートを使用するときに直接観察できます。
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 モジュールを考えてみます。
// cjs.cjs
exports.name = 'exported';
上記のモジュールは、ES モジュールで名前付きインポートをサポートします。
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の違い
require
、exports
、module.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_PATH
は import
指定子の解決の一部ではありません。 この動作が必要な場合は、シンボリックリンクを使用してください。
require.extensions
がない
require.extensions
は import
では使用されません。 モジュールのカスタマイズフックで代替を提供できます。
require.cache
がない
ESモジュールローダーには独自のキャッシュがあるため、require.cache
は import
では使用されません。
JSONモジュール
[履歴]
バージョン | 変更点 |
---|---|
v23.1.0 | JSONモジュールは実験的ではなくなりました。 |
JSONファイルは import
で参照できます。
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
に以下が含まれているとします。
import * as M from './module.wasm';
console.log(M);
以下のように実行した場合:
node --experimental-wasm-modules index.mjs
module.wasm
のインスタンス化のためのエクスポートインターフェースが提供されます。
トップレベルの await
Added in: v14.8.0
await
キーワードは、ECMAScript モジュールのトップレベルの本体で使用できます。
a.mjs
に以下が含まれていると仮定します。
export const five = await Promise.resolve(5);
そして、b.mjs
に以下が含まれていると仮定します。
import { five } from './a.mjs';
console.log(five); // Logs `5`
node b.mjs # works
トップレベルの await
式が解決されない場合、node
プロセスは 13
の ステータスコード で終了します。
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があります。