Webpack 4'ten 5'e geçiş, yalnızca bir sürüm numarasını güncellemek değildir; yapılandırma mantığı, dahili mimari ve bazı temel varsayımlar kökten değişmiştir. Geçiş yapan projelerde en sık karşılaşılan tablo şudur: webpack komutu çalışmayı reddeder, derleme sürer gider ya da bundle beklenenden büyük çıkar. Bunların her biri farklı bir nedene işaret eder.

Webpack 5, Ekim 2020'de yayımlandı. Yıllar geçmesine rağmen hâlâ webpack 4 kullanan projeler var; kısmen mevcut yapılandırmaların ne kadar karmaşık hale geldiği, kısmen de test ve CI/CD bağımlılıklarının yarattığı dirençten dolayı. Geçişi ertelemek teknik borç üretir ve bu borcun kendine özgü bir performans maliyeti vardır - ancak geçişin kendisi de anlık bir maliyet taşır.

Persistent cache, asset modules, Node polyfill'lerinin kalkması ve Module Federation: dört değişiklik ayrı ayrı kırılır, ayrı ayrı ölçülür. Geçişte bozulan yapılandırma genelde bu dördünden birine iner.

Persistent Cache: Build Süresini Doğrudan Etkileyen Değişiklik

Webpack 4'te her derleme sıfırdan başlardı. Dosyaları yeniden okur, modülleri yeniden çözümler, sonuçları hafızada tutardı; ama bu hafıza sonraki webpack komutuna aktarılmazdı. Buna karşın webpack-dev-server'ın hafıza içi cache'i, geliştirme sırasında hızı korurdu. Production build'lerde durum farklıydı.

Webpack 5, cache: { type: 'filesystem' } seçeneğiyle kalıcı disk cache'ini getirdi. İlk derlemede bu cache olmadığından süre değişmez; ama sonraki derlemelerde, değişmeyen modüller cache'den okunur ve yalnızca değişen dosyalar yeniden işlenir. Yüzlerce dosyadan oluşan bir uygulamada ilk derleme uzun sürer, ancak tek bir bileşen güncellendiğinde sonraki derleme çok daha kısa tamamlanır.

// webpack.config.js
module.exports = {
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
};

buildDependencies.config ayarı kritiktir: webpack yapılandırma dosyası değiştiğinde cache'i geçersiz kılar. Bu ayar olmadan eski yapılandırmanın cache'i yanlış sonuçlar üretebilir. CI/CD pipeline'larında cache dizininin disk üzerinde korunması da ayrıca yapılandırılmalıdır; aksi halde her çalışma cache'i sıfırdan oluşturur ve kazanım ortadan kalkar.

Asset Modules: file-loader ve url-loader Artık Core'da

Webpack 4'te görseller, fontlar ve diğer statik dosyalar için ayrı loader'lar kurulurdu: file-loader, url-loader, raw-loader. Bunlar webpack tarafından değil, topluluk tarafından geliştirilip yönetilirdi.

Webpack 5, bu işlevleri asset modules (varlık modülleri) adıyla çekirdeğe taşıdı. Artık ek paket kurmadan, yalnızca type belirterek aynı sonucu elde edebilirsiniz:

// asset/resource - eski file-loader davranışı
{
  test: /\.(png|jpg|svg)$/,
  type: 'asset/resource',
}

// asset/inline - her zaman base64 (url-loader)
{
  test: /\.svg$/,
  type: 'asset/inline',
}

// asset - boyut eşiğine göre karar (url-loader limit davranışı)
{
  test: /\.(png|jpg)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024, // 8 KB altı inline
    },
  },
}

Geçiş sırasında en sık bozulan nokta şudur: eski yapılandırmada hem file-loader hem de asset/resource aynı dosya türü için tanımlanmış olursa webpack 5 bunları çakışma olarak değil, iç içe geçmiş kurallar olarak işleyebilir; dosyalar iki kez işlenir ya da beklenmedik çıktı yollarına yazılır. Yapılandırmayı birleştirirken file-loader ve url-loader'ı kural listesinden tamamen çıkarmak gerekir.

Node.js Polyfill'lerinin Kaldırılması

Webpack 4, tarayıcı için derleme yaparken Node.js çekirdek modüllerini (process, Buffer, path, crypto gibi) otomatik olarak polyfill ile değiştirirdi. Bir npm paketi içinde require('crypto') görünce webpack, tarayıcı uyumlu bir alternatifini bundle'a eklerdi.

Webpack 5, bunu kaldırdı. Otomatik polyfill ekleme artık yapılmıyor; eğer bir paket Node.js modülü kullanıyorsa derleme şu hatayı üretir:

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

Hata mesajı doğrudan sizin yazdığınız koda değil, kullandığınız npm paketlerine işaret eder. Paket Node.js ortamı varsayımıyla yazılmış olabilir. Polyfill gerekiyorsa resolve.fallback ile belirtilir:

resolve: {
  fallback: {
    crypto: require.resolve('crypto-browserify'),
    path: require.resolve('path-browserify'),
    process: require.resolve('process/browser'),
  },
},

Bu değişiklik gereksiz polyfill'lerin bundle'a girmesini engeller ve geçiş sırasında zorunlu bir bağımlılık incelemesi yapmayı gerektirir. Hangi paketin ne neden Node.js API'si beklediğini anlamak, geçişin en zaman alan kısmı olabilir; ama bu bilgi projenin bağımlılık sağlığını da ortaya çıkarır.

Module Federation: Farklı Uygulamalar Arasında Çalışma Zamanı Paylaşımı

Module Federation webpack 5'in en kapsamlı yeniliğidir. İki veya daha fazla webpack uygulamasının, çalışma zamanında birbirinden modül alıp vermesini sağlar; yani bir "host" uygulama, başka bir "remote" uygulamanın dışa aktardığı bileşeni kendi bundle'ını büyütmeden kullanabilir.

// Remote uygulama
new ModuleFederationPlugin({
  name: 'remoteApp',
  filename: 'remoteEntry.js',
  exposes: {
    './Button': './src/Button',
  },
  shared: ['react', 'react-dom'],
})

// Host uygulama
new ModuleFederationPlugin({
  name: 'hostApp',
  remotes: {
    remoteApp: 'remoteApp@https://cdn.ornek.com/remoteEntry.js',
  },
  shared: ['react', 'react-dom'],
})

shared dizisi kritiktir: her iki tarafta da aynı React sürümünün çalışmasını sağlar. Aksi halde iki farklı React instance devreye girer ve hook'lar hata üretir. Sürüm uyumsuzluğu sessiz bir hata olarak görünmeyebilir; çalışma zamanında Invalid hook call olarak yüzeye çıkar.

Module Federation özellikle büyük organizasyonlarda, birden fazla ekibin bağımsız deploy ettiği uygulamalarda anlam kazanır. Tek ekipli, tek deploy'lu bir projede gereksizdir; o yapıda normal code splitting daha sade ve daha öngörülebilir bir çözümdür. Kod bölme kararını vermeden önce projenin gerçek dağıtım modelini değerlendirmek, fazladan karmaşıklıktan kaçınmayı sağlar.

Tree Shaking ve Long-Term Caching Farkları

Webpack 4 de tree shaking yapardı, ancak bazı sınırlılıkları vardı. İç içe re-export'lar bazen tamamen analiz edilemezdi; side effect analizi de modül düzeyinde kalırdı.

Webpack 5, inner-module tree shaking getirdi. Bir modül içinde yalnızca kullanılan export'lar değil, kullanılmayan iç değişkenler de eleniyor. package.json içindeki "sideEffects": false bayrağı webpack 5'te daha agresif biçimde uygulanıyor; bu yüzden geçiş sonrasında bazı paketlerin kütüphane kodunun beklenmedik biçimde küçüldüğü görülebilir.

Long-term caching tarafındaki fark şudur: webpack 4, modül ID'lerini sıra numarasıyla üretirdi. Yeni bir modül eklenince mevcut ID'ler kayardı ve bu tüm chunk'ların hash'ini değiştirirdi. Tarayıcı cache'i tümüyle geçersizleşirdi. Webpack 5, modül ID'sini içerik hash'inden türetir; yalnızca değişen dosyayı içeren chunk güncellenir. Vendor bundle değişmemişse tarayıcı onu cache'den sunar. Bundle boyutunu etkileyen kararlar arasında bu cache davranışı da bir değişken olarak sayılabilir.

