Mengapa menggunakan dependensi rekan di npm untuk plugin?

217

Mengapa, misalnya, plugin Grunt mendefinisikan ketergantungannya pada grunt sebagai " dependensi rekan "?

Mengapa plugin tidak memiliki Grunt sebagai ketergantungannya sendiri di grunt-plug / node_modules ?

Ketergantungan sebaya dijelaskan di sini: https://nodejs.org/en/blog/npm/peer-dependencies/

Tapi saya tidak begitu mengerti.

Contoh

Saya sedang bekerja dengan AppGyver Steroid saat ini yang menggunakan tugas Grunt untuk membangun file sumber saya ke folder / dist / untuk dilayani di perangkat lokal. Saya cukup baru di npm dan mendengus jadi saya ingin sepenuhnya memahami apa yang sedang terjadi.

Sejauh ini saya mendapatkan ini:

[rootfolder] /package.json memberitahu npm itu tergantung pada grunt-steroidspaket npm untuk pengembangan:

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

Baik. Menjalankan npm install di [rootfolder] mendeteksi ketergantungan dan menginstal grunt-steroid di [rootfolder] / node_modules / grunt-steroids .

Npm kemudian membaca [rootfolder] /node_modules/grunt-steroids/package.json sehingga dapat menginstal grunt-steroidsdependensi sendiri .:

"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"
  },

Paket " dependensi " diinstal ke dalam [rootfolder] / node_modules / grunt-steroids / node_modules yang logis untuk saya.

" DevDependencies " tidak diinstal, yang saya yakin dikendalikan oleh npm mendeteksi saya hanya mencoba untuk menggunakan grunt-steroids, dan tidak mengembangkannya.

Tapi kemudian kita memiliki " peerDependencies ".

Ini dipasang di [rootfolder] / node_modules , dan saya tidak mengerti mengapa ada dan tidak di [rootfolder] / node_modules / grunt-steroids / node_modules sehingga konflik dengan plugin kasar lainnya (atau apa pun) dihindari?

Thomas Stock
sumber

Jawaban:

420

TL; DR: [1] peerDependencies adalah untuk dependensi yang terpapar (dan diharapkan akan digunakan oleh) kode konsumsi, sebagai lawan dari "pribadi" dependensi yang tidak diekspos, dan hanya detail implementasi.

Masalah dependensi rekan memecahkan

Sistem modul NPM bersifat hierarkis. Satu keuntungan besar untuk skenario yang lebih sederhana adalah ketika Anda menginstal paket npm, paket itu membawa dependensinya sendiri sehingga paket itu akan bekerja di luar kotak.

Tetapi masalah muncul ketika:

  • Baik proyek Anda dan beberapa modul yang Anda gunakan bergantung pada modul lain.
  • Tiga modul harus saling berbicara.

Dalam contoh

Katakanlah Anda sedang membangun YourCoolProjectdan Anda menggunakan keduanya JacksModule 1.0dan JillsModule 2.0. Dan anggap saja itu JacksModulejuga tergantung JillsModule, tetapi pada versi yang berbeda, katakanlah 1.0. Selama 2 versi itu tidak bertemu, tidak ada masalah. Fakta yang JacksModulemenggunakan di JillsModulebawah permukaan hanyalah detail implementasi. Kami bundling JillsModuledua kali, tapi itu harga kecil yang harus dibayar ketika kami mengeluarkan perangkat lunak yang stabil.

Tapi sekarang bagaimana jika JacksModulememperlihatkan ketergantungannya JillsModuledalam beberapa cara. Ini menerima contoh JillsClassmisalnya ... Apa yang terjadi ketika kita membuat new JillsClassversi menggunakan 2.0perpustakaan dan meneruskannya jacksFunction? Semua akan hancur berantakan! Hal-hal sederhana seperti jillsObject instanceof JillsClasstiba-tiba akan kembali falsekarena jillsObjectsebenarnya contoh lain JillsClass , yang 2.0versi.

Bagaimana dependensi rekan menyelesaikan ini

Mereka memberi tahu npm

Saya memerlukan paket ini, tetapi saya membutuhkan versi yang merupakan bagian dari proyek, bukan versi pribadi untuk modul saya.

Ketika npm melihat bahwa paket Anda sedang diinstal ke dalam proyek yang tidak memiliki ketergantungan itu, atau yang memiliki versi yang tidak kompatibel , itu akan memperingatkan pengguna selama proses instalasi.

Kapan Anda harus menggunakan dependensi rekan?

  • Ketika Anda sedang membangun perpustakaan untuk digunakan oleh proyek lain, dan
  • Perpustakaan ini menggunakan beberapa perpustakaan lain, dan
  • Anda mengharapkan / membutuhkan pengguna untuk bekerja dengan perpustakaan lain itu juga

