Mengapa Anda meninggalkan paket-lock.json dukungan di npm 7

Dari saat kami mengumumkan bahwa file akan didukung di npm 7 yarn.lock, mereka mengajukan pertanyaan yang sama beberapa kali. Kedengarannya seperti ini: β€œLalu mengapa meninggalkan dukungan package-lock.json? Kenapa tidak pakai saja yarn.lock? ” Jawaban singkat untuk pertanyaan ini adalah: "Karena tidak sepenuhnya memenuhi kebutuhan npm. Jika Anda hanya mengandalkan itu, itu akan merusak kemampuan npm untuk membentuk skema instalasi paket yang optimal dan kemampuan untuk menambahkan fungsionalitas baru ke proyek. " Jawabannya disajikan lebih detail dalam materi ini.







yarn.lock



Struktur dasar file yarn.lock



File yarn.lockadalah deskripsi korespondensi antara kualifikasi dependensi paket dan metadata yang menjelaskan resolusi dependensi ini. Sebagai contoh:



mkdirp@1.x:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.2.tgz#5ccd93437619ca7050b538573fc918327eba98fb"
  integrity sha512-N2REVrJ/X/jGPfit2d7zea2J1pf7EAR5chIUcfHffAZ7gmlam5U65sAm76+o4ntQbSRdTjYf7qZz3chuHlwXEA==


Cuplikan ini menyatakan sebagai berikut: "Ketergantungan apa pun pada mkdirp@1.xharus menyelesaikan persis seperti yang ditentukan di sini." Jika beberapa paket bergantung mkdirp@1.x, maka semua dependensi ini akan diselesaikan dengan cara yang sama.



Dalam npm 7, jika ada file dalam proyek yarn.lock, npm akan menggunakan metadata yang terkandung di dalamnya. Nilai-nilai bidang resolvedakan memberi tahu npm dari mana mengunduh paket-paket dari, dan nilai integrity- nilai bidang akan digunakan untuk memeriksa apa yang diterima terhadap apa yang diharapkan akan diterima. Jika paket ditambahkan atau dihapus dari proyek, konten diperbarui sesuai yarn.lock.



Pada saat yang sama, npm, seperti sebelumnya, membuat filepackage-lock.json... Jika file ini ada dalam proyek, itu akan digunakan sebagai sumber informasi otoritatif tentang struktur (bentuk) dari pohon ketergantungan.



Pertanyaannya di sini adalah, "Jika yarn.lockcukup baik untuk manajer paket Yarn, mengapa npm tidak dapat menggunakan file ini saja?"



Hasil Instalasi Ketergantungan Deterministik



Hasil menginstal paket dengan Benang dijamin sama ketika menggunakan file yang sama yarn.lockdan versi Benang yang sama. Menggunakan berbagai versi Benang dapat menyebabkan file paket berada secara berbeda pada disk.



File yarn.lockmenjamin resolusi dependensi deterministik. Misalnya, jika foo@1.xdiizinkan masuk foo@1.2.3, maka, mengingat penggunaan file yang sama yarn.lock, ini akan selalu terjadi, di semua versi Benang. Tetapi ini (setidaknya dalam dirinya sendiri) tidak setara dengan menjamin determinisme struktur pohon ketergantungan!



Pertimbangkan grafik ketergantungan berikut:



root -> (foo@1, bar@1)
foo -> (baz@1)
bar -> (baz@2)


Berikut adalah beberapa skema pohon dependensi, yang masing-masing dapat dianggap benar.



Nomor pohon 1:



root
+-- foo
+-- bar
|   +-- baz@2
+-- baz@1


Nomor pohon 2:



+-- foo
|   +-- baz@1
+-- bar
+-- baz@2


File yarn.locktidak dapat memberi tahu kami pohon dependensi mana yang akan digunakan. Jika rootperintah dieksekusi dalam paket require(Β«bazΒ»)(yang tidak benar, karena dependensi ini tidak tercermin di pohon dependensi), file yarn.locktidak menjamin eksekusi yang benar dari operasi ini. Ini adalah bentuk determinisme yang dapat diberikan file package-lock.json, tetapi tidak yarn.lock.



Dalam prakteknya, tentu saja, sejak Benang, dalam fileyarn.lock, ada semua informasi yang diperlukan untuk memilih versi ketergantungan yang tepat, pilihannya adalah deterministik selama semua orang menggunakan versi Benang yang sama. Ini berarti pemilihan versi selalu dilakukan dengan cara yang sama. Kode tidak berubah sampai seseorang mengubahnya. Perlu dicatat bahwa Benang cukup pintar untuk tidak terpengaruh oleh perbedaan mengenai waktu muat manifes paket saat membuat pohon dependensi. Jika tidak, determinisme hasil tidak dapat dijamin.



