JavaScript Kaynak Haritaları'na giriş

Ryan Seddon

Müşteri tarafı kodunuzu birleştirip küçülttükten sonra bile performansı etkilemeden okunabilir ve daha da önemlisi hata ayıklama yapılabilir durumda tutmayı hiç dilediniz mi? Artık kaynak haritalar sayesinde bunu yapabilirsiniz.

Kaynak eşlemeleri, birleştirilmiş/küçültülmüş bir dosyayı derlenmemiş bir duruma geri eşlemenin bir yoludur. Üretim için derleme yaptığınızda, JavaScript dosyalarınızı küçültüp birleştirmenin yanı sıra orijinal dosyalarınızla ilgili bilgileri içeren bir kaynak haritası oluşturursunuz. Oluşturulan JavaScript'inizde belirli bir satır ve sütun numarasını sorgularken kaynak haritada orijinal konumu döndüren bir arama yapabilirsiniz. Geliştirici araçları (şu anda WebKit gecelik derlemeleri, Google Chrome veya Firefox 23 ve sonraki sürümler), kaynak haritayı otomatik olarak ayrıştırabilir ve sıkıştırılmamış ve birleştirilmemiş dosyalar çalıştırdığınızı gösterebilir.

Demo, oluşturulan kaynağı içeren metin alanında herhangi bir yeri sağ tıklamanıza olanak tanır. "Orijinal konumu al"ı seçtiğinizde, oluşturulan satır ve sütun numarasını ileterek kaynak haritayı sorgulanır ve orijinal koddaki konum döndürülür. Çıktıyı görebilmeniz için konsolunuzun açık olduğundan emin olun.

Mozilla JavaScript kaynak eşleme kitaplığının kullanım örneği.

Gerçek hayat

Kaynak haritalarının gerçek dünyadaki uygulamasını görüntülemeden önce, geliştirici araçları panelindeki ayarlar dişli çarkı simgesini tıklayıp "Kaynak haritalarını etkinleştir " seçeneğini işaretleyerek Chrome Canary veya WebKit nightly'de kaynak haritaları özelliğini etkinleştirdiğinizden emin olun.

WebKit geliştirici araçlarında kaynak eşlemelerini etkinleştirme.

Firefox 23 ve sonraki sürümlerde, yerleşik geliştirici araçlarında kaynak haritalar varsayılan olarak etkindir.

Firefox geliştirici araçlarında kaynak eşlemelerini etkinleştirme.

Kaynak haritalarını neden kullanmalıyım?

Şu anda kaynak eşleme yalnızca sıkıştırılmamış/birleştirilmiş JavaScript ile sıkıştırılmış/birleştirilmemiş JavaScript arasında çalışıyor ancak CoffeeScript gibi JavaScript'e derlenmiş diller ve hatta SASS veya LESS gibi CSS önişleyiciler için destek ekleme olasılığıyla gelecek parlak görünüyor.

Gelecekte, kaynak haritalarla neredeyse tüm dilleri tarayıcıda yerel olarak desteklenirmiş gibi kolayca kullanabileceğiz:

  • CoffeeScript
  • ECMAScript 6 ve sonraki sürümler
  • SASS/LESS ve diğerleri
  • JavaScript'e derlenen hemen hemen tüm diller

Firefox konsolunun deneysel bir sürümünde CoffeeScript'te hata ayıklama işleminin yapıldığı bu ekran video kaydına göz atın:

Google Web Toolkit'e (GWT) kısa süre önce kaynak eşlemeleri desteği eklendi. GWT ekibinden Ray Cromwell, kaynak harita desteğinin işleyişini gösteren harika bir ekran kaydı hazırladı.

Hazırladığım bir diğer örnekte, ES6 (ECMAScript 6 veya Next) yazıp ES3 uyumlu koda derlemenize olanak tanıyan Google'ın Traceur kitaplığı kullanılmaktadır. Traceur derleyicisi de bir kaynak haritası oluşturur. Kaynak haritası sayesinde ES6 özelliklerinin ve sınıflarının tarayıcıda yerel olarak desteklendikleri gibi kullanıldığı bu demo'ya göz atın.

