Skip to content

Module: Packages

[Historie]

VersionÄnderungen
v14.13.0, v12.20.0Unterstützung für "exports"-Muster hinzugefügt.
v14.6.0, v12.19.0Package "imports"-Feld hinzugefügt.
v13.7.0, v12.17.0Bedingte Exporte nicht mehr als experimentell markiert.
v13.7.0, v12.16.0Option --experimental-conditional-exports entfernt. In 12.16.0 sind bedingte Exporte noch hinter --experimental-modules.
v13.6.0, v12.16.0Selbstreferenzierung eines Packages unter Verwendung seines Namens nicht mehr als experimentell markiert.
v12.7.0Einführung des Feldes "exports" in package.json als leistungsfähigere Alternative zum klassischen Feld "main".
v12.0.0Unterstützung für ES-Module mit der Erweiterung .js über das Feld "type" in package.json hinzugefügt.

Einführung

Ein Package ist eine Ordnerstruktur, die durch eine package.json-Datei beschrieben wird. Das Package besteht aus dem Ordner, der die package.json-Datei enthält, und allen Unterordnern bis zum nächsten Ordner, der eine weitere package.json-Datei enthält, oder einem Ordner namens node_modules.

Diese Seite bietet Hilfestellung für Package-Autoren beim Schreiben von package.json-Dateien sowie eine Referenz für die von Node.js definierten Felder in package.json.

Bestimmen des Modulsystems

Einführung

Node.js behandelt Folgendes als ES-Module, wenn es als anfängliche Eingabe an node übergeben oder durch import-Anweisungen oder import()-Ausdrücke referenziert wird:

  • Dateien mit der Erweiterung .mjs.
  • Dateien mit der Erweiterung .js, wenn die nächste übergeordnete package.json-Datei ein Feld "type" auf oberster Ebene mit dem Wert "module" enthält.
  • Zeichenketten, die als Argument an --eval übergeben oder über STDIN an node weitergeleitet werden, mit dem Flag --input-type=module.
  • Code, der nur Syntax enthält, die erfolgreich als ES-Module geparst wurde, wie z. B. import- oder export-Anweisungen oder import.meta, ohne explizite Markierung, wie er interpretiert werden soll. Explizite Markierungen sind die Erweiterungen .mjs oder .cjs, Felder "type" in package.json mit den Werten "module" oder "commonjs" oder das Flag --input-type. Dynamische import()-Ausdrücke werden in CommonJS- oder ES-Modulen unterstützt und würden keine Datei dazu zwingen, als ES-Modul behandelt zu werden. Siehe Syntaxerkennung.

Node.js behandelt Folgendes als CommonJS, wenn es als anfängliche Eingabe an node übergeben oder durch import-Anweisungen oder import()-Ausdrücke referenziert wird:

  • Dateien mit der Erweiterung .cjs.
  • Dateien mit der Erweiterung .js, wenn die nächste übergeordnete package.json-Datei ein Feld "type" auf oberster Ebene mit dem Wert "commonjs" enthält.
  • Zeichenketten, die als Argument an --eval oder --print übergeben oder über STDIN an node weitergeleitet werden, mit dem Flag --input-type=commonjs.
  • Dateien mit der Erweiterung .js ohne übergeordnete package.json-Datei oder bei denen die nächste übergeordnete package.json-Datei kein Feld type enthält und der Code erfolgreich als CommonJS ausgewertet werden kann. Mit anderen Worten: Node.js versucht zunächst, solche "mehrdeutigen" Dateien als CommonJS auszuführen, und versucht dann erneut, sie als ES-Module auszuwerten, wenn die Auswertung als CommonJS fehlschlägt, weil der Parser ES-Modulsyntax gefunden hat.

Das Schreiben von ES-Modulsyntax in "mehrdeutigen" Dateien verursacht Performance-Kosten. Daher wird empfohlen, dass Autoren so weit wie möglich explizit vorgehen. Insbesondere sollten Package-Autoren das Feld "type" immer in ihre package.json-Dateien aufnehmen, auch in Packages, bei denen alle Quellen CommonJS sind. Die explizite Angabe des Typs des Packages sichert das Package für den Fall ab, dass sich der Standardtyp von Node.js jemals ändert, und es erleichtert Build-Tools und Loadern auch die Bestimmung, wie die Dateien im Package interpretiert werden sollen.

Syntaxerkennung

[Historie]

VersionÄnderungen
v22.7.0Die Syntaxerkennung ist standardmäßig aktiviert.
v21.1.0, v20.10.0Hinzugefügt in: v21.1.0, v20.10.0

[Stabil: 1 - Experimentell]

Stabil: 1 Stabilität: 1.2 - Release-Kandidat

Node.js inspiziert den Quellcode von mehrdeutigen Eingaben, um festzustellen, ob er ES-Modulsyntax enthält. Wenn eine solche Syntax erkannt wird, wird die Eingabe als ES-Modul behandelt.

