Ders 12 / 14
Kaynak Haritaları
Üretilmiş konumdan kaynak konuma eşleme, harita dosyasının alanları, eşleme dizgisinin kodlanması, haritanın elle üretilmesi ve üretim ortamında yayımlama kararı.
İçindekiler
Önceki ders, aktarımın kaynak ile çıktıyı birbirinden ayırdığı noktada durdu. Çıktının satırları kaymış, yapıları değişmiş olabilir. Bir hata oluştuğunda yığıt izindeki satır numarası üretilmiş dosyaya aittir ve geliştiricinin elindeki kaynakla eşleşmez.
Kaynak haritası (source map), bu iki dosya arasındaki konum eşlemesini taşıyan bir veri dosyasıdır. Kod değildir, çalıştırılmaz; yalnızca “üretilmiş dosyanın şu satır ve sütunu, kaynak dosyanın şu satır ve sütununa karşılık gelir” bilgisini tutar.
Eşlemenin Konusu
Eşleme, dosya değil konum düzeyindedir. Bir üretilmiş konum verildiğinde, karşılığı olan kaynak dosya, satır ve sütun bulunur. Bu ayrıntı düzeyi gereklidir: tek bir üretilmiş satır, birden çok kaynak satırdan gelmiş olabilir.
Eşlemeyi kullanan üç işlem vardır: yığıt izlerinin kaynağa çevrilmesi, hata ayıklayıcıda kesme noktalarının kaynak dosyaya konması ve başarım ölçümlerinde sürelerin kaynak işlevlere yazılması. Üçü de Eşzamansız JavaScript kursundaki hata ayıklama ve profilleme derslerinde tanıtılan araçların üretilmiş kodla çalışabilmesini sağlar.
Harita Dosyasının Alanları
Harita, JSON biçiminde bir nesnedir:
| Alan | İçerik |
|---|---|
version |
Biçimin sürüm numarası |
file |
Haritanın ait olduğu üretilmiş dosyanın adı |
sources |
Kaynak dosyaların yolları |
sourcesContent |
Kaynak dosyaların içeriği; isteğe bağlı |
names |
Yeniden adlandırılmış tanımlayıcıların özgün adları |
mappings |
Konum eşlemelerinin kodlanmış hâli |
sourcesContent alanı, kaynağın harita dosyasının içine gömülmesini sağlar. Gömülüyse
araç kaynağı ayrıca aramaz; gömülü değilse sources yollarından okumaya çalışır ve
bulamazsa kaynağı gösteremez.
Üretilmiş dosya, haritasına son satırındaki bir yorumla bağlanır. Bu yorum, çalışma zamanına ve hata ayıklama araçlarına haritayı nerede arayacaklarını söyler.
Eşleme Dizgisinin Yapısı
mappings alanı, tüm eşlemeleri tek bir dizgide taşır. Yapı üç düzeylidir:
- Noktalı virgül, üretilmiş satırları ayırır. Ardışık iki noktalı virgül, o satırda hiç eşleme olmadığını gösterir.
- Virgül, aynı satırdaki bölümleri (segment) ayırır.
- Her bölüm, dört veya beş sayıdan oluşur: üretilmiş sütun, kaynak dosya dizini, kaynak satır, kaynak sütun ve varsa ad dizini.
Sayılar doğrudan yazılmaz. İki sıkıştırma uygulanır: her sayı bir öncekine göre fark olarak tutulur ve farklar değişken uzunluklu nicelik (variable-length quantity) kodlamasıyla yazılır. Kodlama, sayıyı beş bitlik gruplara böler, en düşük bite işaret bitini koyar ve devam eden grupları altıncı bitle işaretler; her grup 64 karakterlik bir abeceden bir harfle yazılır.
Sonuç, insan tarafından okunamayan ama küçük bir dizgidir. Bin satırlık bir dosyanın eşlemesi, ayrıntısız yazıldığında yüz binlerce karakter tutar; bu kodlamayla onda birine iner.
Haritayı Üretmek
Kodlamayı anlamanın en kısa yolu, çalışan bir üretici yazmaktır. Aşağıdaki dönüşüm bilinçli olarak yalındır: kaynağın başına üç satırlık bir başlık ekler. Dönüşüm sonunda her kaynak satırı üç satır aşağı kaymıştır ve bu kayma harita olmadan izlenemez.
// dosya: kaynak/olcer.mjs export function olc(metin) { if (typeof metin !== 'string') { throw new TypeError('metin bekleniyordu'); } return metin.split(/\s+/).length; } console.log(olc('bir iki uc')); console.log(olc(42));
// dosya: uret.mjs import { mkdir, readFile, writeFile } from 'node:fs/promises'; const ABECE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // Değişken uzunluklu nicelik kodlaması: işaret biti en düşük bitte, 5 bitlik gruplar. function vlq(sayi) { let deger = sayi < 0 ? (-sayi << 1) | 1 : sayi << 1; let cikti = ''; do { let parca = deger & 0b11111; deger >>>= 5; if (deger > 0) parca |= 0b100000; cikti += ABECE[parca]; } while (deger > 0); return cikti; } await mkdir('cikti', { recursive: true }); const kaynakAdi = 'kaynak/olcer.mjs'; const kaynak = await readFile(kaynakAdi, 'utf8'); const satirlar = kaynak.split('\n'); const banner = ['/* üretilmiş dosya */', '/* elle düzenlemeyin */', '']; const cikti = [...banner, ...satirlar]; // Banner satırlarının eşlemesi yok; her üretilmiş satır, kaynaktaki karşılığına bakar. const bolumler = banner.map(() => ''); let oncekiKaynakSatiri = 0; for (let i = 0; i < satirlar.length; i += 1) { const girinti = satirlar[i].length - satirlar[i].trimStart().length; bolumler.push([vlq(girinti), vlq(0), vlq(i - oncekiKaynakSatiri), vlq(girinti)].join('')); oncekiKaynakSatiri = i; } const harita = { version: 3, file: 'olcer.mjs', sources: ['../kaynak/olcer.mjs'], sourcesContent: [kaynak], names: [], mappings: bolumler.join(';'), }; await writeFile('cikti/olcer.mjs', cikti.join('\n') + '\n//# sourceMappingURL=olcer.mjs.map\n'); await writeFile('cikti/olcer.mjs.map', JSON.stringify(harita)); console.log('mappings:', harita.mappings.slice(0, 60));
$ node uret.mjs mappings: ;;;AAAA;AACA;EACE;IACI;EACE;EACE;AACA;AACA;AACA;AACA;AACA
Dizginin başındaki üç noktalı virgül, başlık satırlarının eşlemesiz olduğunu söyler.
Dördüncü satırın bölümü AAAA — dört sıfır: üretilmiş sütun 0, kaynak dosya 0, kaynak
satır farkı 0, kaynak sütun 0. Beşinci satırın bölümü AACA biçimindedir: üçüncü alan
artık sıfır değil, kaynak satırın bir arttığını gösteren bir fark. Girintili satırlarda
ilk ve son alanlar da sıfırdan çıkar; EACE bölümü, üretilmiş sütun 2 ve kaynak sütun 2
demektir.
Üretilen dosyanın ilk ve son satırları, bağlantının nasıl kurulduğunu gösterir:
$ head -5 cikti/olcer.mjs
/* üretilmiş dosya */
/* elle düzenlemeyin */
// dosya: kaynak/olcer.mjs
export function olc(metin) {
$ tail -2 cikti/olcer.mjs
//# sourceMappingURL=olcer.mjs.map
Geri İzleme
Üretilmiş dosya çalıştırıldığında hata, üretilmiş konumu bildirir:
$ node cikti/olcer.mjs 2>&1 | grep 'at olc' | sed "s|file://||; s|$(pwd -P)/||"
at olc (cikti/olcer.mjs:7:11)
Aynı dosya, çalışma zamanının kaynak haritası desteği açılarak çalıştırıldığında:
$ node --enable-source-maps cikti/olcer.mjs 2>&1 | grep 'at olc' | sed "s|$(pwd -P)/||"
at olc (kaynak/olcer.mjs:4:7)
Yığıt izi kaynak dosyayı ve kaynaktaki satırı gösterdi. Satır numarası kesindir; sütun
numarası yaklaşıktır, çünkü bu üretici satır başına tek bir bölüm yazdı. Gerçek araçlar
her sözcük birimi için bölüm yazar ve sütun da kesinleşir. sed çağrıları yalnızca
çıktıdaki mutlak dizin yolunu kısaltır.
Zincirleme ve Bütünlük
Bir kaynak birden çok dönüşümden geçebilir: önce aktarılır, sonra paketlenir, sonra küçültülür. Her adım kendi haritasını üretir ve sonuncusu bir öncekine bakar. Araçların bu zinciri birleştirip tek bir harita üretmesi gerekir; birleştirmeyen bir adım, zincirin tümünü kullanılamaz hâle getirir.
Bu, aşağıdaki gözlemi açıklar: yığıt izi bazen kaynağa değil, ara bir çıktıya götürür. Neden genellikle zincirin bir halkasında haritanın taşınmamış olmasıdır.
Üretim Ortamında Harita
Harita yayımlanırsa, kaynak kodun tamamı da yayımlanmış olur — özellikle sourcesContent
alanı doluysa. Karar üç seçenek arasındadır:
Yayımlamamak. Harita üretilir, çıktının yanında yayımlanmaz. Kaynak korunur; buna karşılık üretimden gelen yığıt izleri elle çözülmelidir.
Yayımlamak. Harita çıktının yanında durur. Hata ayıklama en kolay hâlindedir; kaynak kod herkese açıktır.
Ayrı bir yere yayımlamak. Harita, hata toplama sistemine yüklenir; çıktının yanında bulunmaz. Yığıt izleri o sistemde çözülür, kaynak dışarıya açılmaz. Ek altyapı gerektirir.
Seçim kaynağın gizliliğine bağlıdır. Kaynak zaten açıksa harita yayımlamamak için bir neden yoktur. Kapalıysa üçüncü seçenek, ikisinin arasındaki dengeyi kurar. Hangi seçenek kullanılırsa kullanılsın, haritanın üretilmesi vazgeçilmezdir: üretilmeyen bir harita sonradan elde edilemez.
Özet
- Kaynak haritası, üretilmiş dosyadaki konumları kaynak dosyadaki konumlara eşleyen bir veri dosyasıdır; kod içermez ve çalıştırılmaz.
- Eşlemeler tek bir dizgide, satır ve bölüm ayraçlarıyla ve fark alınıp değişken uzunluklu nicelik kodlamasıyla sıkıştırılarak tutulur.
- Üretilmiş dosya haritasına son satırındaki bir yorumla bağlanır; kaynak metin haritaya gömülebilir.
- Çalışma zamanının kaynak haritası desteği açıldığında yığıt izleri kaynak konumları gösterir; sütun kesinliği yazılan bölüm sıklığına bağlıdır.
- Çok adımlı dönüşümlerde her adım haritasını bir öncekiyle birleştirmelidir; birleştirmeyen bir adım zinciri kırar.
- Haritayı yayımlamak kaynağı da yayımlamaktır; ayrı bir hata toplama sistemine yükleme seçeneği bu ödünleşimi dengeler.
Sonraki Adım
Buraya kadarki araçlar kodu dönüştürdü. Bir başka araç sınıfı kodu dönüştürmez, yalnızca okur ve kural kümesine göre yargılar: hangi yazımlar kusur belirtisidir, hangi biçim tutarsızdır. Sonraki ders, çalıştırmadan yapılan bu denetimi ve biçimlendirmenin ondan neden ayrı tutulduğunu ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.