モジュール: 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 | トップレベル Await のフラグを解除しました。 |
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'
// 出力: 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
指定子が異なるクエリまたはフラグメントを持つ場合、モジュールは複数回ロードされます。
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
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 | インポートアサーションからインポート属性に切り替え。 |
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()
式
動的な import()
は、CommonJS と ES モジュールの両方でサポートされています。CommonJS モジュールでは、ES モジュールをロードするために使用できます。
import.meta
import.meta
メタプロパティは、次のプロパティを含む Object
です。これは ES モジュールでのみサポートされています。
import.meta.dirname
追加バージョン: v21.2.0, v20.11.0
- <string> 現在のモジュールのディレクトリ名。これは、
import.meta.filename
のpath.dirname()
と同じです。
import.meta.filename
追加バージョン: v21.2.0, v20.11.0
- <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)
[履歴]
バージョン | 変更点 |
---|---|
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 | 追加バージョン: v13.9.0, v12.16.2 |
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 | CJS 名前空間に 'module.exports' エクスポートマーカーを追加しました。 |
v14.13.0 | v14.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 モジュールのデフォルトインポートまたは対応する糖衣構文を使用して確実にインポートできます。
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
がない
require.cache
は、ES モジュールローダーが独自の分離されたキャッシュを持っているため、import
では使用されません。
JSON モジュール
[履歴]
バージョン | 変更 |
---|---|
v23.1.0 | JSON モジュールは実験的ではなくなりました。 |
JSON ファイルは import
で参照できます。
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
があるとします。
import * as M from './module.wasm'
console.log(M)
これを次のコマンドで実行すると:
node --experimental-wasm-modules index.mjs
module.wasm
のインスタンス化のためのエクスポートインターフェースが提供されます。
トップレベル await
追加: v14.8.0
await
キーワードは、ECMAScript モジュールのトップレベルの本体で使用できます。
例えば、次のような a.mjs
があるとします。
export const five = await Promise.resolve(5)
そして、次のような b.mjs
があるとします。
import { five } from './a.mjs'
console.log(five) // `5` をログ出力
node b.mjs # 動作します
トップレベルの await
式が解決されない場合、node
プロセスは 13
のステータスコードで終了します。
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があります。