Mehrdeutige Eingaben sind definiert als:

  • Dateien mit der Erweiterung .js oder ohne Erweiterung; und entweder ohne steuernde package.json-Datei oder eine solche, der ein type-Feld fehlt.
  • String-Eingaben (--eval oder STDIN), wenn --input-type nicht angegeben ist.

ES-Modulsyntax ist definiert als Syntax, die bei der Auswertung als CommonJS einen Fehler auslösen würde. Dazu gehören die folgenden:

  • import-Anweisungen (aber nicht import()-Ausdrücke, die in CommonJS gültig sind).
  • export-Anweisungen.
  • import.meta-Referenzen.
  • await auf der obersten Ebene eines Moduls.
  • Lexikalische Redeklarationen der CommonJS-Wrapper-Variablen (require, module, exports, __dirname, __filename).

Modul-Loader

Node.js verfügt über zwei Systeme zur Auflösung eines Spezifizierers und zum Laden von Modulen.

Es gibt den CommonJS-Modul-Loader:

  • Er ist vollständig synchron.
  • Er ist für die Behandlung von require()-Aufrufen zuständig.
  • Er kann per Monkey-Patching verändert werden.
  • Er unterstützt Ordner als Module.
  • Wenn bei der Auflösung eines Spezifizierers keine genaue Übereinstimmung gefunden wird, wird versucht, Erweiterungen (.js, .json und schließlich .node) hinzuzufügen und dann versucht, Ordner als Module aufzulösen.
  • Er behandelt .json als JSON-Textdateien.
  • .node-Dateien werden als kompilierte Add-on-Module interpretiert, die mit process.dlopen() geladen werden.
  • Er behandelt alle Dateien ohne .json- oder .node-Erweiterungen als JavaScript-Textdateien.
  • Er kann nur zum Laden von ECMAScript-Modulen aus CommonJS-Modulen verwendet werden, wenn der Modulgraf synchron ist (der kein await auf oberster Ebene enthält). Wenn er zum Laden einer JavaScript-Textdatei verwendet wird, die kein ECMAScript-Modul ist, wird die Datei als CommonJS-Modul geladen.

Es gibt den ECMAScript-Modul-Loader:

  • Er ist asynchron, es sei denn, er wird zum Laden von Modulen für require() verwendet.
  • Er ist für die Behandlung von import-Anweisungen und import()-Ausdrücken zuständig.
  • Er kann nicht per Monkey-Patching verändert werden, sondern mit Loader-Hooks angepasst werden.
  • Er unterstützt keine Ordner als Module, Verzeichnisindizes (z. B. './startup/index.js') müssen vollständig angegeben werden.
  • Er führt keine Erweiterungssuche durch. Eine Dateierweiterung muss angegeben werden, wenn der Spezifizierer eine relative oder absolute Datei-URL ist.
  • Er kann JSON-Module laden, aber ein Importtypattribut ist erforderlich.
  • Er akzeptiert nur die Erweiterungen .js, .mjs und .cjs für JavaScript-Textdateien.
  • Er kann zum Laden von JavaScript-CommonJS-Modulen verwendet werden. Solche Module werden durch den cjs-module-lexer geleitet, um zu versuchen, benannte Exporte zu identifizieren, die verfügbar sind, wenn sie durch statische Analyse bestimmt werden können. Importierte CommonJS-Module haben ihre URLs in absolute Pfade konvertiert und werden dann über den CommonJS-Modul-Loader geladen.

package.json und Dateiendungen

Innerhalb eines Pakets definiert das Feld "type" in der package.json, wie Node.js .js-Dateien interpretieren soll. Wenn eine package.json-Datei kein Feld "type" hat, werden .js-Dateien als CommonJS behandelt.

Ein "type"-Wert von "module" in einer package.json weist Node.js an, .js-Dateien innerhalb dieses Pakets als ES-Module zu interpretieren.

Das Feld "type" gilt nicht nur für die anfänglichen Einstiegspunkte (node my-app.js), sondern auch für Dateien, auf die durch import-Anweisungen und import()-Ausdrücke verwiesen wird.

js
// my-app.js, wird als ES-Modul behandelt, da sich eine package.json-
// Datei im selben Ordner mit "type": "module" befindet.

import './startup/init.js'
// Geladen als ES-Modul, da ./startup keine package.json-Datei enthält
// und daher den "type"-Wert von einer Ebene höher erbt.

import 'commonjs-package'
// Geladen als CommonJS, da ./node_modules/commonjs-package/package.json
// entweder kein "type"-Feld hat oder "type": "commonjs" enthält.

import './node_modules/commonjs-package/index.js'
// Geladen als CommonJS, da ./node_modules/commonjs-package/package.json
// entweder kein "type"-Feld hat oder "type": "commonjs" enthält.

Dateien, die mit .mjs enden, werden immer als ES-Module geladen, unabhängig von der nächstgelegenen übergeordneten package.json.

Dateien, die mit .cjs enden, werden immer als CommonJS geladen, unabhängig von der nächstgelegenen übergeordneten package.json.

js
import './legacy-file.cjs'
// Geladen als CommonJS, da .cjs immer als CommonJS geladen wird.

