Ringkasan Utama
pnpm Catalogs memungkinkan monorepo menetapkan setiap versi dependency sekali saja di pnpm-workspace.yaml, lalu merujuknya dari setiap package.json lewat protokol catalog:. Upgrade cukup mengubah satu baris, versi tetap sama di seluruh paket, dan konflik merge hilang. Catalogs mengelola versi; task runner seperti Turborepo menangani build dan caching.
Setiap monorepo yang pernah saya kerjakan pada akhirnya mengidap penyakit diam yang sama. Satu paket mengunci React di 18.2.0, paket lain di 18.3.1, paket ketiga menulis caret longgar dan mendapat versi apa pun yang terakhir dipecahkan lockfile. Tidak ada yang jelas rusak, jadi tidak ada yang membetulkannya, lalu muncul bug halus yang hanya kambuh ketika dua salinan library yang sama berakhir di dalam bundle. Mendiagnosisnya menghabiskan satu sore yang tak akan kembali.
Akar masalahnya adalah bahwa dalam workspace biasa, versi sebuah dependency bersama ditulis di sebanyak jumlah paket yang memakainya. Tidak ada satu sumber kebenaran, jadi drift menjadi keadaan bawaan dan menjaga sinkronisasi adalah kerja manual. pnpm Catalogs, ditambahkan di pnpm 9.5 pada Juli 2024, mengatasi ini di tingkat perkakas: Anda mendeklarasikan versi sekali dan merujuknya di mana pun.
Pajak version drift yang Anda bayar tanpa catalogs
Sebelum catalogs, menjaga sebuah dependency selaras di dua puluh paket berarti menyunting dua puluh file package.json untuk setiap kenaikan versi. Dalam praktiknya itu jarang berjalan bersih. Seseorang memperbarui tiga paket yang sedang dia sentuh, melupakan sisanya, dan repo pun tergelincir kembali ke campuran versi. Lockfile kemudian memasang banyak salinan, yang menggelembungkan ukuran install dan, untuk library stateful seperti React atau library validasi dengan registry bersama, bisa memicu bug runtime nyata.
Ada biaya kedua yang lebih sepele: konflik merge. Ketika dua branch sama-sama menaikkan sebuah dependency yang dipakai luas, keduanya bertabrakan di setiap package.json yang menyebutnya. Reviewer membuang waktu menyelesaikan string versi yang sama berulang kali. Catalogs memindahkan string versi itu keluar dari setiap package.json sepenuhnya, sehingga tabrakan tidak punya tempat untuk terjadi.
pnpm-workspace.yaml: satu tempat mendeklarasikan versi
Sebuah pnpm workspace didefinisikan oleh file pnpm-workspace.yaml di akar repositori; field packages-nya mendaftar glob yang membentuk monorepo. Catalogs hidup di file yang sama. Default catalog adalah peta tanpa nama dari nama paket ke rentang versi di bawah kunci catalog. Anda juga bisa mendeklarasikan named catalogs di bawah catalogs ketika Anda sengaja perlu lebih dari satu versi sesuatu berjalan sekaligus.
# pnpm-workspace.yaml (at the repo root)
packages:
- "apps/*"
- "packages/*"
# The default catalog: unnamed version ranges
catalog:
react: ^18.3.1
react-dom: ^18.3.1
typescript: ^5.5.4
zod: ^3.23.8
# Named catalogs: deliberate, parallel version sets
catalogs:
react17:
react: ^17.0.2
react-dom: ^17.0.2Rentang versi yang Anda taruh di sebuah catalog adalah specifier semver biasa, persis yang jika tidak akan Anda tulis di package.json. Catalog tidak mengubah cara resolusi bekerja; ia hanya memindahkan di mana rentang itu ditulis. Itulah sebabnya mengadopsi catalogs adalah refaktor mekanis tanpa perubahan versi terpasang di hari pertama.
Protokol catalog: di package.json
Di dalam sebuah paket, Anda mengganti rentang versi konkret dengan protokol catalog:. Ditulis polos, catalog: menarik versi dari default catalog. Ia bekerja di dependencies, devDependencies, peerDependencies, dan optionalDependencies, sehingga sebuah package.json bisa mengarahkan setiap dependency bersama ke catalog dan berhenti membawa nomor versinya sendiri.
{
"name": "@acme/web",
"dependencies": {
"react": "catalog:",
"react-dom": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"typescript": "catalog:"
}
}Ketika Anda menjalankan pnpm publish atau pnpm pack, pnpm mengganti rujukan catalog: dengan rentang versi nyata yang dipecahkannya, persis seperti yang dilakukannya untuk protokol workspace:. Paket yang dipublikasikan karenanya berisi rentang versi biasa, sehingga konsumen di luar monorepo Anda tak pernah melihat atau perlu memahami catalogs.
Named catalogs untuk pemisahan versi yang disengaja
Kadang Anda memang perlu dua versi hidup berdampingan, misalnya saat memigrasikan widget lama dari React 17 ke React 18. Sebuah named catalog menangkap niat itu. Anda merujuknya dengan nama lewat catalog:react17, yang terbaca jelas sebagai pengecualian yang disengaja alih-alih drift yang tak sengaja. Namanya mendokumentasikan alasan pemisahan itu ada.
{
"name": "@acme/legacy-widget",
"dependencies": {
"react": "catalog:react17",
"react-dom": "catalog:react17"
}
}Inilah beda antara drift dan keputusan. Drift tidak terlihat dan tak seorang pun memilihnya; sebuah named catalog adalah pilihan berlabel yang sudah ditinjau, duduk di satu file. Ketika migrasi selesai Anda menghapus named catalog itu dan mengembalikan paket-paket tersebut ke default, dan pengecualian pun lenyap dalam satu commit yang jelas.
Catalogs memusatkan rentang versi, bukan mekanisme pembaruan. Ketika catalogs pertama dirilis, pnpm update tidak menulis ulang entri catalog, jadi Anda meng-upgrade dengan menyunting pnpm-workspace.yaml langsung lalu memasang ulang. Selalu periksa dokumentasi versi pnpm Anda untuk dukungan perintah update terkini sebelum berasumsi perintah kenaikan versi akan menyentuh catalog untuk Anda.
Dengan catalogs versus tanpa
Kontrasnya paling mudah dilihat berdampingan. Tiga kepedulian yang sama, ditangani cara lama dan cara catalog, menunjukkan mengapa fitur ini sepadan dengan migrasi kecilnya.
| Kepedulian | Tanpa catalogs | Dengan catalogs |
|---|---|---|
| Sumber kebenaran sebuah versi | Diulang di setiap package.json | Satu entri di pnpm-workspace.yaml |
| Meng-upgrade dependency bersama | Sunting setiap paket yang memakainya | Sunting satu baris catalog |
| Konflik merge saat kenaikan versi | Satu per package.json terdampak | Tidak ada: package.json tak tersentuh |
| Risiko versi ganda | Tinggi, drift adalah bawaan | Rendah, versi tetap selaras |
Di mana catalogs berhenti dan Turborepo mulai
Catalogs memecahkan tepat satu masalah: versi setiap dependency yang disepakati seluruh workspace. Ia tidak berkata apa pun tentang bagaimana Anda membangun, menguji, atau mengirim paket-paket itu. Setelah versi dipusatkan, rasa sakit monorepo berikutnya adalah orkestrasi tugas, dan itu adalah pekerjaan alat yang berbeda.
Turborepo adalah build system dan task runner untuk monorepo JavaScript dan TypeScript. Dokumentasinya sendiri tegas bahwa ia menyerahkan pengelolaan dependency ke package manager Anda dan bekerja dengan npm, yarn, atau pnpm. Turborepo bertanggung jawab atas sekumpulan kepedulian yang terpisah:
- Menjadwalkan tugas antar paket dalam urutan dependency dan menjalankan pekerjaan independen secara paralel.
- Meng-cache output tugas, secara lokal dan jarak jauh, sehingga pekerjaan yang tak berubah tak pernah dibangun dua kali.
- Membaca skrip package.json dan graf dependency yang sudah Anda miliki, lewat satu konfigurasi turbo.json.
Jadi pembagiannya bersih. pnpm workspaces plus Catalogs memutuskan apa yang dipasang dan pada versi berapa; Turborepo memutuskan bagaimana tugas yang dihasilkan berjalan dan bagaimana outputnya di-cache. Raih catalogs begitu sebuah dependency muncul di lebih dari satu paket, dan tambahkan task runner ketika menjalankan build dalam urutan yang benar menjadi bagian yang lambat.