Demodaki metin alanı, anında derlenecek ve bir kaynak haritası ile eşdeğer ES3 kodu oluşturacak ES6 yazmanıza da olanak tanır.

Kaynak haritaları kullanarak Traceur ES6 hata ayıklama.

Demo: ES6 yazma, hata ayıklama, kaynak eşlemeyi çalışırken görüntüleme

Kaynak eşlemesi nasıl çalışır?

Şu anda kaynak haritası oluşturma desteğine sahip olan tek JavaScript derleyici/küçültücü Closure derleyicidir. (Bu özelliği nasıl kullanacağınızı daha sonra açıklayacağım.) JavaScript'inizi birleştirip küçülttükten sonra, bunun yanında bir kaynak haritası dosyası bulunur.

Şu anda Closure derleyici, tarayıcı geliştirici araçlarına kaynak haritasının mevcut olduğunu belirtmek için gereken özel yorumu sonuna eklemiyor:

//# sourceMappingURL=/path/to/file.js.map

Bu sayede geliştirici araçları, çağrıları orijinal kaynak dosyalardaki konumlarıyla eşleyebilir. Önceden yorum pragması //@ idi ancak bu ve IE koşullu derleme yorumlarıyla ilgili bazı sorunlar nedeniyle //# olarak değiştirilmesi kararlaştırıldı. Şu anda Chrome Canary, WebKit Nightly ve Firefox 24 ve sonraki sürümler yeni yorum pragmasını desteklemektedir. Bu söz dizimi değişikliği, sourceURL'yi de etkiler.

Tuhaf yorum fikrini beğenmiyorsanız alternatif olarak derlenmiş JavaScript dosyanızda özel bir başlık ayarlayabilirsiniz:

X-SourceMap: /path/to/file.js.map

Yorum gibi bu da kaynak eşleme tüketicinize, JavaScript dosyasıyla ilişkili kaynak eşlemeyi nerede arayacağını söyler. Bu başlık, tek satırlık yorumları desteklemeyen dillerde kaynak haritalarına referans verme sorununu da çözer.

Kaynak haritalarının açık ve kapalı olduğu WebKit Geliştirici Araçları örneği.

Kaynak eşleme dosyası yalnızca kaynak eşlemelerinizi etkinleştirdiyseniz ve geliştirici araçlarınızı açık bıraktıysanız indirilir. Geliştirme araçlarının gerektiğinde bu dosyalara referans vermesi ve bunları görüntülemesi için orijinal dosyalarınızı da yüklemeniz gerekir.

Nasıl kaynak haritası oluşturabilirim?

JavaScript dosyalarınızı küçültmek, birleştirmek ve kaynak haritası oluşturmak için Closure derleyicisini kullanmanız gerekir. Komut şu şekildedir:

java -jar compiler.jar \
--js script.js \
--create_source_map ./script-min.js.map \
--source_map_format=V3 \
--js_output_file script-min.js

İki önemli komut işareti --create_source_map ve --source_map_format'dur. Varsayılan sürüm V2 olduğu ve yalnızca V3 ile çalışmak istediğimiz için bu gereklidir.

Bir kaynak eşlemesinin anatomisi

Kaynak haritasını daha iyi anlamak için Closure derleyicisi tarafından oluşturulacak bir kaynak haritası dosyasını küçük bir örnek olarak ele alıp "eşlemeler" bölümünün işleyiş şekliyle ilgili daha ayrıntılı bilgi vereceğiz. Aşağıdaki örnek, V3 spesifikasyonundaki örnekten biraz farklıdır.

{
    version : 3,
    file: "out.js",
    sourceRoot : "",
    sources: ["foo.js", "bar.js"],
    names: ["src", "maps", "are", "fun"],
    mappings: "AAgBC,SAAQ,CAAEA"
}

Yukarıda, kaynak eşlemenin çok sayıda yararlı bilgi içeren bir nesne ifadesi olduğunu görebilirsiniz:

  • Kaynak eşlemenin temel aldığı sürüm numarası
  • Oluşturulan kodun dosya adı (minileştirilmiş/birleştirilmiş üretim dosyanız)
  • sourceRoot, kaynaklara klasör yapısı eklemenize olanak tanır. Bu, yer tasarrufu sağlayan bir tekniktir.
  • sources, birleştirilen tüm dosya adlarını içerir
  • adlar, kodunuzda görünen tüm değişken/yöntem adlarını içerir.
  • Son olarak, Base64 VLQ değerlerinin kullanıldığı haritalar mülkünde sihrin gerçekleştiği yerdir. Gerçek alan tasarrufu burada yapılır.