Karena ini ditentukan oleh spesifikasi algoritma Benang, dan bukan oleh struktur data pada disk (tidak mengidentifikasi algoritma yang akan digunakan), jaminan determinisme ini secara inheren lebih lemah daripada jaminan bahwapackage-lock.jsonberisi deskripsi lengkap tentang struktur pohon dependensi yang disimpan pada disk.



Dengan kata lain, bagaimana Benang membangun pohon ketergantungan dipengaruhi oleh file yarn.lockdan implementasi Benang itu sendiri. Dan dalam npm, hanya file yang memengaruhi tampilan struktur ketergantungan package-lock.json. Karena ini, struktur proyek yang dijelaskan dalam package-lock.json, menjadi lebih sulit untuk secara tidak sengaja rusak, menggunakan versi npm yang berbeda. Dan jika perubahan dilakukan ke file (mungkin - secara tidak sengaja, atau sengaja), perubahan ini akan terlihat jelas dalam file ketika menambahkan versi modifikasi ke repositori proyek, yang menggunakan sistem kontrol versi.



Dependensi bersarang dan deduplikasi ketergantungan



Selain itu, ada seluruh kelas situasi yang melibatkan bekerja dengan dependensi bersarang dan dependensi deduplicating, ketika file yarn.locktidak dapat secara akurat mencerminkan hasil resolusi dependensi, yang, dalam praktiknya, akan digunakan oleh npm. Selain itu, ini berlaku bahkan untuk kasus-kasus ketika npm menggunakan yarn.lockmetadata sebagai sumber. Walaupun npm menggunakannya yarn.locksebagai sumber informasi yang dapat diandalkan, npm tidak menganggap file ini sebagai sumber informasi otoritatif tentang pembatasan yang diberlakukan pada versi ketergantungan.



Dalam beberapa kasus, Benang menciptakan pohon dependensi dengan tingkat duplikasi paket yang sangat tinggi, dan kami tidak membutuhkannya. Akibatnya, ternyata mengikuti algoritma Benang dengan ketat dalam kasus seperti itu jauh dari solusi yang ideal.



Pertimbangkan grafik ketergantungan berikut:



root -> (x@1.x, y@1.x, z@1.x)
x@1.1.0 -> ()
x@1.2.0 -> ()
y@1.0.0 -> (x@1.1, z@2.x)
z@1.0.0 -> ()
z@2.0.0 -> (x@1.x)


Proyek roottergantung pada versi 1.xpaket x, ydan z. Paket ytergantung pada x@1.1dan di z@2.x. Paket zversi 1 tidak memiliki dependensi, tetapi paket versi 2 tidak x@1.x.



Berdasarkan informasi ini, npm menghasilkan pohon dependensi berikut:



root (x@1.x, y@1.x, z@1.x) <--   x@1.x
+-- x 1.2.0                <-- x@1.x   1.2.0
+-- y (x@1.1, z@2.x)
|   +-- x 1.1.0            <-- x@1.x   1.1.0
|   +-- z 2.0.0 (x@1.x)    <--   x@1.x
+-- z 1.0.0


Paket z@2.0.0tergantung pada x@1.x, hal yang sama dapat dikatakan tentang root. File yarn.lockmemetakan ke x@1.xc 1.2.0. Namun, ketergantungan paket yang zjuga ditentukan x@1.xakan diselesaikan x@1.1.0.



Akibatnya, meskipun dependensi x@1.xdijelaskan dalam yarn.lock, di mana ini mengindikasikan bahwa itu harus diselesaikan dalam versi paket 1.2.0, ada hasil resolusi kedua x@1.xdalam versi paket 1.1.0.



Jika Anda menjalankan npm dengan flag --prefer-dedupe, sistem akan melangkah lebih jauh dan menginstal hanya satu instance dari dependensi x, yang akan mengarah pada pembentukan pohon dependensi berikut:



root (x@1.x, y@1.x, z@1.x)
+-- x 1.1.0       <-- x@1.x       1.1.0
+-- y (x@1.1, z@2.x)
|   +-- z 2.0.0 (x@1.x)
+-- z 1.0.0


Ini meminimalkan duplikasi dependensi, pohon dependensi yang dihasilkan diperbaiki dalam file package-lock.json.



Karena file yarn.lockhanya menangkap urutan penyelesaian dependensi, bukan pohon paket yang dihasilkan, Benang akan menghasilkan pohon dependensi seperti ini:



root (x@1.x, y@1.x, z@1.x) <--   x@1.x
+-- x 1.2.0                <-- x@1.x   1.2.0
+-- y (x@1.1, z@2.x)
|   +-- x 1.1.0            <-- x@1.x   1.1.0
|   +-- z 2.0.0 (x@1.x)    <-- x@1.1.0   , ...
|       +-- x 1.2.0        <-- Yarn     ,    yarn.lock
+-- z 1.0.0


Paket xmuncul tiga kali di pohon dependensi saat menggunakan benang. Saat menggunakan npm tanpa pengaturan tambahan - 2 kali. Dan ketika menggunakan bendera --prefer-dedupe- hanya sekali (meskipun kemudian pohon dependensi bukan yang terbaru dan bukan versi terbaik dari paket).



Ketiga pohon dependensi yang dihasilkan dapat dianggap benar dalam arti bahwa setiap paket akan menerima versi dependensi yang memenuhi persyaratan yang dinyatakan. Tapi kami tidak ingin membuat pohon paket dengan duplikat terlalu banyak. Pikirkan apa yang akan terjadi jika x- ini adalah paket besar yang memiliki banyak dependensinya sendiri!



Akibatnya, hanya ada satu cara npm dapat mengoptimalkan pohon paket sambil mempertahankan penciptaan pohon dependensi deterministik dan dapat direproduksi. Metode ini terdiri dari penggunaan file-kunci, prinsip pembentukan dan penggunaan yang berbeda dari level fundamental yarn.lock.



Memperbaiki hasil penerapan niat pengguna



Seperti yang telah disebutkan, pada npm 7 pengguna dapat menggunakan flag --prefer-dedupeuntuk menerapkan algoritma pembuatan pohon dependensi, di mana prioritas diberikan kepada deduplikasi dependensi, dan bukan keinginan untuk selalu menginstal versi paket terbaru. Bendera --prefer-dedupebiasanya ideal dalam situasi di mana paket duplikat perlu diminimalkan.



Jika flag ini digunakan, pohon yang dihasilkan untuk contoh di atas akan terlihat seperti ini:



root (x@1.x, y@1.x, z@1.x) <--   x@1.x 
+-- x 1.1.0                <-- x@1.x   1.1.0   
+-- y (x@1.1, z@2.x)
|   +-- z 2.0.0 (x@1.x)    <--   x@1.x
+-- z 1.0.0


Dalam hal ini, npm melihat bahwa meskipun x@1.2.0itu adalah versi terbaru dari paket yang memenuhi persyaratan x@1.x, Anda dapat memilih sebagai gantinya x@1.1.0. Memilih versi ini akan menghasilkan lebih sedikit duplikasi paket di pohon dependensi.



Jika kami tidak memperbaiki struktur pohon ketergantungan dalam file kunci, maka setiap programmer yang mengerjakan proyek dalam tim harus mengatur lingkungan kerjanya dengan cara yang sama dengan anggota tim lainnya yang mengaturnya. Hanya ini yang akan memungkinkannya untuk mendapatkan hasil yang sama dengan yang lain. Jika "implementasi" mekanisme pembangunan pohon ketergantungan dapat diubah dengan cara yang sama, ini memberi pengguna npm peluang serius untuk mengoptimalkan dependensi berdasarkan kebutuhan spesifik mereka sendiri. Tetapi, jika hasil penciptaan pohon tergantung pada implementasi sistem, ini membuat mustahil untuk membuat pohon dependensi deterministik. Inilah tepatnya yang menyebabkan penggunaan file yarn.lock.



Berikut adalah beberapa contoh tentang bagaimana npm tweak tambahan dapat menghasilkan pembuatan pohon dependensi yang berbeda:



  • --legacy-peer-deps, sebuah flag yang menyebabkan npm diabaikan sepenuhnya peerDependencies.
  • --legacy-bundling, sebuah bendera yang memberi tahu npm bahwa ia seharusnya tidak mencoba membuat pohon dependensi lebih "datar".
  • --global-style, sebuah bendera yang dengannya semua dependensi transitif ditetapkan sebagai dependensi bersarang dalam folder dependensi dari level yang lebih tinggi.


Menangkap dan memperbaiki hasil resolusi dependensi dan harapan bahwa algoritma yang sama akan digunakan untuk menghasilkan pohon dependensi tidak berfungsi dalam kondisi ketika kami memberi pengguna kemampuan untuk menyesuaikan mekanisme untuk membangun pohon dependensi.



