Storybook Projesinde Bundle Boyutu Neden Şişer ve Nasıl Kontrol Edilir?
Storybook geliştirme ortamında kalması gereken bağımlılıklar, yanlış import zincirleri veya paylaşılan konfigürasyon nedeniyle production bundle'a sızabilir. Builder seçimi, devDependencies sınıflandırması ve derleme izolasyonu bir arada kurulduğunda şişme kendiliğinden durur.
Storybook, bileşen geliştirme ve belgeleme sürecini kolaylaştıran bir araçtır. Ama projeye eklendiği anda paket yapısını karmaşıklaştırmaya başlar. Sorun tek başına Storybook'un ağır olmasından değil, getirdiği bağımlılıkların üretim yapısına (production build) karışmasından kaynaklanır. Bir gün bundle boyutunu incelerken @storybook/addon-docs veya babel-loader gibi paketlerin çıktıda göründüğünü fark edebilirsiniz; bu durum hem boyutu şişirir hem de gereksiz kod tarayıcıya ulaşır.
package.json içindeki bağımlılık kategorileri (dependencies, devDependencies, peerDependencies) mantıksal ayrımlar sunar; ancak bu ayrım otomatik olarak bundle'a yansımaz. Uygulamanızı derleyen bundler hangi dosyayı içe aktardığınıza bakar, paketi nereye koyduğunuza değil. Sınıflandırma yanlış yerde olduğunda izolasyon bozulabilir; ama sınıflandırma doğru olsa bile import satırı kaynak ağacının içine girdiği sürece sızma devam eder.
Şişmeyi durdurmak iki ayrı katmanda çalışmayı gerektirir: bağımlılık sınıflandırması ve derleme izolasyonu. Her ikisi doğru kurulduğunda Storybook geliştirme ortamında tam kapasiteyle çalışır; üretim paketi ise ondan hiç haberdar olmaz.
Bağımlılıkların Production'a Sızma Yolları
Storybook bağımlılıklarının production bundle'a karışmasının birinci nedeni, bileşen kaynak dosyalarının içinde doğrudan Storybook yardımcı fonksiyonlarını içe aktarmasıdır. @storybook/react veya @storybook/addon-actions bir Button.tsx dosyasında import edildiğinde, bundler bu satırı görür ve paketi çıktıya dahil eder. Derleme zamanında bu paketin "yalnızca geliştirmede kullanılan" olduğu bilgisi bundler'a kendiliğinden aktarılmaz.
İkinci neden daha örtük bir biçimde ortaya çıkar: paylaşılan yardımcı modüller. Bir theme.ts veya utils/decorators.ts dosyası hem bileşenler hem de story dosyaları tarafından kullanılıyorsa, bundler tüm bu ağı çözümlerken Storybook bağımlılıklarını da beraberinde getirebilir. Sorun doğrudan import'ta değil, dolaylı bağımlılık zincirindedir.
Üçüncü neden, .storybook/ klasöründeki konfigürasyonun ana webpack veya vite yapılandırmasıyla örtüşmesidir. Bazı projeler webpack.config.js dosyasını hem Storybook hem üretim derlemesi için paylaşır; bu pratik görünse de Storybook'a özgü loader ve plugin'lerin production çıktısına girmesine yol açar. İzolasyon kırılır. Ve genellikle fark edilmeden haftalarca birikerek devam eder.
Dördüncü neden test altyapısıyla ilgilidir. jest.setup.ts veya global test yardımcıları bazı projelerde Storybook'un dekoratörlerini veya mock'larını doğrudan çağırır. Bu dosyalar production derleme hedefinin dışında tutulmadığında, bağımlılık zinciri oradan da uzanabilir. Ayrı bir test yapılandırması tutmak ve testPathIgnorePatterns ile exclude ayarlarını dikkatli kurmak bu kanalı kapatır.
Builder Seçimi ve Bundle Boyutu İlişkisi
Storybook uzun süre Webpack 4 veya 5 üzerine kurulu kendi builder'ını kullandı. Bu yapı çalışır ama ağırdır: başlangıç süresi uzun, oluşturulan geliştirme paketi büyük, ara dosyalar birikir. Storybook 7 ile birlikte Vite tabanlı builder resmi olarak desteklenmeye başladı; bu geçiş yalnızca hızla sınırlı kalmadı, üretim artefaktlarının izolasyonu da iyileşti.
Webpack builder kullanıldığında Storybook, node_modules içinden çok sayıda paketi çözümler ve geliştirme sunucusu için ara dosyalar üretir. Bu ara dosyaların bir kısmı sunucu kapandıktan sonra temizlenmez; bir sonraki production derlemesinde bundler bu dosyaları bağımlılık ağına dahil edebilir. Vite builder önbellek mekanizmasını farklı biçimde yönetir ve iki ortam arasındaki sınır daha belirgindir.
Yanlış yerde duran import'lar ve paylaşılan konfigürasyonlar hangi builder kullanılırsa kullanılsın sızmaya devam eder. Builder değiştirmek genel hijyenin bir parçasıdır, tek başına çözüm değildir.
Tree Shaking Storybook Paketlerinde Neden Yetersiz Kalır?
Tree shaking, kullanılmayan export'ların bundle dışında bırakılması anlamına gelir; ama bu mekanizma her zaman beklenen biçimde çalışmaz. İlk kısıtlama modül formatından gelir: CommonJS (require / module.exports) tabanlı paketler tree shaking'e uyumlu değildir. Storybook'un eski addon paketleri büyük çoğunlukla CommonJS formatındaydı; bu yüzden bir addon'dan tek bir fonksiyon import edilse bile paketin tamamı bundle'a girerdi.
ESM formatına geçişle bu durum kısmen iyileşti. Ancak sideEffects bildirimi olmayan paketlerde Rollup veya Webpack, tek bir import görünce tüm modülü güvenli saymak zorunda kalır. package.json içinde "sideEffects": false bildirimi yoksa, bundler hangi kodun kesilebileceğini belirleyemez ve hepsini saklar.
Bir diğer kısıtlama dinamik import'lardan gelir. @storybook/addon-docs gibi paketler kendi içinde dinamik import kullandığında, statik analiz bu zinciri çözümleyemez. Tree shaking devreye girse de paketin büyük bölümü çıktıda kalır. Çözüm tree shaking'i zorlamak değil, bu paketlerin kaynak ağacına hiç girmemesini sağlamaktır. Barrel dosyaları da benzer bir tehlike taşır: bir index.ts içinde onlarca export varsa ve Storybook yardımcıları da aynı barrel üzerinden aktarılıyorsa, tree shaking'in devreye girecek noktaya ulaşması güçleşir.
devDependencies Sınıflandırması ve Import Sınırı
Storybook ve tüm addon paketleri devDependencies altında yer almalıdır; bu paketler yalnızca geliştirme aşamasında kullanılır, son kullanıcıya ulaşması gereken hiçbir şey içermezler. Sınıflandırma doğru olduğunda npm audit ve bağımlılık raporları okunabilir, otomatik güncelleme araçları gereksiz yükseltme önerisi getirmez.
Sızıntı riski taşıyan asıl durum şudur: bileşen kaynak dosyaları (src/components/) içinde Storybook paketlerini import eden satırlar varsa, bundler bunları bağımlılık listesinden değil, doğrudan kaynak ağacından takip ederek çıktıya ekler. Sınıflandırma doğru olsa bile import satırı derleme hedefinin içine girdiği sürece sızma gerçekleşir.
Pratik sınır şöyle çizilir: story dosyaları (*.stories.tsx, *.stories.ts) bileşen dosyalarından hiçbir şeyi dışa aktarmamalı, bileşen dosyaları da Storybook paketlerinden hiçbir şeyi içe aktarmamalıdır. Bu sınır korunduğunda, bundler üretim derlemesinde story dosyalarına erişemez ve Storybook bağımlılıkları otomatik olarak dışarıda kalır.
Bundle Analizi ile Sızıntının Tespiti
Sorunun nerede olduğunu görmeden önce üretim paketini analiz etmeniz gerekir. Webpack kullananlar için webpack-bundle-analyzer, Vite kullananlar için rollup-plugin-visualizer bu işi görür. Her iki araç da dosya boyutlarını ve içe aktarma zincirlerini görsel olarak gösterir.
Analiz raporunu açtığınızda arama yapın: storybook, addon, babel-jest, ts-jest, chromatic gibi kelimeler production bundle'da görünmemeli. Bu kelimelerin göründüğü her blok, sızıntının izini sürebileceğiniz bir başlangıç noktasıdır.
# Webpack için
npx webpack-bundle-analyzer stats.json
# Vite için (rollup-plugin-visualizer ekledikten sonra)
npx vite build --mode production
source-map-explorer kullanarak üretim kaynak haritasını (source map) inceleyebilirsiniz. Kaynak harita, hangi kaynak dosyasının nihai bundle'a ne kadar katkı sağladığını byte düzeyinde gösterir; yüzeysel görsel incelemeden daha kesin bir tablo sunar. İlk analizde beklenmedik bir paket çıkarsa, o paketin neden orada olduğunu import zincirini geriye doğru izleyerek bulmak mümkündür. CI/CD pipeline'ına bundlesize veya size-limit eklemek, her derleme sonrasında paketi otomatik olarak öncekiyle karşılaştırır ve eşik aşımında yapı başarısız sayılır; böylece sızıntı commit düzeyinde fark edilir.
Storybook Yapılandırmasını Üretimden İzole Etmek
En güvenilir çözüm iki derleme yapılandırmasını kesin olarak birbirinden ayırmaktır. Storybook kendi .storybook/main.ts ve .storybook/preview.ts dosyalarıyla konfigüre edilir; üretim derlemesi vite.config.ts veya webpack.config.js üzerinden yürür. Bu iki konfigürasyonun hiçbir şeyi paylaşmaması temel hedeftir.
Bazı projelerde tema değişkenleri veya global stil dosyaları her iki ortamda da kullanılır. Kaçınılmazsa, paylaşılan dosyaların Storybook'a özgü bir import içermemesi gerekir. Storybook dekoratörü veya bir addon'a özgü herhangi bir şey paylaşılan dosyaya girdiğinde izolasyon bozulur.
Monorepo yapılarında durum daha da karmaşıklaşır. Birden fazla paket aynı workspace içinde yaşadığında, bir paketin devDependencies'i başka bir paketin dependencies'ine çözümlenebilir. Her paketin package.json sınıflandırması ayrı ayrı gözden geçirilmeli, ortak workspace konfigürasyonu Storybook bağımlılıklarını üst düzeye taşımamalıdır. Workspace kökündeki package.json içinde Storybook bağımlılıkları görünüyorsa, bunlar tüm alt paketler için ortak bir kaynak haline gelir ve izolasyon amaçlanan düzeyde sağlanamaz.
Storybook bağımlılıklarının production bundle'a sızması bir kez yaşandığında çözümü bulmak zor değildir; fark edilmeden uzun süre birikmesi asıl tehlikeyi oluşturur. Küçük artışlar başlangıçta gözden kaçar, tek bir bileşen dosyasındaki yanlış import aylarca üretim paketinde taşınabilir. Analiz aracını CI/CD pipeline'ına ekleyerek her derleme sonrasında çıktı boyutunu öncekiyle karşılaştırmak bu riski erken aşamada yakalar. Üretim paketinde beklenmedik bir büyüme fark ettiğinizde ilk bakılacak yer bileşen kaynak dosyalarının import satırlarıdır; ardından paylaşılan yardımcı modüller ve jest kurulum dosyaları gözden geçirilir.
Builder tercihi ve bağımlılık sınıflandırması birbirini tamamlayan iki katmandır; ikisi birlikte doğru çalıştığında Storybook'un geliştirme ortamına katkısı production'a hiç yansımaz. Vite builder geçişi tek başına sihirli bir çözüm değildir; asıl iş import sınırlarını korumak ve iki yapılandırmayı birbirinden ayrı tutmaktır. Yeni bir bileşen eklendiğinde ya da bir addon güncellendiğinde aynı sınırın geçerliliğini koruyup korumadığını kısa bir analizle doğrulamak, uzun vadeli bir alışkanlık olarak yerleştirilmeye değer. Sınır bir kez çizildikten sonra korumak, her seferinde sıfırdan analiz yapmaktan çok daha az maliyet gerektirir.