---
title: 'Modül Paketleme'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/modul-paketleme'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Modül Paketleme

Kaynak ağacın çalıştırılabilir çıktıya dönüşmesi; bağımlılık çizgesinin giriş noktalarından çıkarılması, değerlendirme sırası, kullanım imzasına göre çıktı parçası oluşturma ve parçanın içindeki çalışma zamanı.

Strateji seçildi. Kuzey Yamaç Ölçüm İstasyonu sitesinin hangi sayfasının nerede
üretileceği belli: tanıtım sayfası derleme zamanında, ölçüm listesi sunucuda, sorumlu
paneli istemcide, yönlendirme kararı kenarda. Bu kararların ortak bir varsayımı vardı ve
o varsayım henüz karşılanmadı. Her strateji, çalıştırılabilir bir **çıktı** ister:
derleme zamanı üretim, sayfaları basacak bir programın hazır olmasını; sunucuda oluşturma,
sunucuya yüklenecek bir modülü; istemcide oluşturma, tarayıcıya inecek bir betiği.

Kaynak ağaç bunların hiçbiri değildir. Kaynakta onlarca küçük dosya, aralarında içe
aktarım ilişkileri ve hedef ortamın çözemeyeceği belirteçler vardır. Bu dersin sorusu
şudur: kaynak dosyalar arasındaki bağımlılık ilişkisinden hangi çıktı parçaları, hangi
ölçütle üretilir?

## Kaynak Düzeni ile Çıktı Düzeni Aynı Şey Değildir

Modüller, Araçlar ve Ekosistem kursundaki paketleyici dersi bu ayrımı kurmuştu: kaynak
düzeni okunabilirlik için, çıktı düzeni hedef ortamın kısıtları için tasarlanır. Orada
tek girişli tek çıktı üretilmişti. Oluşturma stratejisi birden çok hedef seçtiği anda
soru değişir. Aynı kaynak ağaçtan hem tarayıcıya inecek bir betik hem de sunucuda
çalışacak bir modül üretiliyorsa, iki çıktının ortak modülleri vardır. Bu ortaklığın nasıl
ele alındığı, hem çıktı boyutunu hem de önbellekleme davranışını belirler.

**Modül paketleme (module bundling)**, giriş noktalarından erişilen modül kümesini bulup
bu kümeyi hedefe uygun sayıda dosyaya bölme işidir. İki ayrı karar içerir: çizgede kimin
kime bağlı olduğunun çıkarılması ve bu çizgenin parçalara ayrılması.

## Uygulamanın Kaynak Ağacı

Kurs boyunca aynı kaynak ağaç kullanılacak. Aşağıdaki blok onu üretir.

```bash
mkdir -p kaynak

cat > kaynak/birim.js <<'EOF'
export const SICAKLIK_ALT = -60;
export const SICAKLIK_UST = 60;
export const sinirdaMi = (c) => c >= SICAKLIK_ALT && c <= SICAKLIK_UST;
export const fahrenhayt = (c) => c * 9 / 5 + 32;
EOF

cat > kaynak/tarih.js <<'EOF'
const iki = (s) => String(s).padStart(2, "0");
export const saatDakika = (d) => iki(new Date(d).getUTCHours()) + ":" + iki(new Date(d).getUTCMinutes());
export const gunAnahtari = (d) => new Date(d).toISOString().slice(0, 10);
EOF

cat > kaynak/bicim.js <<'EOF'
import { saatDakika } from "./tarih.js";
import { sinirdaMi, fahrenhayt } from "./birim.js";

export function olcumSatiri(o) {
  const uyari = sinirdaMi(o.sicaklik) ? "" : " (sinir disi)";
  return saatDakika(o.damga) + "  " + o.sicaklik.toFixed(1) + " C  %" + o.nem + uyari;
}
export const ikiliBirim = (o) => o.sicaklik.toFixed(1) + " C / " + fahrenhayt(o.sicaklik).toFixed(1) + " F";
EOF

cat > kaynak/sablon.js <<'EOF'
import { olcumSatiri } from "./bicim.js";

export const liste = (olcumler) =>
  "<ul>" + olcumler.map((o) => "<li>" + olcumSatiri(o) + "</li>").join("") + "</ul>";
export const baslik = (istasyon) => "<h1>" + istasyon + "</h1>";
EOF

cat > kaynak/grafik.js <<'EOF'
import { SICAKLIK_ALT, SICAKLIK_UST } from "./birim.js";

const olcekle = (v, uzunluk) => ((v - SICAKLIK_ALT) / (SICAKLIK_UST - SICAKLIK_ALT)) * uzunluk;

export function sicaklikYolu(olcumler, genislik, yukseklik) {
  const adim = olcumler.length > 1 ? genislik / (olcumler.length - 1) : 0;
  return olcumler
    .map((o, i) => (i ? "L" : "M") + (i * adim).toFixed(1) + " " +
      (yukseklik - olcekle(o.sicaklik, yukseklik)).toFixed(1))
    .join(" ");
}
EOF

cat > kaynak/arsiv.js <<'EOF'
import { gunAnahtari } from "./tarih.js";
import { liste } from "./sablon.js";

export function gunlereBol(olcumler) {
  const kova = new Map();
  for (const o of olcumler) {
    const g = gunAnahtari(o.damga);
    kova.set(g, [...(kova.get(g) ?? []), o]);
  }
  return kova;
}
export const arsivSayfasi = (gun, olcumler) => "<h2>" + gun + "</h2>" + liste(olcumler);
EOF

cat > kaynak/giris-tarayici.js <<'EOF'
import { liste, baslik } from "./sablon.js";
import { sicaklikYolu } from "./grafik.js";

export function baslat(kok, istasyon, olcumler) {
  kok.innerHTML = baslik(istasyon) + liste(olcumler);
  return sicaklikYolu(olcumler, 320, 120);
}
EOF

cat > kaynak/giris-sunucu.js <<'EOF'
import { liste, baslik } from "./sablon.js";
import { gunlereBol, arsivSayfasi } from "./arsiv.js";

export function sayfaUret(istasyon, olcumler) {
  const gunluk = [...gunlereBol(olcumler)].map(([g, l]) => arsivSayfasi(g, l));
  return { canli: baslik(istasyon) + liste(olcumler), arsiv: gunluk };
}
EOF
```

Sekiz modül, iki giriş noktası. Tarayıcı girişi ekranı çizer ve sıcaklık grafiğini üretir;
sunucu girişi aynı listeyi ve günlük arşiv sayfalarını üretir. Biçimlendirme, şablon,
birim ve tarih modülleri ikisinde de kullanılır.

## Çizgenin Çıkarılması

Çizge, giriş noktalarından başlayan bir dolaşımla bulunur. Her modül bir düğüm, her içe
aktarım bir yönlü kenardır. Veri Yapıları kursundaki enine arama, burada dosya sistemi
üzerinde çalışır: kuyruktan bir yol alınır, kaynağı okunur, belirteçleri çözülür, bulunan
komşular kuyruğa eklenir.

```js
// cizge.mjs — kaynak agacindan bagimlilik cizgesi ve cikti parcalari.
import { readFileSync, statSync } from "node:fs";
import { dirname, join, relative } from "node:path";

const GIRISLER = ["kaynak/giris-tarayici.js", "kaynak/giris-sunucu.js"];
const ICE_AKTARIM = /^\s*(?:import|export)[^"']*from\s*["']([^"']+)["']/gm;

function cizgeCikar(girisler) {
  const dugum = new Map();                      // yol -> { boyut, komsu[] }
  const kuyruk = [...girisler];
  while (kuyruk.length > 0) {
    const yol = kuyruk.shift();
    if (dugum.has(yol)) continue;
    const kaynak = readFileSync(yol, "utf8");
    const komsu = [...kaynak.matchAll(ICE_AKTARIM)]
      .map(([, belirtec]) => relative(".", join(dirname(yol), belirtec)));
    dugum.set(yol, { boyut: statSync(yol).size, komsu });
    kuyruk.push(...komsu);
  }
  return dugum;
}

function kapanis(cizge, kok) {                  // kokten erisilen tum dugumler
  const gorulen = new Set(), yigit = [kok];
  while (yigit.length > 0) {
    const yol = yigit.pop();
    if (gorulen.has(yol)) continue;
    gorulen.add(yol);
    yigit.push(...cizge.get(yol).komsu);
  }
  return gorulen;
}

function degerlendirmeSirasi(cizge, kok) {      // sonradan-gezinme: bagimlilik once
  const sira = [], gorulen = new Set();
  (function gez(yol) {
    if (gorulen.has(yol)) return;
    gorulen.add(yol);
    for (const k of cizge.get(yol).komsu) gez(k);
    sira.push(yol);
  })(kok);
  return sira;
}

const cizge = cizgeCikar(GIRISLER);
const ad = (y) => y.replace("kaynak/", "");
const kenarSayisi = [...cizge.values()].reduce((t, d) => t + d.komsu.length, 0);

console.log("-- cizge --");
console.log("dugum:", cizge.size, " kenar:", kenarSayisi);
for (const [yol, d] of [...cizge].sort()) {
  console.log("  " + ad(yol).padEnd(20) + String(d.boyut).padStart(5) + " B  -> " +
    (d.komsu.map(ad).join(", ") || "(yaprak)"));
}

console.log("-- degerlendirme sirasi (tarayici girisi) --");
console.log("  " + degerlendirmeSirasi(cizge, GIRISLER[0]).map(ad).join(" -> "));

const kapanislar = new Map(GIRISLER.map((g) => [g, kapanis(cizge, g)]));
console.log("-- giris kapanislari --");
for (const [g, k] of kapanislar) {
  const bayt = [...k].reduce((t, y) => t + cizge.get(y).boyut, 0);
  console.log("  " + ad(g).padEnd(20) + String(k.size).padStart(2) + " modul  " +
    String(bayt).padStart(5) + " B");
}

// Imza: modulu isteyen girislerin kumesi. Ayni imza = ayni cikti parcasi.
const imza = new Map();
for (const yol of cizge.keys()) {
  const isteyen = GIRISLER.filter((g) => kapanislar.get(g).has(yol));
  const anahtar = isteyen.map(ad).join(" + ");
  if (!imza.has(anahtar)) imza.set(anahtar, []);
  imza.get(anahtar).push(yol);
}

console.log("-- cikti parcalari --");
let toplamParca = 0;
for (const [anahtar, moduller] of [...imza].sort((a, b) => b[0].length - a[0].length)) {
  const bayt = moduller.reduce((t, y) => t + cizge.get(y).boyut, 0);
  toplamParca += bayt;
  console.log("  isteyen: " + anahtar);
  console.log("    " + String(bayt).padStart(5) + " B  " + moduller.map(ad).sort().join(", "));
}
const kaynakToplam = [...cizge.values()].reduce((t, d) => t + d.boyut, 0);
console.log("-- toplam --");
console.log("  kaynak:", kaynakToplam, "B   parcalar:", toplamParca, "B");
console.log("  kopyalanan modul yok:", kaynakToplam === toplamParca);
```

```
$ node cizge.mjs
-- cizge --
dugum: 8  kenar: 10
  arsiv.js              367 B  -> tarih.js, sablon.js
  bicim.js              387 B  -> tarih.js, birim.js
  birim.js              186 B  -> (yaprak)
  giris-sunucu.js       302 B  -> sablon.js, arsiv.js
  giris-tarayici.js     239 B  -> sablon.js, grafik.js
  grafik.js             458 B  -> birim.js
  sablon.js             229 B  -> bicim.js
  tarih.js              227 B  -> (yaprak)
-- degerlendirme sirasi (tarayici girisi) --
  tarih.js -> birim.js -> bicim.js -> sablon.js -> grafik.js -> giris-tarayici.js
-- giris kapanislari --
  giris-tarayici.js    6 modul   1726 B
  giris-sunucu.js      6 modul   1698 B
-- cikti parcalari --
  isteyen: giris-tarayici.js + giris-sunucu.js
     1029 B  bicim.js, birim.js, sablon.js, tarih.js
  isteyen: giris-tarayici.js
      697 B  giris-tarayici.js, grafik.js
  isteyen: giris-sunucu.js
      669 B  arsiv.js, giris-sunucu.js
-- toplam --
  kaynak: 2395 B   parcalar: 2395 B
  kopyalanan modul yok: true
```

Bayt sayıları dosyaların gerçek boyutlarıdır; kaynak dosyalar birebir yukarıdaki blokla
üretildiğinde bu değerler çıkar. Bir satırlık bir düzenleme bile sayıları değiştirir.

## Değerlendirme Sırası

Çizgenin ilk kullanımı sıralamadır. Bir modülün gövdesi, bağımlılıklarının gövdesi
çalıştıktan sonra çalışmalıdır; aksi hâlde henüz tanımlanmamış bir bağa erişilir. Sonradan
gezinmeli dolaşım bu sırayı verir: `tarih.js` ve `birim.js` en başta, giriş noktası en
sonda. Bu, Veri Yapıları kursundaki topolojik sıralamanın çizgeye uygulanmış hâlidir.

Sıralamanın çıktıya iki yansıması vardır. Tek dosyaya yazılan bir parçada modüllerin
gövdeleri bu sırayla dizilir. Birden çok dosyaya bölünen çıktıda ise sıra, dosyaların
yükleme sırasına dönüşür: bir parça, bağımlı olduğu parça değerlendirilmeden
çalıştırılamaz.

Çizgede döngü varsa sonradan gezinme tek bir doğru sıra vermez. Modül sistemleri
konusundaki dairesel bağımlılık tartışması burada da geçerlidir: paketleyici döngüyü
kırmaz, yalnızca bir sıra seçer ve seçtiği sıra hangi bağın erken okunacağını belirler.

## Giriş Noktası Kümesi

Bir derlemede kaç giriş noktası olacağı, oluşturma stratejisi kararının doğrudan
sonucudur. Tarayıcıda çalışan kod için bir giriş, sunucuda çalışan kod için ayrı bir
giriş, kenarda çalışan modül için üçüncü bir giriş gerekir. Her girişin kendi kapanışı
vardır ve kapanışlar kesişir: örnekte iki girişin toplam altışar modülünden dördü ortaktır.

Girişler yalnız kapsam olarak değil, **hedef** olarak da ayrılır. Aynı kaynaktan üretilen
iki çıktı farklı biçimlerde yazılır: sunucu çıktısı çalışma zamanının doğrudan
yükleyebileceği modül biçiminde, tarayıcı çıktısı belgeye eklenecek betik biçiminde. Dış
bırakılan bağımlılıklar da hedefe göre değişir — sunucu girişinde çalışma zamanının kendi
sağladığı modüller çizgeye alınmaz, tarayıcı girişinde böyle bir kaynak yoktur ve her
şeyin çıktıya girmesi gerekir.

Bu, kaynak ağaçta tek bir dosyanın iki farklı çıktıda iki farklı biçimde görünebileceği
anlamına gelir. Ortak modüllerin her iki hedefte de çalışabilir olması, kaynak düzenine
konan bir kısıttır: ortak katmanda yalnız ortama bağımsız kod bulunur.

## Parçalama Ölçütü

Ortak modüller iki yolla ele alınabilir. Her girişin çıktısına kopyalanabilirler; o zaman
aynı kod iki dosyada iki kez durur. Ya da ayrı bir parçaya konup her iki girişten
yüklenebilirler; o zaman bir dosya daha üretilir.

Karar, çizgeden okunabilen tek bir bilgiye dayanır: bir modülü hangi girişler ister. Bu
kümeye modülün **kullanım imzası (usage signature)** denir. Aynı imzaya sahip modüller
birlikte gerekir, dolayısıyla birlikte konabilir. Farklı imzalar farklı parçalar üretir.

Çıktıda üç parça görünmesinin nedeni budur: iki girişin de istediği dört modül tek bir
paylaşılan parçada, yalnız tarayıcının istediği iki modül bir parçada, yalnız sunucunun
istediği iki modül üçüncü parçada. Son satırdaki denetim ölçütün sağlamasıdır: parçaların
toplamı kaynağın toplamına eşitse hiçbir modül iki parçaya kopyalanmamıştır.

Uygulama Mimarisi kursundaki kod bölme dersi aynı ölçütü rota sınırları için kullanmıştı;
orada imzayı oluşturan küme rotalardı. Ölçüt aynıdır, imzayı üreten kaynak farklıdır: bu
derste girişler, orada rotalar.

## Parçanın İçinde Ne Var

Bir parça, modül gövdelerinin arka arkaya yazılmasından ibaret değildir. Her modülün kendi
kapsamı korunmalı, dışa aktarımları başka modüllere görünür olmalı ve bir modül birden çok
kez içe aktarılsa bile bir kez değerlendirilmelidir. Bunu sağlayan üç şey parçanın içinde
yer alır: modül sarmalayıcıları, tek değerlendirmeyi güvenceye alan bir kayıt, ve
belirteçleri çıktı anahtarlarına çeviren bir eşleme. Modüller, Araçlar ve Ekosistem
kursundaki paketleyici dersi bu yapıyı çalışır hâlde kurmuştu.

Paylaşılan parçanın varlığı bir şey daha ekler: parçalar arası dışa aktarım yüzeyi.
Tarayıcı parçası `sablon.js` içindeki iki adı kullanıyorsa, paylaşılan parça bu adları
dışarıya vermek zorundadır. Parça sınırı, bu yüzden yalnız bir dosya sınırı değil, aynı
zamanda bir arayüz sınırıdır: sınırı geçen her ad çıktıda görünür kalmalı, yeniden
adlandırılamaz veya elenemez.

Kapsam birleştirme bu noktada sınırlanır. Aynı parçanın içindeki modüller tek kapsamda
eritilebilir; parça sınırını geçen adlar eritilemez. Parça sayısını artırmak, bu yüzden
yalnız istek sayısını değil, uygulanabilecek dönüşümlerin alanını da etkiler.

## Çizgenin Söylemediği Şeyler

Buradaki çizge yalnız statik içe aktarımlardan kuruldu ve bu, gerçek bir derlemenin
girdisinin bir parçasıdır.

Belirteçler düzenli ifadeyle bulundu. Bir dizginin içinde geçen içe aktarım yazımı da
eşleşir, yorumdaki bir satır da. Gerçek araçlar kaynağı ayrıştırıp soyut sözdizim ağacı
üzerinde çalışır; uzantı tamamlama, dizin girişleri, çıplak belirteç araması ve koşullu
girişlerin çözümü de o katmanın işidir.

Çizgeye giren tek kenar türü statik içe aktarım değildir. Dinamik içe aktarım bir kenardır
ama zamanı farklıdır; biçem dosyaları, görseller ve yazı tipleri de kenar üretir. Bunların
her biri parça oluşumunu değiştirir ve sonraki üç ders sırayla bunları ekler.

Son olarak çizge, bir modülün *tamamının* gerekli olduğunu söyler; içindeki hangi adın
kullanıldığını söylemez. Paylaşılan parçadaki `birim.js` dört ad dışa aktarıyor, bunların
ikisi hiç kullanılmıyor olabilir. Bu ayrımı görmek için çizgeyi modül düzeyinden ad
düzeyine indirmek gerekir.

## Özet

- Modül paketleme iki karardır: giriş noktalarından erişilen modül kümesinin çıkarılması
  ve bu kümenin çıktı parçalarına bölünmesi.
- Bağımlılık çizgesi giriş noktalarından dolaşımla kurulur; sonradan gezinmeli sıra,
  bağımlılıkları kendinden önce koyan değerlendirme sırasını verir.
- Giriş noktası sayısı oluşturma stratejisinden gelir; her girişin ayrı bir kapanışı, ayrı
  bir çıktı biçimi ve ayrı bir dış bağımlılık sınırı vardır.
- Modüller kullanım imzasına — kendilerini isteyen girişler kümesine — göre parçalara
  ayrılır; parçaların toplam boyutunun kaynağın toplamına eşit olması, hiçbir modülün
  kopyalanmadığının sağlamasıdır.
- Parça sınırı aynı zamanda bir arayüz sınırıdır: sınırı geçen adlar çıktıda korunmak
  zorundadır ve kapsam birleştirme parça içiyle sınırlıdır.

## Sonraki Adım

Bu dersteki parçalar giriş noktalarından doğdu ve hepsi ilk yüklemede gerekliydi. Oysa bir
uygulamanın her parçası aynı anda gerekmez: istasyon sorumlusunun paneli yalnız o panel
açıldığında, dışa aktarma ekranı yalnız o düğmeye basıldığında ister. Kaynakta bu ayrımı
işaretleyen bir yazım vardır — `import()` çağrısı bir kenarı çizgeye ekler ama onu
zamanında ayırır. Sonraki ders bu sınırı ele alır: dinamik içe aktarımdan doğan parçalar
nasıl hesaplanır, ilk yüklemede inen ile sonradan inen nasıl ayrılır ve parça sayısını
artırmanın hangi noktada zarara döndüğü nasıl ölçülür.