Memperbaiki struktur pohon dependensi yang telah selesai memungkinkan kami untuk menyediakan peluang bagi pengguna dan pada saat yang sama tidak akan mengganggu proses pembangunan pohon dependensi deterministik dan yang dapat direproduksi.



Kinerja dan kelengkapan data



File ini package-lock.jsonberguna tidak hanya ketika Anda perlu memastikan determinisme dan reproduktifitas pohon dependensi. Selain itu, kami mengandalkan file ini untuk melacak dan menyimpan metadata paket, menghemat waktu secara signifikan, yang jika tidak, hanya menggunakan package.json, akan diperlukan untuk bekerja dengan registri npm. Karena kemampuan file yarn.locksangat terbatas, tidak ada metadata di dalamnya yang perlu kita unduh secara konstan.



Di npm 7, file package-lock.jsonberisi semua yang diperlukan npm untuk sepenuhnya membangun pohon ketergantungan proyek. Di npm 6, data ini tidak disimpan dengan nyaman, jadi ketika kita menemukan file kunci lama, kita harus memuat sistem dengan pekerjaan tambahan, tetapi ini dilakukan, untuk satu proyek, hanya sekali.



Akibatnya, bahkan jika dalamyarn.lock dan informasi ditulis tentang struktur pohon dependensi, kita harus menggunakan file lain untuk menyimpan metadata tambahan.



Peluang masa depan



Apa yang telah kita bicarakan di sini dapat berubah secara dramatis jika Anda memperhitungkan berbagai pendekatan baru untuk menempatkan dependensi pada disk. Ini adalah pnpm, benang 2 / berry dan Benang PnP.



Kami, bekerja pada npm 8, akan mengeksplorasi pendekatan untuk membangun pohon dependensi berdasarkan pada sistem file virtual. Ide ini dimodelkan pada Tink, dan konsepnya dikonfirmasi untuk bekerja pada 2019. Kami juga mendiskusikan ide pindah ke sesuatu seperti struktur yang digunakan oleh pnpm, meskipun ini, dalam arti tertentu, perubahan yang bahkan lebih dramatis daripada menggunakan sistem file virtual.



Jika semua dependensi berada dalam beberapa repositori pusat, dan dependensi bersarang hanya diwakili oleh tautan simbolik atau sistem file virtual, maka memodelkan struktur pohon dependensi tidak akan menjadi masalah penting bagi kami. Tetapi kita masih membutuhkan lebih banyak metadata daripada yang dapat disediakan oleh file yarn.lock. Akibatnya, memperbarui dan merampingkan format file yang ada lebih masuk akal package-lock.json, daripada transisi lengkap ke yarn.lock.



Ini bukan artikel yang bisa disebut "Tentang bahaya benang. Buka"



Saya ingin menekankan bahwa, menilai dari apa yang saya ketahui, Benang andal menciptakan pohon ketergantungan proyek yang benar. Dan, untuk versi spesifik Benang (pada saat penulisan ini, ini berlaku untuk semua versi segar Benang), pohon-pohon ini, seperti halnya npm, sepenuhnya deterministik.



File ini yarn.lockcukup untuk membuat pohon dependensi deterministik menggunakan versi Benang yang sama. Tetapi kita tidak dapat mengandalkan mekanisme yang bergantung pada implementasi manajer paket, mengingat penggunaan mekanisme tersebut dalam banyak alat. Ini bahkan lebih benar ketika Anda menganggap bahwa penerapan format fileyarn.locktidak didokumentasikan secara formal di mana pun. (Ini bukan masalah unik untuk Benang; npm adalah situasi yang sama. Mendokumentasikan format file adalah pekerjaan yang cukup besar.) Cara



terbaik untuk memastikan keandalan membangun pohon dependensi yang sangat deterministik adalah, dalam jangka panjang, untuk merekam hasil resolusi ketergantungan. Jangan mengandalkan keyakinan bahwa implementasi masa depan dari manajer paket akan, ketika menyelesaikan dependensi, mengikuti jalur yang sama dengan implementasi sebelumnya. Pendekatan ini membatasi kemampuan kami untuk merancang pohon ketergantungan yang dioptimalkan.



Penyimpangan dari struktur pohon dependensi yang semula ditetapkan seharusnya merupakan hasil dari keinginan pengguna yang dinyatakan dengan jelas. Penyimpangan tersebut harus mendokumentasikan diri mereka sendiri, membuat perubahan pada data yang sebelumnya direkam pada struktur pohon ketergantungan.



Hanya package-lock.json, atau mekanisme seperti file ini yang mampu memberikan kemampuan seperti npm.



Manajer paket apa yang Anda gunakan dalam proyek JavaScript Anda?






All Articles