import 'commonjs-package/src/index.mjs'
// Geladen als ES-Modul, da .mjs immer als ES-Modul geladen wird.

Die Erweiterungen .mjs und .cjs können verwendet werden, um Typen innerhalb desselben Pakets zu mischen:

  • Innerhalb eines Pakets mit "type": "module" kann Node.js angewiesen werden, eine bestimmte Datei als CommonJS zu interpretieren, indem sie mit der Erweiterung .cjs benannt wird (da sowohl .js- als auch .mjs-Dateien innerhalb eines "module"-Pakets als ES-Module behandelt werden).
  • Innerhalb eines Pakets mit "type": "commonjs" kann Node.js angewiesen werden, eine bestimmte Datei als ES-Modul zu interpretieren, indem sie mit der Erweiterung .mjs benannt wird (da sowohl .js- als auch .cjs-Dateien innerhalb eines "commonjs"-Pakets als CommonJS behandelt werden).

--input-type-Flag

Hinzugefügt in: v12.0.0

Strings, die als Argument an --eval (oder -e) übergeben oder über STDIN an node gepipt werden, werden als ES-Module behandelt, wenn das Flag --input-type=module gesetzt ist.

bash
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

Der Vollständigkeit halber gibt es auch --input-type=commonjs, um String-Eingaben explizit als CommonJS auszuführen. Dies ist das Standardverhalten, wenn --input-type nicht angegeben ist.

Bestimmung des Paketmanagers

[Stabil: 1 - Experimentell]

Stabil: 1 Stabilität: 1 - Experimentell

Obwohl von allen Node.js-Projekten erwartet wird, dass sie nach der Veröffentlichung von allen Paketmanagern installiert werden können, müssen ihre Entwicklungsteams häufig einen bestimmten Paketmanager verwenden. Um diesen Prozess zu erleichtern, wird Node.js mit einem Tool namens Corepack ausgeliefert, das darauf abzielt, alle Paketmanager transparent in Ihrer Umgebung verfügbar zu machen - vorausgesetzt, Sie haben Node.js installiert.

Standardmäßig erzwingt Corepack keinen bestimmten Paketmanager und verwendet die generischen "Last Known Good"-Versionen, die jeder Node.js-Version zugeordnet sind. Sie können diese Erfahrung jedoch verbessern, indem Sie das Feld "packageManager" in der package.json Ihres Projekts festlegen.

Paket-Einstiegspunkte

In der package.json-Datei eines Pakets können zwei Felder Einstiegspunkte für ein Paket definieren: "main" und "exports". Beide Felder gelten sowohl für ES-Module als auch für CommonJS-Modul-Einstiegspunkte.

Das Feld "main" wird in allen Versionen von Node.js unterstützt, aber seine Fähigkeiten sind begrenzt: Es definiert nur den Haupteinstiegspunkt des Pakets.

Das Feld "exports" bietet eine moderne Alternative zu "main" und ermöglicht die Definition mehrerer Einstiegspunkte, die bedingte Auflösungsunterstützung zwischen Umgebungen und verhindert andere Einstiegspunkte außer denen, die in "exports" definiert sind. Diese Kapselung ermöglicht es Modulautoren, die öffentliche Schnittstelle für ihr Paket klar zu definieren.

Für neue Pakete, die auf die aktuell unterstützten Versionen von Node.js abzielen, wird das Feld "exports" empfohlen. Für Pakete, die Node.js 10 und darunter unterstützen, ist das Feld "main" erforderlich. Wenn sowohl "exports" als auch "main" definiert sind, hat das Feld "exports" in unterstützten Versionen von Node.js Vorrang vor "main".

Bedingte Exporte können innerhalb von "exports" verwendet werden, um verschiedene Paket-Einstiegspunkte pro Umgebung zu definieren, einschließlich der Frage, ob das Paket über require oder über import referenziert wird. Weitere Informationen zur Unterstützung von CommonJS- und ES-Modulen in einem einzigen Paket finden Sie im Abschnitt Dual-CommonJS/ES-Modul-Pakete.

Vorhandene Pakete, die das Feld "exports" einführen, verhindern, dass Konsumenten des Pakets Einstiegspunkte verwenden, die nicht definiert sind, einschließlich der package.json (z. B. require('your-package/package.json')). Dies wird wahrscheinlich eine grundlegende Änderung sein.

Um die Einführung von "exports" nicht zu einer grundlegenden Änderung zu machen, stellen Sie sicher, dass jeder zuvor unterstützte Einstiegspunkt exportiert wird. Es ist am besten, Einstiegspunkte explizit anzugeben, damit die öffentliche API des Pakets gut definiert ist. Beispielsweise könnte ein Projekt, das zuvor main, lib, feature und die package.json exportiert hat, die folgende package.exports verwenden:

json
{
  "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"
  }
}

Alternativ könnte ein Projekt ganze Ordner mit und ohne erweiterte Unterpfade mithilfe von Exportmustern exportieren:

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"
  }
}

Wobei die obige Rückwärtskompatibilität für alle kleineren Paketversionen bietet, kann eine zukünftige wichtige Änderung für das Paket die Exporte dann richtig auf die spezifischen Feature-Exporte beschränken:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}

