モジュール: パッケージ
[履歴]
バージョン | 変更 |
---|---|
v14.13.0, v12.20.0 | "exports" パターンのサポートを追加 |
v14.6.0, v12.19.0 | パッケージ "imports" フィールドを追加 |
v13.7.0, v12.17.0 | 条件付きエクスポートのフラグを解除 |
v13.7.0, v12.16.0 | --experimental-conditional-exports オプションを削除。12.16.0 では、条件付きエクスポートは依然として --experimental-modules の背後にある |
v13.6.0, v12.16.0 | 名前を使用してパッケージを参照する自己参照を解除 |
v12.7.0 | 古典的な "main" フィールドに代わる、より強力な代替手段として "exports" package.json フィールドを導入 |
v12.0.0 | package.json の "type" フィールドを介して .js ファイル拡張子を使用する ES モジュールのサポートを追加 |
はじめに
パッケージは、package.json
ファイルによって記述されるフォルダーツリーです。パッケージは、package.json
ファイルを含むフォルダーと、次の package.json
ファイルを含むフォルダー、または node_modules
という名前のフォルダーに達するまで、すべてのサブフォルダーで構成されます。
このページでは、package.json
ファイルを作成するパッケージ作成者向けのガイダンスと、Node.js によって定義された package.json
フィールドのリファレンスを提供します。
モジュールシステムの決定
はじめに
Node.js は、初期入力として node
に渡された場合、または import
文または import()
式によって参照された場合、次のものを ES モジュール として扱います。
.mjs
拡張子のファイル。- 最も近い親の
package.json
ファイルに、トップレベルの"type"
フィールドが"module"
の値で含まれている場合の.js
拡張子のファイル。 --eval
に引数として渡された文字列、または--input-type=module
フラグを使用してnode
にSTDIN
を介してパイプされた文字列。import
やexport
文、import.meta
など、ES モジュール としてのみ正常に解析される構文を含むコードで、解釈方法を示す明示的なマーカーがないもの。明示的なマーカーとは、.mjs
や.cjs
拡張子、package.json
の"type"
フィールドに"module"
または"commonjs"
の値があること、または--input-type
フラグです。動的なimport()
式は CommonJS と ES モジュールの両方でサポートされており、ファイルを ES モジュールとして扱うことを強制することはありません。構文検出 を参照してください。
Node.js は、初期入力として node
に渡された場合、または import
文または import()
式によって参照された場合、次のものを CommonJS として扱います。
.cjs
拡張子のファイル。- 最も近い親の
package.json
ファイルに、トップレベルのフィールド"type"
が"commonjs"
の値で含まれている場合の.js
拡張子のファイル。 --eval
や--print
に引数として渡された文字列、または--input-type=commonjs
フラグを使用してnode
にSTDIN
を介してパイプされた文字列。- 親の
package.json
ファイルがないか、最も近い親のpackage.json
ファイルにtype
フィールドがない.js
拡張子のファイルで、コードが CommonJS として正常に評価できるもの。言い換えれば、Node.js はこのような「あいまいな」ファイルを最初に CommonJS として実行しようと試み、パーサーが ES モジュール構文を見つけたために CommonJS としての評価が失敗した場合、ES モジュールとして評価し直します。
「あいまいな」ファイルに ES モジュール構文を記述するとパフォーマンスコストが発生するため、可能な限り明示的に記述することをお勧めします。特に、パッケージ作成者は、すべてのソースが CommonJS であるパッケージであっても、常に package.json
ファイルに "type"
フィールドを含める必要があります。パッケージの type
を明示的に記述することで、Node.js のデフォルトのタイプが将来変更された場合にもパッケージは将来にわたって使用可能になり、ビルドツールやローダーがパッケージ内のファイルの解釈方法を決定しやすくなります。
構文検出
[履歴]
バージョン | 変更 |
---|---|
v22.7.0 | 構文検出がデフォルトで有効化されました。 |
v21.1.0, v20.10.0 | 追加: v21.1.0, v20.10.0 |
Node.js は、曖昧な入力のソースコードを検査して、ES モジュール構文が含まれているかどうかを判断します。そのような構文が検出された場合、入力は ES モジュールとして扱われます。
曖昧な入力は、次のように定義されます。
.js
拡張子を持つファイル、または拡張子を持たないファイル。そして、制御するpackage.json
ファイルがないか、type
フィールドがないかのいずれか。--input-type
が指定されていない場合の文字列入力 (--eval
またはSTDIN
)。
ES モジュール構文は、CommonJS として評価されたときに例外をスローする構文として定義されます。これには以下が含まれます。
import
文(ただし、CommonJS でも有効なimport()
式は除く)。export
文。import.meta
参照。- モジュールの最上位レベルでの
await
。 - CommonJS ラッパー変数(
require
、module
、exports
、__dirname
、__filename
)の字句的な再宣言。
モジュールローダー
Node.js には、指定子を解決し、モジュールをロードするための 2 つのシステムがあります。
CommonJS モジュールローダーがあります。
- 完全同期です。
require()
コールの処理を担当します。- モンキーパッチ可能です。
- フォルダをモジュールとしてサポートしています。
- 指定子を解決する場合、正確な一致が見つからない場合、拡張子(
.js
、.json
、最後に.node
)を追加してから、フォルダをモジュールとして解決しようとします。 .json
を JSON テキストファイルとして扱います。.node
ファイルは、process.dlopen()
でロードされるコンパイル済みアドオンモジュールとして解釈されます。.json
または.node
拡張子を持たないすべてのファイルを JavaScript テキストファイルとして扱います。- モジュールグラフが同期している場合(最上位レベルの
await
を含まない場合)のみ、CommonJS モジュールから ECMAScript モジュールをロードするために使用できます。JavaScript テキストファイルで ECMAScript モジュールでないファイルをロードする場合、そのファイルは CommonJS モジュールとしてロードされます。
ECMAScript モジュールローダーがあります。
require()
のためにモジュールをロードする場合を除き、非同期です。import
文とimport()
式の処理を担当します。- モンキーパッチ可能ではありません。ローダーフックを使用してカスタマイズできます。
- フォルダをモジュールとしてサポートしていません。ディレクトリインデックス(例:
'./startup/index.js'
)は完全に指定する必要があります。 - 拡張子の検索を行いません。指定子が相対または絶対ファイル URL である場合、ファイル拡張子を指定する必要があります。
- JSON モジュールをロードできますが、import type 属性が必要です。
- JavaScript テキストファイルには
.js
、.mjs
、.cjs
拡張子のみを受け入れます。 - JavaScript CommonJS モジュールをロードするために使用できます。このようなモジュールは
cjs-module-lexer
を通して渡され、静的解析によって決定できる場合は、名前付きエクスポートを識別しようとします。インポートされた CommonJS モジュールの URL は絶対パスに変換され、その後、CommonJS モジュールローダーによってロードされます。
package.json
とファイル拡張子
パッケージ内では、package.json
の"type"
フィールドによって、Node.js が.js
ファイルをどのように解釈するかが定義されます。package.json
ファイルに"type"
フィールドがない場合、.js
ファイルはCommonJSとして扱われます。
package.json
の"type"
値が"module"
の場合、Node.js はそのパッケージ内の.js
ファイルをES モジュール構文を使用するものとして解釈します。
"type"
フィールドは、初期のエントリポイント(node my-app.js
)だけでなく、import
文やimport()
式によって参照されるファイルにも適用されます。
// my-app.js、"type": "module"を持つpackage.jsonファイルが同じフォルダにあるため、ESモジュールとして扱われます。
import './startup/init.js'
// ./startupにはpackage.jsonファイルがないため、上位レベルの"type"値を継承し、ESモジュールとして読み込まれます。
import 'commonjs-package'
// ./node_modules/commonjs-package/package.jsonに"type"フィールドがないか、"type": "commonjs"が含まれているため、CommonJSとして読み込まれます。
import './node_modules/commonjs-package/index.js'
// ./node_modules/commonjs-package/package.jsonに"type"フィールドがないか、"type": "commonjs"が含まれているため、CommonJSとして読み込まれます。
.mjs
で終わるファイルは、最も近い親のpackage.json
に関係なく、常にES モジュールとして読み込まれます。
.cjs
で終わるファイルは、最も近い親のpackage.json
に関係なく、常にCommonJSとして読み込まれます。
import './legacy-file.cjs'
// .cjsは常にCommonJSとして読み込まれるため、CommonJSとして読み込まれます。
import 'commonjs-package/src/index.mjs'
// .mjsは常にESモジュールとして読み込まれるため、ESモジュールとして読み込まれます。
.mjs
と.cjs
拡張子は、同じパッケージ内で型を混在させるために使用できます。
"type": "module"
パッケージ内では、.cjs
拡張子を使用してファイルに名前を付けることで、特定のファイルをCommonJSとして解釈するように Node.js に指示できます(.js
と.mjs
の両方のファイルは"module"
パッケージ内では ES モジュールとして扱われるため)。"type": "commonjs"
パッケージ内では、.mjs
拡張子を使用してファイルに名前を付けることで、特定のファイルをES モジュールとして解釈するように Node.js に指示できます(.js
と.cjs
の両方のファイルは"commonjs"
パッケージ内では CommonJS として扱われるため)。
--input-type
フラグ
追加バージョン: v12.0.0
--eval
(または -e
)に引数として渡された文字列、または STDIN
経由で node
にパイプされた文字列は、--input-type=module
フラグが設定されている場合、ES モジュールとして扱われます。
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"
echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module
完全性を期すために、文字列入力を CommonJS として明示的に実行するための --input-type=commonjs
もあります。 --input-type
が指定されていない場合は、これがデフォルトの動作です。
パッケージマネージャーの決定
すべての Node.js プロジェクトは、公開後にはすべてのパッケージマネージャーでインストール可能であることが期待されますが、開発チームは特定のパッケージマネージャーを使用する必要があることがよくあります。このプロセスを容易にするために、Node.js には、Node.js がインストールされていれば、すべてのパッケージマネージャーを環境で透過的に利用可能にすることを目的とした Corepack というツールが付属しています。
デフォルトでは、Corepack は特定のパッケージマネージャーを強制せず、各 Node.js リリースに関連付けられた一般的な「最後に正常に動作した」バージョンを使用しますが、プロジェクトの package.json
に "packageManager"
フィールドを設定することで、このエクスペリエンスを向上させることができます。
パッケージのエントリーポイント
パッケージの package.json
ファイルでは、"main"
と "exports"
の 2 つのフィールドでパッケージのエントリーポイントを定義できます。どちらのフィールドも、ES モジュールと CommonJS モジュールの両方のエントリーポイントに適用されます。
"main"
フィールドは、すべてのバージョンの Node.js でサポートされていますが、その機能は限定的です。パッケージのメインエントリーポイントのみを定義します。
"exports"
は、"main"
の最新の代替手段であり、複数のエントリーポイントの定義、環境間の条件付きエントリー解決のサポート、および"exports"
で定義されているもの以外のエントリーポイントの防止を可能にします。このカプセル化により、モジュール作成者はパッケージのパブリックインターフェースを明確に定義できます。
現在サポートされているバージョンの Node.js をターゲットとする新しいパッケージの場合、"exports"
フィールドをお勧めします。Node.js 10 以前をサポートするパッケージの場合は、"main"
フィールドが必要です。"exports"
と "main"
の両方が定義されている場合、サポートされているバージョンの Node.js では、"exports"
フィールドが "main"
フィールドよりも優先されます。
"exports"
内では、条件付きエクスポート を使用して、パッケージが require
経由で参照されるか import
経由で参照されるかなど、環境ごとに異なるパッケージエントリーポイントを定義できます。単一のパッケージで CommonJS と ES モジュールの両方をサポートする方法の詳細については、デュアル CommonJS/ES モジュールパッケージのセクション を参照してください。
"exports"
フィールドを導入する既存のパッケージは、package.json
(例: require('your-package/package.json')
)を含む、定義されていないエントリーポイントをパッケージのコンシューマーが使用することを防ぎます。これは、おそらく破壊的変更になります。
"exports"
の導入を破壊的変更にしないようにするには、以前にサポートされていたすべてのエントリーポイントをエクスポートするようにします。パッケージのパブリック API が明確に定義されるように、エントリーポイントを明示的に指定することをお勧めします。たとえば、以前に main
、lib
、feature
、および package.json
をエクスポートしていたプロジェクトでは、次の package.exports
を使用できます。
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./feature": "./feature/index.js",
"./feature/index": "./feature/index.js",
"./feature/index.js": "./feature/index.js",
"./package.json": "./package.json"
}
}
あるいは、拡張子付きと拡張子なしのサブパスを含む、フォルダ全体をエクスポートパターンを使用してエクスポートすることもできます。
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/*": "./lib/*.js",
"./lib/*.js": "./lib/*.js",
"./feature": "./feature/index.js",
"./feature/*": "./feature/*.js",
"./feature/*.js": "./feature/*.js",
"./package.json": "./package.json"
}
}
上記により、マイナーなパッケージバージョンの下位互換性が提供されるため、パッケージの将来のメジャーな変更により、エクスポートを公開されている特定の機能エクスポートのみに適切に制限できます。
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./feature/*.js": "./feature/*.js",
"./feature/internal/*": null
}
}
メインエントリポイントのエクスポート
新しいパッケージを作成する際には、"exports"
フィールドを使用することをお勧めします。
{
"exports": "./index.js"
}
"exports"
フィールドが定義されている場合、パッケージのすべてのサブパスはカプセル化され、インポーターから利用できなくなります。例えば、require('pkg/subpath.js')
はERR_PACKAGE_PATH_NOT_EXPORTED
エラーをスローします。
このエクスポートのカプセル化は、ツールを使用する場合やパッケージの semver アップグレードを処理する場合に、パッケージインターフェースに関するより信頼性の高い保証を提供します。require('/path/to/node_modules/pkg/subpath.js')
などのパッケージの絶対サブパスの直接的な require は、依然としてsubpath.js
をロードするため、これは強力なカプセル化ではありません。
現在サポートされているすべての Node.js バージョンと最新のビルドツールは"exports"
フィールドをサポートしています。古いバージョンの Node.js または関連するビルドツールを使用しているプロジェクトでは、同じモジュールを指す"main"
フィールドを"exports"
と共に含めることで互換性を達成できます。
{
"main": "./index.js",
"exports": "./index.js"
}
サブパスのエクスポート
追加されたバージョン: v12.7.0
"exports"
フィールドを使用する場合、メインエントリポイントを.
サブパスとして扱うことで、メインエントリポイントと共にカスタムサブパスを定義できます。
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
これで、"exports"
で定義されたサブパスのみがコンシューマーによってインポートできます。
import submodule from 'es-module-package/submodule.js'
// ./node_modules/es-module-package/src/submodule.js をロードします
一方、他のサブパスはエラーになります。
import submodule from 'es-module-package/private-module.js'
// ERR_PACKAGE_PATH_NOT_EXPORTED をスローします
サブパス内の拡張子
パッケージ作成者は、拡張子付き(import 'pkg/subpath.js'
)または拡張子なし(import 'pkg/subpath'
)のサブパスをエクスポートで提供する必要があります。これにより、エクスポートされた各モジュールに対してサブパスが 1 つのみになり、すべての依存関係が同じ一貫性のある指定子をインポートするため、コンシューマーにとってパッケージ契約が明確になり、パッケージサブパスの補完が簡素化されます。
従来、パッケージは拡張子なしのスタイルを使用することが多く、可読性とパッケージ内のファイルの実際のパスを隠すという利点がありました。
インポートマップがブラウザやその他の JavaScript ランタイムでのパッケージ解決の標準を提供するようになったため、拡張子なしのスタイルを使用すると、インポートマップの定義が膨張する可能性があります。明示的なファイル拡張子を使用すると、可能な限り複数のサブパスをマップするためにパッケージフォルダーマッピングを使用できるようになり、パッケージサブパスエクスポートごとに個別のマップエントリを作成する必要がなくなります。これは、相対パスと絶対パスのインポート指定子で完全な指定子パスを使用するという要件も反映しています。
エクスポートのシンタックスシュガー
追加バージョン: v12.11.0
"."
エクスポートのみの場合、"exports"
フィールドは、直接的な"exports"
フィールド値としてこのケースのためのシンタックスシュガーを提供します。
{
"exports": {
".": "./index.js"
}
}
は以下のように記述できます。
{
"exports": "./index.js"
}
サブパスインポート
追加バージョン: v14.6.0, v12.19.0
"exports"
フィールドに加えて、パッケージ自身からのインポート指定子にのみ適用されるプライベートマッピングを作成するためのパッケージ"imports"
フィールドがあります。
"imports"
フィールドのエントリは、外部パッケージ指定子と区別するために常に#
で始める必要があります。
例えば、"imports"
フィールドを使用して、内部モジュールに対して条件付きエクスポートの利点を取得できます。
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
ここでimport '#dep'
は、外部パッケージdep-node-native
(そのエクスポートを含む)の解決を取得せず、代わりに他の環境ではパッケージを基準としたローカルファイル./dep-polyfill.js
を取得します。
"exports"
フィールドとは異なり、"imports"
フィールドは外部パッケージへのマッピングを許可します。
"imports"
フィールドの解決ルールは、それ以外の場合"exports"
フィールドと同様です。
サブパスパターン
[履歴]
バージョン | 変更 |
---|---|
v16.10.0, v14.19.0 | "imports"フィールドでパターントレーラーをサポート |
v16.9.0, v14.19.0 | パターントレーラーをサポート |
v14.13.0, v12.20.0 | 追加バージョン: v14.13.0, v12.20.0 |
エクスポートまたはインポートが少ないパッケージの場合、各エクスポートサブパスエントリを明示的にリストすることをお勧めします。しかし、多数のサブパスを持つパッケージでは、package.json
の肥大化とメンテナンスの問題が発生する可能性があります。
これらのユースケースでは、サブパスエクスポートパターンを使用できます。
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
*
マップは、文字列置換構文のみであるため、ネストされたサブパスをそのまま公開します。
右辺の*
のすべてのインスタンスは、/
セパレータを含む場合でも、この値に置き換えられます。
import featureX from 'es-module-package/features/x.js'
// ./node_modules/es-module-package/src/features/x.js をロードします
import featureY from 'es-module-package/features/y/y.js'
// ./node_modules/es-module-package/src/features/y/y.js をロードします
import internalZ from '#internal/z.js'
// ./node_modules/es-module-package/src/internal/z.js をロードします
これは、ファイル拡張子に関する特別な処理を行わない直接的な静的マッチングと置換です。マッピングの両側に"*.js"
を含めることで、公開されたパッケージエクスポートは JS ファイルのみに制限されます。
エクスポートが静的に列挙可能であるという特性は、エクスポートパターンでも維持されます。これは、パッケージの個々のエクスポートを、右辺のターゲットパターンをパッケージ内のファイルリストに対する**
グロブとして扱うことで決定できるためです。node_modules
パスはエクスポートターゲットでは禁止されているため、この展開はパッケージ自体のファイルのみに依存します。
プライベートサブフォルダーをパターンから除外するには、null
ターゲットを使用できます。
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null
}
}
import featureInternal from 'es-module-package/features/private-internal/m.js'
// エラー発生: ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js'
// ./node_modules/es-module-package/src/features/x.js をロードします
条件付きエクスポート
[履歴]
バージョン | 変更内容 |
---|---|
v13.7.0, v12.16.0 | 条件付きエクスポートのフラグを外しました。 |
v13.2.0, v12.16.0 | 追加: v13.2.0, v12.16.0 |
条件付きエクスポートは、特定の条件に応じて異なるパスにマップする方法を提供します。CommonJS と ES モジュールのインポートの両方でサポートされています。
たとえば、require()
とimport
に対して異なる ES モジュールエクスポートを提供したいパッケージは、次のように記述できます。
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js は、条件を定義する際には最も具体的なものから最も具体的なものへと順にリストされている以下の条件を実装します。
"node-addons"
-"node"
と同様に、任意の Node.js 環境に一致します。この条件は、より普遍的でネイティブアドオンに依存しないエントリポイントとは対照的に、ネイティブ C++アドオンを使用するエントリポイントを提供するために使用できます。この条件は、--no-addons
フラグで無効にすることができます。"node"
- 任意の Node.js 環境に一致します。CommonJS または ES モジュールファイルです。ほとんどの場合、Node.js プラットフォームを明示的に指定する必要はありません。"import"
- パッケージがimport
またはimport()
、または ECMAScript モジュールローダーによる任意のトップレベルインポートまたは解決操作によってロードされた場合に一致します。ターゲットファイルのモジュール形式に関係なく適用されます。常に"require"
と相互に排他的です。"require"
- パッケージがrequire()
によってロードされた場合に一致します。参照されるファイルはrequire()
でロード可能である必要がありますが、条件はターゲットファイルのモジュール形式に関係なく一致します。CommonJS、JSON、ネイティブアドオン、ES モジュールなど、様々な形式が想定されます。常に"import"
と相互に排他的です。"module-sync"
- パッケージがimport
、import()
、またはrequire()
のいずれかでロードされたかに関係なく一致します。形式は、モジュールグラフにトップレベルの await を含まない ES モジュールであることが期待されます。もし含まれている場合、モジュールがrequire()
されたときにERR_REQUIRE_ASYNC_MODULE
がスローされます。"default"
- 常に一致する一般的なフォールバックです。CommonJS または ES モジュールファイルです。この条件は常に最後に来るべきです。
"exports"
オブジェクト内では、キーの順序が重要です。条件の照合中は、前のエントリの方が優先順位が高く、後のエントリよりも優先されます。一般的なルールとしては、条件はオブジェクトの順序で最も具体的なものから最も具体的なものへと記述する必要があります。
"import"
と"require"
の条件を使用すると、いくつかの危険性があります。これについては、デュアル CommonJS/ES モジュールパッケージのセクションで詳しく説明します。
"node-addons"
条件を使用して、ネイティブ C++アドオンを使用するエントリポイントを提供できます。ただし、この条件は--no-addons
フラグで無効にすることができます。"node-addons"
を使用する場合は、"default"
を、より普遍的なエントリポイント(たとえば、ネイティブアドオンの代わりに WebAssembly を使用する)を提供する拡張機能として扱うことをお勧めします。
条件付きエクスポートは、エクスポートサブパスにも拡張できます。例:
{
"exports": {
".": "./index.js",
"./feature.js": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
これは、require('pkg/feature.js')
とimport 'pkg/feature.js'
が、Node.js とその他の JS 環境間で異なる実装を提供できるパッケージを定義します。
環境ブランチを使用する場合は、可能な限り常に"default"
条件を含めてください。"default"
条件を提供することで、不明な JS 環境でもこの普遍的な実装を使用できるようになり、これらの JS 環境が条件付きエクスポートを持つパッケージをサポートするために既存の環境になりすます必要がなくなります。このため、"node"
と"browser"
の条件ブランチを使用するよりも、"node"
と"default"
の条件ブランチを使用する方が通常は好ましいです。
ネストされた条件
直接マッピングに加えて、Node.js はネストされた条件オブジェクトもサポートしています。
例えば、Node.js では使用できるがブラウザでは使用できないデュアルモードのエントリポイントのみを持つパッケージを定義するには、次のようにします。
{
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs"
}
}
フラットな条件と同様に、条件は順に照合され続けます。ネストされた条件にマッピングがない場合、親条件の残りの条件の確認を続けます。このように、ネストされた条件は、ネストされた JavaScript の if
文と同様に動作します。
ユーザー条件の解決
追加されたバージョン: v14.9.0、v12.19.0
Node.js を実行する際には、--conditions
フラグを使用してカスタムユーザー条件を追加できます。
node --conditions=development index.js
これにより、パッケージのインポートとエクスポートで "development"
条件が解決され、既存の "node"
、"node-addons"
、"default"
、"import"
、"require"
条件が適切に解決されます。
繰り返しフラグを使用して、任意の数のカスタム条件を設定できます。
一般的な条件は、英数字のみを含み、必要に応じて ":"、"-"、または "=" をセパレータとして使用する必要があります。それ以外のものを使用すると、Node.js 以外の環境で互換性の問題が発生する可能性があります。
Node.js では、条件には非常に少ない制限しかありませんが、具体的には以下のものがあります。
コミュニティ条件の定義
"import"
、"require"
、"node"
、"module-sync"
、"node-addons"
、"default"
条件(Node.js コアに実装済み)以外の条件文字列は、デフォルトでは無視されます。
他のプラットフォームでは他の条件が実装される可能性があり、--conditions
/ -C
フラグ を使用して Node.js でユーザー条件を有効にすることができます。
カスタムパッケージ条件は、正しい使用方法を確保するために明確な定義が必要であるため、一般的な既知のパッケージ条件とその厳密な定義のリストを以下に示し、エコシステムの調整に役立てます。
"types"
- 型システムが、指定されたエクスポートの型ファイルを確認するために使用できます。この条件は常に最初に含める必要があります。"browser"
- 任意の Web ブラウザ環境。"development"
- 開発専用環境のエントリポイントを定義するために使用できます。例えば、開発モードで実行する場合に、より良いエラーメッセージなどの追加のデバッグコンテキストを提供するために使用できます。常に"production"
と相互に排他的である必要があります。"production"
- 本番環境のエントリポイントを定義するために使用できます。常に"development"
と相互に排他的である必要があります。
他のランタイムの場合、プラットフォーム固有の条件キー定義は、WinterCG によって Runtime Keys 提案仕様で維持されています。
このリストに新しい条件定義を追加するには、このセクションの Node.js ドキュメント にプルリクエストを作成します。ここで新しい条件定義をリストする要件は次のとおりです。
- 定義は、すべての導入者にとって明確で曖昧さがありません。
- 条件が必要な理由のユースケースを明確に説明する必要があります。
- 既存の実装使用例が十分にある必要があります。
- 条件名は、他の条件定義または広く使用されている条件と競合してはなりません。
- 条件定義のリストは、そうでなければ不可能なエコシステムへの調整上の利点を提供する必要があります。例えば、これは、企業固有またはアプリケーション固有の条件には必ずしも当てはまりません。
- 条件は、Node.js ユーザーが Node.js コア ドキュメントにあることを期待するようなものである必要があります。
"types"
条件は良い例です。Runtime Keys 提案には実際には属しませんが、Node.js ドキュメントではここに適切に収まります。
上記の定義は、適宜専用の条件レジストリに移動される可能性があります。
名前を使用してパッケージを参照する
[履歴]
バージョン | 変更 |
---|---|
v13.6.0, v12.16.0 | 名前を使用してパッケージを参照する際のフラグを外しました。 |
v13.1.0, v12.16.0 | 追加:v13.1.0, v12.16.0 |
パッケージ内では、パッケージのpackage.json
の"exports"
フィールドで定義された値は、パッケージの名前を使用して参照できます。例えば、package.json
が以下のようになっているとします。
// package.json
{
"name": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
そのパッケージ内の任意のモジュールは、パッケージ自体のエクスポートを参照できます。
// ./a-module.mjs
import { something } from 'a-package' // ./index.mjsから"something"をインポートします。
自己参照は、package.json
に"exports"
が存在する場合にのみ利用でき、"exports"
(package.json
内)が許可するもののみをインポートできます。そのため、前のパッケージを考えると、以下のコードはランタイムエラーを生成します。
// ./another-module.mjs
// ./m.mjsから"another"をインポートします。"package.json"の"exports"フィールドが
// "./m.mjs"という名前のエクスポートを提供していないため失敗します。
import { another } from 'a-package/m.mjs'
自己参照は、ES モジュールと CommonJS モジュールの両方でrequire
を使用する場合にも利用できます。例えば、このコードも動作します。
// ./a-module.js
const { something } = require('a-package/foo.js') // ./foo.jsからロードします。
最後に、自己参照はスコープ付きパッケージでも機能します。例えば、このコードも動作します。
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42
// ./other.js
console.log(require('@my/package'))
$ node other.js
42
Dual CommonJS/ES モジュールパッケージ
詳細はパッケージの例のリポジトリを参照してください。
Node.js package.json
フィールド定義
このセクションでは、Node.js ランタイムで使用されるフィールドについて説明します。他のツール(npmなど)は、Node.js によって無視され、ここで説明されていない追加のフィールドを使用します。
package.json
ファイルの以下のフィールドは Node.js で使用されます。
"name"
- パッケージ内で名前付きインポートを使用する場合に関連します。パッケージマネージャーによってもパッケージの名前として使用されます。"main"
- エクスポートが指定されていない場合、およびエクスポートが導入される前のバージョンの Node.js では、パッケージをロードする場合のデフォルトモジュールです。"packageManager"
- パッケージへの貢献時に推奨されるパッケージマネージャー。Corepackシムによって活用されます。"type"
-.js
ファイルを CommonJS モジュールとしてロードするか ES モジュールとしてロードするかを決定するパッケージの種類。"exports"
- パッケージのエクスポートと条件付きエクスポート。存在する場合、パッケージ内からロードできるサブモジュールを制限します。"imports"
- パッケージ自体のモジュールで使用するためのパッケージインポート。
"name"
[履歴]
バージョン | 変更 |
---|---|
v13.6.0, v12.16.0 | --experimental-resolve-self オプションを削除しました。 |
v13.1.0, v12.16.0 | 追加されました: v13.1.0, v12.16.0 |
- 型: <string>
{
"name": "package-name"
}
"name"
フィールドは、パッケージの名前を定義します。npm レジストリへの公開には、特定の要件 を満たす名前が必要です。
"name"
フィールドは、"exports"
フィールドに加えて、名前を使用してパッケージを自己参照 するために使用できます。
"main"
追加されました: v0.4.0
- 型: <string>
{
"main": "./index.js"
}
"main"
フィールドは、node_modules
検索を介して名前でインポートされた場合のパッケージのエントリポイントを定義します。その値はパスです。
パッケージに"exports"
フィールドがある場合、名前でパッケージをインポートする際には、このフィールドが"main"
フィールドよりも優先されます。
また、パッケージディレクトリがrequire()
を介してロードされた場合に使用されるスクリプトも定義します。
// これは ./path/to/directory/index.js に解決されます。
require('./path/to/directory')
"packageManager"
追加されました: v16.9.0, v14.19.0
- 型: <string>
{
"packageManager": "<package manager name>@<version>"
}
"packageManager"
フィールドは、現在のプロジェクトで使用するパッケージマネージャーを定義します。これは、サポートされているパッケージマネージャーのいずれかに設定でき、Node.js 以外のものをインストールする必要なく、チームがまったく同じパッケージマネージャーのバージョンを使用することを保証します。
このフィールドは現在実験的なものであり、オプトインする必要があります。手順については、Corepackページを参照してください。
"type"
[履歴]
バージョン | 変更点 |
---|---|
v13.2.0, v12.17.0 | --experimental-modules フラグを削除 |
v12.0.0 | 追加: v12.0.0 |
- 型: <string>
"type"
フィールドは、そのpackage.json
ファイルを最も近い親とするすべての.js
ファイルに対して Node.js が使用するモジュール形式を定義します。
.js
で終わるファイルは、最も近い親のpackage.json
ファイルに最上位レベルのフィールド"type"
があり、その値が"module"
の場合、ES モジュールとして読み込まれます。
最も近い親のpackage.json
は、現在のフォルダ、そのフォルダの親フォルダなどを検索し、node_modules
フォルダまたはボリュームルートに到達するまでに見つかった最初のpackage.json
として定義されます。
// package.json
{
"type": "module"
}
# 前のpackage.jsonと同じフォルダ内 {#in-same-folder-as-preceding-packagejson}
node my-app.js # ESモジュールとして実行
最も近い親のpackage.json
に"type"
フィールドがない場合、または"type": "commonjs"
が含まれている場合、.js
ファイルはCommonJSとして扱われます。ボリュームルートに到達し、package.json
が見つからない場合も、.js
ファイルはCommonJSとして扱われます。
最も近い親のpackage.json
に"type": "module"
が含まれている場合、.js
ファイルのimport
文は ES モジュールとして扱われます。
// my-app.js、上記の例と同じ部分
import './startup.js' // package.jsonのためESモジュールとして読み込まれる
"type"
フィールドの値に関係なく、.mjs
ファイルは常に ES モジュールとして、.cjs
ファイルは常に CommonJS として扱われます。
"exports"
[履歴]
バージョン | 変更点 |
---|---|
v14.13.0, v12.20.0 | "exports" パターンのサポートを追加 |
v13.7.0, v12.17.0 | 条件付きエクスポートのフラグを外す |
v13.7.0, v12.16.0 | 論理的な条件付きエクスポートの順序付けを実装 |
v13.7.0, v12.16.0 | --experimental-conditional-exports オプションを削除。12.16.0 では、条件付きエクスポートはまだ--experimental-modules の背後にある |
v13.2.0, v12.16.0 | 条件付きエクスポートを実装 |
v12.7.0 | 追加: v12.7.0 |
- 型: <Object> | <string> | <string[]>
{
"exports": "./index.js"
}
"exports"
フィールドを使用すると、node_modules
ルックアップまたは自己参照によって自身の名前で読み込まれたときに、名前でインポートされるパッケージのエントリポイントを定義できます。これは Node.js 12 以降で"main"
の代替としてサポートされており、サブパスエクスポートと条件付きエクスポートを定義しながら、内部のエクスポートされていないモジュールをカプセル化できます。
条件付きエクスポートは"exports"
内でも使用でき、パッケージがrequire
経由かimport
経由かで参照されるかなど、環境ごとに異なるパッケージエントリポイントを定義できます。
"exports"
で定義されているすべてのパスは、./
から始まる相対ファイル URL でなければなりません。
"imports"
追加バージョン: v14.6.0, v12.19.0
- 型: <Object>
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
imports
フィールドのエントリは、#
で始まる文字列でなければなりません。
パッケージインポートは、外部パッケージへのマッピングを許可します。
このフィールドは、現在のパッケージのサブパスインポートを定義します。