klonů v node_modules? Odpojte svůj souborový systém

(14. března 2019)

Kolik kopií adresy [email protected] by máš rád? (Photo by 浮萍 闪电 on Unsplash )

AKTUALIZACE: níže popsaná implementace má problémy se soubory, které mají závislosti (require / import příkazy ). Kvůli struktuře node\_modules/ může identický soubor žijící na dvou místech stahovat různé verze svých vlastních závislostí, takže pro větší bezpečnost byste se měli omezit na deduplikování souborů s nulou závislosti.

Jedním z problémů, které jsme narazili na Tradle v několika velkých projektech Node.js / React Native / webové aplikace, je to, že node\_modules končí duplikáty kopií určité závislosti. Když se aplikace spustí, analyzuje se, spouští a poté plýtvá pamětí na stejný modul několikrát.

V běžném starém Node.js obvykle není dostatečně velký trest za výkon, který by mohl kňučet, ale stále bolí vědět, že se to děje. Na mobilu / webu je kňučení povinné. V jednu chvíli si myslím, že jsme měli 10 kopií verze readable-stream v naší aplikaci React Native, a já nejsem dobrý v emodži, takže nechám Wat říct to za mě:

Wat učí Chaucer anglicky

Chcete cítit bolest? Zde je ukázka struktury závislostí, která problém způsobuje:

# assume for simplicity that deps specify exact versions, i.e.:
# myProject"s package.json specifies "lodash": "4.0.0"
# depA and depB"s package.json specifies "lodash": "3.0.0"myProject/
node\_modules/
lodash # 4.0.0
depA
node\_modules/lodash # 3.0.0
depB
node\_modules/lodash # 3.0.0 <- not cool

Váš projekt závisí na [email protected], ale na dvou vašich závislosti závisí na [email protected]. Správce balíků nemůže deduplikovat [email protected] tím, že jej zvedne až ke kořenu, protože [email protected] tam sedí. Takže skončíte se dvěma kopiemi [email protected], jednou pod depA a druhou pod depB . Jsou identické. A nemůžete s tím nic dělat, pokud nemáte čas posílat PR autorům depA a depB a upgradovat je na [email protected] (PR, jejichž sloučení může trvat měsíce) a počkejte, až se spojí. To nebo udržujte vidlice obou těchto modulů. Takže jděte vyřešit nějaký jiný problém.

Nevím, jak to všechno věděl tehdy

Gratulujeme! Nyní jste odložili problém s deduplikací na svého svazku. Webpack, Browserify a Metro to nyní musí řešit samostatně. Toto je otevřené vydání ve Webpacku. Je otevřený od roku 2017 a kdybych byl Webpackem, byl bych naštvaný na lidi, kteří se mi snaží udělat z toho můj problém.

Tento problém není nový a trpělivě se vysmíval z vedlejší koleje, protože mnoho inkarnace správců balíčků a balíčků pro Node.js a prohlížeč. Pravděpodobně tu bude ještě chvíli, jako vydatný digitální šváb.

Naštěstí si díky nedávnému snížení daní nyní můžeme dovolit rozsvítit světlo na konci tunelu. (Ale jen pro bohaté).

Světlo na konci tunelu

Abychom ořezali stejné kopie závislostí v balíčku aplikace Tradles React Native, rozhodli jsme se nezkusit přidejte podporu pro Metro Packager nebo se obraťte na tým Metro Packager. Naše aplikace také dodáváme s React Native pro Web, který je dodáván s Webpackem, a chtěli jsme trik, který by fungoval pro oba.

Rozhodli jsme se zacílit na to, co mají všichni správci balíčků a svazky společné: resolve() algoritmus Node.js. Toto je algoritmus, který se provede při spuštění require("some-module") k nalezení absolutní cesty modulu ve vašem souborovém systému. Na tento algoritmus spoléhají jak správci balíčků, tak svazovači. První sestavuje adresářovou strukturu node\_modules a druhý ji prochází sestavováním balíčku.

Řešení je jednoduché: procházet node\_modules, identifikovat všechny duplikáty, vybrat jeden z kanonických (např. první abecedně) a přepsat soubory *.js do všichni ostatní, aby ukázali na kanonickou. Soubory *.js v duplicitních složkách nakonec vypadají takto:

// deduped by dedupe-deps
module.exports = require("../../../changes-feed/node\_modules/readable-stream/duplex.js")

Cítím se trochu špinavě, ale funguje to a náš balíček se zmenšuje a tím rychleji načítá. Modul je open source, můžete to zkusit: https://github.com/tradle/dedupe-deps

Musí existovat lepší způsob

Někteří z vás pravděpodobně nyní kroutili očima a křičeli „stačí použít pnpm!"Opatrně, kroužení očí může být návykové." A vy nechcete vědět, co vám předepíšou, abyste se vyhnuli oklamavému výběru.

pnpm je relativně nový balicí program v město. Kombinace pevných odkazů a symbolických odkazů používá k zajištění toho, že modul je v daném projektu node\_modules přítomen pouze jednou. Ve skutečnosti zaručuje, že se v celém vašem souborovém systému objeví pouze jednou! To je skvělé. Jednoho dne všichni použijeme pnpm a všechno bude kouzelně fungovat, ale mám pocit, že dnes u některých balíčků stále budou problémy s kompatibilitou.

… Ano, právě jsem zkontroloval a v Metro je to stále problém, i když to vypadá, že se blíží řešení: https://github.com/facebook/metro/ pull / 257

Posunutí o krok dále

Takže zpět k našemu řešení založenému na souborovém systému. Můžeme posunout věci o krok dále a zabít více ptáků stejným kamenem. Pravděpodobně jsou to ještě větší ptáci než původní. Představme si, že máme tento strom závislostí:

myProject/
node\_modules/
lodash # 4.0.0
depA
node\_modules/lodash # 3.0.5
depB
node\_modules/lodash # 3.0.0 <- not cool

Zde máme dvě různé verze [email protected]. V závislosti na semver rozsahu zadaném pro lodash v depA a depB mohou být velmi snadno dedupovatelné. V současné době modul dedupe-deps tento scénář neřeší, ale rádi přijímáme PR.