Base64 VLQ ve kaynak haritayı küçük tutma

Başlangıçta kaynak eşleme spesifikasyonu, tüm eşlemelerin çok ayrıntılı bir çıktısına sahipti ve kaynak eşlemenin, oluşturulan kodun yaklaşık 10 katı boyutunda olmasına neden oldu. İkinci sürümde bu oran yaklaşık% 50, üçüncü sürümde ise %50 daha azaltıldı. Böylece 133 KB boyutunda bir dosya için yaklaşık 300 KB boyutunda bir kaynak haritası elde edersiniz.

Peki karmaşık eşlemeleri korurken boyutu nasıl küçülttüler?

Değerin Base64 değerine kodlanmasıyla birlikte VLQ (Değişken Uzunlukta Miktar) kullanılır. Eşlemeler mülkü çok büyük bir dizedir. Bu dize, oluşturulan dosyadaki bir satır numarasını temsil eden noktalı virgüller (;) içerir. Her satırda, ilgili satırdaki her segmenti temsil eden virgüller (,) bulunur. Bu segmentlerin her biri, değişken uzunluktaki alanlarda 1, 4 veya 5'tir. Bazıları daha uzun görünebilir ancak bunlar devam bitleri içerir. Her segment önceki segmentin üzerine inşa edilir. Bu sayede her bit önceki segmentlerine göre olduğundan dosya boyutu azaltılır.

Kaynak harita JSON dosyasındaki bir segmentin dökümü.

Yukarıda belirtildiği gibi, her segment değişken uzunlukta 1, 4 veya 5 olabilir. Bu diyagram, bir devam bit'i (g) içeren dört değişken uzunlukta olarak kabul edilir. Bu segmenti ayrıntılı olarak inceleyip kaynak haritanın orijinal konumu nasıl hesapladığını göstereceğiz.

Yukarıda gösterilen değerler yalnızca Base64 kodunun çözüldüğü değerlerdir. Gerçek değerlerini almak için daha fazla işlem yapılması gerekir. Her segment genellikle beş şeyi belirler:

  • Oluşturulan sütun
  • Bu içeriğin yer aldığı orijinal dosya
  • Orijinal satır numarası
  • Orijinal sütun
  • Varsa orijinal ad

Her segmentin adı, yöntem adı veya bağımsız değişkeni yoktur. Bu nedenle, segmentler dört ile beş değişken uzunluğu arasında geçiş yapar. Yukarıdaki segment diyagramındaki g değeri, devam bit'i olarak adlandırılır ve Base64 VLQ kod çözme aşamasında daha fazla optimizasyon sağlar. Devam bit'i, segment değerini temel almanıza olanak tanır. Böylece, büyük bir sayı depolamak zorunda kalmadan büyük sayılar depolayabilirsiniz. Bu, kökenleri midi biçiminde olan çok akıllı bir yer tasarrufu tekniğidir.

Yukarıdaki diyagram AAgBC daha fazla işlendikten sonra 0, 0, 32, 16, 1 değerini döndürür. 32, sonraki 16 değerini oluşturmaya yardımcı olan devam bitini temsil eder. B, Base64'te tamamen kodu çözülmüş olarak 1'dir. Dolayısıyla kullanılan önemli değerler 0, 0, 16, 1'dir. Bu, oluşturulan dosyanın 1. satırının (satırlar noktalı virgüllerle sayılır) 0. sütununun, 0. dosyayla (dosya dizisi 0, foo.js) 1. sütunun 16. satırıyla eşleştiğini gösterir.

Segmentlerin nasıl kodunun çözüldüğünü göstermek için Mozilla'nın Source Map JavaScript kitaplığına atıfta bulunacağım. JavaScript ile yazılmış WebKit geliştirici araçları kaynak eşleme koduna da bakabilirsiniz.

