Perché utilizzare le dipendenze peer in npm per i plugin?


218

Perché, ad esempio, un plug-in Grunt definisce la sua dipendenza da grunt come " dipendenze tra pari "?

Perché il plugin non può avere Grunt come propria dipendenza in grunt-plug / node_modules ?

Le dipendenze tra pari sono descritte qui: https://nodejs.org/en/blog/npm/peer-dependencies/

Ma non capisco davvero.

Esempio

Al momento sto lavorando con AppGyver Steroids, che utilizza le attività di Grunt per creare i miei file sorgente in una cartella / dist / da servire su un dispositivo locale. Sono abbastanza nuovo su npm e grugnito, quindi voglio comprendere appieno cosa sta succedendo.

Finora ho capito questo:

[rootfolder] /package.json dice a npm che dipende dal grunt-steroidspacchetto npm per lo sviluppo:

  "devDependencies": {
    "grunt-steroids": "0.x"
  },

Va bene. L'esecuzione di npm install in [rootfolder] rileva la dipendenza e installa grunt-steroidi in [rootfolder] / node_modules / grunt-steroids .

Npm quindi legge [rootfolder] /node_modules/grunt-steroids/package.json in modo da poter installare le grunt-steroidsproprie dipendenze .:

"devDependencies": {
    "grunt-contrib-nodeunit": "0.3.0",
    "grunt": "0.4.4"
  },
"dependencies": {
    "wrench": "1.5.4",
    "chalk": "0.3.0",
    "xml2js": "0.4.1",
    "lodash": "2.4.1"
  },
"peerDependencies": {
    "grunt": "0.4.4",
    "grunt-contrib-copy": "0.5.0",
    "grunt-contrib-clean": "0.5.0",
    "grunt-contrib-concat": "0.4.0",
    "grunt-contrib-coffee": "0.10.1",
    "grunt-contrib-sass": "0.7.3",
    "grunt-extend-config": "0.9.2"
  },

I pacchetti " dipendenze " sono installati in [rootfolder] / node_modules / grunt-steroids / node_modules che è logico per me.

Le " devDependencies " non sono installate, che sono sicuro che è controllato da npm rilevando che sto solo cercando di usare grunt-steroidse non svilupparle.

Ma poi abbiamo le " Dipendenze tra pari ".

Questi sono installati in [rootfolder] / node_modules e non capisco perché lì e non in [rootfolder] / node_modules / grunt-steroids / node_modules in modo da evitare conflitti con altri plug -in grugniti (o altro)?

Risposte:


421

TL; DR: peerDependencies sono per le dipendenze che sono esposte (e si prevede che saranno utilizzate da) il codice di consumo, al contrario delle dipendenze "private" che non sono esposte e sono solo un dettaglio di implementazione.

Il problema delle dipendenze tra pari si risolve

Il sistema di moduli di NPM è gerarchico. Un grande vantaggio per gli scenari più semplici è che quando si installa un pacchetto npm, quel pacchetto porta le proprie dipendenze con esso in modo che funzioni immediatamente.

Ma sorgono problemi quando:

  • Sia il tuo progetto che alcuni moduli che stai utilizzando dipendono da un altro modulo.
  • I tre moduli devono dialogare.

Per esempio

Diciamo che stai costruendo YourCoolProjecte stai usando entrambi JacksModule 1.0e JillsModule 2.0. E supponiamo che JacksModuledipenda anche da JillsModule, ma da una versione diversa, diciamo 1.0. Finché quelle 2 versioni non si incontrano, non ci sono problemi. Il fatto che JacksModulesta usando JillsModulesotto la superficie è solo un dettaglio di implementazione. Stiamo raggruppando JillsModuledue volte, ma questo è un piccolo prezzo da pagare quando otteniamo un software stabile pronto all'uso.

Ma ora se JacksModuleesponesse la sua dipendenza JillsModulein qualche modo. Ad esempio accetta un'istanza di JillsClass... Cosa succede quando creiamo una new JillsClassversione 2.0della libreria e la passiamo a jacksFunction? Si scatenerà l'inferno! Cose semplici come jillsObject instanceof JillsClassimprovvisamente torneranno falseperché in jillsObjectrealtà è un'istanza di un'altra JillsClass , la 2.0versione.

Come le dipendenze dei pari risolvono questo problema

Dicono a npm

Ho bisogno di questo pacchetto, ma ho bisogno della versione che fa parte del progetto, non di una versione privata del mio modulo.

