npm'den pnpm'e geçiş ve TypeScript'i neden ~5.9'da tuttum
Proje npm ile başladı, sonra paket yöneticisini pnpm'e taşıdım. Bu notta geçişte neler yaptığımı anlatıyorum: kilit dosyasının dönüşümü, packageManager ve engines alanları, katı node_modules yapısı ve TypeScript'i neden ~5.9 aralığına sabitlediğim.
01Neden geçtim
Geçişin sebebi hız ya da disk rakamları değildi. Bunları ölçmedim, o yüzden burada rakam da vermeyeceğim. Benim için asıl mesele katılık. npm node_modules'u büyük ölçüde düz bir ağaç halinde kuruyor; bir bağımlılığın bağımlılığı da kökte duruyor ve kod, package.json'da hiç yazmayan bir paketi import edebiliyor. Bu hayalet bağımlılık bugün çalışır, onu getiren paket bir gün bırakınca bozulur.
pnpm'de ise kökteki node_modules yalnızca doğrudan bağımlılıkları gösteriyor. Kod bildirmediği bir pakete dayanıyorsa bunu üretimde değil, kurulumdan hemen sonra görüyorum. Küçük bir projede bunun değeri az gibi görünse de alışkanlığın kendisi önemli: package.json'daki liste, kodun gerçekten neye dayandığını söylemeli.
02Kilit dosyası
En çok dikkat ettiğim şey sürümlerin kaymamasıydı. pnpm-lock.yaml'ı sıfırdan çözdürmek yerine mevcut package-lock.json'dan içe aktardım, böylece çözülen sürümler aynı kaldı. Sonra package-lock.json'ı sildim; iki kilit dosyasının yan yana durması, hangisinin doğru olduğu sorusunu açık bırakır. Commit mesajına da bunu not ettim: aynı çözülmüş sürümler, eski kilit dosyası kaldırıldı.
Yeni kilit dosyası lockfileVersion: '9.0' ile başlıyor ve her paketi çözülmüş sürümü ve eş bağımlılıklarıyla birlikte yazıyor. Örneğin @unhead/vue satırı hangi vite, vue ve typescript sürümleriyle çözüldüğünü de taşıyor. Bir şey değiştiğinde diff'te neyin neden değiştiğini görmek böylece kolaylaşıyor.
Kilit dosyasının başındaki settings bölümü de okumaya değer: autoInstallPeers: true. Yani bir paketin eş bağımlılığı projede yoksa pnpm onu kendisi kuruyor. Bu projede vue gibi eş bağımlılıklar zaten doğrudan bağımlılık olarak listelendiği için bunun pek görünür bir etkisi yok, ama ayarın kilit dosyasına yazılmış olması kurulumun nasıl davranacağını açık ediyor.
03Sürümü sabitlemek
package.json'a iki alan ekledim, script'lere ise dokunmadım.
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build && vite build --ssr src/app/entry-server.ts --outDir dist-ssr && node scripts/prerender.mjs && node scripts/check-seo.mjs",
...
},
"devDependencies": {
...
"typescript": "~5.9",
"vite": "^8.3.4",
"vue-tsc": "^3.3.13"
},
"packageManager": "pnpm@11.9.0",
"engines": {
"node": ">=20.19",
"pnpm": ">=11"
}packageManager alanı projenin hangi pnpm sürümüyle çalıştığını söylüyor. Corepack açık bir makinede (corepack enable) pnpm komutu bu alana bakıp doğru sürümü kendisi getiriyor. README'ye de bunu yazdım; projeyi klonlayan birinin global pnpm sürümünü düşünmesine gerek yok. engines alanı alt sınırları belirtiyor: Node 20.19 ve pnpm 11.
Script'ler olduğu gibi kaldı; pnpm package.json'daki script'leri aynı şekilde çalıştırıyor. Değişen yalnızca yazılış: npm run dev yerine pnpm dev, npm run build yerine pnpm build. README ve geliştirme sunucusunu başlatan launch yapılandırması bu komutlara güncellendi. Build zinciri de aynı: vue-tsc ile tip kontrolü, istemci build'i, SSR build'i, prerender ve SEO kontrolü.
Kilit dosyası taşınırken amaç yeni sürümler değil, aynı sürümlerin başka bir araçla kurulmasıdır.
04TypeScript neden ~5.9
devDependencies içinde paketlerin çoğu ^ ile yazılı, TypeScript ise ~5.9. Bu bilinçli bir tercih. ^ bir sonraki ana sürüme kadar her şeyi kabul ederken ~ yalnızca 5.9.x yamalarını kabul ediyor. Sebep TypeScript 7: bu notu yazdığım dönemde vue-tsc ile uyumlu değildi. vue-tsc, .vue dosyalarının tip kontrolünü TypeScript'in üzerine kurulu olarak yapıyor ve build script'i ilk adım olarak vue-tsc --noEmit çalıştırıyor. Uyumsuz bir TypeScript sürümü, build'in daha ilk adımda kırılması demek.
Kilit dosyasında typescript 5.9.3 olarak çözülmüş durumda; vue ve @unhead/vue satırları da bu sürüme bağlı. vue-tsc tarafında TypeScript 7 desteği geldiğinde aralığı genişletmek tek satırlık bir değişiklik olacak. O zamana kadar tip denetiminin bir install sonrasında sessizce değişmesindense açık bir sınırla durmasını tercih ediyorum.
05Geçişten sonra
Bu projenin bağımlılık listesi zaten kısa: çalışma zamanında beş paket (vue, vue-router, @unhead/vue, gsap, animejs), geliştirme tarafında Vite, vue-tsc, OG görselleri için satori ile resvg ve fontlar için birkaç paket. Geçiş bu yüzden tek bir commit'e sığdı: yeni kilit dosyası, silinen eski kilit dosyası, package.json'daki iki alan ve güncellenen komutlar.
Geçişte bir şeyi de bilerek yapmadım: bağımlılıkları güncellemedim. Aynı commit'te hem paket yöneticisini hem sürümleri değiştirseydim, bir şey bozulduğunda hangisinin sebep olduğunu ayırmak zorlaşırdı. Önce aracı değiştirdim, sürüm yükseltmelerini ayrı işler olarak bıraktım.
Asıl değer ileride görünecek. Yeni bir import eklediğimde paketi package.json'a yazmayı unutursam kurulum bunu saklamayacak; bir başka makinede projeyi açtığımda da aynı pnpm sürümü, aynı kilit dosyası ve aynı TypeScript ile çalışacağım.