B'den 16 değerini nasıl aldığımızı doğru şekilde anlamak için bit operatörleri ve kaynak eşleme için spesifikasyonun işleyiş şekli hakkında temel düzeyde bilgi sahibi olmamız gerekir. Bitsel VE (&) operatörü kullanılarak hane (32) ve VLQ_CONTINUATION_BIT (ikili 100000 veya 32) karşılaştırılarak önceki hane (g) devam bit olarak işaretlenir.

32 & 32 = 32
// or
100000
|
|
V
100000

Bu, her ikisinin de göründüğü her bit konumunda 1 döndürür. Bu nedenle, yukarıdaki şemada görebileceğiniz gibi yalnızca 32 bit konumu paylaştıkları için Base64 kod çözme işlemi uygulanmış 33 & 32 değeri 32 döndürür. Bu işlem, önceki her devam biti için bit kaydırma değerini 5 artırır. Yukarıdaki durumda yalnızca bir kez 5'e kaydırılır. Yani 1 (B) 5'e sola kaydırılır.

1 <<../ 5 // 32

// Shift the bit by 5 spots
______
|    |
V    V
100001 = 100000 = 32

Ardından bu değer, sayı (32) bir basamak sağa kaydırılarak VLQ işaretli değerden dönüştürülür.

32 >> 1 // 16
//or
100000
|
 |
 V
010000 = 16

İşte bu kadar. 1'i 16'ya dönüştürme işlemini tamamladınız. Bu süreç çok karmaşık görünebilir ancak sayılar arttıkça daha mantıklı hale gelir.

Olası XSSI sorunları

Spesifikasyonda, kaynak haritanın kullanılmasından kaynaklanabilecek siteler arası komut dosyası dahil etme sorunlarından bahsedilmektedir. Bu sorunu azaltmak için kaynak haritanızın ilk satırına ")]} " eklemeniz önerilir. Böylece JavaScript'i kasıtlı olarak geçersiz kılarak söz dizimi hatası oluşturulur. WebKit geliştirici araçları bu işlemi zaten yapabilir.

if (response.slice(0, 3) === ")]}") {
    response = response.substring(response.indexOf('\n'));
}

Yukarıda gösterildiği gibi, ilk üç karakter, spesifikasyondaki söz dizimi hatasıyla eşleşip eşleşmediğini kontrol etmek için dilimlenir ve eşleşirse ilk yeni satır öğesine (\n) kadarki tüm karakterler kaldırılır.

sourceURL ve displayName'ün kullanımı: Değerlendirme ve anonim işlevler

Kaynak haritası spesifikasyonunun bir parçası olmasa da aşağıdaki iki kural, evals ve anonim işlevlerle çalışırken geliştirmeyi çok daha kolay hale getirmenize olanak tanır.

İlk yardımcı, //# sourceMappingURL mülküne çok benzer ve aslında kaynak harita V3 spesifikasyonunda belirtilmiştir. Kodunuza aşağıdaki özel yorumu ekleyerek (bu yorum değerlendirilir) değerlendirmeleri, geliştirici araçlarınızda daha mantıklı isimler olarak görünecek şekilde adlandırabilirsiniz. CoffeeScript derleyicisini kullanan basit bir demoya göz atın:

Demo: eval() kodunu sourceURL aracılığıyla komut dosyası olarak görme

//# sourceURL=sqrt.coffee
Geliştirici araçlarında sourceURL özel yorumunun görünümü

Diğer yardımcı, anonim işlevin mevcut bağlamında bulunan displayName mülkünü kullanarak anonim işlevleri adlandırmanıza olanak tanır. displayName mülkünün nasıl çalıştığını görmek için aşağıdaki demoyu profilleyin.

btns[0].addEventListener("click", function(e) {
    var fn = function() {
        console.log("You clicked button number: 1");
    };

    fn.displayName = "Anonymous function of button 1";

    return fn();
}, false);
displayName özelliğinin kullanımını gösteren resim.

Geliştirme araçları içinde kodunuzun profilini oluştururken (anonymous) gibi bir şey yerine displayName mülkü gösterilir. Ancak displayName artık kullanılmıyor ve Chrome'a eklenmeyecek. Ancak tüm umutlar tükenmiş değil. debugName adlı çok daha iyi bir öneri sunuldu.

eval adlandırması, bu makalenin yazıldığı tarih itibarıyla yalnızca Firefox ve WebKit tarayıcılarında kullanılabilir. displayName mülkü yalnızca WebKit gece sürümlerinde bulunur.

Birlikte harekete geçelim

Şu anda CoffeeScript'e kaynak eşleme desteği eklenmesi konusunda çok uzun bir tartışma sürüyor. Sorunun olduğu sayfaya gidip kaynak haritası oluşturma özelliğinin CoffeeScript derleyicisine eklenmesi için desteğinizi ekleyin. Bu, CoffeeScript ve onun sadık takipçileri için büyük bir kazanç olacak.

UglifyJS'de de göz atmanız gereken bir kaynak haritası sorunu var.

Coffeescript derleyicisi de dahil olmak üzere birçok araç kaynak haritası oluşturur. Bu konuyu artık tartışmaya açık bulmuyorum.

Kaynak haritası oluşturabileceğimiz ne kadar çok araç olursa o kadar iyi olur. Bu nedenle, en sevdiğiniz açık kaynak projesine kaynak haritası desteği ekleyin veya isteyin.

Mükemmel değil

Kaynak eşlemeleri şu anda izleme ifadeleri için destek sunmamaktadır. Sorun şudur: Geçerli yürütme bağlamında bir bağımsız değişkeni veya değişken adını incelemeye çalıştığınızda, gerçekten mevcut olmadığı için hiçbir şey döndürülmez. Bu durumda, incelemek istediğiniz bağımsız değişkenin/değişkenin gerçek adını, derlenmiş JavaScript'inizdeki gerçek bağımsız değişken/değişken adıyla karşılaştırmak için bir tür ters eşleme gerekir.

Bu elbette çözülebilir bir sorundur. Kaynak haritalara daha fazla önem vererek bazı harika özellikleri ve daha iyi kararlılığı görmeye başlayabiliriz.

Sorunlar

Yakın zamanda jQuery 1.9, resmi CDN'lerden yayınlanırken kaynak haritalar için destek ekledi. Ayrıca, jQuery yüklenmeden önce IE koşullu derleme yorumları ("//@cc_on") kullanıldığında tuhaf bir hata olduğunu da belirtti. Bu tarihten sonra, sourceMappingURL'yi çok satırlı bir yoruma sarmalayarak bu sorunu azaltmak için bir commit yapıldı. Koşullu yorum kullanmayın.

Bu sorun, söz dizimi //# olarak değiştirilerek çözüme ulaştırıldı.

Araçlar ve kaynaklar

İncelemeniz gereken bazı diğer kaynaklar ve araçlar:

  • Nick Fitzgerald, kaynak eşleme desteğine sahip bir UglifyJS çatalı oluşturdu.
  • Paul Irish'ın kaynak haritaları gösteren kullanışlı bir demo'su var.
  • Bu özelliğin kaldırıldığı WebKit değişiklik kaydına göz atın.
  • Değişiklik grubu, bu makalenin yazılmasını sağlayan bir düzen testi de içeriyordu.
  • Mozilla'da, yerleşik konsoldaki kaynak haritalarının durumunu takip etmeniz gereken bir hata var.
  • Conrad Irwin, tüm Ruby kullanıcıları için son derece kullanışlı bir source map gem yazdı.
  • eval adlandırma ve displayName mülkü hakkında daha fazla okuma
  • Kaynak haritası oluşturmak için Closure Compilers kaynağına göz atabilirsiniz.
  • Bazı ekran görüntüleri ve GWT kaynak haritaları için destekten bahsediliyor.

Kaynak haritaları, geliştiricilerin araç setinde bulunan çok güçlü bir yardımcı programdır. Web uygulamanızı basit ancak kolayca hata ayıklanabilir tutabilmek son derece faydalıdır. Ayrıca, yeni geliştiricilerin okunamayan sıkıştırılmış kodlar arasında gezinmek zorunda kalmadan deneyimli geliştiricilerin uygulamalarını nasıl yapılandırdığını ve yazdığını görmesi için çok güçlü bir öğrenme aracıdır.

Neyi bekliyorsunuz? Tüm projeler için kaynak haritalar oluşturmaya hemen başlayın.