Pengantar Peta Sumber JavaScript

Pernahkah Anda berharap dapat membuat kode sisi klien tetap dapat dibaca dan yang lebih penting dapat di-debug bahkan setelah Anda menggabungkan dan mengecilkannya, tanpa memengaruhi performa? Sekarang Anda dapat melakukannya melalui keajaiban peta sumber.

Source map adalah cara untuk memetakan file gabungan/yang diminifikasi kembali ke status yang belum di-build. Saat Anda membuat versi untuk produksi, bersama dengan minifikasi dan kombinasi file JavaScript, Anda akan menghasilkan source map yang menyimpan informasi tentang file asli Anda. Saat membuat kueri baris dan nomor kolom tertentu dalam JavaScript yang dihasilkan, Anda dapat melakukan pencarian di peta sumber yang menampilkan lokasi asli. Alat developer (saat ini build WebKit nightly, Google Chrome, atau Firefox 23+) dapat mengurai peta sumber secara otomatis dan membuatnya tampak seperti Anda menjalankan file yang tidak diminifikasi dan tidak digabungkan.

Demo ini memungkinkan Anda mengklik kanan di mana saja di textarea yang berisi sumber yang dihasilkan. Memilih "Get original location" akan membuat kueri peta sumber dengan meneruskan nomor baris dan kolom yang dihasilkan, serta menampilkan posisi dalam kode asli. Pastikan konsol terbuka sehingga Anda dapat melihat output.

Contoh penerapan library peta sumber JavaScript Mozilla.

Dunia nyata

Sebelum melihat implementasi Source Maps di dunia nyata berikut, pastikan Anda telah mengaktifkan fitur peta sumber di Chrome Canary atau WebKit nightly dengan mengklik roda gigi setelan di panel alat developer dan mencentang opsi "Aktifkan peta sumber".

Cara mengaktifkan peta sumber di alat developer WebKit.

Firefox 23+ memiliki peta sumber yang diaktifkan secara default di alat developer bawaan.

Cara mengaktifkan peta sumber di alat developer Firefox.

Mengapa saya harus memperhatikan peta sumber?

Saat ini, pemetaan sumber hanya berfungsi antara JavaScript yang tidak dikompresi/digabungkan ke JavaScript yang dikompresi/tidak digabungkan, tetapi masa depannya terlihat cerah dengan pembicaraan tentang bahasa yang dikompilasi menjadi JavaScript seperti CoffeeScript dan bahkan kemungkinan menambahkan dukungan untuk preprocessor CSS seperti SASS atau LESS.

Di masa mendatang, kita dapat dengan mudah menggunakan hampir semua bahasa seolah-olah didukung secara native di browser dengan peta sumber:

  • CoffeeScript
  • ECMAScript 6 dan yang lebih baru
  • SASS/LESS dan lainnya
  • Hampir semua bahasa yang dikompilasi menjadi JavaScript

Lihat screencast CoffeeScript yang di-debug dalam build eksperimental konsol Firefox ini:

Google Web Toolkit (GWT) baru-baru ini menambahkan dukungan untuk Source Maps. Ray Cromwell dari tim GWT membuat screencast yang luar biasa yang menunjukkan dukungan peta sumber.

Contoh lain yang telah saya buat menggunakan library Traceur Google yang memungkinkan Anda menulis ES6 (ECMAScript 6 atau Next) dan mengompilasikannya ke kode yang kompatibel dengan ES3. Compiler Traceur juga menghasilkan peta sumber. Lihat demo tentang trait dan class ES6 yang digunakan seperti didukung secara native di browser, berkat peta sumber.

Textarea dalam demo juga memungkinkan Anda menulis ES6 yang akan dikompilasi dengan cepat dan menghasilkan peta sumber serta kode ES3 yang setara.

Proses debug Traceur ES6 menggunakan peta sumber.

Demo: Menulis ES6, men-debug-nya, melihat cara kerja pemetaan sumber

Bagaimana cara kerja peta sumber?

Saat ini, satu-satunya compiler/minifier JavaScript yang memiliki dukungan untuk pembuatan peta sumber adalah compiler Closure. (Saya akan menjelaskan cara menggunakannya nanti.) Setelah Anda menggabungkan dan melakukan minifikasi JavaScript, file peta sumber akan ada di sampingnya.

Saat ini, compiler Closure tidak menambahkan komentar khusus di bagian akhir yang diperlukan untuk memberi tahu alat developer browser bahwa source map tersedia:

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

