Node.js ohne node_modules

    Letzte Woche haben die Entwickler von Yarn (einem Paketmanager für Javascript) eine neue Funktion angekündigt - die Plug'n'Play-Installation. Mit dieser Funktion können Sie Node.js-Projekte ohne den Ordner node_modules ausführen, in dem Projektabhängigkeiten normalerweise vor dem Start installiert werden. Die Beschreibung des Features gibt an, dass node_modules nicht mehr benötigt wird - die Module werden aus dem gemeinsam genutzten Cache des Paketmanagers geladen.


    Gleichzeitig mit ihnen NPM Entwickler auch bekannt gegeben , ihre ähnliche Lösung.


    Sehen wir uns diese Lösungen einmal genauer an und versuchen Sie, sie in realen Projekten zu testen.


    Geschichte des Problems


    Das modulare System von NodeJS basierte ursprünglich vollständig auf dem Dateisystem. Jeder Aufruf require()mappitsya im Dateisystem. Für die Organisation von Fremdmodulen wurde der Ordner node_modules erfunden, in den wiederverwendbare Module und Bibliotheken heruntergeladen und installiert werden sollten. Daher erhielt jedes Projekt eine eigene Gruppe von Abhängigkeiten, wodurch der Festplattenspeicherplatz vernachlässigt wurde.


    Die Installation von Abhängigkeiten nimmt in CI-Systemen die meiste Zeit in Anspruch. Wenn Sie diesen Schritt beschleunigen, wirkt sich dies positiv auf die gesamte Build-Zeit aus.


    Die vereinfachte Installation von Modulen umfasst die folgenden Schritte:


    1. Berechnet eine bestimmte Version des Moduls aus dem zulässigen Intervall
    2. Alle Module der erforderlichen Versionen werden aus dem Repository heruntergeladen und im lokalen Cache gespeichert.
    3. Module aus dem lokalen Cache werden in den Ordner node_modules des Projekts kopiert

    Wenn die ersten beiden Schritte bereits ausreichend optimiert und schnell ausgeführt wurden, wenn Sie bereits Module zwischengespeichert haben, ist der dritte Schritt im Vergleich zu den ersten Versionen von node und npm nahezu unverändert geblieben.


    Der neue Ansatz schlägt vor, den dritten Schritt zu beseitigen und das eigentliche Kopieren von Dateien durch die Erstellung einer Tabelle zu ersetzen, die die angeforderten Module ihren Kopien im lokalen Cache zuordnet.


    Symlinks verwenden


    Anstatt die Module tatsächlich zu kopieren, können Sie an ihrer Stelle im Cache einen Symlink hinzufügen. Dieser Ansatz ist in PNPM , einem anderen alternativen Paketmanager, implementiert . Der Ansatz funktioniert möglicherweise gut, aber bei Symlinks gibt es viele Probleme im Zusammenhang mit dem doppelten Speicherort der Datei, der Suche nach benachbarten Modulen usw. Außerdem ist das Erstellen von Symlinks eine Dateioperation, die ich bei der idealen Arbeitsweise vermeiden möchte.


    Wir versuchen Garn PNP


    Weitere Informationen zu dieser Funktion finden Sie in der offiziellen Beschreibung . Dieser Absatz enthält eine kurze Nacherzählung davon.


    Die PNP-Version von Yarn befindet sich jetzt im Feature- Garn- PNP .


    Klonen Sie das Repository lokal mit dem gewünschten Zweig


    git clone git@github.com:yarnpkg/yarn.git --branch yarn-pnp

    Montageanleitung Garn ist hier , eine Reihe von Schritten ist sehr trivial.


    Nachdem der Build abgeschlossen ist, fügen wir der benutzerdefinierten Version des Garns einen Alias ​​hinzu und können damit arbeiten:


    alias yarn-local="node $PWD/lib/cli/index.js"

    Plug'n'play schaltete auf zwei Arten: entweder durch einen Flag: yarn --pnpoder eine zusätzliche Konfiguration package.json: "installConfig": {"pnp": true}.


    Zum Beispiel haben die Entwickler von Yarn bereits ein Demoprojekt vorbereitet . Es verfügt über ein Webpack, Babel und andere für das moderne Frontend typische Tools. Versuchen wir, die Abhängigkeiten auf unterschiedliche Weise zu installieren und die folgenden Ergebnisse zu erhalten:


    • Typische Installation yarn: 19s
    • Installation über yarn --pnp: 3s

    Vor der Messung wurde eine Kaltinstallation durchgeführt, so dass sich bereits alle erforderlichen Module im Cache befanden.


    Lassen Sie uns nun herausfinden, wie das funktioniert. Nach der PNP-Installation wird im Projektstamm eine zusätzliche Datei erstellt, .pnp.jsdie eine Überschreibung der nativen Logik in der in Node.js eingebetteten Module-Klasse enthält. Durch das Laden dieser Datei in unseren Code geben wir der Funktion die require()Möglichkeit, Module aus dem globalen Cache zu holen und nicht hinein zu schauen node_modules. Alle eingebauten Garne-Befehle laden diese Datei (wie yarn startoder yarn teststandardmäßig) vor. Sie müssen also keine Änderungen an Ihrem Code vornehmen, wenn Sie bereits Yarn verwendet haben.


    Neben der Zuordnung von Modulen führt pnp.js eine zusätzliche Abhängigkeitsüberprüfung durch. Wenn Sie zu anrufen versuchen require('test'), ohne die deklarierten Abhängigkeiten package.json, erhalten Sie den folgenden Fehler: Error: You cannot require a package ("test") that is not declared in your dependencies. Diese Verbesserung sollte die Zuverlässigkeit und Vorhersagbarkeit des Codes verbessern.


    Zu den Unzulänglichkeiten des neuen Ansatzes muss erwähnt werden, dass Tools, die direkt mit dem Verzeichnis node_modules ohne die integrierten Node-Mechanismen arbeiten, eine zusätzliche Integration erfordern. Für Webpack und andere Frontend-Collectors werden zum Beispiel zusätzliche Plugins benötigt, damit sie die für die Bündelung erforderlichen Dateien finden können.


    Im Demoprojekt gibt es Skizzen von Resolvern für Eslint, Jest, Rollup und Webpack.


    In meinem Experiment gibt es immer noch Probleme mit Typescript, das stark an das Vorhandensein von node_modules gebunden ist, und es gibt keine einfache Möglichkeit, die Modulsuchstrategie zu überschreiben.


    Es werden auch Probleme mit Postintall-Skripten auftreten. Da das Modul im Cache verbleibt, können Nachinstallationsskripts, die ihren Status ändern (z. B. zusätzliche Dateien herunterladen), den Cache beschädigen und andere davon abhängige Projekte beschädigen. Garnentwickler empfehlen, die Skriptausführung zu deaktivieren --ignore-scripts. Sie haben bereits standardmäßig mit der Aufnahme dieses Flags für alle Projekte in Facebook experimentiert und keine ernsthaften Probleme gefunden. Langfristig scheint die Ablehnung von Postinstall-Skripts angesichts bekannter Sicherheitsprobleme ein guter Schritt zu sein .


    Versuchen Sie es mit NPM-Tink


    Das NPM-Team kündigte auch seine alternative Lösung an. Ihr neues Werkzeug, tink, wird als separates, NPM-unabhängiges Modul geliefert. Bei der Eingabe akzeptiert tink eine Datei package-lock.json, die beim Start automatisch generiert wird npm install. Basierend auf der Sperrdatei generiert tink eine Datei, node_modules/.package-map.jsonin der die Projektion lokaler Module an ihrem tatsächlichen Ort im Cache gespeichert wird.


    Im Gegensatz zu Yarn gibt es keine Hook-Datei, die vorab in Ihr Projekt geladen werden kann, um den Bedarf zu patchen. Stattdessen wird vorgeschlagen, tinkstattdessen den Befehl zu verwenden node, um die richtige Umgebung zu erhalten. Dieser Ansatz ist weniger ergonomisch, da in Ihrem Code Änderungen erforderlich sind, damit er funktioniert. Wie auch ein Proof-of-Concept.


    Ich habe versucht, die Installationsgeschwindigkeit der Module mit den Befehlen npm ciund zu vergleichen tink, aber die Verbindung war noch langsamer, daher gebe ich keine Ergebnisse. Offensichtlich ist dieses Projekt viel rauer als Yarn und ist überhaupt nicht optimiert. Nun, wir werden auf neue Releases warten.


    Fazit


    Die Ablehnung des node_modules-Verzeichnisses ist ein logischer Schritt, unter Berücksichtigung der Erfahrungen mit anderen Sprachen, in denen dieser Ansatz ursprünglich nicht verwendet wurde. Dies wirkt sich günstig auf die Assembliergeschwindigkeit von CI-Systemen aus, bei denen Cache-Pakete zwischen den Builds gespeichert werden können. Wenn Sie den Paketcache und die Datei .pnp.jsvon einem Computer auf einen anderen übertragen, können Sie die Umgebung außerdem reproduzieren, ohne Yarn zu starten. Dies kann in Containersystemen beim Zusammenstellen nützlich sein: Mounten Sie das Verzeichnis mit dem Cache, legen Sie die .pnp.jsDatei ab, und Sie können die Tests sofort ausführen.


    Der neue Ansatz sieht ungewöhnlich aus und bricht einige etablierte Praktiken, da alle Module immer in node_modules verfügbar sind. Die .pnp.jsDatei bietet jedoch eine API, mit der Sie von der tatsächlichen Position der Dateien abstrahieren und mit dem virtuellen Baum arbeiten können. In extremen Fällen gibt es außerdem einen Befehl yarn unplug --persist, mit dem das Modul aus dem Cache-Speicher extrahiert und lokal darin abgelegt wird node_modules.


    Auf jeden Fall wurde noch nichts abgeschlossen, selbst wenn die Pull-Anfrage in Yarn noch nicht gesendet wurde, sollten wir mit Änderungen rechnen. Für mich war es jedoch interessant, die Alpha-Version des Features in ein paar meiner persönlichen Projekte zu testen und sicherzustellen, dass dieser Ansatz wirklich funktioniert und die Installation schneller wird.


    Links



    Jetzt auch beliebt: