İçeriğe geç
academia.sh

Ders 05 / 14

Paket Kaydı ve Bağımlılıklar

Bildirim dosyasının alanları, çıplak belirtecin dizin ağacında aranması, dışa aktarım haritasıyla kapsülleme, bağımlılık türleri ve kilit dosyasının işlevi.

İçindekiler

Modül sistemleri konusundaki tüm belirteçler göreli dosya yollarıydı. Gerçek projelerde içe aktarımların çoğu bicim-yardimcisi gibi çıplak adlardır: ne uzantı taşırlar ne de dizin bilgisi.

Bu ad ile dosya arasındaki bağı kuran şey, dosya sistemindeki bir kural ve paketin kendi bildirimidir. Ders, önce paketin ne olduğunu tanımlar, sonra çıplak adın nasıl arandığını adım adım izler.

Bildirim Dosyası

Bir paket (package), kendi bildirimini taşıyan bir dizindir. Bildirim dosyası (manifest) paketin kimliğini, giriş noktalarını ve bağımlılıklarını tanımlar. Linux’a Giriş kursundaki paket yöneticisi kavramı dersinde tanıtılan üstverinin dil düzeyindeki karşılığıdır; alanların işlevi de benzerdir.

Ölçüm kütüphanesinin bildirimi — proje kökündeki package.json:

{
  "name": "metin-olcer",
  "version": "0.1.0",
  "type": "module",
  "exports": {
    ".": "./src/index.mjs",
    "./bolumleme": "./src/bolumleme.mjs"
  },
  "files": ["src"],
  "dependencies": {
    "bicim-yardimcisi": "^1.4.0"
  }
}

Alanların taşıdığı kararlar:

Alan Kararı
name Paketin kayıt içindeki benzersiz kimliği
version Bu yayının sürümü; kimliğin ikinci parçası
type Uzantısı .js olan dosyaların hangi modül sistemine göre okunacağı
exports Dışarıdan hangi yolların içe aktarılabileceği
files Yayımlanan arşive hangi dosyaların gireceği
dependencies Çalışması için gereken paketler ve kabul edilen sürüm aralıkları

Son üç alan tasarım kararıdır, teknik zorunluluk değildir. Hangi dosyanın dışarıdan görüleceğine, hangisinin yayımlanacağına ve hangi sürümlerin kabul edileceğine paketin yazarı karar verir.

Bağımlılık Eklemek

Ölçüm kütüphanesi, ortalama uzunluğu sabit basamakla yazdırmak için küçük bir yardımcıya bağlanır. Yardımcı paket, kendi bildirimini taşıyan bir dizindir — node_modules/bicim-yardimcisi/package.json:

{
  "name": "bicim-yardimcisi",
  "version": "1.4.0",
  "type": "module",
  "main": "index.mjs"
}
// dosya: node_modules/bicim-yardimcisi/index.mjs
export function ondalik(sayi, basamak = 2) {
  return sayi.toFixed(basamak);
}

Kütüphaneye eklenen rapor modülü bu paketi çıplak adıyla içe aktarır. Aşağıdaki dosyanın içe aktardığı src/index.mjs ve onun kullandığı src/bolumleme.mjs ile src/istatistik.mjs, bu konudaki ES Modülleri dersinde yazılmıştı; ölçüm kütüphanesi aynı ağaç üzerinde büyümeye devam ediyor:

// dosya: src/rapor.mjs — src/index.mjs, ölçüm işlevini dışa aktaran giriş noktasıdır
import { ondalik } from 'bicim-yardimcisi';
import { olc } from './index.mjs';

export function rapor(metin) {
  const olcum = olc(metin);
  return `${olcum.sozcukSayisi} sözcük, ortalama ${ondalik(olcum.ortalamaUzunluk)} karakter`;
}
// dosya: dene.mjs
import { rapor } from './src/rapor.mjs';

console.log(rapor('Modul kendi kapsamini tasir. Betik genel kapsamda calisir.'));
console.log(import.meta.resolve('bicim-yardimcisi').split('/node_modules/')[1]);
$ node dene.mjs
8 sözcük, ortalama 6.13 karakter
bicim-yardimcisi/index.mjs

Burada iki ayrı olgu vardır ve birbirini gerektirmez: paketin bildirimde yazılı olması ve paketin dizinde bulunması. Bildirimde yazılı olup dizinde bulunmayan bir paket çözümleme hatası verir:

// dosya: eksik-paket.mjs
import { ondalik } from 'olmayan-paket';

console.log(ondalik(1.5));
$ node eksik-paket.mjs 2>&1 | grep '^Error' | sed "s|$(pwd -P)/||g"
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'olmayan-paket' imported from eksik-paket.mjs