Haupteinstiegspunktexport

Beim Schreiben eines neuen Pakets wird die Verwendung des Feldes "exports" empfohlen:

json
{
  "exports": "./index.js"
}

Wenn das Feld "exports" definiert ist, werden alle Unterpfade des Pakets gekapselt und sind für Importeure nicht mehr verfügbar. Beispielsweise wirft require('pkg/subpath.js') einen ERR_PACKAGE_PATH_NOT_EXPORTED Fehler.

Diese Kapselung von Exporten bietet zuverlässigere Garantien über Paketschnittstellen für Tools und bei der Handhabung von Semver-Upgrades für ein Paket. Es ist keine starke Kapselung, da ein direktes Anfordern eines absoluten Unterpfads des Pakets wie require('/path/to/node_modules/pkg/subpath.js') subpath.js immer noch laden wird.

Alle derzeit unterstützten Versionen von Node.js und moderne Build-Tools unterstützen das Feld "exports". Für Projekte, die eine ältere Version von Node.js oder ein zugehöriges Build-Tool verwenden, kann die Kompatibilität erreicht werden, indem das Feld "main" zusammen mit "exports" hinzugefügt wird, das auf dasselbe Modul verweist:

json
{
  "main": "./index.js",
  "exports": "./index.js"
}

Unterpfadexporte

Hinzugefügt in: v12.7.0

Bei Verwendung des Feldes "exports" können benutzerdefinierte Unterpfade zusammen mit dem Haupteinstiegspunkt definiert werden, indem der Haupteinstiegspunkt als "." Unterpfad behandelt wird:

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}

Nun kann nur noch der definierte Unterpfad in "exports" von einem Consumer importiert werden:

js
import submodule from 'es-module-package/submodule.js'
// Lädt ./node_modules/es-module-package/src/submodule.js

Während andere Unterpfade einen Fehler auslösen:

js
import submodule from 'es-module-package/private-module.js'
// Wirft ERR_PACKAGE_PATH_NOT_EXPORTED

Erweiterungen in Unterpfaden

Paketautoren sollten entweder erweiterte (import 'pkg/subpath.js') oder erweiterungslose (import 'pkg/subpath') Unterpfade in ihren Exporten bereitstellen. Dies stellt sicher, dass es nur einen Unterpfad für jedes exportierte Modul gibt, sodass alle Abhängigen denselben konsistenten Bezeichner importieren, wodurch der Paketvertrag für Verbraucher klar bleibt und Paketunterpfadvervollständigungen vereinfacht werden.

Traditionell neigten Pakete dazu, den erweiterungslosen Stil zu verwenden, der die Vorteile der Lesbarkeit und der Maskierung des tatsächlichen Pfads der Datei innerhalb des Pakets hat.

Da Import Maps nun einen Standard für die Paketauflösung in Browsern und anderen JavaScript-Laufzeitumgebungen bieten, kann die Verwendung des erweiterungslosen Stils zu aufgeblähten Import Map-Definitionen führen. Explizite Dateierweiterungen können dieses Problem vermeiden, indem sie es der Import Map ermöglichen, eine Paketordnerzuordnung zu verwenden, um mehrere Unterpfade nach Möglichkeit zuzuordnen, anstatt eines separaten Karteneintrags pro Paketunterpfadexport. Dies spiegelt auch die Anforderung wider, den vollständigen Bezeichnerpfad in relativen und absoluten Importbezeichnern zu verwenden.

Exports sugar

Hinzugefügt in: v12.11.0

Wenn der Export "." der einzige Export ist, bietet das Feld "exports" eine Vereinfachung für diesen Fall, indem es der direkte Wert des Feldes "exports" ist.

json
{
  "exports": {
    ".": "./index.js"
  }
}

kann geschrieben werden als:

json
{
  "exports": "./index.js"
}

Subpath-Importe

Hinzugefügt in: v14.6.0, v12.19.0

Zusätzlich zum Feld "exports" gibt es ein Paketfeld "imports", um private Zuordnungen zu erstellen, die nur für Importspezifizierer innerhalb des Pakets selbst gelten.

Einträge im Feld "imports" müssen immer mit # beginnen, um sicherzustellen, dass sie von externen Paketspezifizierern unterschieden werden.

Beispielsweise kann das Feld "imports" verwendet werden, um die Vorteile bedingter Exporte für interne Module zu nutzen:

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

wobei import '#dep' nicht die Auflösung des externen Pakets dep-node-native (einschließlich seiner Exporte wiederum) erhält, sondern stattdessen die lokale Datei ./dep-polyfill.js relativ zum Paket in anderen Umgebungen erhält.

Im Gegensatz zum Feld "exports" erlaubt das Feld "imports" die Zuordnung zu externen Paketen.

Die Auflösungsregeln für das Feld "imports" sind ansonsten analog zu denen des Felds "exports".

Subpath-Muster

[Verlauf]