Skenario umum adalah plugin untuk kerangka kerja yang lebih besar. Pikirkan hal-hal seperti Gulp, Grunt, Babel, Mocha, dll. Jika Anda menulis plugin Gulp, Anda ingin plugin itu bekerja dengan Gulp yang sama dengan yang digunakan proyek pengguna, bukan dengan Gulp versi pribadi Anda.


Anotasi

  1. Terlalu panjang; tidak membaca. Digunakan untuk menunjukkan ringkasan singkat untuk teks yang dianggap terlalu panjang.
Stijn de Witt
sumber
2
Satu hal penting yang saya perhatikan dan tidak dikatakan di mana pun, ketika kita sedang membangun sebuah plugin, haruskah kita memiliki duplikat dependensi paket, untuk dependensi rekan? Dalam contoh OP, kita dapat melihat bahwa "grunt": "0.4.4"keduanya dalam devDependencies dan peerDependencies, dan masuk akal bagi saya untuk memiliki duplikat di sana, karena itu berarti bahwa saya memerlukan gruntpaket itu untuk saya gunakan sendiri, tetapi juga para pengguna saya perpustakaan dapat menggunakan versi mereka sendiri, selama itu menghormati kunci versi peerDependencies. Apakah itu benar? Atau apakah contoh OP itu sangat buruk?
Vadorequest
4
Saya bisa membayangkan orang membuat plugin Grunt menjadi penggemar Grunt :) Dengan demikian tampaknya wajar bagi mereka untuk menggunakan Grunt sendiri untuk proses membangun plugin mereka .... Tapi mengapa mereka ingin mengunci kisaran versi Grunt karya plugin mereka bekerja plugin mereka dengan proses membangun yang mereka gunakan untuk membuatnya? Menambahkannya sebagai dependensi dev memungkinkan mereka untuk memisahkan ini. Pada dasarnya ada 2 fase: membangun waktu dan menjalankan waktu. Dev dependensi diperlukan selama waktu pembangunan. Dependensi Reguler dan rekan diperlukan pada saat runtime. Tentu saja dengan dependensi dependensi semuanya menjadi membingungkan dengan cepat :)
Stijn de Witt
1
Terima kasih atas jawaban ini! Hanya untuk mengklarifikasi, dalam contoh Anda, jika JacksModuletergantung JillsModule ^1.0.0dengan JillsModulemenjadi ketergantungan sebaya JacksModuledan YourCoolProjectsedang menggunakan JacksModuledan JillsModule ^2.0.0, kami akan mendapatkan peringatan ketergantungan teman sebaya oleh NPM, yang akan menyarankan kami untuk menginstal JillsModule ^1.0.0juga. Tetapi apa yang terjadi kemudian? YourCoolProjectsekarang akan memiliki dua versi yang JillsModuledapat diimpor melalui import jillsModule from "..."? Dan bagaimana saya ingat bahwa ketika saya menggunakan JacksModulesaya harus memberikannya contoh JillsModule v1.0.0?
tonix
1
@tonix Yah, itu memang akan menjadi masalah bahwa Anda memiliki ketidakcocokan versi. peerDependencies tidak menyelesaikannya. Tetapi itu membantu membuat masalah menjadi eksplisit. Karena itu jelas akan menunjukkan ketidakcocokan versi daripada menggunakan dua versi secara diam-diam. Pengembang aplikasi yang memilih perpustakaan harus menemukan solusi.
Stijn de Witt
2
@tonix Atau opsi ketiga: mengkloning JacksModulerepo, memutakhirkannya untuk bergantung pada JillsModule ^2.0.0dan menawarkan PR kepada pengelola proyek. Mungkin membantu untuk mengirimkan bug terlebih dahulu dengan mengatakan bahwa dependensi ini sudah usang dan Anda ingin membantu memperbaruinya. Jika Anda membuat PR yang baik, sebagian besar pengelola perpustakaan akan menggabungkannya dan berterima kasih untuk itu. Jika pengelola tidak responsif, Anda dapat menerbitkan garpu Anda ke NPM yang diberi nama dengan nama Anda dan menggunakan garpu Anda sebagai gantinya. Bagaimanapun, ada solusi tetapi peerDependenciestidak menyelesaikannya sendiri.
Stijn de Witt
26

Saya akan merekomendasikan Anda untuk membaca artikel itu lagi terlebih dahulu. Agak membingungkan tetapi contoh dengan winston-mail menunjukkan kepada Anda mengapa:

Misalnya, mari kita berpura-pura yang [email protected]ditentukan "winston": "0.5.x"dalam "dependencies"objeknya karena itu versi terbaru yang diuji. Sebagai pengembang aplikasi, Anda menginginkan hal-hal terbaru dan terhebat, sehingga Anda mencari versi terbaru dari winstondan dari winston-maildan memasukkannya ke dalam paket Anda.

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

Tapi sekarang, menjalankan npm menginstal hasil dalam grafik dependensi tak terduga

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

Dalam hal ini, dimungkinkan untuk memiliki beberapa versi paket yang akan menyebabkan beberapa masalah. Ketergantungan sebaya memungkinkan pengembang npm untuk memastikan bahwa pengguna memiliki modul tertentu (dalam folder root). Tapi Anda benar dengan titik yang menggambarkan satu versi spesifik dari suatu paket akan menyebabkan masalah dengan paket lain menggunakan versi lain. Masalah ini berkaitan dengan pengembang npm, seperti yang dinyatakan dalam artikel

Salah satu saran : persyaratan ketergantungan sebaya, tidak seperti yang untuk ketergantungan reguler, harus lunak . Anda tidak boleh mengunci dependensi rekan Anda ke versi tambalan tertentu.

Oleh karena itu pengembang harus mengikuti semver untuk mendefinisikan peerDependencies. Anda harus membuka masalah untuk paket steroid-steroid di GitHub ...

Fer Ke
sumber
1
Anda mengatakan itu multiple versions of a package which would cause some issuestetapi bukankah itu inti dari manajer paket? Mereka bahkan mendiskusikan hal ini lebih jauh di artikel yang sama di mana ada 2 versi dari paket yang sama dalam proyek: satu disediakan oleh pengembang dan satu disediakan oleh perpustakaan pihak ke-3.
Adam Beck
1
Saya pikir saya mengerti titik ketergantungan teman tetapi dalam winstoncontoh apakah sekarang saya tidak dapat menggunakan winston-mailperpustakaan karena versi saya tidak cocok dengan ketergantungan teman? Saya lebih suka memiliki downgrade sementara dari yang terbaru dan terbaik untuk perpustakaan 1 daripada tidak dapat menggunakannya sama sekali.
Adam Beck
1
untuk komentar pertama Anda, sejauh yang saya mengerti dan menggunakannya, itu ada hubungannya dengan pengujian, misalnya jika Anda memiliki paket yang telah diuji oleh Anda untuk paket pihak ke-3 tertentu, Anda tidak dapat memastikan bahwa jika ada perubahan dependensi Anda (perbaikan bug, pembaruan fitur utama) bahwa paket Anda akan berfungsi. Oleh karena itu, Anda dapat menentukan versi plugin tertentu dan menyimpan dengan tes Anda.
Fer To
1
Pada komentar kedua Anda: itulah sebabnya mereka mengatakan dalam dokumen bahwa pengembang harus toleran dengan dependensi paket mereka dan harus menggunakan semver, misalnya alih-alih "0.2.1", "~ 0.2.1" -> memungkinkan "0.2.x" tetapi bukan "0.3.x", atau "> = 0.2.1" -> semuanya mulai dari "0.2.x" hingga "1.x" atau "x.2.". .. (tapi tidak terlalu disukai untuk paket npm akan pergi dengan ~
Fer To
15

peerDependencies dijelaskan dengan contoh sesederhana mungkin:

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


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

menjalankan npm menginstal di myPackage akan menimbulkan kesalahan karena mencoba menginstal versi Bereaksi ^15.0.0DAN fooyang hanya kompatibel dengan Bereaksi ^16.0.0.

peerDependencies TIDAK diinstal.

Christopher Tokar
sumber
mengapa tidak menempatkan reaksi 16 sebagai dep dalam foo? dengan cara itu baik 15 dan 16 akan tersedia dan foo dapat menggunakan 16 dan mypackage dapat menggunakan 15?
nitinsh99
React adalah kerangka kerja yang di-bootstrap saat runtime, agar React 15 dan React 16 ada di halaman yang sama, Anda perlu keduanya ditingkatkan secara bersamaan yang akan sangat berat dan bermasalah bagi pengguna akhir. Jika fooberfungsi dengan kedua Bereaksi 15 dan Bereaksi 16 maka ia dapat mendaftar peerDependency sebagai >=15 < 17.
Jens Bodal
nitinsh99 jawaban saya adalah untuk menjelaskan tujuan peerDependencies dengan contoh sesederhana mungkin, bukan bagaimana menyingkirkan kesalahan yang dilemparkan oleh peerDependencies
Christopher Tokar
@ nitinsh99 menambahkan reaksi di dalam paket dependensi akan memberikan masalah seperti Hooks - Beberapa reaksi dalam sebuah paket
Masood