
npm overrides: die Version einer transitiven Abhängigkeit erzwingen
- VersionDude
- guides
- 7 Min. Lesezeit
Ein verwundbares Paket drei Ebenen tiefer, und ein Elternpaket ohne veröffentlichten Fix. Das Feld overrides in der package.json im Projektstamm lässt Sie die Version selbst festlegen, für den ganzen Baum oder nur für ein Elternpaket. Drei Regeln erklären außerdem die meisten Fälle, in denen es scheinbar nichts bewirkt.
Ein Schwachstellenbericht nennt ein Paket, das Sie nie installiert haben. Es liegt drei Ebenen tiefer, hereingezogen von der Abhängigkeit einer Abhängigkeit, und das Paket dazwischen hat keine Version veröffentlicht, die daran vorbeiführt. Eine `package.json`, die Ihnen nicht gehört, können Sie nicht bearbeiten. Sie können aber in Ihrer eigenen `package.json` im Projektstamm ein Feld `overrides` anlegen und npm mitteilen, welche Version stattdessen installiert werden soll.
Der flache Override: eine Version, überall im Baum

Die kürzeste Form nennt das Paket und die Version. Mit `"overrides": { "foo": "1.2.4" }` installiert npm `foo` überall, wo es auftaucht, in Version 1.2.4, gleichgültig, welche Version die davon abhängigen Pakete verlangt haben. Die npm-Dokumentation nennt drei vorgesehene Einsatzzwecke: eine Version mit bekannter Sicherheitslücke ersetzen, eine Abhängigkeit durch einen Fork ersetzen und sicherstellen, dass überall dieselbe Version eines Pakets verwendet wird.
Der Wert muss keine exakte Version sein. Laut Dokumentation ist jeder Spezifizierer erlaubt, den npm für eine Abhängigkeit akzeptiert: eine exakte Version, ein Semver-Bereich, ein dist-tag oder ein Ersatz wie `npm:`, `file:` oder eine Git-URL. Für einen Sicherheitsfix ist ein Bereich oft die bessere Wahl. `"foo": "^1.2.4"` setzt die gepatchte Version als Minimum durch und lässt spätere Patches weiterhin zu, während eine exakte Version das Paket einfriert, bis jemand daran denkt, die Zeile zu entfernen. Wer den Unterschied zwischen den beiden Operatoren nicht mehr genau vor Augen hat, findet ihn unter Caret oder Tilde.
Bevor Sie den Override schreiben, klären Sie, wer das Paket anfordert. `npm explain foo` gibt die Kette der Abhängigkeiten aus, die zur Installation von `foo` führt, einen Block pro Exemplar im Baum, jeweils bis zum Wurzelprojekt zurückverfolgt. Diese Ausgabe liefert die beiden nötigen Angaben: welches Elternpaket verantwortlich ist und ob es um ein Exemplar oder um mehrere geht. Ein flacher Override ändert alle, und das ist nicht immer gewollt.
Den Override auf ein einziges Elternpaket begrenzen
Verschachteln Sie den Schlüssel, um den Override auf einen Zweig des Baums zu begrenzen. `"overrides": { "bar": { "foo": "1.2.4" } }` ersetzt `foo` nur dann, wenn es ein Kind von `bar` ist, ein Enkel oder noch tiefer darunter liegt. Alle anderen Pakete, die von `foo` abhängen, behalten die Version, die sie selbst aufgelöst haben. Das ist die passende Form, wenn ein einzelnes Elternpaket an einer verwundbaren Version festhängt und der Rest des Baums in Ordnung ist.
- "foo": "1.2.4" : foo überall im Baum in Version 1.2.4
- "bar": { "foo": "1.2.4" } : foo in 1.2.4 nur unterhalb von bar, in jeder Tiefe
- "bar@2.0.0": { "foo": "1.2.4" } : nur unterhalb genau dieser Version von bar
- "foo": "$foo" : die Angabe Ihrer direkten Abhängigkeit foo übernehmen
- "foo": "npm:@scope/fork@1.2.4" : foo durch ein anderes Paket ersetzen
Schlüssel können eine Version tragen und beliebig tief verschachtelt werden. `"bar@2.0.0": { "foo": "1.2.4" }` greift nur, wenn `foo` unter genau dieser Version von `bar` liegt; der Override endet also von selbst an dem Tag, an dem `bar` aktualisiert wird. Schlüssel lassen sich in beliebiger Länge verschachteln, um einen längeren Pfad zu beschreiben. Und wenn ein Paket und eines seiner Kinder gleichzeitig ersetzt werden sollen, nimmt die Objektform einen Schlüssel `.` für das Paket selbst auf, neben den Schlüsseln für seine Kinder.
Die direkte Abhängigkeit ist der eine Fall, den npm ablehnt. Ein Paket, von dem Sie direkt abhängen, dürfen Sie nicht überschreiben, es sei denn, Override und Abhängigkeit tragen exakt dieselbe Angabe; alles andere bricht die Installation mit einem `EOVERRIDE`-Fehler ab. Der dokumentierte Ausweg ist eine Referenz: `"foo": "$foo"` weist npm an, die Angabe zu übernehmen, die Ihr eigener Eintrag für `foo` in `dependencies` deklariert, und zwar in jeder Tiefe. Die Version wird an einer Stelle geändert, und der ganze Baum folgt.
Wenn der Override scheinbar nichts bewirkt
Overrides werden nur aus der `package.json` im Projektstamm gelesen. Die npm-Dokumentation ist eindeutig: Overrides in installierten Abhängigkeiten, Workspaces eingeschlossen, werden bei der Auflösung des Baums nicht berücksichtigt. Daraus folgt zweierlei. In einem Monorepo gehört das Feld ins Wurzelmanifest, nicht in das Paket, in dem das Problem sichtbar wird. Und ein `overrides`-Block in einer Bibliothek, die Sie veröffentlichen, bewirkt nichts für diejenigen, die sie installieren; die Dokumentation verweist Bibliotheksautoren stattdessen auf das Festlegen der Abhängigkeit oder auf `bundleDependencies`.
Prüfen Sie danach das Ergebnis, statt es anzunehmen. Führen Sie `npm install` aus, damit der Baum neu aufgelöst wird, und rufen Sie `npm explain foo` ein zweites Mal auf: Die Version am Kopf jedes Blocks ist die, die jetzt installiert ist. Committen Sie `package.json` und `package-lock.json` gemeinsam, denn die Lockfile trägt die aufgelöste Version auf jede andere Maschine. package-lock.json gegen package.json erklärt, warum sich beide Dateien nur als Paar bewegen dürfen.
Ein Override installiert eine Version, von der das Elternpaket nie erklärt hat, damit zu funktionieren. Genau darum geht es, wenn der deklarierte Bereich nur eine verwundbare Version enthält, und genau darin liegt auch das Risiko: npm hindert Sie nicht daran, eine Hauptversion zu erzwingen, für die das Elternpaket nicht geschrieben wurde. Behandeln Sie die Zeile wie ein Pflaster mit Verfallsdatum. Sobald das Elternpaket eine Version veröffentlicht, die den Fix akzeptiert, löschen Sie den Override.
Yarn und pnpm nutzen ein anderes Feld
Dieselbe Idee gibt es in den beiden anderen Paketmanagern, unter anderen Namen. Yarn liest ein Feld `resolutions`, das die Dokumentation seines Manifests als Möglichkeit beschreibt, eine bestimmte Auflösung anstelle dessen zu verwenden, was der Resolver normalerweise wählen würde; die Schlüssel erlauben eine Ebene der Eingrenzung, geschrieben als `eltern/kind`. pnpm nennt die Einstellung wie npm `overrides`, führt sie in seiner aktuellen Dokumentation in `pnpm-workspace.yaml` und grenzt sie mit dem Trennzeichen `>` ein, etwa `bar@1>foo`. Beide beschränken das Feld auf die Wurzel des Projekts, wie npm. Die Syntax lässt sich nicht von einem Werkzeug auf das andere übertragen: Ein Projekt, das den Paketmanager wechselt, muss den Block neu schreiben.
Die Entscheidungsfolge ist kurz. Gibt es vom Elternpaket bereits eine Version, die den Fix akzeptiert, aktualisieren Sie das Elternpaket und schreiben gar keinen Override. Wenn nicht, begrenzen Sie den Override auf dieses Elternpaket statt auf den ganzen Baum. Muss dasselbe Paket überall identisch sein, nehmen Sie die flache Form, mit einem Bereich, wenn ein Minimum genügt. Und wenn die Installation an einem Peer-Dependency-Konflikt scheitert und nicht an einer verwundbaren Version, liegt ein anderes Problem vor: Peer Dependencies behandelt es.
FAQ
Funktionieren npm overrides in der package.json eines Workspace?
Nein. npm berücksichtigt nur Overrides, die in der package.json im Projektstamm stehen. Laut Dokumentation werden Overrides in installierten Abhängigkeiten, Workspaces eingeschlossen, bei der Auflösung des Baums nicht beachtet. Das Feld gehört also ins Wurzelmanifest.
Was bedeutet der Fehler EOVERRIDE?
Er bedeutet, dass ein Override ein Paket betrifft, von dem Sie direkt abhängen, und zwar mit einer anderen Angabe als in Ihren dependencies. npm erlaubt diesen Override nur, wenn beide Angaben identisch sind. Gleichen Sie sie an, oder schreiben Sie den Override als Referenz, etwa "foo": "$foo", die die Angabe Ihrer direkten Abhängigkeit übernimmt.
Kann ein Override einen Bereich statt einer exakten Version verwenden?
Ja. Laut npm-Dokumentation kann der Wert eines Overrides jeder Spezifizierer sein, den npm für eine Abhängigkeit akzeptiert: exakte Version, Semver-Bereich, dist-tag oder ein Ersatz wie npm:, file: oder eine Git-URL. Ein Bereich wie ^1.2.4 setzt eine gepatchte Mindestversion durch, ohne das Paket einzufrieren.
Wie finde ich heraus, welches Paket eine transitive Abhängigkeit hereinzieht?
Führen Sie npm explain gefolgt vom Paketnamen aus. Der Befehl gibt die Kette der Abhängigkeiten aus, die zur Installation des Pakets führt, einen Block pro Exemplar im Baum, jeweils bis zum Wurzelprojekt zurückverfolgt.
Ist npm overrides dasselbe wie Yarn resolutions?
Der Zweck ist derselbe, die Syntax nicht. npm verwendet ein Feld overrides mit verschachtelten Objekten, Yarn ein Feld resolutions mit Schlüsseln der Form eltern/kind und pnpm eine Einstellung overrides mit dem Trennzeichen >. Alle drei beschränken das Feld auf die Wurzel des Projekts.



Ein Override installiert eine Version, von der das Elternpaket nie erklärt hat, damit zu funktionieren. Genau darum geht es, wenn der deklarierte Bereich nur eine verwundbare Version enthält, und genau darin liegt auch das Risiko: npm hindert Sie nicht daran, eine Hauptversion zu erzwingen, für die das Elternpaket nicht geschrieben wurde. Behandeln Sie die Zeile wie ein Pflaster mit Verfallsdatum. Sobald das Elternpaket eine Version veröffentlicht, die den Fix akzeptiert, löschen Sie den Override.