VersionÄnderungen
v16.10.0, v14.19.0Unterstützung für Musteranhänge im Feld "imports".
v16.9.0, v14.19.0Unterstützung für Musteranhänge.
v14.13.0, v12.20.0Hinzugefügt in: v14.13.0, v12.20.0

Für Pakete mit einer kleinen Anzahl von Exporten oder Importen empfehlen wir, jeden Subpath-Eintrag explizit aufzulisten. Bei Paketen mit einer großen Anzahl von Subpfaden kann dies jedoch zu package.json-Aufblähung und Wartungsproblemen führen.

Für diese Anwendungsfälle können stattdessen Subpath-Exportmuster verwendet werden:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}

*-Zuordnungen legen verschachtelte Subpfade offen, da es sich nur um eine String-Ersetzungssyntax handelt.

Alle Instanzen von * auf der rechten Seite werden dann durch diesen Wert ersetzt, auch wenn dieser /-Trennzeichen enthält.

js
import featureX from 'es-module-package/features/x.js'
// Lädt ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js'
// Lädt ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js'
// Lädt ./node_modules/es-module-package/src/internal/z.js

Dies ist eine direkte statische Übereinstimmung und Ersetzung ohne besondere Behandlung für Dateierweiterungen. Die Einbeziehung von "*.js" auf beiden Seiten der Zuordnung beschränkt die offengelegten Paketexporte auf nur JS-Dateien.

Die Eigenschaft, dass Exporte statisch aufzählbar sind, bleibt mit Exportmustern erhalten, da die einzelnen Exporte für ein Paket bestimmt werden können, indem das rechte Zielmuster als **-Glob gegen die Liste der Dateien innerhalb des Pakets behandelt wird. Da node_modules-Pfade in Exportzielen verboten sind, hängt diese Erweiterung nur von den Dateien des Pakets selbst ab.

Um private Unterordner von Mustern auszuschließen, können null-Ziele verwendet werden:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js'
// Wirft: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js'
// Lädt ./node_modules/es-module-package/src/features/x.js

Bedingte Exporte

[Historie]

VersionÄnderungen
v13.7.0, v12.16.0Bedingte Exporte nicht mehr als experimentell markiert.
v13.2.0, v12.16.0Hinzugefügt in: v13.2.0, v12.16.0

Bedingte Exporte bieten eine Möglichkeit, je nach bestimmten Bedingungen auf verschiedene Pfade abzubilden. Sie werden sowohl für CommonJS- als auch für ES-Modulimporte unterstützt.

Beispielsweise kann ein Paket, das unterschiedliche ES-Modulexporte für require() und import bereitstellen möchte, wie folgt geschrieben werden:

json
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

Node.js implementiert die folgenden Bedingungen, die in der Reihenfolge vom spezifischsten zum unspezifischsten aufgeführt sind, da Bedingungen definiert werden sollten:

  • "node-addons" - ähnelt "node" und gilt für jede Node.js-Umgebung. Diese Bedingung kann verwendet werden, um einen Einstiegspunkt bereitzustellen, der native C++-Add-ons verwendet, im Gegensatz zu einem Einstiegspunkt, der universeller ist und nicht auf nativen Add-ons basiert. Diese Bedingung kann über das Flag --no-addons deaktiviert werden.
  • "node" - gilt für jede Node.js-Umgebung. Kann eine CommonJS- oder ES-Moduldatei sein. In den meisten Fällen ist es nicht notwendig, die Node.js-Plattform explizit anzugeben.
  • "import" - gilt, wenn das Paket über import oder import() oder über eine Import- oder Auflösungsoperation auf oberster Ebene durch den ECMAScript-Modul-Loader geladen wird. Gilt unabhängig vom Modulformat der Zieldatei. Ist immer gegenseitig exklusiv mit "require".
  • "require" - gilt, wenn das Paket über require() geladen wird. Die referenzierte Datei sollte mit require() ladbar sein, obwohl die Bedingung unabhängig vom Modulformat der Zieldatei übereinstimmt. Erwartete Formate sind CommonJS, JSON, native Add-ons und ES-Module. Ist immer gegenseitig exklusiv mit "import".
  • "module-sync" - gilt unabhängig davon, ob das Paket über import, import() oder require() geladen wird. Das Format wird als ES-Module erwartet, die kein Top-Level-Await in ihrem Modulgraphen enthalten - wenn dies der Fall ist, wird ERR_REQUIRE_ASYNC_MODULE ausgelöst, wenn das Modul mit require() geladen wird.
  • "default" - der generische Fallback, der immer übereinstimmt. Kann eine CommonJS- oder ES-Moduldatei sein. Diese Bedingung sollte immer als letzte angegeben werden.

Innerhalb des "exports"-Objekts ist die Reihenfolge der Schlüssel von Bedeutung. Beim Bedingungsabgleich haben frühere Einträge eine höhere Priorität und haben Vorrang vor späteren Einträgen. Die allgemeine Regel ist, dass die Bedingungen in der Objektreihenfolge vom spezifischsten zum unspezifischsten erfolgen sollten.

Die Verwendung der Bedingungen "import" und "require" kann zu einigen Gefahren führen, die im Abschnitt Duale CommonJS/ES-Modul-Pakete näher erläutert werden.

Die Bedingung "node-addons" kann verwendet werden, um einen Einstiegspunkt bereitzustellen, der native C++-Add-ons verwendet. Diese Bedingung kann jedoch über das Flag --no-addons deaktiviert werden. Bei Verwendung von "node-addons" wird empfohlen, "default" als Verbesserung zu behandeln, die einen universelleren Einstiegspunkt bietet, z. B. die Verwendung von WebAssembly anstelle eines nativen Add-ons.

Bedingte Exporte können auch auf Export-Unterpfade erweitert werden, zum Beispiel:

json
{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}

Definiert ein Paket, in dem require('pkg/feature.js') und import 'pkg/feature.js' unterschiedliche Implementierungen zwischen Node.js und anderen JS-Umgebungen bereitstellen könnten.

Wenn Sie Umgebungszweige verwenden, fügen Sie nach Möglichkeit immer eine "default"-Bedingung ein. Die Bereitstellung einer "default"-Bedingung stellt sicher, dass alle unbekannten JS-Umgebungen diese universelle Implementierung verwenden können, was verhindert, dass diese JS-Umgebungen vorgeben müssen, vorhandene Umgebungen zu sein, um Pakete mit bedingten Exporten zu unterstützen. Aus diesem Grund ist die Verwendung von "node"- und "default"-Bedingungszweigen in der Regel der Verwendung von "node"- und "browser"-Bedingungszweigen vorzuziehen.

Verschachtelte Bedingungen

Zusätzlich zu direkten Zuordnungen unterstützt Node.js auch verschachtelte Bedingungsobjekte.

Um beispielsweise ein Paket zu definieren, das nur Dual-Mode-Einstiegspunkte für die Verwendung in Node.js, aber nicht im Browser hat:

json
{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}

Bedingungen werden weiterhin der Reihe nach abgeglichen wie bei flachen Bedingungen. Wenn eine verschachtelte Bedingung keine Zuordnung hat, werden die verbleibenden Bedingungen der übergeordneten Bedingung weiter geprüft. Auf diese Weise verhalten sich verschachtelte Bedingungen analog zu verschachtelten JavaScript if-Anweisungen.

Auflösen von Benutzerbedingungen

Hinzugefügt in: v14.9.0, v12.19.0

Beim Ausführen von Node.js können benutzerdefinierte Benutzerbedingungen mit dem Flag --conditions hinzugefügt werden:

bash
node --conditions=development index.js

Dadurch wird dann die Bedingung "development" in Paketimporten und -exporten aufgelöst, während die vorhandenen Bedingungen "node", "node-addons", "default", "import" und "require" entsprechend aufgelöst werden.

Es kann eine beliebige Anzahl benutzerdefinierter Bedingungen mit wiederholten Flags festgelegt werden.

Typische Bedingungen sollten nur alphanumerische Zeichen enthalten und ":" , "-" oder "=" als Trennzeichen verwenden, falls erforderlich. Alles andere kann zu Kompatibilitätsproblemen außerhalb von Node führen.

In Node gibt es sehr wenige Einschränkungen für Bedingungen, aber insbesondere diese:

Definitionen von Community-Bedingungen

Bedingungs-Strings, die nicht die Bedingungen "import", "require", "node", "module-sync", "node-addons" und "default" sind, die im Node.js-Kern implementiert sind, werden standardmäßig ignoriert.

Andere Plattformen können andere Bedingungen implementieren, und Benutzerbedingungen können in Node.js über das Flag --conditions / -C aktiviert werden.

Da benutzerdefinierte Paketbedingungen klare Definitionen erfordern, um eine korrekte Verwendung zu gewährleisten, wird im Folgenden eine Liste gängiger bekannter Paketbedingungen und ihrer strikten Definitionen bereitgestellt, um die Ökosystemkoordination zu unterstützen.

  • "types" - kann von Typsystemen verwendet werden, um die Typisierungsdatei für den angegebenen Export aufzulösen. Diese Bedingung sollte immer zuerst angegeben werden.
  • "browser" - jede Webbrowserumgebung.
  • "development" - kann verwendet werden, um einen reinen Entwicklungsumgebungseinstiegspunkt zu definieren, z. B. um zusätzlichen Debugging-Kontext bereitzustellen, z. B. bessere Fehlermeldungen beim Ausführen im Entwicklungsmodus. Muss immer sich gegenseitig mit "production" ausschließen.
  • "production" - kann verwendet werden, um einen Produktionsumgebungseinstiegspunkt zu definieren. Muss sich immer mit "development" gegenseitig ausschließen.

Für andere Laufzeiten werden plattformspezifische Bedingungsschlüsseldefinitionen von der WinterCG in der Runtime Keys-Vorschlagspezifikation verwaltet.