Hal ini memungkinkan alat developer memetakan panggilan kembali ke lokasinya dalam file sumber asli. Sebelumnya, pragma komentar adalah //@, tetapi karena beberapa masalah terkait hal tersebut dan komentar kompilasi bersyarat IE, keputusan dibuat untuk mengubahnya menjadi //#. Saat ini, Chrome Canary, WebKit Nightly, dan Firefox 24+ mendukung pragma komentar baru. Perubahan sintaksis ini juga memengaruhi sourceURL.

Jika tidak menyukai komentar aneh, Anda dapat menetapkan header khusus pada file JavaScript yang dikompilasi:

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

Seperti komentar, ini akan memberi tahu konsumen source map Anda tempat mencari source map yang terkait dengan file JavaScript. Header ini juga menghindari masalah referensi peta sumber dalam bahasa yang tidak mendukung komentar satu baris.

Contoh WebKit Devtools untuk source map aktif dan source map nonaktif.

File peta sumber hanya akan didownload jika Anda mengaktifkan peta sumber dan alat developer terbuka. Anda juga harus mengupload file asli agar alat developer dapat mereferensikan dan menampilkannya jika diperlukan.

Bagaimana cara membuat peta sumber?

Anda harus menggunakan compiler Closure untuk melakukan minifikasi, penggabungan, dan membuat peta sumber untuk file JavaScript. Perintahnya adalah sebagai berikut:

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

Dua flag perintah yang penting adalah --create_source_map dan --source_map_format. Hal ini diperlukan karena versi default adalah V2 dan kita hanya ingin menggunakan V3.

Anatomi peta sumber

Untuk lebih memahami source map, kita akan mengambil contoh kecil file source map yang akan dihasilkan oleh compiler Closure dan mempelajari lebih lanjut cara kerja bagian "pemetaan". Contoh berikut adalah variasi kecil dari contoh spesifikasi V3.

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

Di atas, Anda dapat melihat bahwa peta sumber adalah literal objek yang berisi banyak info menarik:

  • Nomor versi yang menjadi dasar peta sumber
  • Nama file kode yang dihasilkan (File produksi yang diminifikasi/digabungkan)
  • sourceRoot memungkinkan Anda menambahkan sumber dengan struktur folder - ini juga merupakan teknik menghemat ruang
  • sumber berisi semua nama file yang digabungkan
  • nama berisi semua nama variabel/metode yang muncul di seluruh kode Anda.
  • Terakhir, properti pemetaan adalah tempat keajaiban terjadi menggunakan nilai VLQ Base64. Penghematan ruang yang sebenarnya dilakukan di sini.

VLQ Base64 dan menjaga peta sumber tetap kecil

Awalnya, spesifikasi peta sumber memiliki output yang sangat panjang dari semua pemetaan dan menghasilkan peta sumber yang berukuran sekitar 10 kali ukuran kode yang dihasilkan. Versi kedua menguranginya sekitar 50% dan versi ketiga menguranginya lagi sebesar 50%, sehingga untuk file 133 kB, Anda akan mendapatkan peta sumber ~300 kB.

Jadi, bagaimana mereka mengurangi ukurannya sambil tetap mempertahankan pemetaan yang kompleks?

VLQ (Variable Length Quantity) digunakan bersama dengan mengenkode nilai menjadi nilai Base64. Properti pemetaan adalah string yang sangat besar. Dalam string ini terdapat titik koma (;) yang mewakili nomor baris dalam file yang dihasilkan. Dalam setiap baris, terdapat koma (,) yang mewakili setiap segmen dalam baris tersebut. Setiap segmen ini adalah 1, 4, atau 5 di kolom panjang variabel. Beberapa mungkin tampak lebih panjang, tetapi berisi bit lanjutan. Setiap segmen dibuat berdasarkan segmen sebelumnya, yang membantu mengurangi ukuran file karena setiap bit bersifat relatif terhadap segmen sebelumnya.

Perincian segmen dalam file JSON peta sumber.

Seperti yang disebutkan di atas, setiap segmen dapat memiliki panjang variabel 1, 4, atau 5. Diagram ini dianggap sebagai panjang variabel empat dengan satu bit lanjutan (g). Kita akan menguraikan segmen ini dan menunjukkan cara peta sumber menentukan lokasi asli.

Nilai yang ditampilkan di atas murni merupakan nilai yang didekode Base64, ada beberapa pemrosesan lagi untuk mendapatkan nilai sebenarnya. Setiap segmen biasanya mengerjakan lima hal:

  • Kolom yang dihasilkan
  • File asli tempat ini muncul
  • Nomor baris asli
  • Kolom asli
  • Dan, jika tersedia, nama asli