Ters durum daha sinsidir: dizinde bulunup bildirimde yazılı olmayan bir paket, yerelde sorunsuz çalışır. Çözümleme yalnızca dosya sistemine bakar, bildirimi denetlemez. Paket başka bir yere kurulduğunda o dosya orada bulunmayabilir ve hata ilk kullanımda ortaya çıkar. Bir bağımlılık kullanılıyorsa bildirimde de yazılı olmalıdır; bu kural, sonraki derslerde ele alınacak ağaç düzenlemelerinin altında yatan varsayımdır.

Çıplak Belirteç Nasıl Çözülür

Çıplak ad bir dosya yolu değildir; aranması gereken bir addır. Kural tek cümleyle yazılabilir: içe aktarmayı yapan dosyanın dizininden başlanır, her dizinde node_modules alt dizini içinde bu ada bakılır, bulunamazsa bir üst dizine çıkılır.

Kuralı doğrudan uygulayan bir betik, aramanın gerçekten böyle yürüdüğünü gösterir:

// dosya: src/derin/arama.mjs
import { dirname, join } from 'node:path';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

// Çıplak belirtecin arandığı dizinler: bulunulan dizinden köke doğru.
let dizin = dirname(fileURLToPath(import.meta.url));
while (true) {
  const aday = join(dizin, 'node_modules', 'bicim-yardimcisi');
  console.log(`${existsSync(aday) ? 'BULUNDU ' : 'yok     '} ${aday}`);
  if (existsSync(aday) || dizin === '/') break;
  dizin = dirname(dizin);
}

console.log('çalışma zamanının çözdüğü:', import.meta.resolve('bicim-yardimcisi'));
$ node src/derin/arama.mjs | sed "s|$(pwd -P)|.|"
yok      ./src/derin/node_modules/bicim-yardimcisi
yok      ./src/node_modules/bicim-yardimcisi
BULUNDU  ./node_modules/bicim-yardimcisi
çalışma zamanının çözdüğü: file://./node_modules/bicim-yardimcisi/index.mjs

sed çağrısı çıktıdaki mutlak yolu kısaltır; son satırdaki adres, o kısaltmanın adres biçimine uygulanmış hâlidir. Aramanın kendisi dört özelliği açığa çıkarır:

Konuma bağlıdır. Aynı ad, farklı dizinlerdeki iki dosya için farklı pakete çözülebilir. Bu, sonraki derslerde ele alınacak sürüm çakışması çözümünün temelidir.

Kayıt gerektirmez. Çözümleme tümüyle yereldir; ağa çıkılmaz. Ağ, paketler dizine yerleştirilirken kullanılır, çözümleme sırasında değil.

Paketin kendisi konuşur. Dizin bulunduktan sonra hangi dosyanın yükleneceğini paketin bildirimi söyler.

Belirlenimcidir. Aynı dosya ağacında aynı belirteç her zaman aynı dosyaya çözülür. Modül sistemlerinin ilk dersinde sayılan üçüncü güvence budur.

Dışa Aktarım Haritası

Paket dizini bulunduğunda, dışarıdan hangi yolların erişilebilir olduğunu bildirimdeki harita belirler. Haritada listelenmeyen bir yol, dizinde bulunsa bile içe aktarılamaz.

Aşağıdaki iki dosya, metin-olcer paketini kuran ayrı bir tüketici projededir ve o projenin dizininde çalıştırılır. Tüketicinin kendi bildirimi — tuketici/package.json:

{
  "name": "tuketici",
  "version": "1.0.0",
  "type": "module",
  "dependencies": { "metin-olcer": "^0.1.0" }
}
// dosya: tuketici/kullan.mjs
import { olc } from 'metin-olcer';
import { sozcuklereBol } from 'metin-olcer/bolumleme';

console.log(olc('Bir cumle. Iki cumle.').sozcukSayisi);
console.log(sozcuklereBol('bir iki uc').length);
$ node kullan.mjs
4
3
// dosya: tuketici/derin.mjs — haritada olmayan bir iç dosyaya erişmeye çalışır
import { ortalamaUzunluk } from 'metin-olcer/src/istatistik.mjs';

console.log(ortalamaUzunluk(['bir', 'iki']));
$ node derin.mjs 2>&1 | grep -E '^Error' | sed "s|$(pwd -P)/||g"
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './src/istatistik.mjs' is not defined by "exports" in node_modules/metin-olcer/package.json imported from derin.mjs

Bu, paket düzeyinde kapsüllemedir. Haritası olmayan bir pakette her iç dosya dışarıya açıktır; tüketiciler iç dosyalara bağlanır ve dosyaların yeniden düzenlenmesi kırıcı değişikliğe dönüşür. Harita, kütüphanenin hangi yollarının sözleşme olduğunu ve hangi dosyalarının serbestçe değiştirilebileceğini ayırır.