Neue Bedingungsdefinitionen können dieser Liste hinzugefügt werden, indem eine Pull-Request an die Node.js-Dokumentation für diesen Abschnitt erstellt wird. Die Anforderungen für die Auflistung einer neuen Bedingungsdefinition hier sind:

  • Die Definition sollte für alle Implementierer klar und eindeutig sein.
  • Der Anwendungsfall, warum die Bedingung benötigt wird, sollte klar begründet sein.
  • Es sollte eine ausreichende bestehende Implementierungsnutzung geben.
  • Der Bedingungsname sollte nicht mit einer anderen Bedingungsdefinition oder einer Bedingung in breiter Verwendung in Konflikt stehen.
  • Die Auflistung der Bedingungsdefinition sollte einen Koordinationsvorteil für das Ökosystem bieten, der sonst nicht möglich wäre. Dies wäre beispielsweise nicht unbedingt bei unternehmens- oder anwendungsspezifischen Bedingungen der Fall.
  • Die Bedingung sollte so sein, dass ein Node.js-Benutzer erwarten würde, dass sie in der Node.js-Kern-Dokumentation enthalten ist. Die Bedingung "types" ist ein gutes Beispiel: Sie gehört nicht wirklich in den Runtime Keys-Vorschlag, passt aber gut hier in die Node.js-Dokumentation.

Die obigen Definitionen können zu gegebener Zeit in ein dediziertes Bedingungsregister verschoben werden.

Selbstverweis auf ein Paket unter Verwendung seines Namens

[Historie]

VersionÄnderungen
v13.6.0, v12.16.0Selbstverweis auf ein Paket unter Verwendung seines Namens nicht mehr als Fehler markiert.
v13.1.0, v12.16.0Hinzugefügt in: v13.1.0, v12.16.0

Innerhalb eines Pakets können die im Feld package.json "exports" des Pakets definierten Werte über den Namen des Pakets referenziert werden. Wenn beispielsweise die package.json Folgendes ist:

json
// package.json
{
  "name": "a-package",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}

Dann kann jedes Modul in diesem Paket einen Export im Paket selbst referenzieren:

js
// ./a-module.mjs
import { something } from 'a-package' // Importiert "something" aus ./index.mjs.

Selbstverweise sind nur verfügbar, wenn package.json "exports" hat und erlauben nur das Importieren, was dieses "exports" (in der package.json) erlaubt. Der folgende Code erzeugt also bei dem vorherigen Paket einen Laufzeitfehler:

js
// ./another-module.mjs

// Importiert "another" aus ./m.mjs. Schlägt fehl, da
// das Feld "exports" in "package.json"
// keinen Export namens "./m.mjs" bereitstellt.
import { another } from 'a-package/m.mjs'

Selbstverweise sind auch verfügbar, wenn require verwendet wird, sowohl in einem ES-Modul als auch in einem CommonJS-Modul. Dieser Code funktioniert beispielsweise auch:

js
// ./a-module.js
const { something } = require('a-package/foo.js') // Lädt von ./foo.js.

Schließlich funktionieren Selbstverweise auch mit Paketen mit Gültigkeitsbereich. Dieser Code funktioniert beispielsweise auch:

json
// package.json
{
  "name": "@my/package",
  "exports": "./index.js"
}
js
// ./index.js
module.exports = 42
js
// ./other.js
console.log(require('@my/package'))
bash
$ node other.js
42

Dual CommonJS/ES-Modul-Pakete

Details finden Sie im Repository für Paketbeispiele.

Node.js package.json-Felddefinitionen

Dieser Abschnitt beschreibt die Felder, die von der Node.js-Laufzeit verwendet werden. Andere Tools (wie npm) verwenden zusätzliche Felder, die von Node.js ignoriert und hier nicht dokumentiert werden.

Die folgenden Felder in package.json-Dateien werden in Node.js verwendet:

  • "name" - Relevant bei der Verwendung von benannten Importen innerhalb eines Pakets. Wird auch von Paketmanagern als Name des Pakets verwendet.
  • "main" - Das Standardmodul beim Laden des Pakets, wenn "exports" nicht angegeben ist, und in Node.js-Versionen vor der Einführung von "exports".
  • "packageManager" - Der Paketmanager, der bei der Mitarbeit am Paket empfohlen wird. Wird von den Corepack-Shims verwendet.
  • "type" - Der Pakettyp, der bestimmt, ob .js-Dateien als CommonJS- oder ES-Module geladen werden sollen.
  • "exports" - Paketexporte und bedingte Exporte. Wenn vorhanden, wird eingeschränkt, welche Untermodule innerhalb des Pakets geladen werden können.
  • "imports" - Paketimporte zur Verwendung durch Module innerhalb des Pakets selbst.

"name"

[Historie]

VersionÄnderungen
v13.6.0, v12.16.0Entfernen der Option --experimental-resolve-self.
v13.1.0, v12.16.0Hinzugefügt in: v13.1.0, v12.16.0
json
{
  "name": "package-name"
}

Das Feld "name" definiert den Namen Ihres Pakets. Die Veröffentlichung im npm-Register erfordert einen Namen, der bestimmte Anforderungen erfüllt.

Das Feld "name" kann zusätzlich zum Feld "exports" verwendet werden, um ein Paket mithilfe seines Namens selbst zu referenzieren.

