Les clones dans node_modules vous ont déprimé? Dédupliquez votre système de fichiers

(14 mars 2019)

Combien de copies de [email protected] vous aimez? (Photo de 浮萍 闪电 sur Unsplash )

UPDATE: limplémentation décrite ci-dessous a des problèmes avec les fichiers qui ont des dépendances (instructions require / import ). En raison de la structure de node\_modules/, un fichier identique vivant à deux emplacements peut extraire différentes versions de ses propres dépendances, donc pour être plus sûr, vous devez vous limiter à la déduplication des fichiers avec zéro dépendances.

Lun des problèmes que nous avons rencontrés chez Tradle dans plusieurs grands projets Node.js / React Native / web app est que node\_modules aboutit à des copies dupliquées de certaines dépendances. Lorsque lapplication démarre, elle analyse, exécute puis gaspille de la mémoire sur le même module plusieurs fois.

Dans lancien Node.js normal, il ny a généralement pas de pénalité de performances assez importante pour se plaindre, mais ça fait toujours mal de savoir que ça se passe. Sur mobile / web, pleurnicher est obligatoire. À un moment donné, je pense que nous avions 10 exemplaires dune version de readable-stream dans notre application React Native, et je ne suis pas doué pour les emoji, alors je vais laisser Wat le dire pour moi:

Wat enseigne langlais Chaucer

Envie de ressentir la douleur? Voici un exemple de structure de dépendance à lorigine du problème:

# 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

Votre projet dépend de [email protected], mais deux de vos les dépendances dépendent de [email protected]. Votre gestionnaire de packages ne peut pas dédupliquer [email protected] en le hissant à la racine car [email protected] se trouve là. Vous vous retrouvez donc avec deux copies de [email protected], une sous depA et une sous depB . Ils sont identiques. Et vous ne pouvez rien y faire, sauf si vous avez le temps denvoyer des PR aux auteurs de depA et depB pour les mettre à niveau vers [email protected] (PR dont la fusion peut prendre des mois), et attendez qu’ils soient fusionnés. Cela ou maintenez les fourches de ces deux modules. Alors vous allez résoudre un autre problème.

Je ne sais pas comment il a tout su à lépoque

Félicitations! Vous avez maintenant reporté le problème de déduplication à votre bundler. Webpack, Browserify et Metro doivent maintenant chacun le résoudre indépendamment. Voici le problème en cours sur Webpack. Il est ouvert depuis 2017, et si jétais Webpack, je serais ennuyé par les gens qui essaient den faire mon problème.

Ce problème nest pas nouveau et a patiemment ricané depuis les coulisses comme les nombreux incarnations de gestionnaires de packages et de bundlers pour Node.js et le navigateur défilé par. Il restera probablement un peu plus longtemps, comme un cafard numérique copieux.

Heureusement, avec les récentes réductions dimpôts, nous pouvons maintenant nous permettre dallumer la lumière au bout du tunnel. (Mais juste pour les riches).

La lumière au bout du tunnel

Pour élaguer les copies identiques des dépendances dans le bundle de lapplication React Native de Tradle, nous avons décidé de ne pas essayer de ajoutez un support à Metro Packager ou faites appel à léquipe Metro Packager. Nous livrons également notre application avec React Native for Web, qui se combine avec Webpack, et nous voulions une astuce qui fonctionnerait pour les deux.

Nous avons décidé de cibler le point commun à tous les gestionnaires de packages et bundlers: Algorithme resolve() de Node.js. Il sagit de lalgorithme qui sexécute lorsque vous exécutez require("some-module"), pour trouver le chemin absolu dun module sur votre système de fichiers. Les gestionnaires de packages et les bundleurs sappuient sur cet algorithme, le premier pour construire la structure de répertoires node\_modules, le second pour la parcourir pour construire le bundle.

La solution est simple: parcourez node\_modules, identifiez tous les doublons, choisissez lun dentre eux comme canonique (par exemple le premier par ordre alphabétique) et réécrivez les fichiers *.js dans tous les autres pour pointer vers le canonique. Les fichiers *.js dans les dossiers en double finissent par ressembler à ceci:

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

Cela semble un peu sale, mais cela fonctionne et rend notre bundle plus petit et donc plus rapide à charger. Le module est open source, nhésitez pas à lessayer: https://github.com/tradle/dedupe-deps

Il doit y avoir un meilleur moyen

Certains d’entre vous sont probablement en train de rouler des yeux et de crier "utilisez simplement pnpm!«Attention, le fait de rouler les yeux peut créer une dépendance. Et vous ne voulez pas savoir ce quils vous prescriront pour réduire le retrait des yeux.

pnpm est un packager relativement nouveau dans ville. Il utilise une combinaison de liens physiques et de liens symboliques pour garantir qu’un module n’est présent qu’une seule fois dans le node\_modules d’un projet donné. En fait, cela garantit quil ne sera présent quune seule fois dans tout votre système de fichiers! Cest très cool. Un jour, nous utiliserons tous pnpm et tout fonctionnera comme par magie, mais jai le sentiment quaujourdhui il y aura encore des problèmes de compatibilité avec certains bundlers.

… Oui, je viens de vérifier et cest toujours un problème dans Metro, même sil semble quils se rapprochent dune solution: https://github.com/facebook/metro/ pull / 257

Aller plus loin

Revenons donc à notre solution basée sur le système de fichiers. Nous pouvons aller plus loin et tuer plus doiseaux avec la même pierre. On peut soutenir que ce sont des oiseaux encore plus gros que loriginal. Imaginons que nous ayons cet arbre de dépendances:

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

Nous avons ici deux versions différentes de [email protected]. Cependant, selon les plages de semver spécifiées pour lodash dans depA et depB, ils peuvent très bien être dédupliqués. Actuellement, le module dedupe-deps ne répond pas à ce scénario, mais nous sommes heureux daccepter les RP.