Monorepository-Tooling für Node.js-Projekte

Donnerstag, 3.8.2023

Titelbild Monorepositories

Im vorherigen Beitrag habe ich den Begriff des Monorepositories und etwaige Vor- und Nachteile beleuchtet. In diesem Beitrag möchte ich auf Tooling zur Umsetzung eines Monorepositories mit Node.js eingehen.

npm workspaces

Für Monorepositories bietet npm mittlerweile das Feature workspaces. Ein Workspace beinhaltet mehrere NPM-Pakete. Diese Pakete definieren durch ihre package.json Abhängigkeiten, Skripte usw. Zusätzlich gibt es eine Top-Level package.json, welche den Workspace definiert. Hier sind alle dem Workspace zugehörigen NPM-Projekte in der Eigenschaft workspaces angegeben. Dies bedeutet, dass die Pakete im Ordner node_modules verlinkt werden und somit als Abhängigkeiten verfügbar sind.  Änderungen an einem verlinkten Paket sind somit sofort wirksam und es muss nicht zuerst eine neue Version des Paketes released und diese per npm install installiert werden. Zu beachten ist hierbei, dass npm verlinkte Dependencies automatisch hoistet.

 

Exkurs: Hoisting

Unter Hoisting von npm-Paketen versteht man, dass sie in den darüberliegenden node_modules-Ordner installiert werden, indem man dort eine package.json anlegt. Diese Abhängigkeiten können dann in allen darunterliegenden Ordnern verwendet werden.

 

Hoisting kann in einigen Fällen problematisch sein. Betrachten wir als Beispiel ein Monorepository, in dem eine Utility-Bibliothek, eine UI-Bibliothek und ein Frontend liegen: Wenn alle drei Pakete Teil des Workspaces sind, dann kann in der UI-Bibliothek auf die Utility-Bibliothek zugegriffen werden, denn diese wurde von npm als Teil des Workspaces gehoistet. Wenn jetzt aber die UI-Bibliothek auch als Paket publiziert wird und dort die Abhängigkeit auf die Utility-Bibliothek nicht definiert ist, wird es bei der Nutzung der UI-Bibliothek außerhalb des Monorepositories zu Fehlern kommen, da die Utility-Bibliothek nicht mitinstalliert wurde. Hier ist also Vorsicht geboten! 

Viele Kommandos wie etwa npm install können für jedes Paket im Workspace ausgeführt werden, indem an das Kommando das Argument –-workspaces angehängt wird. Dies gilt auch für Skripte. Möchte man ein Kommando nur für ein Paket nutzen, etwa das Paket foo, dann kann man an das Kommando –-workspace=foo hängen. Soll es für mehrere spezifische Pakete ausgeführt werden, so wird die Option –-workspace=<Paketname> mehrfach genutzt.

Weitere Informationen zu npm workspaces finden sich in der Dokumentation.

Yarn und pnpm sind zwei weitere Paketmanager für Node.js, welche Workspace-Support bieten.

Nx

Nx erfreut sich insbesondere in der Angular-Community großer Beliebtheit. Es kann durch Plugins erweitert werden. Die Ergebnisse eines Skripts, genannt Task, werden gecached, um Build-Zeit zu sparen. Dieser Cache kann über die Cloud mit anderen Entwickler:innen geteilt werden. Außerdem versucht Nx herauszufinden, welche Pakete von einer Änderung betroffen sind. Wurde beispielsweise eine Änderung an einem Angular-Frontend durchgeführt, so würde Nx automatisch erkennen, dass sich der Code des dazugehörigen Backends nicht geändert hat. Würde man jetzt einen ESLint-Task auf dem gesamten Workspace ausführen, so würde Nx das Linting für das Frontend durchführen und das Resultat des Backend-Lintings aus dem Cache auslesen.

Hierfür analysiert Nx den Source Code und die Abhängigkeiten der Pakete im Repository. Aus diesen Informationen erstellt es einen Graphen, der Abhängigkeiten zwischen den Projekten darstellt. Dieser Graph kann auch per Konfiguration überschrieben werden. Der Graph bietet Informationen darüber, welches Paket auf anderen aufbaut und somit, wann Tasks nacheinander in den verschiedenen Paketen ausgeführt werden müssen. Gleichzeitig zeigt der Graph auch an, wenn Skripte parallel ausgeführt werden können.

Weitere Informationen zu Nx finden sich auf der offiziellen Website.

Turborepo

Turborepo ist ein neuer Vertreter des Monorepository-Toolings. Es gehört zur Turbo-Toolsuite von Vercel, den Entwicklern von Next.js. Ergebnisse von Skripts können auch hier sowohl remote als lokal gecached werden. Genau wie bei Nx werden Skripte also nur ausgeführt, wenn das Resultat noch nicht im Cache vorliegt oder sich der Code geändert hat. Die Konfigurationsdatei turbo.json kann genutzt werden, um Turborepo mitzuteilen, welche Pakete oder Kommandos voneinander abhängen. Somit können Skripte auch in Turborepo parallel ausgeführt werden. Für Code-Generierungen bietet Turborepo ein eigenes Kommando gen. Dieses wird durch Plop-Konfigurationen gespeist. Turborepo durchsucht den Workspace automatisch nach Plop-Konfigurationen.

Weitere Informationen zu Turborepo finden sich auf der offiziellen Website.

Lerna

Insbesondere in älteren Monorepositories stößt man immer mal wieder auf das Tool lerna. Es bot eine Möglichkeit zur Nutzung von Workspaces, bevor diese offiziell von npm unterstützt wurden. Diese Möglichkeiten werden mittlerweile nicht mehr empfohlen. Stattdessen soll man die native Workspace-Funktionalität von npm nutzen. Das Projekt wurde mittlerweile von Nrwl, den Machern von Nx, übernommen und lerna kann als eine Ergänzung zu Nx betrachtet werden.

Weitere Informationen zu lerna finden sich auf der offiziellen Website.

Rush

Rush ist ein Monorepository-Tool von Microsoft. Es wird vom Sharepoint-Team entwickelt. Rush unterstützt den Paketmanager pnpm am besten. Falls man Rush stattdessen mit npm nutzen möchte, empfiehlt die Website die npm-Version 4.5. Diese Version von npm ist jedoch extrem veraltet. Auch die Nutzung von yarn wird nur bedingt empfohlen. Wer pnpm verwendet, kann sich Rush jedoch einmal ansehen. Für ein Monorepository in Rust können spezielle Richtlinien definiert werden, etwa dass die Einführung neuer Abhängigkeiten genehmigt werden muss.

Mehr Informationen zu Rush finden sich auf der offiziellen Website.

Fazit

Es gibt jede Menge Tooling für Monorepositories. Für die meisten Anwendungsfälle reicht wahrscheinlich der Workspace-Support von npm, yarn oder pnpm bereits aus. Wer ein sehr großes Monorepository nutzen möchte, wird eher bei Nx oder Turborepo fündig, welche noch Orchestrierung und Caching der Skriptausführung bieten. Der Einsatz von Rush oder lerna ist vom Einzelfall abhängig, eine generelle Empfehlung kann hier aber nicht gegeben werden.


zurück zur Blogübersicht

Diese Beiträge könnten Sie ebenfalls interessieren

Keinen Beitrag verpassen – viadee Blog abonnieren

Jetzt Blog abonnieren!

Kommentare

Christian Siebmanns

Christian Siebmanns

Christian Siebmanns ist Berater bei der viadee IT-Unternehmensberatung. Schwerpunkt seiner Arbeit ist der Einsatz in verschiedenen Kundenprojekten im Webumfeld. Christian ist Experte für Java und TypeScript.