Tidak semua segmen memiliki nama, nama metode, atau argumen, sehingga segmen di seluruh segmen akan beralih antara empat dan lima panjang variabel. Nilai g dalam diagram segmen di atas disebut bit lanjutan yang memungkinkan pengoptimalan lebih lanjut di tahap decoding VLQ Base64. Bit kelanjutan memungkinkan Anda membuat nilai segmen sehingga Anda dapat menyimpan angka besar tanpa harus menyimpan angka besar, teknik menghemat ruang yang sangat cerdas yang berasal dari format midi.

Diagram AAgBC di atas setelah diproses lebih lanjut akan menampilkan 0, 0, 32, 16, 1 - 32 adalah bit lanjutan yang membantu membuat nilai 16 berikut. B yang didekode sepenuhnya dalam Base64 adalah 1. Jadi, nilai penting yang digunakan adalah 0, 0, 16, 1. Hal ini kemudian memberi tahu kita bahwa baris 1 (baris dihitung dengan titik koma) kolom 0 dari file yang dihasilkan dipetakan ke file 0 (array file 0 adalah foo.js), baris 16 di kolom 1.

Untuk menunjukkan cara segmen didekode, saya akan mereferensikan library JavaScript Peta Sumber Mozilla. Anda juga dapat melihat kode pemetaan sumber alat developer WebKit, yang juga ditulis dalam JavaScript.

Untuk memahami dengan benar cara mendapatkan nilai 16 dari B, kita perlu memiliki pemahaman dasar tentang operator bitwise dan cara kerja spesifikasi untuk pemetaan sumber. Digit sebelumnya, g, ditandai sebagai bit lanjutan dengan membandingkan digit (32) dan VLQ_CONTINUATION_BIT (biner 100000 atau 32) menggunakan operator AND (&) bitwise.

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

Tindakan ini akan menampilkan 1 di setiap posisi bit tempat keduanya muncul. Jadi, nilai 33 & 32 yang didekode Base64 akan menampilkan 32 karena hanya berbagi lokasi 32 bit seperti yang dapat Anda lihat pada diagram di atas. Hal ini kemudian akan meningkatkan nilai pergeseran bit sebesar 5 untuk setiap bit kelanjutan sebelumnya. Dalam kasus di atas, nilainya hanya digeser sebanyak 5 kali, sehingga menggeser 1 (B) ke kiri sebanyak 5.

1 <<../ 5 // 32

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

Nilai tersebut kemudian dikonversi dari nilai bertanda VLQ dengan menggeser bilangan (32) ke kanan satu posisi.

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

Jadi, itulah cara mengubah 1 menjadi 16. Proses ini mungkin tampak terlalu rumit, tetapi setelah jumlahnya mulai bertambah besar, proses ini akan menjadi lebih masuk akal.

Potensi masalah XSSI

Spesifikasi ini menyebutkan masalah penyertaan skrip lintas situs yang dapat muncul dari penggunaan peta sumber. Untuk mengurangi hal ini, sebaiknya tambahkan baris pertama peta sumber dengan ")]}" untuk sengaja membatalkan validasi JavaScript sehingga error sintaksis akan ditampilkan. Alat developer WebKit sudah dapat menangani hal ini.

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

Seperti yang ditunjukkan di atas, tiga karakter pertama dipotong untuk memeriksa apakah karakter tersebut cocok dengan error sintaksis dalam spesifikasi dan jika ya, hapus semua karakter yang mengarah ke entitas baris baru pertama (\n).

sourceURL dan displayName dalam tindakan: Fungsi eval dan anonim

Meskipun bukan bagian dari spesifikasi peta sumber, dua konvensi berikut memungkinkan Anda membuat pengembangan jadi jauh lebih mudah saat menggunakan eval dan fungsi anonim.

Helper pertama terlihat sangat mirip dengan properti //# sourceMappingURL dan sebenarnya telah disebutkan dalam spesifikasi source map V3. Dengan menyertakan komentar khusus berikut dalam kode Anda, yang akan dievaluasi, Anda dapat menamai evaluasi agar muncul sebagai nama yang lebih logis di alat pengembangan Anda. Lihat demo sederhana menggunakan compiler CoffeeScript:

Demo: Melihat kode eval() ditampilkan sebagai skrip melalui sourceURL

//# sourceURL=sqrt.coffee
Tampilan komentar khusus sourceURL di alat developer

Helper lainnya memungkinkan Anda memberi nama fungsi anonim menggunakan properti displayName yang tersedia di konteks fungsi anonim saat ini. Buat profil demo berikut untuk melihat cara kerja properti displayName.

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);
Menampilkan properti displayName.

Saat membuat profil kode dalam alat developer, properti displayName akan ditampilkan, bukan sesuatu seperti (anonymous). Namun, displayName hampir tidak digunakan lagi dan tidak akan masuk ke Chrome. Namun, semua harapan belum hilang dan proposal yang jauh lebih baik telah disarankan, yaitu debugName.

Saat ini, penamaan eval hanya tersedia di browser Firefox dan WebKit. Properti displayName hanya ada di WebKit nightlies.

Mari kita dukung bersama

Saat ini ada diskusi yang sangat panjang tentang dukungan peta sumber yang ditambahkan ke CoffeeScript. Lihat masalahnya dan tambahkan dukungan Anda untuk mendapatkan pembuatan peta sumber yang ditambahkan ke compiler CoffeeScript. Ini akan menjadi kemenangan besar bagi CoffeeScript dan para pengikut setianya.

UglifyJS juga memiliki masalah peta sumber yang juga harus Anda lihat.

Banyak alat yang menghasilkan peta sumber, termasuk compiler coffeescript. Saya menganggap ini sebagai poin yang tidak relevan sekarang.

Makin banyak alat yang tersedia untuk kita yang dapat menghasilkan peta sumber, makin baik hasilnya. Jadi, teruslah bertanya atau tambahkan dukungan peta sumber ke project open source favorit Anda.

Tidak sempurna

Saat ini, peta sumber tidak mendukung ekspresi smartwatch. Masalahnya adalah mencoba memeriksa argumen atau nama variabel dalam konteks eksekusi saat ini tidak akan menampilkan apa pun karena tidak benar-benar ada. Hal ini akan memerlukan semacam pemetaan balik untuk mencari nama sebenarnya dari argumen/variabel yang ingin Anda periksa dibandingkan dengan nama argumen/variabel yang sebenarnya dalam JavaScript yang dikompilasi.

Tentu saja, ini adalah masalah yang dapat dipecahkan dan dengan lebih banyak perhatian pada peta sumber, kita dapat mulai melihat beberapa fitur luar biasa dan stabilitas yang lebih baik.

Masalah

Baru-baru ini, jQuery 1.9 menambahkan dukungan untuk peta sumber saat ditayangkan dari CDN resmi. Hal ini juga menunjukkan bug aneh saat komentar kompilasi bersyarat IE (//@cc_on) digunakan sebelum jQuery dimuat. Sejak itu, telah ada commit untuk mengurangi hal ini dengan menggabungkan sourceMappingURL dalam komentar multibaris. Pelajaran yang dapat dipetik adalah jangan gunakan komentar bersyarat.

Masalah ini telah diselesaikan dengan mengubah sintaksis menjadi //#.

Alat dan referensi

Berikut beberapa referensi dan alat lebih lanjut yang harus Anda lihat:

  • Nick Fitzgerald memiliki fork UglifyJS dengan dukungan peta sumber
  • Paul Irish memiliki demo kecil yang praktis yang menampilkan peta sumber
  • Lihat changeset WebKit saat perubahan ini dihapus
  • Setelan perubahan juga menyertakan pengujian tata letak yang memulai seluruh artikel ini
  • Mozilla memiliki bug yang harus Anda ikuti terkait status peta sumber di konsol bawaan
  • Conrad Irwin telah menulis gem peta sumber yang sangat berguna untuk semua pengguna Ruby
  • Beberapa bacaan lebih lanjut tentang pemberian nama eval dan properti displayName
  • Anda dapat melihat sumber Closure Compilers untuk membuat peta sumber
  • Ada beberapa screenshot dan diskusi tentang dukungan untuk peta sumber GWT

Peta sumber adalah utilitas yang sangat canggih dalam rangkaian alat developer. Sangat berguna untuk dapat menjaga aplikasi web Anda tetap ramping, tetapi mudah di-debug. Ini juga merupakan alat pembelajaran yang sangat efektif bagi developer baru untuk melihat cara developer berpengalaman menyusun dan menulis aplikasi mereka tanpa harus menelusuri kode yang diminifikasi dan tidak dapat dibaca.

Tunggu apa lagi? Mulai buat peta sumber untuk semua project sekarang.