Ein npm-Paket erstellen, das 2024 mit ESM und CJS kompatibel ist
18. April 2024
0 Min. Lesezeit
JavaScript-Pakete zu veröffentlichen, die sowohl mit ECMAScript Modules (ESM) als auch mit CommonJS (CJS) kompatibel sind, ist eine wichtige Fähigkeit für Entwickler, die vielfältige Bibliotheken integrieren möchten.
Dieser Beitrag konzentriert sich auf praktische Ansätze und Best Practices, um die Unterstützung für ESM und CJS aufrechtzuerhalten. Wir untersuchen die Auswirkungen des Verzichts auf die Deklaration ”type: module” in Bibliotheken, die beide Formate unterstützen, und sehen uns an, wie sich die Felder main und module in package.json verwenden lassen, um Einstiegspunkte zu unterscheiden.
Der Artikel erläutert außerdem Zweck und Verwendung des Felds exports in der Datei package.json, das für die Kontrolle der Modulauflösung unerlässlich ist. Für TypeScript-Projekte untersuchen wir darüber hinaus, wie sich Exporte aus dem Paketmanifest mit einem Modultyp kombinieren lassen, um Kompatibilität und Typsicherheit zu gewährleisten.
Sie können den nachfolgenden Codeabschnitten Schritt für Schritt folgen. Ein GitHub-Code-Repository namens package-json-exports enthält jedoch vollständige Beispiele zur Reproduktion, die auf dem Open-Source-npm-Proxy-Projekt Verdaccio basieren.
Verzichten Sie bei Bibliotheken, die ESM und CJS unterstützen, auf die Angabe von „Type: Module“
Wussten Sie, dass das Feld ”type” standardmäßig implizit auf commonjs gesetzt wird, wenn Sie es in der Datei package.json weglassen? Zum Beispiel: ”type”: “commonjs”.
Sobald Sie ”type”: “module” in die Datei package.json aufnehmen, legen Sie explizit fest, dass die Bibliothek ausschließlich auf ESM-Projekte ausgerichtet ist. Im Paketmanifest müssen Sie weiterhin ein main-Feld definieren, das dann auf das exportierte, ESM-kompatible Modul verweist.
Ein praktisches Beispiel: Die folgende Definition in der Datei package.json funktioniert nicht für nachgelagerte ESM-Nutzer, obwohl sie „reines“ ESM ist:
Die Definition module bezeichnet ein ESM-Modul, und der Eintrag type legt eindeutig fest, dass diese Bibliothek auf ESM-Nutzer ausgerichtet ist. Es fehlt jedoch das Feld main in der Datei package.json. Sie werden sehen, dass Node.js eine Ausnahme mit dem Fehler auslöst, dass das Paket nicht gefunden werden kann. Zum Beispiel:
Zusammenfassend gilt: Verwenden Sie in einem Paketmanifest für ein npm-Paket keine Deklaration ”type”: “module”.
Sehen wir uns an, wie sich die Felder main und module in der Datei package.json verwenden lassen und warum sie bessere Vorgaben sind.
ESM- und CJS-Kompatibilität mit den Feldern main und module
Vor der Einführung von ESM wurde das Feld main in der Datei package.json dafür verwendet, der Node.js-Laufzeitumgebung den Einstiegspunkt des Pakets mitzuteilen. Üblicherweise legten Entwickler im Stammverzeichnis eine Datei wie index.js oder app.js ab und verwiesen mit dem Feld main darauf, zum Beispiel mit ”main”: “index.js”.
Wenn Sie das Feld main in der Datei package.json weglassen, versucht die Node.js-Laufzeitumgebung, den Einstiegspunkt des Pakets anhand der Dateikonvention für server.js im Stammverzeichnis des Pakets aufzulösen.
Damit ein npm-Paket sowohl mit ESM als auch mit CJS kompatibel ist, können wir eine Konvention verwenden, bei der main auf einen CJS-Export und module auf einen ESM-Export verweist.
Bibliothek:
Nachgelagerte Nutzer können sowohl CJS- als auch ESM-Projekte sein. CJS-Projekte verwenden die Datei src/index.cjs, ESM-Projekte die Datei src/index.mjs. Keine der beiden Arten nachgelagerter Nutzer muss für die Abhängigkeit math-add etwas Besonderes angeben – sie funktioniert einfach.
Das Feld exports in package.json verstehen
Mit dem Feld exports in der Datei package.json können Sie noch genauer steuern, welche Konstrukte aus Ihrem npm-Paket exportiert werden und wie sie genutzt werden.
Sie können beispielsweise den vollständigen Pfad zur Einstiegsdatei angeben, wenn eine Node.js-Laufzeitumgebung Ihr npm-Paket mit require(‘math-add’) lädt, und eine ganz andere Datei als Einstiegspunkt festlegen, wenn sie das Paket mit import .. from ‘math-add’) lädt.
Hier ein Codebeispiel für ein Dual-Mode-Paket mit CJS und ESM:
Exports in package.json und ein Modultyp für ein TypeScript-Projekt
Wenn Sie den ESM-Code Ihres Pakets mit TypeScript schreiben und die Abwärtskompatibilität mit CJS beibehalten möchten, sollten Sie auch types deklarieren und die TypeScript-Kompilierung sowie die Transpilierung für den CJS-Teil einrichten.
Für die TypeScript-Kompilierung und das Bundling empfehle ich tsup. Anschließend benötigen Sie einen build-Skript-Schritt – und denken Sie daran, diesen Build vor einem CI-Job oder dem manuellen Veröffentlichen des npm-Pakets auszuführen.
Hier ein vollständiges Beispiel:
Ihnen wird auffallen, dass wir außerdem die neue Unterstützung der Node.js-Laufzeitumgebung für die Überwachung von Änderungen mit dem Befehlszeilen-Flag --watch src nutzen. Früher ließ sich das mit nodemon erreichen. Das ist ein großartiges Paket, aber weniger Abhängigkeiten sind besser.
Nächste Schritte: Moderne npm-Pakete veröffentlichen und strukturieren – 2024
Dieser kurze, fokussierte Beitrag bietet JavaScript-Entwicklern unkomplizierte, direkt umsetzbare Einblicke in den effektiven Umgang mit Modulformaten in ihren Projekten.
Achten Sie außerdem darauf, Best Practices für die Veröffentlichung von npm-Paketen zu befolgen. Anleitungen zum Erstellen moderner npm-Pakete gehen noch ausführlicher auf TypeScript-Setup, Tests, CI, Sicherheit und weitere Aspekte ein.
Sichern Sie Ihre Open-Source-Abhängigkeiten
Snyk erstellt PRs zur Behebung von Schwachstellen in Open-Source-Abhängigkeiten und deren transitiven Abhängigkeiten – mit nur einem Klick.