Bağımlılık Türleri

Bildirimde bağımlılıklar tek bir liste değildir; her liste farklı bir soruya yanıt verir.

Çalışma bağımlılıkları. Paket çalışırken gereken paketler. Paketi kuran herkes bunları da kurar.

Geliştirme bağımlılıkları. Yalnızca paketi geliştirirken gerekenler: test koşucusu, çözümleyici, derleme aracı. Paketi kullanan taraf bunları kurmaz. Ayrımın anlamı, tüketicinin indirdiği ağacın küçük kalmasıdır.

Eş bağımlılıklar. Paketin çalışması için gereken, ancak paketin kendisinin kurmaması gereken bağımlılıklar. Bir eklenti, genişlettiği kütüphanenin aynı örneğini kullanmak zorundaysa bu listeye yazılır; böylece kütüphaneyi kuran taraf uygulamanın kendisi olur ve ağaçta tek kopya bulunur. Yinelenen kopyanın neden sorun olduğu, bağımlılık ağacı dersinde gösterilecektir.

İsteğe bağlı bağımlılıklar. Kurulumu başarısız olsa da işlemi durdurmayan bağımlılıklar. Paket, eksikliğe karşı yedek bir yol taşımak zorundadır; taşımıyorsa bu liste yanlış kullanılmıştır.

Paket Kaydı ve Kilit Dosyası

Paket kaydı (package registry), ad ve sürüm çiftini bir arşive eşleyen bir dizindir. Yayımlanmış bir ad–sürüm çiftinin içeriği değiştirilemez kabul edilir: aynı çift her zaman aynı baytları vermelidir. Bu değişmezlik, sonraki tüm güvencelerin dayanağıdır.

Bildirimdeki ^1.4.0 gibi bir aralık, tek bir sürümü değil bir kümeyi gösterir. Kurulum zamanı geldiğinde kümeden bir seçim yapılır. İki farklı zamanda yapılan iki kurulum, küme büyüdüğü için farklı seçimler yapabilir; sonuç, aynı kaynaktan farklı çalışan iki ağaçtır.

Kilit dosyası (lock file) bu belirsizliği ortadan kaldırır. Kaydettiği üç bilgi vardır:

  1. Seçilen kesin sürüm. Aralık değil, tek bir sürüm numarası.
  2. Kaynak adresi. Arşivin nereden alındığı.
  3. Bütünlük özeti (integrity digest). Arşivin içeriğinin sağlaması.

Ayrıca ağacın biçimi — hangi paketin hangi düzeyde durduğu — kaydedilir. Kilit dosyası bulunduğunda kurulum bir arama değil, bir yeniden üretimdir: yazılı ağaç aynen kurulur.

Bu ayrım, Linux’a Giriş kursundaki bağımlılık çözümlemesinin kısıt problemi olarak tanımlanmasıyla aynı yerden gelir. Çözümleyicinin ürettiği çözüm, o çözümü yeniden hesaplamadan saklanabilir olmalıdır; kilit dosyası bunun kaydıdır. Bu nedenle sürüm denetimine eklenir: bir depoyu klonlayan ikinci geliştiricinin ve yapı sunucusunun aynı ağacı kurması buna bağlıdır.

Özet

  • Paket, bildirim dosyası taşıyan bir dizindir; bildirim kimliği, giriş noktalarını, yayımlanacak dosyaları ve bağımlılıkları tanımlar.
  • Çıplak belirteç, içe aktaran dosyanın dizininden köke doğru her düzeydeki node_modules dizininde aranır; çözümleme yereldir, konuma bağlıdır ve belirlenimcidir.
  • Dışa aktarım haritası paket düzeyinde kapsülleme sağlar; haritada olmayan iç dosyalar dışarıdan içe aktarılamaz.
  • Bağımlılık listeleri farklı sorulara yanıt verir: çalışma, geliştirme, eş ve isteğe bağlı.
  • Sürüm aralığı bir küme belirtir; kilit dosyası kümeden yapılan seçimi, kaynağı ve bütünlük özetini kaydederek kurulumu yeniden üretilebilir kılar.

Sonraki Adım

Aralıkların bir küme belirttiği söylendi, ama kümenin sınırlarını neyin çizdiği söylenmedi. ^1.4.0 yazan bir bildirimin 1.9.0 sürümünü kabul edip 2.0.0 sürümünü reddetmesinin arkasında, sürüm numarasının parçalarına anlam yükleyen bir sözleşme vardır. Sonraki ders bu sözleşmeyi ve karşılaştırma kurallarını çalışan kodla kurar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat