Apa itu MagicString dan apakah garis-garis ini begitu ajaib?

MagicString adalah perpustakaan yang sedikit dikenal. Meskipun demikian, ini memecahkan salah satu masalah yang paling mendesak - mengubah kode sumber menggunakan strukturnya (AST - pohon sintaksis abstrak).

Pada artikel ini kita akan belajar apa itu MagicString dan apakah garis-garis ini benar-benar "ajaib". Ini akan membantu kita memahami artikel selanjutnya di mana saya akan menjelaskan bagaimana kita berhasil menerjemahkan dokumentasi Angular dengan begitu cepat, dan bagaimana itu akan membantu dalam pembuatan penerjemah universal untuk kedua Penurunan harga dan file format lainnya.





2 minggu yang lalu saya merilis dokumentasi berbahasa Rusia Angular ( angular24.ru ). Selama waktu ini, 35 masalah ditambahkan dengan koreksi dalam teks dan 2 permintaan tarik. Saya sangat meragukan bahwa sistem di mana Anda memilih teks, menawarkan terjemahan dan secara otomatis masalah pada GitHub akan berfungsi. Tapi crowdsourcing berhasil! :) Anda dapat mempelajari lebih lanjut tentang ini dari artikel ini .

Setelah rilis, salah satu pertanyaan yang paling sering diajukan adalah: "Mengapa?" Pertanyaannya benar sekali, tetapi untuk menjawabnya, Anda harus terlebih dahulu memahami apa itu MagicString, bagaimana cara kerjanya, dan bagaimana itu berguna.

Misalkan kita memiliki kode sumber sederhana:

const a = 1;

Kami ingin mengganti const dengan var . Solusi paling sederhana adalah mengganti const dengan var dengan String.prototype.replace yang biasa . Dan untuk tugas ini, kemungkinan besar ini adalah solusi yang paling benar. Tetapi bagaimana jika kita perlu mengganti const dengan var hanya dalam lingkup global? Tapi jangan menggantinya di dalam fungsi? Anda dapat, tentu saja, datang dengan keteraturan yang lebih kompleks atau menulis kode rumit, tetapi ada cara yang lebih scalable dan fleksibel.

Kita dapat menggunakan parser untuk mendapatkan AST - Pohon Sintaks Abstrak. Jika Anda tertarik dengan apa itu AST, pergilah ke astexplorer.net . Intinya, ini adalah pohon yang secara akurat menampilkan struktur kode Anda.

Lebih jauh, setiap Node dalam AST ini memiliki awaldan mengakhiri indeks yang menunjukkan posisi elemen-elemen ini dalam kode sumber. Mengetahui koordinat ini dan memiliki struktur dokumen, kita dapat membuat penggantian dan permutasi yang kompleks dengan menjaga struktur dokumen.



Biasanya, penggantian dilakukan menggunakan desain pola pengunjung dan beberapa pembantu , yang biasanya membungkus diri mereka dalam satu perpustakaan, yang dapat disebut "API transformator". Setiap parser memiliki "transformator API" sendiri.

Perpustakaan seperti itu sangat mudah digunakan, tetapi mereka memiliki beberapa masalah. Salah satunya adalah kinerja.

Karena setiap (well, hampir setiap) Node di AST tree berisi koordinat, ketika mengubah 1 node kita sering perlu memperbarui koordinat untuk sisa tree. Di sini Anda dapat berdebat bahwa Anda dapat melakukannya dengan sedikit darah - jangan perbarui koordinat di mana-mana, tetapi cukup render AST kembali ke teks berdasarkan struktur. Tetapi ada 1 masalah: Anda akan segera kehilangan format teks asli, yang bertentangan dengan tugas kami - untuk mengganti const dengan var di baris yang ada. Bahkan, kami mendapatkan baris baru dengan pemformatan baru. Dan jika ini bukan masalah untuk sebuah baris kecil, bayangkan file 1000 baris di mana pemformatan telah benar-benar berubah karena mengganti const dengan var . Kedengarannya tidak terlalu bagus.



Dan inilah keajaiban MagicString. Saya pertama kali mengetahui keberadaan mereka dari proyek Rich Harris, yang disebut butternut . Butternut adalah minifier JavaScript. Butternutt diklaim 3 kali lebih cepat dari UglifyJS dan 10-15 kali lebih cepat dari Babili . Saya akan berlari ke depan dan mengatakan bahwa proyek itu ditutupi dengan baskom tembaga setidaknya 3 tahun yang lalu. Tetapi bahkan kemudian, saya tertarik dengan rahasia kinerjanya. Itu adalah MagicString.

Mari kita lihat bekerja dengan MagicString:

var MagicString = require( 'magic-string' );
var s = new MagicString( 'const a = 1; const b = 2;' );

s.overwrite( 0, 5, 'var' );
s.toString(); // 'var a = 1; const b = 2;'

//  

Algoritma MagicString sangat sederhana: kita membungkus string asli dalam suatu objek di mana kita tidak secara langsung menerapkan perubahan pada string, tetapi menambahkan koordinat dan apa yang perlu dilakukan ke dalam array untuk masa depan. Dan hanya ketika seseorang ingin mendapatkan baris yang dihasilkan, kita mulai 1 hingga 1 untuk melakukan operasi akumulasi. Sebagai contoh:

  1. Kami mengganti const dengan var, mulai dari indeks 0 dan berakhir pada indeks 5
  2. Kita tahu bahwa semua penggantian berikutnya harus memiliki indeks kurang dari 2 (var kurang dari const oleh 2 karakter, satu baris lebih pendek)
  3. Kami memperbarui koordinat semua operasi
  4. Kami menerapkan operasi berikut, dll.




Semuanya terlihat sangat sederhana. Tetapi mengapa MagicString lebih cepat? Jawabannya cukup sederhana: jumlah operasi yang kami lakukan di pohon kami jauh lebih sedikit daripada jumlah node AST. Belum lagi jumlah memori yang diperlukan untuk AST dan fakta bahwa Tree Traversal (bepergian melalui pohon) bukan operasi bebas, tetapi O (n + m)



Dan jika saya siap menunggu setengah jam lagi? Dan inilah plus kedua MagicString. Setiap parser menciptakan API sendiri untuk transformasi. Dan ini masih sangat bagus, jika ada API seperti itu (tidak setiap parser menyediakannya), sangat sering kita dibiarkan tanpa kemampuan untuk mengganti teks sumber menggunakan AST secara normal. Tetapi MagicString adalah API universal tunggal untuk mengubah string sumber. Tidak masalah parser atau kombinasi parser mana yang Anda gunakan. Dengan MagicString, Anda dapat bekerja sama baiknya dengan AST apa pun.



Saya harap Anda tertarik dengan MagicString. Pada artikel selanjutnya saya akan berbicara tentang MagicString ganda dan bagaimana membuat penerjemah universal dari dokumen Markdown.

Berlangganan saluran Telegram saya @obenjiro_notes dan Twitter obenjiroagar tidak ketinggalan artikel berikut tentang topik dan banyak hal menarik lainnya.

All Articles