"main"

Hinzugefügt in: v0.4.0

json
{
  "main": "./index.js"
}

Das Feld "main" definiert den Einstiegspunkt eines Pakets, wenn es über einen node_modules-Lookup per Namen importiert wird. Sein Wert ist ein Pfad.

Wenn ein Paket ein Feld "exports" hat, hat dieses Vorrang vor dem Feld "main", wenn das Paket per Namen importiert wird.

Es definiert auch das Skript, das verwendet wird, wenn das Paketverzeichnis über require() geladen wird.

js
// Dies wird zu ./path/to/directory/index.js aufgelöst.
require('./path/to/directory')

"packageManager"

Hinzugefügt in: v16.9.0, v14.19.0

[Stabil: 1 - Experimentell]

Stabil: 1 Stabilität: 1 - Experimentell

json
{
  "packageManager": "<Paketmanager-Name>@<Version>"
}

Das Feld "packageManager" definiert, welcher Paketmanager bei der Arbeit am aktuellen Projekt verwendet werden soll. Es kann auf einen der unterstützten Paketmanager gesetzt werden und stellt sicher, dass Ihre Teams genau die gleichen Paketmanager-Versionen verwenden, ohne etwas anderes als Node.js installieren zu müssen.

Dieses Feld ist derzeit experimentell und muss aktiviert werden; Details zum Verfahren finden Sie auf der Seite Corepack.

"type"

[Verlauf]

VersionÄnderungen
v13.2.0, v12.17.0Entferne --experimental-modules-Flag.
v12.0.0Hinzugefügt in: v12.0.0

Das Feld "type" definiert das Modulformat, das Node.js für alle .js-Dateien verwendet, deren nächstgelegene übergeordnete Datei die package.json-Datei ist.

Dateien, die mit .js enden, werden als ES-Module geladen, wenn die nächstgelegene übergeordnete package.json-Datei ein Feld der obersten Ebene namens "type" mit dem Wert "module" enthält.

Die nächstgelegene übergeordnete package.json-Datei ist definiert als die erste package.json-Datei, die bei der Suche im aktuellen Ordner, dem übergeordneten Ordner dieses Ordners usw. gefunden wird, bis ein node_modules-Ordner oder das Stammverzeichnis des Volumes erreicht wird.

json
// package.json
{
  "type": "module"
}
bash
# Im gleichen Ordner wie die vorhergehende package.json {#in-same-folder-as-preceding-packagejson}
node my-app.js # Wird als ES-Modul ausgeführt

Wenn die nächstgelegene übergeordnete package.json-Datei kein Feld "type" aufweist oder "type": "commonjs" enthält, werden .js-Dateien als CommonJS behandelt. Wenn das Stammverzeichnis des Volumes erreicht wird und keine package.json-Datei gefunden wird, werden .js-Dateien als CommonJS behandelt.

import-Anweisungen von .js-Dateien werden als ES-Module behandelt, wenn die nächstgelegene übergeordnete package.json-Datei "type": "module" enthält.

js
// my-app.js, Teil des gleichen Beispiels wie oben
import './startup.js' // Wird aufgrund von package.json als ES-Modul geladen

Unabhängig vom Wert des Feldes "type" werden .mjs-Dateien immer als ES-Module und .cjs-Dateien immer als CommonJS behandelt.

"exports"

[Verlauf]

VersionÄnderungen
v14.13.0, v12.20.0Unterstützung für "exports"-Muster hinzugefügt.
v13.7.0, v12.17.0Bedingte Exporte freigegeben.
v13.7.0, v12.16.0Logische Sortierung von bedingten Exporten implementiert.
v13.7.0, v12.16.0Option --experimental-conditional-exports entfernt. In 12.16.0 befinden sich bedingte Exporte immer noch hinter --experimental-modules.
v13.2.0, v12.16.0Bedingte Exporte implementiert.
v12.7.0Hinzugefügt in: v12.7.0
json
{
  "exports": "./index.js"
}

Das Feld "exports" ermöglicht die Definition der Einstiegspunkte eines Pakets, wenn es mit Namen geladen über eine node_modules-Suche oder eine Selbst-Referenz auf seinen eigenen Namen importiert wird. Es wird in Node.js 12+ als Alternative zu "main" unterstützt, die die Definition von Subpfad-Exporten und bedingten Exporten ermöglicht, während interne, nicht exportierte Module gekapselt werden.

Bedingte Exporte können auch innerhalb von "exports" verwendet werden, um verschiedene Paketeinstiegspunkte pro Umgebung zu definieren, einschließlich der Frage, ob auf das Paket über require oder über import verwiesen wird.

Alle im Feld "exports" definierten Pfade müssen relative Datei-URLs sein, die mit ./ beginnen.

"imports"

Hinzugefügt in: v14.6.0, v12.19.0

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

Einträge im Feld "imports" müssen Strings sein, die mit # beginnen.

Paketimporte ermöglichen die Zuordnung zu externen Paketen.

Dieses Feld definiert Subpath-Importe für das aktuelle Paket.