Geçişte En Sık Bozulan Yapılandırmalar

Birkaç kategori öne çıkıyor.

Loader çakışmaları. Eski file-loader kuralları, webpack 5'in kendi asset kurallarıyla çakışınca dosyalar ya iki kez işlenir ya da hatalı yola yazılır. Her loader kuralını gözden geçirip webpack 5'in dahili karşılığının olup olmadığını kontrol etmek gerekir.

output.publicPath varsayımı. Webpack 4'te publicPath çoğunlukla boş bırakılırdı; webpack 5'te publicPath: 'auto' seçeneği geldi ve bu bazı yapılandırmalarla beklenmedik davranışlar üretebilir. Sub-path üzerinde çalışan uygulamalarda mutlak bir publicPath belirtmek daha güvenlidir.

DevServer konfigürasyonu. webpack-dev-server v4, webpack 5 ile birlikte büyük bir kırılma değişikliği yaşadı. contentBase yerine static geldi, historyApiFallback davranışı değişti. DevServer sürümünü ayrıca güncellemek gerekir; bu güncelleme webpack sürümünden bağımsız bir adımdır.

optimization.splitChunks değişiklikleri. Webpack 5, splitChunks API'sini büyük ölçüde korudu; ancak bazı varsayılan değerler değişti. minSize değeri farklı; chunks: 'all' davranışı bazı edge case'lerde farklı sonuç üretebilir. Build çıktısını webpack 4 ve 5 ile ayrı ayrı alıp karşılaştırmak, farkı en hızlı biçimde ortaya koyar.

Bu yapılandırmaları sırayla kontrol etmek, "neden çalışmıyor" sorusuna cevap aramaktan çok daha hızlıdır. Hata mesajı hangi katmanda kırıldığını söyler: cache, asset module, polyfill veya federation.

Build Çıktısını Karşılaştırmak

Geçişin etkisini sayılarla görmek için webpack'in kendi analiz araçlarını kullanmak yeterlidir. --profile ve --json flag'leriyle derleme verisi dosyaya yazılır:

npx webpack --profile --json > stats-v4.json
# webpack 5'e geçiş sonrası
npx webpack --profile --json > stats-v5.json

Chunk sayısı, her chunk'ın boyutu ve modül sayısı arasındaki farklar, geçişin bundle üzerindeki etkisini doğrudan gösterir. Eğer toplam bundle boyutu beklenmedik biçimde büyümüşse sebep çoğunlukla iki şeyden biridir: polyfill'ler elle eklenmiş ama gereksiz olanlar temizlenmemiştir, ya da loader çakışması nedeniyle bazı dosyalar iki kez bundle'a girmiştir.

Build süresini ölçmek için --progress flag'i ilk aşamada yeterlidir; persistent cache devreye alındıktan sonra ikinci ve üçüncü derlemedeki sürelere bakmak gerekir, ilk derlemeye değil. Vite ile yapılandırma yaklaşımları bu analizde kıyaslama noktası olarak kullanılabilir; farklı bir araç olsa da build çıktısını analiz etme mantığı benzerdir.

Webpack 5 geçişi tek seferlik bir iş değildir. Yapılandırma dosyasını güncellemek başlangıçtır; ardından loader'ları, DevServer'ı, babel ve ts-loader sürümlerini de uyumlu hale getirmek gerekir. Her araç kendi kırılma değişiklikleriyle gelir ve bu değişiklikler birbirinden farklı versiyonlara yayılmış olabilir.

Geçiş sonrası kazanımlar da tek boyutlu değildir. Persistent cache build süresini kısaltır, asset modules yapılandırmayı basitleştirir, polyfill temizliği bundle boyutunu küçültür, gelişmiş tree shaking gereksiz kodu ayıklar. Dört kazanç ayrı ayrı ölçülür; bakımı kolay hale getiren, hepsinin aynı yapılandırmada durmasıdır.

Teknik borç gibi değerlendirilen webpack 4 bağımlılığı, projeden projeye farklı ağırlık taşır. Küçük ve aktif geliştirilmeyen bir projede geçişin maliyeti faydayı geçebilir. Büyük, sürekli gelişen, birden fazla geliştirici çalışan bir projede ise geçişi ertelemenin bedeli her sprint biraz daha artar.