Quando npm vede che il tuo pacchetto viene installato in un progetto che non ha quella dipendenza, o che ne ha una versione incompatibile , avviserà l'utente durante il processo di installazione.

Quando dovresti usare le dipendenze tra pari?

  • Quando stai costruendo una libreria per essere utilizzata da altri progetti, e
  • Questa libreria sta utilizzando un'altra libreria e
  • Ti aspetti / hai bisogno che l'utente lavori anche con quell'altra libreria

Gli scenari comuni sono plugin per framework più grandi. Pensa a cose come Gulp, Grunt, Babel, Mocha, ecc. Se scrivi un plug-in Gulp, vuoi che quel plug-in funzioni con lo stesso Gulp utilizzato dal progetto dell'utente, non con la tua versione privata di Gulp.


2
Una cosa importante che ho notato e che non viene detto da nessuna parte, quando stiamo costruendo un plugin, dovremmo avere un duplicato delle dipendenze dei pacchetti, per le dipendenze tra pari? Nell'esempio OP, possiamo vedere che "grunt": "0.4.4"è sia in devDependencies che peerDependencies, e ha senso per me avere un duplicato lì, perché significa sia che ho bisogno di quel gruntpacchetto per il mio uso, ma anche che gli utenti del mio la libreria può utilizzare la propria versione, purché rispetti il ​​blocco della versione peerDependencies. È corretto? Oppure l'esempio OP è molto negativo?
Vadorequest,

4
Posso immaginare che le persone che creano un plug-in Grunt siano fan di Grunt :) In quanto tali, sembra naturale per loro usare Grunt per il processo di compilazione del loro plug-in .... Ma perché dovrebbero voler bloccare la gamma di versioni di Grunt che il loro plug-in funziona con il processo di compilazione che usano per crearlo? Aggiungendolo come dipendenza dev consente loro di disaccoppiarlo. Fondamentalmente ci sono 2 fasi: tempo di costruzione e tempo di esecuzione. Le dipendenze degli sviluppatori sono necessarie durante il tempo di compilazione. Dipendenze regolari e peer sono necessarie in fase di esecuzione. Naturalmente con le dipendenze delle dipendenze tutto diventa rapidamente confuso :)
Stijn de Witt,

1
Grazie per questa risposta! Giusto per chiarire, nel tuo esempio, se JacksModuledipende da JillsModule ^1.0.0con JillsModuleessendo una dipendenza pari di JacksModulee YourCoolProjectstavano usando JacksModulee JillsModule ^2.0.0, avremo l'avvertimento pari della dipendenza da NPM, che ci consiglierà di installare JillsModule ^1.0.0pure. Ma cosa succede allora? YourCoolProjectavranno ora due versioni di JillsModuleimportable attraverso import jillsModule from "..."? E come ricordo che quando uso JacksModuledevo passare un'istanza di JillsModule v1.0.0?
tonix,

1
@tonix Bene, sarà davvero un problema avere una incompatibilità di versione. peerDependencies non lo risolve. Ma aiuta a rendere esplicito il problema. Perché mostrerà chiaramente la mancata corrispondenza della versione invece di usare due versioni silenziosamente. Lo sviluppatore dell'app che sta selezionando le librerie dovrà trovare una soluzione.
Stijn de Witt,

2
@tonix O la terza opzione: clonare il JacksModulerepository, aggiornarlo per dipendere JillsModule ^2.0.0e offrire un PR al responsabile del progetto. Potrebbe essere utile in primo luogo inviare un bug dicendo che questa dipendenza è obsoleta e si desidera aiutare ad aggiornarlo. Se fai un buon PR, la maggior parte dei manutentori delle biblioteche lo uniranno e ti ringrazieranno. Se i manutentori non rispondono, è possibile pubblicare il proprio fork su NPM con il proprio nome e utilizzare invece il fork. In ogni caso, ci sono soluzioni ma peerDependenciesnon le risolve da solo.
Stijn de Witt,

26

Ti consiglierei di leggere di nuovo l'articolo prima. È un po 'confuso ma l'esempio con winston-mail ti mostra la risposta perché:

Ad esempio, facciamo finta che sia winston-mail@0.2.3specificato "winston": "0.5.x"nel suo "dependencies"oggetto perché è l'ultima versione per cui è stato testato. Come sviluppatore di app, vuoi le cose più recenti e migliori, quindi cerchi le ultime versioni di winstone di winston-maile inseriscile nel tuo pacchetto. Json as

