Struttura `node_modules` con collegamenti simbolici
Questo articolo descrive solo come è strutturata la cartella node_modules di pnpm quando non ci sono pacchetti con dipendenze peer. Per lo scenario più complesso di dipendenze con i peer, vedere come vengono risolti i peer.
il layout node_modules pnpm utilizza collegamenti simbolici per creare una struttura nidificata di dipendenze.
Ogni file di ogni pacchetto all'interno di node_modules è un collegamento fisico all'archivio indirizzabile al contenuto. Diciamo che installi foo@1.0.0 che dipende da bar@1.0.0. pnpm collegherà entrambi i pacchetti a node_modules in questo modo:
node_modules
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       └── bar -> <store>/bar
    │           ├── index.js
    │           └── package.json
    └── foo@1.0.0
        └── node_modules
            └── foo -> <store>/foo
                ├── index.js
                └── package.json
Questi sono gli unici file "reali" in node_modules. Una volta che tutti i pacchetti sono collegati fisicamente a node_modules, vengono creati collegamenti simbolici per creare la struttura del grafico delle dipendenze annidata.
Come potresti aver notato, entrambi i pacchetti sono collegati fisicamente in una sottocartella all'interno di una cartella node_modules (foo@1.0.0/node_modules/foo). Ciò è necessario per:
- permettere ai pacchetti di importarsi. foodovrebbe essere in grado di eseguirerequire('foo/package.json')oimport * as package from "foo/package.json".
- evitare i collegamenti simbolici circolari. Le dipendenze dei pacchetti sono posizionate nella stessa cartella in cui si trovano i pacchetti dipendenti. Per Node.js non fa differenza se le dipendenze si trovano all'interno di node_modulesdel pacchetto o in qualsiasi altronode_modulesdelle cartelle superiori.
La fase successiva dell'installazione è il collegamento simbolico delle dipendenze. bar sarà collegato simbolicamente alla cartella foo@1.0.0/node_modules:
node_modules
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       └── bar -> <store>/bar
    └── foo@1.0.0
        └── node_modules
            ├── foo -> <store>/foo
            └── bar -> ../../bar@1.0.0/node_modules/bar
Successivamente, vengono gestite le dipendenze dirette. pippo verrà collegato simbolicamente nella cartella root node_modules perché foo è una dipendenza del progetto:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       └── bar -> <store>/bar
    └── foo@1.0.0
        └── node_modules
            ├── foo -> <store>/foo
            └── bar -> ../../bar@1.0.0/node_modules/bar
Questo è un esempio molto semplice. Tuttavia, il layout manterrà questa struttura indipendentemente dal numero di dipendenze e dalla profondità del grafico delle dipendenze.
Aggiungiamo qar@2.0.0 come dipendenza di bar e foo. Ecco come apparirà la nuova struttura:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       ├── bar -> <store>/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    ├── foo@1.0.0
    │   └── node_modules
    │       ├── foo -> <store>/foo
    │       ├── bar -> ../../bar@1.0.0/node_modules/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    └── qar@2.0.0
        └── node_modules
            └── qar -> <store>/qar
Come puoi vedere, anche se il grafico ora è più profondo (pippo > bar > qar), la cartella nel file system è sempre la stessa.
Questo layout potrebbe sembrare strano a prima vista, ma è completamente compatibile con l'algoritmo di risoluzione del modulo di Node! Quando risolve i moduli, Node ignora i collegamenti simbolici, quindi quando è richiesto bar da foo@1.0.0/node_modules/foo/index.js, Node non usa bar da foo@1.0.0/node_modules/bar, ma invece bar è risolto nella sua posizione reale (bar@1.0.0/node_modules/bar). Di conseguenza, bar può anche risolvere le sue dipendenze che sono in bar@1.0.0/node_modules.
Un grande bonus di questo layout è che solo i pacchetti che sono davvero nelle dipendenze sono accessibili. Con una struttura node_modules appiattita, tutti i pacchetti installati sono accessibili. Per saperne di più sul perché questo è un vantaggio, vedi "la severità di pnpm aiuta ad evitare bug stupidi"