Pekan lalu, pengembang Yarn (pengelola paket untuk Javascript) mengumumkan fitur baru - instalasi Plug'n'Play. Fitur ini memungkinkan Anda untuk menjalankan proyek Node.js tanpa menggunakan folder node_modules, di mana dependensi proyek biasanya diinstal sebelum memulai. Deskripsi fitur menyatakan bahwa node_modules tidak lagi diperlukan - modul akan diambil dari cache umum dari manajer paket.
Pada saat yang sama, pengembang NPM juga mengumumkan solusi serupa untuk masalah tersebut.
Mari kita melihat lebih dekat solusi ini dan mencoba mengujinya dalam proyek nyata.
Sejarah masalah
Awalnya, sistem modular NodeJS sepenuhnya didasarkan pada sistem file. Setiap panggilan yang require()
dipetakan ke sistem file. Untuk mengatur modul pihak ketiga, folder node_modules ditemukan, di mana modul dan pustaka yang dapat digunakan kembali harus diunduh dan diinstal. Jadi, setiap proyek menerima set dependensi terpisahnya sendiri, membuang ruang disk secara tidak efisien.
Instalasi ketergantungan membutuhkan sebagian besar waktu pembuatan dalam sistem CI, jadi mempercepat langkah ini akan memengaruhi waktu pembuatan secara keseluruhan.
Sederhana, pemasangan modul terdiri dari langkah-langkah berikut:
- Versi spesifik dari modul dihitung dari interval yang valid.
- Semua modul dari versi yang diperlukan diunduh dari repositori dan disimpan dalam cache lokal
- Modul dari cache lokal disalin ke folder node_modules proyek
Jika dua langkah pertama sudah cukup dioptimalkan dan dilakukan dengan cepat, ketika Anda sudah memiliki modul cache, maka langkah ketiga tetap hampir tidak berubah dibandingkan dengan versi node dan npm yang pertama.
Pendekatan baru mengusulkan untuk menyingkirkan langkah ketiga dan mengganti menyalin file yang sebenarnya dengan membuat tabel yang akan memetakan modul yang diminta ke salinan mereka di cache lokal.
Menggunakan symlinks
Alih-alih benar-benar menyalin modul, Anda dapat menambahkan symlink ke lokasi mereka di cache. Pendekatan ini diimplementasikan dalam PNPM , pengelola paket alternatif lain. Pendekatannya mungkin berhasil, tetapi dengan symlink ada banyak masalah yang terkait dengan lokasi ganda file, pencarian modul yang berdekatan, dll. Selain itu, membuat symlink adalah operasi file yang ingin saya hindari dengan cara kerja yang ideal.
Mencoba Benang PNP
Anda dapat membaca lebih lanjut tentang fitur ini di deskripsi resmi . Paragraf ini berisi menceritakan kembali secara singkat.
Versi Benang yang diaktifkan oleh PNP sekarang berada di fitur-branch yarn-pnp .
Kami mengkloning repositori secara lokal dengan cabang yang diinginkan
git clone git@github.com:yarnpkg/yarn.git --branch yarn-pnp
Instruksi perakitan benang ada di sini , serangkaian langkahnya sangat sepele.
Setelah build selesai, tambahkan alias ke versi custom dari benang dan mulailah bekerja dengannya:
alias yarn-local="node $PWD/lib/cli/index.js"
Plug'n'play diaktifkan dengan dua cara: baik melalui flag: yarn --pnp
, atau dengan konfigurasi tambahan di package.json
: "installConfig": {"pnp": true}
.
Sebagai contoh, pengembang benang telah menyiapkan proyek demo . Ini memiliki Webpack, Babel dan alat-alat lain yang khas dari front-end modern. Mari kita coba membangun ketergantungannya dengan cara yang berbeda dan dapatkan hasil berikut:
- Instalasi
yarn
tipikal: 19 detik - Instalasi melalui
yarn --pnp
: 3s
Sebelum pengukuran, satu instalasi dingin dilakukan sehingga semua modul yang diperlukan sudah ada dalam cache.
Mari kita lihat cara kerjanya. Setelah instalasi pnp, file .pnp.js
tambahan dibuat di root proyek yang berisi override dari logika asli di kelas Module yang dibangun ke Node.js. Dengan memuat file ini ke dalam kode kami, kami memberikan fungsi require()
kemampuan untuk mendapatkan modul dari cache global dan tidak melihat node_modules
. Semua perintah benang bawaan, seperti yarn start
atau yarn test
, pramuat file ini secara default, jadi tidak ada perubahan pada kode Anda yang akan diperlukan jika Anda telah menggunakan Benang sebelumnya.
Selain memetakan modul, pnp.js melakukan validasi dependensi tambahan. Jika Anda mencoba menelepon require('test')
, tanpa ketergantungan yang dinyatakan dalam package.json
, Anda mendapatkan kesalahan berikut: Error: You cannot require a package ("test") that is not declared in your dependencies
. Peningkatan ini harus meningkatkan keandalan dan kepastian kode.
Di antara kekurangan dari pendekatan baru, perlu dicatat bahwa integrasi tambahan akan diperlukan untuk alat yang bekerja secara langsung dengan direktori node_modules tanpa mekanisme Node bawaan. Misalnya, Webpack dan pembuat front-end lainnya akan membutuhkan plugin tambahan sehingga mereka dapat menemukan file yang diperlukan untuk bundling.
Dalam proyek demo ada sketsa resolvers untuk Eslint, Jest, Rollup dan Webpack.
Dalam percobaan saya, masih ada masalah dengan Typescript, yang sangat terkait dengan keberadaan node_modules dan tidak ada cara mudah untuk mengganti strategi pencarian modul.
Juga akan ada masalah dengan skrip postintall. Karena modul tetap ada dalam cache, skrip postinstall yang mengubah statusnya (misalnya, mengunggah file tambahan) dapat merusak cache dan menghancurkan proyek lain yang bergantung padanya. Pengembang benang merekomendasikan untuk menonaktifkan eksekusi skrip dengan --ignore-scripts
. Mereka telah bereksperimen dengan mengaktifkan tanda ini secara default untuk semua proyek di Facebook dan tidak menemukan masalah serius. Dalam jangka panjang, meninggalkan skrip postinstall sepertinya merupakan langkah bagus mengingat masalah keamanan yang diketahui.
Mencoba NPM tink
Tim NPM juga mengumumkan solusi alternatifnya. Alat baru mereka, tink, dilengkapi dengan modul independen NPM. Tink menerima file package-lock.json
sebagai package-lock.json
, yang secara otomatis dihasilkan ketika npm install
dijalankan. Berdasarkan file kunci, tink menghasilkan file node_modules/.package-map.json
, yang menyimpan proyeksi modul lokal di lokasi aktualnya di cache.
Tidak seperti Yarn, tidak ada file kait yang dapat dimuat ke proyek Anda untuk ditambal. Sebagai gantinya, disarankan agar Anda menggunakan perintah tink
alih-alih node
untuk mendapatkan lingkungan yang tepat. Pendekatan ini kurang ergonomis karena akan memerlukan modifikasi pada kode Anda untuk membuatnya berfungsi. Namun, sebagai pembuktian konsep akan dilakukan.
Saya mencoba membandingkan kecepatan pemasangan modul dengan perintah npm ci
dan tink
, tetapi tink bahkan lebih lambat, jadi saya tidak akan memberikan hasilnya. Jelas, proyek ini jauh lebih mentah dibandingkan dengan Benang dan tidak dioptimalkan sama sekali. Baiklah, kami akan menunggu rilis baru.
Kesimpulan
Menolak direktori node_modules adalah langkah logis, mengingat pengalaman bahasa lain, di mana pendekatan ini awalnya tidak ada. Ini akan memengaruhi kecepatan build dengan sistem CI, di mana dimungkinkan untuk menyimpan cache paket di antara build. Selain itu, jika Anda mentransfer cache paket dan file .pnp.js
dari satu komputer ke komputer lain, Anda dapat mereproduksi lingkungan tanpa memulai Benang. Ini dapat berguna dalam sistem pembuatan wadah: pasang direktori dengan cache, letakkan file .pnp.js
, dan Anda dapat segera menjalankan tes.
Pendekatan baru terlihat tidak biasa dan memecah beberapa praktik mapan berdasarkan fakta bahwa semua modul selalu tersedia dalam node_modules. Tetapi file .pnp.js
menawarkan API yang memungkinkan Anda untuk abstrak dari posisi sebenarnya dari file dan bekerja dengan pohon virtual. Selain itu, sebagai upaya terakhir, ada perintah yarn unplug --persist
yang akan mengekstrak modul dari cache dan menempatkannya secara lokal di node_modules
.
Dalam kasus apa pun, belum ada yang diselesaikan, bahkan permintaan tarik di Benang belum dituangkan, kita harus mengharapkan perubahan. Tapi itu menarik bagi saya untuk mencoba versi alpha dari fitur dalam praktek dan mengujinya pada beberapa proyek pribadi saya dan memastikan bahwa pendekatan ini benar-benar berfungsi, membuat instalasi lebih cepat.
Referensi