{
  "dependencies": {  
    "winston": "0.6.2",  
    "winston-mail": "0.2.3"  
  }  
}

Ma ora, l'esecuzione di npm install risulta nel grafico delle dipendenze impreviste di

├── winston@0.6.2  
└─┬ winston-mail@0.2.3                
  └── winston@0.5.11

In questo caso, è possibile avere più versioni di un pacchetto che potrebbero causare alcuni problemi. Le dipendenze tra pari consentono agli sviluppatori npm di assicurarsi che l'utente disponga del modulo specifico (nella cartella principale). Ma hai ragione sul fatto che la descrizione di una versione specifica di un pacchetto porterebbe a problemi con altri pacchetti che usano altre versioni. Questo problema ha a che fare con gli sviluppatori npm, come afferma l'articolo

Un consiglio : i requisiti di dipendenza tra pari, a differenza di quelli per le dipendenze regolari, dovrebbero essere indulgenti . Non è necessario bloccare le dipendenze dei peer fino a versioni di patch specifiche.

Pertanto gli sviluppatori dovrebbero seguire semver per la definizione di peerDependencies. Si dovrebbe aprire un problema per il pacchetto di steroidi grunt su GitHub ...


1
Lo dici multiple versions of a package which would cause some issuesma non è questo il punto di un gestore di pacchetti? Ne discutono anche più avanti nello stesso articolo in cui ci sono 2 versioni dello stesso pacchetto nel progetto: una fornita dallo sviluppatore e una fornita da una libreria di terze parti.
Adam Beck,

1
Penso di aver compreso il punto di dipendenza tra pari, ma winstonnell'esempio non riesco ora a utilizzare la winston-maillibreria perché la mia versione non corrisponde alla dipendenza tra pari? Preferirei avere quel downgrade temporaneo dall'ultimo e massimo per la libreria 1 piuttosto che non poterlo usare affatto.
Adam Beck,

1
per il tuo primo commento, per quanto ho capito e lo uso, ha a che fare con i test, ad esempio se hai un pacchetto che è stato testato da te su un pacchetto di terze parti specifico, non puoi essere sicuro che se uno delle tue dipendenze cambiano (correzione di bug, aggiornamento delle funzionalità principali) che il tuo pacchetto funzionerà. Pertanto, è possibile specificare una versione specifica del plug-in e salvare con i test.
Fer fino al

1
Sul tuo secondo commento: ecco perché nei documenti dicono che gli sviluppatori dovrebbero essere indulgenti con le dipendenze dei loro pacchetti e dovrebbero usare semver, ad esempio invece di "0.2.1", "~ 0.2.1" -> consente "0.2.x" ma non "0.3.x" o "> = 0.2.1" -> tutto da "0.2.x" a "1.x" o "x.2.". .. (ma non molto preferibile per un pacchetto npm andrebbe con ~
Fer fino al

15

peerDependencies spiegato con l'esempio più semplice possibile:

{
  "name": "myPackage",
  "dependencies": {
    "foo": "^4.0.0",
    "react": "^15.0.0"
  }
}


{
  "name": "foo"
  "peerDependencies": {
    "react": "^16.0.0"
  }
}

l'esecuzione di npm install in myPackage genererà un errore perché sta tentando di installare la versione di React ^15.0.0E fooche è compatibile solo con React ^16.0.0.

le peerDependencies NON sono installate.


perché non mettere semplicemente la reazione 16 come dep all'interno del foo? in questo modo sia 15 che 16 saranno disponibili e foo può usare 16 e mypackage può usare 15?
nitinsh99,

React è un framework che viene eseguito il bootstrap in fase di runtime, affinché sia ​​React 15 che React 16 esistano nella stessa pagina, è necessario che entrambi vengano sottoposti a bopping simultaneamente, il che sarebbe estremamente pesante e problematico per l'utente finale. Se foofunziona con React 15 e React 16, allora potrebbe elencare la sua peerDependency come >=15 < 17.
Jens Bodal,

nitinsh99 la mia risposta è stata quella di spiegare lo scopo di PeerDependencies con l'esempio più semplice possibile, non come sbarazzarsi dell'errore generato da peerDependencies
Christopher Tokar

@ nitinsh99 aggiungendo la reazione all'interno della dipendenza del pacchetto fornirà un problema come Hooks - Reazioni multiple in un pacchetto
Masood
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.