package.json"dependencies" von package.json"bin" von package.json"license" von package.jsonDieses Kapitel erklärt, was npm-Pakete sind und wie sie mit ESM-Modulen interagieren.
Vorausgesetztes Wissen: Ich gehe davon aus, dass Sie mit der Syntax von ECMAScript-Modulen lose vertraut sind. Wenn nicht, können Sie Kapitel „Module“ in „JavaScript for impatient programmers“ lesen.
Im JavaScript-Ökosystem ist ein Paket eine Möglichkeit, Softwareprojekte zu organisieren: Es ist ein Verzeichnis mit einer standardisierten Struktur. Ein Paket kann alle Arten von Dateien enthalten – zum Beispiel
Ein Paket kann von anderen Paketen (die als seine Abhängigkeiten bezeichnet werden) abhängen, die Folgendes enthalten:
Die Abhängigkeiten eines Pakets werden innerhalb dieses Pakets installiert (wir werden bald sehen, wie).
Eine häufige Unterscheidung zwischen Paketen ist:
Der nächste Unterabschnitt erklärt, wie Pakete veröffentlicht werden können.
Die Hauptmethode zur Veröffentlichung eines Pakets ist der Upload auf ein Paket-Repository – ein Online-Software-Repository. Der De-facto-Standard ist das npm-Repository, aber es ist nicht die einzige Option. Zum Beispiel können Unternehmen ihre eigenen internen Repositories hosten.
Ein Paketmanager ist ein Kommandozeilenwerkzeug, das Pakete aus einem Repository (oder anderen Quellen) herunterlädt und sie lokal oder global installiert. Wenn ein Paket Bin-Skripte enthält, macht es diese auch lokal oder global verfügbar.
Der beliebteste Paketmanager heißt npm und wird mit Node.js ausgeliefert. Sein Name stand ursprünglich für „Node Package Manager“. Später, als npm und das npm-Repository nicht nur für Node.js-Pakete verwendet wurden, wurde die Definition zu „npm ist kein Paketmanager“ geändert (Quelle).
Es gibt andere beliebte Paketmanager wie yarn und pnpm. Alle diese Paketmanager verwenden standardmäßig das npm-Repository.
Jedes Paket im npm-Repository hat einen Namen. Es gibt zwei Arten von Namen:
Globale Namen sind im gesamten Repository eindeutig. Dies sind zwei Beispiele:
minimatch
mochaBenannte Namen bestehen aus zwei Teilen: einem Scope und einem Namen. Scopes sind global eindeutig, Namen sind pro Scope eindeutig. Dies sind zwei Beispiele:
@babel/core
@rauschma/iterable
Der Scope beginnt mit einem @-Symbol und wird durch einen Schrägstrich vom Namen getrennt.
Sobald ein Paket my-package vollständig installiert ist, sieht es fast immer so aus:
my-package/
package.json
node_modules/
[More files]
Welchen Zweck haben diese Dateisystemeinträge?
package.json ist eine Datei, die jedes Paket haben muss.node_modules/ ist ein Verzeichnis, in das die Abhängigkeiten des Pakets installiert werden. Jede Abhängigkeit hat ebenfalls einen node_modules-Ordner mit ihren Abhängigkeiten usw. Das Ergebnis ist ein Baum von Abhängigkeiten.Manche Pakete haben auch die Datei package-lock.json, die neben package.json liegt: Sie speichert die genauen Versionen der installierten Abhängigkeiten und wird bei jeder weiteren Installation von Abhängigkeiten über npm auf dem neuesten Stand gehalten.
package.jsonDies ist eine anfängliche package.json, die über npm erstellt werden kann:
{
"name": "my-package",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}Welchen Zweck haben diese Eigenschaften?
Einige Eigenschaften sind für öffentliche Pakete (die im npm-Repository veröffentlicht werden) erforderlich:
name gibt den Namen dieses Pakets an.version wird für die Versionsverwaltung verwendet und folgt dem semantischen Versionierungsstandard mit drei durch Punkte getrennten Zahlen.Andere Eigenschaften für öffentliche Pakete sind optional:
description, keywords, author sind optional und erleichtern das Finden von Paketen.license klärt, wie dieses Paket verwendet werden darf. Es ist sinnvoll, diesen Wert anzugeben, wenn das Paket auf irgendeine Weise öffentlich ist. „Choose an open source license“ kann bei dieser Entscheidung helfen.main ist eine Eigenschaft für Pakete mit Bibliotheks-Code. Sie gibt das Modul an, das das Paket „ist“ (wird später in diesem Kapitel erklärt).
scripts ist eine Eigenschaft zum Einrichten von Paket-Skripten – Abkürzungen für Shell-Befehle zur Entwicklungszeit. Diese können über npm run ausgeführt werden. Zum Beispiel kann das Skript test über npm run test ausgeführt werden. Weitere Informationen hierzu finden Sie in §15 „Ausführen plattformübergreifender Aufgaben über npm-Paket-Skripte“.
Weitere nützliche Eigenschaften:
dependencies listet die Abhängigkeiten eines Pakets auf. Sein Format wird bald erklärt.
devDependencies sind Abhängigkeiten, die nur während der Entwicklung benötigt werden.
Die folgende Einstellung bedeutet, dass alle Dateien mit der Dateierweiterung .js als ECMAScript-Module interpretiert werden. Sofern wir uns nicht mit Altkode befassen, ist es sinnvoll, sie hinzuzufügen:
"type": "module"bin listet Bin-Skripte auf, Node.js-Module innerhalb des Pakets, die npm als Shell-Skripte installiert. Sein Format wird bald erklärt.
license gibt eine Lizenz für das Paket an. Sein Format wird bald erklärt.
Normalerweise sind die Eigenschaften name und version erforderlich und npm warnt uns, wenn sie fehlen. Wir können dies jedoch über die folgende Einstellung ändern:
"private": trueDas verhindert, dass das Paket versehentlich veröffentlicht wird, und ermöglicht es uns, Name und Version wegzulassen.
Weitere Informationen zu package.json finden Sie in der npm-Dokumentation.
"dependencies" von package.jsonSo sehen die Abhängigkeiten in einer package.json-Datei aus:
"dependencies": {
"minimatch": "^5.1.0",
"mocha": "^10.0.0"
}Die Eigenschaften geben sowohl die Namen der Pakete als auch Einschränkungen für deren Versionen an.
Versionen selbst folgen dem semantischen Versionierungsstandard. Sie bestehen aus bis zu drei Zahlen (die zweite und dritte Zahl sind optional und standardmäßig null), die durch Punkte getrennt sind:
Node's Versionsbereiche werden im semver-Repository erklärt. Beispiele sind:
Eine bestimmte Version ohne zusätzliche Zeichen bedeutet, dass die installierte Version genau mit der Version übereinstimmen muss.
"pkg1": "2.0.1",major.minor.x oder major.x bedeutet, dass die Komponenten, die Zahlen sind, übereinstimmen müssen, während die Komponenten, die x sind oder weggelassen werden, beliebige Werte haben können.
"pkg2": "2.x",
"pkg3": "3.3.x",* stimmt mit jeder Version überein.
"pkg4": "*",>=version bedeutet, dass die installierte Version version oder höher sein muss.
"pkg5": ">=1.0.2",<=version bedeutet, dass die installierte Version version oder niedriger sein muss.
"pkg6": "<=2.3.4",version1-version2 ist dasselbe wie >=version1 <=version2.
"pkg7": "1.0.0 - 2.9999.9999",^version (wie im vorherigen Beispiel verwendet) ist ein Caret-Bereich und bedeutet, dass die installierte Version version oder höher sein kann, aber keine BREAKING CHANGES einführen darf. Das heißt, die Hauptversion muss gleich sein.
"pkg8": "^4.17.21","bin" von package.jsonSo können wir npm mitteilen, Module als Shell-Skripte zu installieren:
"bin": {
"my-shell-script": "./src/shell/my-shell-script.mjs",
"another-script": "./src/shell/another-script.mjs"
}Wenn wir ein Paket mit diesem "bin"-Wert global installieren, stellt Node.js sicher, dass die Befehle my-shell-script und another-script auf der Kommandozeile verfügbar sind.
Wenn wir das Paket lokal installieren, können wir die beiden Befehle in Paket-Skripten oder über den Befehl npx verwenden.
Ein String ist auch als Wert von "bin" zulässig:
{
"name": "my-package",
"bin": "./src/main.mjs"
}Dies ist eine Abkürzung für:
{
"name": "my-package",
"bin": {
"my-package": "./src/main.mjs"
}
}"license" von package.jsonDer Wert der Eigenschaft "license" ist immer ein String mit einer SPDX-Lizenz-ID. Zum Beispiel verweigert der folgende Wert anderen das Recht, ein Paket unter beliebigen Bedingungen zu nutzen (was nützlich ist, wenn ein Paket unveröffentlicht bleibt):
"license": "UNLICENSED"Die SPDX-Website listet alle verfügbaren Lizenz-IDs auf. Wenn es Ihnen schwerfällt, eine auszuwählen, kann die Website „Choose an open source license“ helfen – zum Beispiel ist dies der Rat, wenn Sie „es einfach und permissiv haben möchten“:
Die MIT-Lizenz ist kurz und bündig. Sie erlaubt es den Leuten, fast alles mit Ihrem Projekt zu machen, wie z. B. Closed-Source-Versionen zu erstellen und zu verteilen.
Babel, .NET und Rails verwenden die MIT-Lizenz.
Sie können diese Lizenz wie folgt verwenden:
"license": "MIT"Pakete im npm-Repository werden oft auf zwei verschiedene Arten archiviert:
In beiden Fällen wird das Paket ohne seine Abhängigkeiten archiviert – die wir installieren müssen, bevor wir es verwenden können.
Wenn ein Paket in einem Git-Repository gespeichert ist:
package-lock.json normalerweise enthalten.Wenn ein Paket im npm-Repository veröffentlicht wird:
package-lock.json nie in das npm-Repository hochgeladen.Entwicklungsabhängigkeiten (Eigenschaft devDependencies in package.json) werden nur während der Entwicklung installiert, aber nicht, wenn wir das Paket aus dem npm-Repository installieren.
Beachten Sie, dass unveröffentlichte Pakete in Git-Repositories während der Entwicklung ähnlich wie veröffentlichte Pakete behandelt werden.
Um ein Paket pkg aus Git zu installieren, klonen wir sein Repository und
cd pkg/
npm install
Dann werden die folgenden Schritte ausgeführt:
node_modules wird erstellt und die Abhängigkeiten werden installiert. Die Installation einer Abhängigkeit bedeutet auch, diese Abhängigkeit herunterzuladen und ihre Abhängigkeiten zu installieren (usw.).package.json konfiguriert werden.Wenn das Root-Paket keine package-lock.json-Datei hat, wird diese während der Installation erstellt (wie erwähnt, haben Abhängigkeiten diese Datei nicht).
In einem Abhängigkeitsbaum kann dieselbe Abhängigkeit mehrmals existieren, möglicherweise in verschiedenen Versionen. Es gibt Wege, Duplikate zu minimieren, aber das liegt außerhalb des Rahmens dieses Kapitels.
Dies ist eine (etwas grobe) Methode zur Behebung von Problemen in einem Abhängigkeitsbaum:
cd pkg/
rm -rf node_modules/
rm package-lock.json
npm install
Beachten Sie, dass dies dazu führen kann, dass andere, neuere Pakete installiert werden. Wir können dies vermeiden, indem wir package-lock.json nicht löschen.
Es gibt viele Werkzeuge und Techniken zum Einrichten neuer Pakete. Dies ist eine einfache Methode:
mkdir my-package
cd my-package/
npm init --yes
Danach sieht das Verzeichnis so aus:
my-package/
package.json
Diese package.json enthält den anfänglichen Inhalt, den wir bereits gesehen haben.
Derzeit hat my-package keine Abhängigkeiten. Nehmen wir an, wir möchten die Bibliothek lodash-es verwenden. So installieren wir sie in unser Paket:
npm install lodash-es
Dieser Befehl führt die folgenden Schritte aus:
Das Paket wird in my-package/node_modules/lodash-es heruntergeladen.
Seine Abhängigkeiten werden ebenfalls installiert. Dann die Abhängigkeiten seiner Abhängigkeiten. Usw.
Eine neue Eigenschaft wird zu package.json hinzugefügt:
"dependencies": {
"lodash-es": "^4.17.21"
}package-lock.json wird mit der exakten installierten Version aktualisiert.
Code in anderen ECMAScript-Modulen wird über import-Anweisungen (Zeile A und Zeile B) abgerufen:
// Static import
import {namedExport} from 'https://example.com/some-module.js'; // (A)
console.log(namedExport);
// Dynamic import
import('https://example.com/some-module.js') // (B)
.then((moduleNamespace) => {
console.log(moduleNamespace.namedExport);
});Sowohl statische als auch dynamische Importe verwenden Moduldeskriptoren, um auf Module zu verweisen:
from in Zeile A.Es gibt drei Arten von Moduldeskriptoren:
Absolute Deskriptoren sind vollständige URLs – zum Beispiel:
'https://www.unpkg.com/browse/yargs@17.3.1/browser.mjs'
'file:///opt/nodejs/config.mjs'Absolute Deskriptoren werden meistens verwendet, um direkt im Web gehostete Bibliotheken abzurufen.
Relative Deskriptoren sind relative URLs (beginnend mit '/', './' oder '../') – zum Beispiel:
'./sibling-module.js'
'../module-in-parent-dir.mjs'
'../../dir/other-module.js'Jedes Modul hat eine URL, deren Protokoll von seinem Speicherort abhängt (file:, https: usw.). Wenn es einen relativen Deskriptor verwendet, wandelt JavaScript diesen Deskriptor in eine vollständige URL um, indem es ihn gegen die URL des Moduls auflöst.
Relative Deskriptoren werden meistens verwendet, um andere Module innerhalb desselben Codebases abzurufen.
Bare-Deskriptoren sind Pfade (ohne Protokoll und Domäne), die weder mit Schrägstrichen noch mit Punkten beginnen. Sie beginnen mit den Namen von Paketen. Diese Namen können optional von Subpfaden gefolgt werden:
'some-package'
'some-package/sync'
'some-package/util/files/path-tools.js'Bare-Deskriptoren können auch auf Pakete mit benannten Namen verweisen:
'@some-scope/scoped-name'
'@some-scope/scoped-name/async'
'@some-scope/scoped-name/dir/some-module.mjs'Jeder Bare-Deskriptor verweist auf genau ein Modul innerhalb eines Pakets; wenn er keinen Subpfad hat, verweist er auf das designierte „Main“-Modul seines Pakets. Ein Bare-Deskriptor wird niemals direkt verwendet, sondern immer aufgelöst – in einen absoluten Deskriptor übersetzt. Wie die Auflösung funktioniert, hängt von der Plattform ab. Wir erfahren bald mehr.
.js oder .mjs.Stil 1: kein Subpfad
Stil 2: ein Subpfad ohne Dateinamenserweiterung. In diesem Fall funktioniert der Subpfad wie ein Modifikator für den Paketnamen:
'my-parser/sync'
'my-parser/async'
'assertions'
'assertions/strict'Stil 3: ein Subpfad mit Dateinamenserweiterung. In diesem Fall wird das Paket als eine Sammlung von Modulen betrachtet und der Subpfad zeigt auf eines davon:
'large-package/misc/util.js'
'large-package/main/parsing.js'
'large-package/main/printing.js'Vorbehalt bei Bare-Deskriptoren von Stil 3: Wie die Dateinamenserweiterung interpretiert wird, hängt von der Abhängigkeit ab und kann vom importierenden Paket abweichen. Zum Beispiel kann das importierende Paket .mjs für ESM-Module und .js für CommonJS-Module verwenden, während die von der Abhängigkeit exportierten ESM-Module Bare-Pfade mit der Dateinamenserweiterung .js haben können.
Sehen wir uns an, wie Moduldeskriptoren in Node.js funktionieren.
Der Node.js-Auflösungsalgorithmus funktioniert wie folgt:
Dies ist der Algorithmus:
Wenn ein Deskriptor absolut ist, ist die Auflösung bereits abgeschlossen. Drei Protokolle sind am gebräuchlichsten:
file: für lokale Dateienhttps: für Remote-Dateiennode: für integrierte Module (später besprochen)Wenn ein Deskriptor relativ ist, wird er anhand der URL des importierenden Moduls aufgelöst.
Wenn ein Deskriptor bare ist:
Wenn er mit '#' beginnt, wird er durch Nachschlagen in den Paket-Importen (die später erklärt werden) aufgelöst und das Ergebnis wird aufgelöst.
Andernfalls handelt es sich um einen Bare-Deskriptor, der eines der folgenden Formate hat (der Subpfad ist optional):
«Paket»/sub/path@«scope»/«scoped-package»/sub/pathDer Auflösungsalgorithmus durchläuft das aktuelle Verzeichnis und seine übergeordneten Verzeichnisse, bis er ein Verzeichnis node_modules findet, das ein Unterverzeichnis hat, das mit dem Anfang des Bare-Deskriptors übereinstimmt, d. h. entweder
node_modules/«Paket»/node_modules/@«scope»/«scoped-package»/Dieses Verzeichnis ist das Verzeichnis des Pakets. Standardmäßig wird der (potenziell leere) Subpfad nach der Paket-ID relativ zum Paketverzeichnis interpretiert. Das Standardverhalten kann über Paket-Exports überschrieben werden, die als Nächstes erklärt werden.
Das Ergebnis des Auflösungsalgorithmus muss auf eine Datei verweisen. Das erklärt, warum absolute und relative Deskriptoren immer Dateinamenserweiterungen haben. Bare-Deskriptoren haben sie meistens nicht, da sie Abkürzungen sind, die in Paket-Exports nachgeschlagen werden.
Moduldateien haben normalerweise diese Dateinamenserweiterungen:
.mjs hat, ist sie immer ein ES-Modul..js ist ein ES-Modul, wenn die nächstgelegene package.json diesen Eintrag hat:"type": "module"Wenn Node.js Code aus stdin, --eval oder --print ausführt, verwenden wir die folgende Kommandozeilenoption, damit er als ES-Modul interpretiert wird:
--input-type=module
In diesem Unterabschnitt arbeiten wir mit einem Paket, das die folgende Dateistruktur hat:
my-lib/
dist/
src/
main.js
util/
errors.js
internal/
internal-module.js
test/
Paket-Exports werden über die Eigenschaft "exports" in package.json definiert und unterstützen zwei wichtige Funktionen:
Ohne die Eigenschaft "exports" kann jedes Modul im Paket my-lib über einen relativen Pfad nach dem Paketnamen abgerufen werden – z. B.:
'my-lib/dist/src/internal/internal-module.js'Sobald die Eigenschaft vorhanden ist, können nur die darin aufgeführten Deskriptoren verwendet werden. Alles andere ist nach außen hin verborgen.
Erinnern wir uns an die drei Stile von Bare-Deskriptoren:
Paket-Exports helfen uns bei allen drei Stilen:
package.json:
{
"main": "./dist/src/main.js",
"exports": {
".": "./dist/src/main.js"
}
}Wir geben "main" nur zur Abwärtskompatibilität an (mit älteren Bundlern und Node.js 12 und älter). Andernfalls reicht der Eintrag für "." aus.
Mit diesen Paket-Exports können wir nun wie folgt von my-lib importieren:
import {someFunction} from 'my-lib';Dies importiert someFunction() aus dieser Datei:
my-lib/dist/src/main.js
package.json:
{
"exports": {
"./util/errors": "./dist/src/util/errors.js"
}
}Wir ordnen den Deskriptor-Subpfad 'util/errors' einer Moduldatei zu. Dies ermöglicht den folgenden Import:
import {UserError} from 'my-lib/util/errors';Der vorherige Unterabschnitt erklärte, wie eine einzelne Zuordnung für einen Subpfad ohne Erweiterung erstellt wird. Es gibt auch eine Möglichkeit, mehrere solcher Zuordnungen über einen einzigen Eintrag zu erstellen:
package.json:
{
"exports": {
"./lib/*": "./dist/src/*.js"
}
}Jede Datei, die ein Nachkomme von ./dist/src/ ist, kann nun ohne Dateinamenserweiterung importiert werden.
import {someFunction} from 'my-lib/lib/main';
import {UserError} from 'my-lib/lib/util/errors';Beachten Sie die Sternchen in diesem "exports"-Eintrag:
"./lib/*": "./dist/src/*.js"Dies sind eher Anweisungen, wie Subpfade zu tatsächlichen Pfaden zugeordnet werden, als Wildcards, die Dateipfadfragmente abgleichen.
package.json:
{
"exports": {
"./util/errors.js": "./dist/src/util/errors.js"
}
}Wir ordnen den Deskriptor-Subpfad 'util/errors.js' einer Moduldatei zu. Dies ermöglicht den folgenden Import:
import {UserError} from 'my-lib/util/errors.js';package.json:
{
"exports": {
"./*": "./dist/src/*"
}
}Hier kürzen wir die Moduldeskriptoren des gesamten Unterbaums unter my-package/dist/src:
import {InternalError} from 'my-package/util/errors.js';Ohne die Exports wäre die Importanweisung:
import {InternalError} from 'my-package/dist/src/util/errors.js';Beachten Sie die Sternchen in diesem "exports"-Eintrag:
"./*": "./dist/src/*"Dies sind keine Dateisystem-Globs, sondern Anweisungen, wie externe Moduldeskriptoren internen zugeordnet werden.
Mit dem folgenden Trick exponieren wir alles im Verzeichnis my-package/dist/src/ mit Ausnahme von my-package/dist/src/internal/:
"exports": {
"./*": "./dist/src/*",
"./internal/*": null
}Beachten Sie, dass dieser Trick auch funktioniert, wenn Unterbäume ohne Dateinamenserweiterungen exportiert werden.
Wir können Exports auch bedingt machen: Dann bildet ein gegebener Pfad unterschiedliche Werte ab, je nachdem, in welchem Kontext ein Paket verwendet wird.
Node.js vs. Browser. Zum Beispiel könnten wir unterschiedliche Implementierungen für Node.js und für Browser bereitstellen:
"exports": {
".": {
"node": "./main-node.js",
"browser": "./main-browser.js",
"default": "./main-browser.js"
}
}Die Bedingung "default" passt, wenn keine andere Bedingung zutrifft, und muss zuletzt kommen. Wenn zwischen Plattformen unterschieden wird, ist eine empfohlen, da sie neue und/oder unbekannte Plattformen berücksichtigt.
Entwicklung vs. Produktion. Ein weiterer Anwendungsfall für bedingte Paket-Exports ist der Wechsel zwischen „Entwicklungs“- und „Produktions“-Umgebungen:
"exports": {
".": {
"development": "./main-development.js",
"production": "./main-production.js",
}
}In Node.js können wir eine Umgebung wie folgt angeben:
node --conditions development app.mjs
Paket-Importe ermöglichen es einem Paket, Abkürzungen für Moduldeskriptoren zu definieren, die es selbst intern verwenden kann (während Paket-Exports Abkürzungen für andere Pakete definieren). Dies ist ein Beispiel:
package.json:
{
"imports": {
"#some-pkg": {
"node": "some-pkg-node-native",
"default": "./polyfills/some-pkg-polyfill.js"
}
},
"dependencies": {
"some-pkg-node-native": "^1.2.3"
}
}Der Paket-Import # ist bedingt (mit denselben Funktionen wie bedingte Paket-Exports):
Wenn das aktuelle Paket unter Node.js verwendet wird, bezieht sich der Moduldeskriptor '#some-pkg' auf das Paket some-pkg-node-native.
Andernfalls bezieht sich '#some-pkg' auf die Datei ./polyfills/some-pkg-polyfill.js innerhalb des aktuellen Pakets.
(Nur Paket-Importe können sich auf externe Pakete beziehen, Paket-Exports können dies nicht.)
Welche Anwendungsfälle gibt es für Paket-Imports?
Seien Sie vorsichtig bei der Verwendung von Paket-Imports mit einem Bundler: Diese Funktion ist relativ neu und Ihr Bundler unterstützt sie möglicherweise nicht.
node:-ProtokollNode.js verfügt über viele integrierte Module wie 'path' und 'fs'. Alle sind sowohl als ES-Module als auch als CommonJS-Module verfügbar. Ein Problem bei ihnen ist, dass sie durch in node_modules installierte Module überschrieben werden können, was sowohl ein Sicherheitsrisiko darstellt (wenn es versehentlich geschieht) als auch ein Problem ist, wenn Node.js neue integrierte Module einführen möchte und ihre Namen bereits von npm-Paketen belegt sind.
Wir können das node:-Protokoll verwenden, um klarzustellen, dass wir ein integriertes Modul importieren möchten. Zum Beispiel sind die folgenden beiden Importanweisungen weitgehend äquivalent (wenn kein npm-Modul mit dem Namen 'fs' installiert ist):
import * as fs from 'node:fs/promises';
import * as fs from 'fs/promises';Ein weiterer Vorteil der Verwendung des node:-Protokolls ist, dass wir sofort erkennen, dass ein importiertes Modul integriert ist. Angesichts der vielen integrierten Module hilft dies beim Lesen von Code.
Da node:-Deskriptoren ein Protokoll haben, gelten sie als absolut. Deshalb werden sie nicht in node_modules nachgeschlagen.