webpack'i Anlamak: Entry, Loader, Plugin, Module Federation ve 2026'da Ne Zaman Hâlâ Gerekli?
webpack her dosyayı bir module sayar; loader'lar dosyayı sağdan sola dönüştürür, plugin'ler derleme hook'larına bağlanır. 5.112 CSS, HTML entry ve TypeScript'i loader'sız destekliyor; yeni projede yerini Vite aldı, ama eski Angular builder'ları, Module Federation ve özel loader'lı büyük projelerde hâlâ gerekli. Bookstore widget'ında loader sırası hatasını, kendi manifest plugin'imizi, splitChunks'ın minSize sınırını ve Module Federation'ın maliyetini ölçüyoruz.
Sürüm notu: Bu yazıdaki örnekler webpack 5.112, webpack-cli 7.3, webpack-dev-server 6.1, TypeScript 7.0 ve Node.js 24 ile denenmiştir; tarayıcı kontrolleri Playwright 1.65'in Chromium'uyla yapılmıştır. Süreler yazarın kendi makinesinden alındı.
Önce Tahmin Et
Önceki yazıda Vite ile build ettiğimiz Bookstore kitap arama widget'ını (sitedeki serilerin ortak örnek uygulaması Bookstore, bir online kitapçı) şimdi webpack ile build ediyoruz. Ekipten biri birkaç yıllık bir projeden webpack config'ini kopyaladı. CSS'i ayrı bir dosyaya çıkarmak için mini-css-extract-plugin, CSS'teki @import ve url()'leri çözmek için css-loader kullanıyor. Kural şöyle yazılmış:
module: {
rules: [
{ test: /\.ts$/, resolve: { fullySpecified: false } },
{ test: /\.css$/, use: ["css-loader", MiniCssExtractPlugin.loader] },
],
},
Liste okunduğu sırayla mantıklı görünüyor: önce CSS'i çöz, sonra dosyaya çıkar. npx webpack -c webpack.loaders.config.mjs --mode production çalışınca ne olur? Build geçer ve CSS dosyası boş mu çıkar, CSS JavaScript'in içine mi gömülür, yoksa build hiç bitmez mi? Cevabı aklında tut.
Bu yazı Build Araçları serisinin ikinci yazısı. Önceki yazıda bir build'in adımlarını gördük; burada bu adımları en uzun süredir uygulayan ve hâlâ en çok projede çalışan araca bakıyoruz: webpack'in modeli, loader zinciri, plugin hook'ları, son bir yılda core'a giren native CSS ve HTML desteği, Module Federation ve 2026'da webpack'e hâlâ ne zaman ihtiyaç olduğu.
webpack'in Modeli: Her Şey Bir Module
webpack'in temel fikri şu: entry'den başlayıp ulaşılabilen her dosya bir module'dür. Bir JavaScript dosyası da, bir CSS dosyası da, bir resim de graph'ın bir düğümüdür. webpack yalnız JavaScript ve JSON'u kendiliğinden anlar; geri kalan her dosya türünün bir module type'a ya da bir loader zincirine ihtiyacı vardır. Config'in beş temel parçası bu modelden çıkar:
| Kavram | Görevi | Widget'taki karşılığı |
|---|---|---|
entry | Graph'ın başladığı dosya ya da dosyalar | ./index.html ya da ./src/main.ts |
module.rules | Hangi dosyaya hangi loader'ın ve module type'ın uygulanacağı | .ts ve .css kuralları |
plugins | Derleme sürecinin hook'larına bağlanan class'lar | HTML üretimi, CSS'i dosyaya çıkarma, manifest |
output | Dosyaların nereye, hangi adla yazılacağı | [name].[contenthash:8].js |
mode | development ya da production varsayılanları | minify, tree shaking, process.env.NODE_ENV |
Loader, bir dosyanın içeriğini alıp başka bir içerik döndüren bir fonksiyondur. Plugin ise webpack'in derleme yaşam döngüsündeki hook'lara bağlanan, apply(compiler) method'u olan bir class'tır. Loader tek bir dosyayı dönüştürür; plugin bütün derlemeyi görür.
Loader Zinciri: Sağdan Sola
Tahminin cevabı: build hata verip bitiyor.
ERROR in ./src/styles.css
Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
Error: Execution of module code from module graph (./src/styles.css.webpack[javascript/auto]!=!./src/styles.css) failed: Module parse failed: Unexpected token (1:0)
File was parsed as module type 'javascript/auto'.
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> 1 | #book-finder {
| ^
2 | max-width: 32rem;
@ ./src/main.ts 1:0-22
webpack 5.112.2 compiled with 2 errors in 206 ms
Loader'lar use array'inde sağdan sola (aşağıdan yukarıya yazıldıysa alttan üste) çalışır. Zincirin ilk halkası dosyanın ham içeriğini alır, son halkası webpack'e JavaScript döndürmek zorundadır. Bizim yazdığımız sırada ham CSS önce MiniCssExtractPlugin.loader'a gitti. Bu loader girdinin css-loader'ın ürettiği JavaScript module'ü olmasını bekliyor; onu çalıştırıp CSS'i çıkarmaya çalıştı ve #book-finder { satırını JavaScript olarak parse edemedi. Hata mesajındaki "File was parsed as module type 'javascript/auto'" ifadesi bu sıra hatasının tipik imzası. Doğru sıra:
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] },
Önce css-loader CSS'i okur, @import ve url()'leri module bağımlılığına çevirir ve sonucu bir JavaScript module'ü olarak döndürür; ardından MiniCssExtractPlugin.loader bu module'ü alıp CSS'i ayrı bir dosyaya yazdırır. Sass kullanan bir projede zincir üç halkalı olur: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]; ilk çalışan en sağdaki sass-loader'dır. Doğru sırayla build şunu üretiyor:
asset book-finder.fb0ab0aa.js 3.44 KiB [emitted] [immutable] [minimized] (name: book-finder)
asset book-finder.aeb8e5f8.css 407 bytes [emitted] [immutable] (name: book-finder)
asset index.html 141 bytes [emitted] [minimized]
webpack 5.112.2 compiled successfully in 323 ms
JS [minimized] olarak işaretli ama CSS değil. Çıkan CSS dosyasına bakınca kaynakla birebir aynı olduğunu görüyoruz: iç içe kurallar duruyor, boşluklar duruyor. Loader'lı yolda CSS'i küçültmek için ayrıca css-minimizer-webpack-plugin, nesting'i eski tarayıcılara indirmek için de postcss-loader ve bir PostCSS plugin'i eklemek gerekiyordu. Eski projelerde config'lerin uzamasının sebebi bu: her yetenek bir paket ve bir kural daha.
2026'da webpack: Native CSS, HTML Entry ve Yerleşik TypeScript
webpack'in 2026 roadmap'i (4 Şubat 2026) bu paket kalabalığını core'a almayı hedefliyordu ve son sürümlerde büyük kısmı geldi. 5.107 (Mayıs 2026) HTML module'lerini ve Node.js'in type stripping'ine dayanan deneysel TypeScript desteğini getirdi. 5.108 (Haziran) bir HTML dosyasını doğrudan entry yapmayı mümkün kıldı. 5.109 (Temmuz) experiments.css, experiments.html ve experiments.typescript'in varsayılanını 'auto' yaptı: ilgili dosya türü için config'te bir loader kuralı yoksa yerleşik destek kendiliğinden açılıyor. 5.110 (Ağustos) ise css-loader, style-loader ve mini-css-extract-plugin'i deprecated ilan etti; çalışmaya devam ediyorlar ama yeni özellik almayacaklar. Geçiş için resmî bir codemod var: npx codemod @webpack/css-plugins-to-native-css.
Aynı widget'ın 5.112'deki config'i şu:
import path from "node:path";
import webpack from "webpack";
import { AssetManifestPlugin } from "./webpack/asset-manifest-plugin.mjs";
export default webpack.defineConfig((env, argv) => ({
entry: { index: "./index.html" },
output: {
path: path.resolve(import.meta.dirname, "dist-webpack"),
filename: "[name].[contenthash:8].js",
cssFilename: "[name].[contenthash:8].css",
htmlFilename: "[name].html",
clean: true,
},
module: {
parser: {
html: { chunkName: "book-finder" },
},
rules: [
// "type": "module" olan pakette .ts import'larını uzantısız yazabilmek için
{ test: /\.ts$/, resolve: { fullySpecified: false } },
],
},
devtool: argv.mode === "production" ? false : "eval-source-map",
plugins: [new AssetManifestPlugin()],
}));
Loader yok, html-webpack-plugin yok. Entry, Vite'ın kullandığı index.html'in kendisi: webpack HTML'deki script ve link etiketlerini module bağımlılığı olarak okuyor, /src/main.ts'i proje köküne göre çözüyor ve çıktıda bu etiketleri hash'li dosya adlarıyla yeniden yazıyor. chunkName, HTML'den çıkan chunk'a sayfa ve etiket sırasından türetilen otomatik ad yerine okunur bir ad veriyor. defineConfig 5.108'de geldi; çalışma zamanında hiçbir şey yapmıyor, yalnız editörde tip kontrolü ve tamamlama sağlıyor.
Tek kural dikkat çekici. İlk denemede config'te hiç kural yoktu ve build şu hatayla durdu:
ERROR in ./src/main.ts 2:0-38
Module not found: Error: Can't resolve './books' in '.../widget/src'
Did you mean 'books.ts'?
BREAKING CHANGE: The request './books' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
package.json'da "type": "module" olduğu için webpack bu paketteki module'leri katı ESM (ECMAScript Modules, import/export ile yazılan standart module sistemi) sayıyor ve Node.js'teki gibi import'larda dosya uzantısı istiyor. Vite ve esbuild uzantısız ./books'u sorunsuz çözüyor. İki çözüm var: import'lara uzantı yazmak (./books.ts; tsconfig'te allowImportingTsExtensions gerekir) ya da .ts dosyaları için fullySpecified'ı kapatmak. Kaynağa dokunmamak için ikincisini seçtik.
npm run build:webpack
(node:1024) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
assets by info 3.83 KiB [immutable]
asset book-finder.fb0ab0aa.js 3.44 KiB [emitted] [immutable] [minimized] (name: book-finder)
asset book-finder.7d8ac157.css 396 bytes [emitted] [immutable] [minimized] (name: book-finder)
asset index.html 318 bytes [emitted] [minimized] (auxiliary name: index)
asset asset-manifest.json 88 bytes [emitted]
webpack 5.112.2 compiled successfully in 316 ms
Uyarı satırı webpack'ten değil Node.js'ten geliyor: yerleşik TypeScript desteği, Node.js 24'te hâlâ deneysel olan module.stripTypeScriptTypes API'sini kullanıyor ve bu yüzden Node.js 22.6 ve üstünü istiyor. Type stripping yalnız tipleri siler; enum, namespace ya da constructor parameter property gibi tipten fazlasını üreten syntax'ı desteklemez. Widget'ın tsconfig'indeki erasableSyntaxOnly tam da bu sınırı editörde zorluyor. Type check yine npm run typecheck ile ayrı bir adım.
CSS artık [minimized]: 407 bayttan 396 bayta indi, nesting düzleştirildi ve system-ui'nin yanına eski tarayıcılar için yedek fontlar eklendi. Config'te browserslist olmadığı için webpack'in CSS minimizer'ı Browserslist'in defaults sorgusunu kullanıyor. Aynı widget Vite'ta 4.04 kB JS üretmişti; webpack'te 3.44 KiB. Vite'ın eklediği 670 baytlık modulepreload polyfill'i çıkarınca Vite'ın çıktısı biraz daha küçük kalıyor; araçları karşılaştırmayı serinin son yazısında yapacağız.
Bir uyarı: TypeScript 7.0'ın programatik bir API'si olmadığı için ts-loader onunla çalışmıyor. Denediğimizde build TypeError: Cannot read properties of undefined (reading 'fileExists') hatasıyla ts-loader'ın içinde düştü. Mevcut bir projede ts-loader'ı tutman gerekiyorsa TypeScript 6.0'da kalman ya da ts-loader'ın compiler option'ını TypeScript 6 uyumluluk paketine yönlendirmen gerekiyor; ayrıntıları TypeScript İleri serisinin "TypeScript 7 ve Sonrası: Go ile Yazılan Derleyici ve Yeni Varsayılanlar" yazısında anlattık.
Plugin Hook'ları: Kendi Plugin'ini Yazmak
Config'teki AssetManifestPlugin bizim yazdığımız küçük bir plugin. Bookstore.Web'in Razor layout'u hangi hash'li dosyayı yükleyeceğini bilmek zorunda; bu plugin her entrypoint'in ihtiyaç duyduğu dosyaları asset-manifest.json'a yazıyor:
import webpack from "webpack";
const { Compilation, sources } = webpack;
// asset-manifest.json yazar: her entrypoint'in ihtiyaç duyduğu dosyalar
export class AssetManifestPlugin {
apply(compiler) {
compiler.hooks.compile.tap("AssetManifestPlugin", () => console.log("[manifest] compile"));
compiler.hooks.thisCompilation.tap("AssetManifestPlugin", compilation => {
compilation.hooks.processAssets.tap(
{ name: "AssetManifestPlugin", stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE },
() => {
const manifest = {};
for (const [name, entrypoint] of compilation.entrypoints) {
const files = entrypoint.getFiles().filter(file => !file.endsWith(".map"));
if (files.length > 0) {
manifest[name] = files;
}
}
const json = JSON.stringify(manifest, null, 2);
compilation.emitAsset("asset-manifest.json", new sources.RawSource(json));
console.log("[manifest] processAssets: asset-manifest.json");
});
});
compiler.hooks.done.tap("AssetManifestPlugin", stats => console.log(`[manifest] done in ${stats.endTime - stats.startTime} ms`));
}
}
[manifest] compile
[manifest] processAssets: asset-manifest.json
[manifest] done in 316 ms
{
"book-finder": [
"book-finder.fb0ab0aa.js",
"book-finder.7d8ac157.css"
]
}
webpack'in iki ana nesnesi var. compiler bütün süreci temsil eder ve bir kez oluşturulur; compilation ise tek bir derlemeyi temsil eder ve watch mode'da her değişiklikte yeniden oluşur. Hook'lar Tapable kütüphanesiyle yazılmış; tap senkron, tapAsync ve tapPromise asenkron bağlanma içindir. processAssets çıktı dosyaları hazırlanırken çalışır ve stage değeri plugin'in sıradaki yerini belirler: SUMMARIZE aşaması, minify ve hash işlemleri bittikten sonra gelir, bu yüzden manifest'e son dosya adları yazılıyor. Daha erken bir aşamayı seçen plugin, örneğin minify edilmemiş içeriği görür. html-webpack-plugin'den mini-css-extract-plugin'e kadar webpack ekosisteminin tamamı bu hook'ların üzerine kurulu; webpack'in en güçlü yanı da en karmaşık yanı da bu.
mode ve optimization.splitChunks
--mode production; minify'ı, tree shaking'i, scope hoisting'i (webpack'teki adıyla module concatenation) ve process.env.NODE_ENV'in "production" ile değiştirilmesini açar. Yukarıdaki çıktıda ./src/main.ts + 19 modules satırı bu birleştirmenin sonucu: 20 module tek bir scope'ta. development mode'unda bunların hiçbiri yapılmaz, module adları okunur kalır ve build hızlı olur.
Code splitting'i optimization.splitChunks yönetiyor. Varsayılan chunks: "async", yani yalnız dinamik import() ile yüklenen kod ayrı chunk'lara bölünüyor. "Vendor kodunu ayrı chunk'a alalım" diye chunks: "all" yapınca ne olduğuna baktık:
npx webpack --mode production --optimization-split-chunks-chunks all
# book-finder.fb0ab0aa.js 3.44 KiB, ayrı vendor chunk'ı yok
npx webpack --mode production --optimization-split-chunks-chunks all --optimization-split-chunks-min-size 0
# asset 904.2148c5d5.js 2.28 KiB [emitted] [immutable] [minimized] (id hint: vendors)
# asset book-finder.836d2778.js 2.27 KiB [emitted] [immutable] [minimized] (name: book-finder)
İlk denemede hiçbir şey değişmedi, çünkü minSize varsayılanı 20000 bayt ve lodash'tan kullandığımız 14 module bu sınırın altında. Sınırı sıfırlayınca vendors chunk'ı oluştu, ama toplam JS 3.44 KiB'tan 4.55 KiB'a çıktı: iki chunk'ı birbirine bağlayan chunk yükleme runtime'ı da bundle'a girdi. Küçük bir uygulamada vendor'ı bölmek kazanç değil kayıp. Hangi durumda bölmenin işe yaradığını, HTTP/2 sonrası değişen mantığıyla birlikte serinin code splitting yazısında ele alacağız.
Module Federation ile Micro-Frontend
webpack 5'in 2020'de getirdiği Module Federation, ayrı build edilen ve ayrı deploy edilen uygulamaların çalışma zamanında birbirinin module'lerini yüklemesini sağlar. Micro-frontend mimarisinde, örneğin Bookstore'un kurumsal portal ekibinin kendi bölümünü ana uygulamadan bağımsız yayınlaması gibi durumlarda kullanılır. Widget'ı bir remote olarak yayınlayan config:
import path from "node:path";
import webpack from "webpack";
const { ModuleFederationPlugin } = webpack.container;
export default webpack.defineConfig({
entry: {},
output: {
path: path.resolve(import.meta.dirname, "dist-federation"),
publicPath: "auto",
uniqueName: "bookFinder",
clean: true,
copy: "./federation",
},
module: {
rules: [{ test: /\.ts$/, resolve: { fullySpecified: false } }],
},
plugins: [
new ModuleFederationPlugin({
name: "bookFinder",
filename: "remoteEntry.js",
exposes: { "./finder": "./src/finder.ts" },
shared: { "lodash-es": { singleton: true } },
}),
],
});
output.copy 5.111'de geldi ve federation/ klasöründeki test sayfasını çıktıya kopyalıyor. Bu sayfa, webpack ile build edilmemiş düz bir HTML; remote container'ın protokolünü doğrudan kullanıyor:
<script src="./remoteEntry.js"></script>
...
<script type="module">
await bookFinder.init({});
const factory = await bookFinder.get("./finder");
const { mountBookFinder } = factory();
mountBookFinder(document.querySelector("#book-finder"), [
{ isbn: "9780441172719", title: "Dune", author: "Frank Herbert", price: 450 },
{ isbn: "9780141439587", title: "Emma", author: "Jane Austen", price: 210 },
]);
</script>
npm run build:federation
# asset 271.js 87.4 KiB [emitted] [minimized] (id hint: vendors) 1 related asset
# asset remoteEntry.js 6.02 KiB [emitted] [minimized] (name: bookFinder)
# asset 179.js 896 bytes [emitted] [minimized]
# asset host.html 489 bytes [emitted] [from: federation/host.html] [copied] [minimized]
Chromium'da sayfayı açıp kutuya "emma" yazınca liste Emma · Jane Austen · ₺210,00 satırına indi; widget başka bir build'den yüklenip çalıştı. Ama boyutlara dikkat: normal build'de 3.44 KiB olan widget burada 87.4 KiB'lık bir vendor chunk'ı istiyor. Sebep shared ayarı. Paylaşılan bir paket tree shaking'den geçemez, çünkü remote, host'un o paketten hangi export'ları kullanacağını bilemez; lodash-es'in tamamı chunk'a giriyor. Module Federation'ın gerçek maliyeti genelde buradadır. Gerçek bir projede host da webpack ya da Rspack ile build edilir ve remote'u remotes ayarıyla bağlar; init ve get çağrılarını runtime kendisi yapar. Module Federation 2.0 (@module-federation/enhanced) runtime plugin'leri, tip ipuçları ve manifest ekleyerek aynı fikri Rspack ve Vite'a da taşıdı.
webpack 2026'da Hâlâ Ne Zaman Gerekli?
Yeni bir SPA (tek sayfalık uygulama) ya da kütüphaneye 2026'da webpack ile başlamak için güçlü bir sebep yok; Vite daha az config ile daha hızlı bir geliştirme döngüsü veriyor. webpack'in hâlâ doğru seçim olduğu ya da kaçınılmaz olduğu yerler şunlar:
- Eski Angular projeleri:
@angular-devkit/build-angular:browserbuilder'ı webpack tabanlı ve deprecated;ng update @angular/cli --name use-application-builderile application builder'a geçiş yapılabiliyor.@angular-builders/custom-webpackile config'i özelleştirilmiş projeler ise bu geçişte en çok zorlananlar, çünkü webpack plugin'lerinin esbuild tarafında birebir karşılığı yok. Bu geçişi serinin Angular CLI yazısında ele alacağız. - Module Federation'a dayanan micro-frontend'ler: Ekosistemin en olgun ve en çok kullanılan hâli hâlâ webpack ve Rspack tarafında.
- Özel loader ve plugin'lere bağlı büyük codebase'ler: Yıllar içinde biriken davranışı başka bir araca taşımanın maliyeti, webpack'te kalmanın maliyetinden yüksek olabilir.
Bu son grup için ara yol Rspack. Rspack, webpack'in config'ini ve plugin API'sini büyük ölçüde destekleyen, Rust ile yazılmış bir bundler. 22 Nisan 2026'da çıkan 2.0 sürümünde core paketleri yalnız ESM olarak yayınlanıyor ve Module Federation'daki shared paketlere tree shaking desteği eklendi; yukarıda gördüğümüz 87 KiB'lık sorunun cevabı tam olarak bu. Duyuruya göre Rspack ekibinin kendi benchmark'ında cache'siz production build 1.7'ye göre yaklaşık %10, 1.0'a göre neredeyse iki kat hızlı. Bu sayılar onların projesinde ölçüldü; kendi projen için geçerli olup olmadığını ölçmeden bilemezsin.
webpack'in kendi yolu ise webpack 6. Roadmap'e göre native CSS ve HTML desteği 6'da deneysel olmaktan çıkacak, mini-css-extract-plugin ve html-webpack-plugin gibi paketlere gerek kalmayacak. 5.111'de output.module'ün (ESM çıktı) deneysel bayraktan çıkması ve experiments.futureDefaults'a eklenmesi de bu hazırlığın parçası. Tarih açıklanmış değil.
Üretimde Dikkat
Persistent cache'i aç. cache: { type: "filesystem" } derlenmiş module'leri diske yazar ve bir sonraki build'de değişmeyen dosyaları yeniden işlemez. Büyük projelerde CI'da en büyük kazanç buradan gelir; cache klasörünü (node_modules/.cache/webpack) CI cache'ine almayı ve cache.buildDependencies ile config dosyasını cache anahtarına eklemeyi unutma, yoksa config değişikliği eski cache ile birleşir.
Production build'de source map stratejisini bilinçli seç. Config'teki devtool production'da false; hidden source map ve error tracking servisleriyle kullanımını serinin source map yazısında göreceğiz.
Bundle boyutunu izle. webpack 5.110 ile gelen performance.hints: "stats" ve 5.111'deki unusedModules, duplicateModules, unusedConfig kontrolleri, kullanılmayan module'leri ve config'te boşa duran kuralları build çıktısında raporlayabiliyor. Varsayılan olarak hepsi kapalı.
Loader'lı eski bir config'i native CSS'e taşırken çıktıyı karşılaştır. Biz aynı widget'ta 407 baytlık minify edilmemiş CSS'in 396 bayta indiğini ve nesting'in düzleştiğini gördük; çok sayıda CSS dosyası olan bir projede sıralama ve CSS Modules class adları da değişebilir. Codemod'u çalıştırdıktan sonra kritik sayfaların görüntüsünü bir test ile doğrula.
Sık Düşülen Tuzaklar
Birincisi, config'i kopyala-yapıştır ile büyütmek. İnternetten alınan her kural ve plugin bir varsayım taşır: hangi webpack sürümü, hangi loader sürümü, hangi Node.js. Beş yıllık bir config'te file-loader, url-loader ya da raw-loader görürsen, webpack 5'in asset module'leri (type: "asset/resource", "asset/inline", "asset/source") bunların yerini çoktan aldı.
İkincisi, loader sırasını okuma sırası sanmak. use array'i sağdan sola çalışır; zincirin son halkası her zaman JavaScript döndürmelidir. "File was parsed as module type 'javascript/auto'" hatası çoğu zaman yanlış sıranın ya da eksik bir loader'ın işaretidir.
Üçüncüsü, yavaş HMR'ı (Hot Module Replacement; sayfayı yenilemeden değişen module'ü değiştirme) plugin ekleyerek çözmeye çalışmak. Her plugin hook'lara bir iş daha ekler; HMR'ı yavaşlatan genelde fazladan yapılan iştir. Önce hangi loader'ın ve plugin'in zaman harcadığını ölç: npx webpack --mode production --profile --json=stats.json ile module bazında süreleri bir dosyaya yazdırabilirsin. (--json > stats.json yönlendirmesi, bizim plugin gibi konsola yazan bir plugin varsa bozuk JSON üretir.) Ardından development'ta gereksiz işi kapat (minify, ağır source map türleri, type check'i aynı süreçte yapan plugin'ler) ve persistent cache'i aç. Yetmiyorsa büyük ihtimalle doğru cevap Rspack ya da Vite'a geçiş.
Dördüncüsü, ts-loader'ı TypeScript 7 ile kullanmaya çalışmak. Yukarıda gördüğümüz gibi çalışmıyor; yeni projede webpack'in yerleşik type stripping'i, mevcut projede TypeScript 6 yeterli.
Küçük Bir Deneme
★☆☆ webpack.loaders.config.mjs'teki CSS kuralının sırasını ters çevir, npm run build:webpack-loaders ile hatayı kendin gör ve düzelt. Ardından aynı config'e type: "module" ve package.json arasındaki ilişkiyi görmek için .ts kuralını sil ve çıkan hatayı oku.
★★☆ AssetManifestPlugin'i her dosyanın boyutunu da yazacak şekilde genişlet (compilation.getAsset(file).source.size()). Stage'i PROCESS_ASSETS_STAGE_ADDITIONS yapıp tekrar çalıştır; yazılan adların ve boyutların değişip değişmediğine bak ve nedenini açıkla.
★★★ webpack.loaders.config.mjs'in bir kopyasını native CSS'e elle taşı: CSS kuralını ve MiniCssExtractPlugin'i sil, output.cssFilename ekle. Çıkan CSS dosyasının boyutunu ve içeriğini native config'in çıktısıyla karşılaştır; sonra aynı işi resmî codemod'un nasıl yaptığını webpack 5.110 duyurusundan oku.
Bir sonraki yazıda webpack'in tam tersi bir felsefeyle yazılmış araca geçiyoruz: esbuild. Go ile yazılmış, config'ten çok API'ye dayanan bu aracın neden bu kadar hızlı olduğunu, plugin API'sini ve nerede yetmediğini aynı widget üzerinde göreceğiz.