Das Symptom
Der Entwickler ruft npx create-irgendwas auf, um ein neues Projekt anzulegen, und bekommt ein Gerüst mit veralteten Abhängigkeiten oder eine Warnung, dass diese Version nicht mehr unterstützt wird. Er ist sicher, dass npx immer die neueste Version holt, schließlich installiert es ja nichts dauerhaft. Danach wird die Registry verdächtigt, der Firmenproxy oder ein kaputter npm-Cache, und nicht selten wird npm cache clean –force ausgeführt, was alle Pakete neu lädt, ohne dass die Ursache verstanden ist.
Die „Layer 8“-Wahrheit
Die npx-Dokumentation beschreibt die Auswahlreihenfolge: „Package names provided without a specifier will be matched with whatever version exists in the local project. Package names with a specifier will only be considered a match if they have the exact same name and version as the local dependency.“ Ein Paketname ohne Versionsangabe trifft also zuerst das, was im Projekt liegt. Ist das Paket im Projekt nicht vorhanden, wird es installiert, und zwar nicht nirgendwohin: „If any requested packages are not present in the local project dependencies, then they are installed to a folder in the npm cache, which is added to the PATH environment variable in the executed process.“
Der Abschnitt zum Cache in der Dokumentation von npm exec sagt dazu: „The npm cli utilizes its internal package cache when using the package name specified.“ Was einmal im Cache liegt, wird beim nächsten Aufruf ohne Versionsangabe wiederverwendet. Erst die Einstellung prefer-online ändert das: „Forces staleness checks for packages, making the cli look for updates immediately even if the package is already in the cache.“
Wer also die aktuelle Fassung will, sagt es: npx create-irgendwas@latest. Genau darum steht in den meisten Projekt-Startanleitungen inzwischen ein @latest hinter dem Paketnamen; es ist kein Schmuck, sondern die Versionsangabe, die den lokalen Treffer und den Cache-Treffer außer Kraft setzt, weil nur ein Paket mit genau dieser Version als Treffer gilt.
Der Glaubenssatz, npx lade immer das Neueste, kommt daher, dass npx nichts in das Projekt schreibt. Nichts installieren und immer das Neueste holen sind aber zwei verschiedene Eigenschaften, und npx hat nur die erste. Die zweite muss man bestellen, mit sieben Zeichen hinter dem Namen oder mit einem Schalter, und beide stehen in der Dokumentation, nicht im Cache.
Für den Alltag bleibt eine einfache Regel: Ein Werkzeug, das ein Projekt anlegt, bekommt immer eine Versionsangabe, und ein Werkzeug, das zum Projekt gehört, läuft in der Version des Projekts. npx macht beides richtig, wenn man ihm sagt, welches von beiden gemeint ist. Ohne diese Angabe entscheidet es selbst, und seine Entscheidung ist die naheliegende, nicht die neueste.