← Blog

Me-render React ke console.log

Text.js menggambar aplikasi React di konsol DevTools sebagai satu panggilan console.log bergaya per frame.

Adam

Text.js me-render aplikasi React di konsol Chrome DevTools. Selain input tersembunyi, halaman ini tidak memiliki UI. Setiap frame aplikasi berupa satu panggilan console.log.

Konsol sebagai layar

console.log menerima string format. Setiap %c menerapkan CSS dalam argumen berikutnya ke teks yang mengikutinya, hingga %c berikutnya:

console.log("%c▼ Text.js%c ready", "color:#ededed;font-weight:700", "color:#45a557");

Gaya ini dapat mengatur warna, latar belakang, cetak tebal, cetak miring, dan garis bawah, sementara font konsol adalah monospace (Menlo di macOS). Dengan font monospace dan warna yang dapat berubah pada setiap rangkaian karakter, konsol berfungsi sebagai kisi sel karakter bergaya. Bingkai, panel, input, dan grafik semuanya dapat digambar di sana.

Frame adalah kisi sel

Renderer menyimpan setiap frame dalam CellBuffer. Karakter berada dalam satu typed array, warna teks dan latar belakang dalam dua array lainnya, serta atribut seperti cetak tebal dan garis bawah dalam array keempat. Array tersebut dialokasikan sekali untuk ukuran tertentu dan digunakan kembali pada setiap frame.

Untuk mencatat frame ke konsol, driver menelusuri setiap baris dan mengelompokkan sel bersebelahan dengan gaya yang sama menjadi rangkaian. Setiap rangkaian menjadi satu span %c, dan baris digabungkan dengan \n. Dasbor uji kami berukuran 100×32, dengan 3.200 sel, dikodekan menjadi sekitar 200 span.

Satu panggilan per frame

Mencatat setiap baris sebagai pesan tersendiri tidak berhasil. Setiap pesan mendapat bingkai dan tautan sumbernya sendiri di DevTools, dan DevTools meringkas pesan identik yang berurutan dengan penghitung pengulangan, sehingga dua baris tabel yang identik akan bergabung menjadi satu. Frame yang dicatat sebagai satu pesan tetap utuh.

Dua hal dalam teks perlu di-escape terlebih dahulu. Karakter literal % ditulis sebagai %%, sehingga teks seperti 100% tidak dapat memakai argumen gaya. DevTools juga mengubah URL menjadi tautan dan menggambarnya ulang dengan warnanya sendiri, sehingga :// dan www. diberi word joiner (U+2060), yang tidak memiliki lebar:

export function escapeConsoleText(text: string, allowLink = false): string {  let s = text;  if (s.includes("%")) s = s.replaceAll("%", "%%");  if (!allowLink) {    if (s.includes(LINK_SCHEME)) s = s.replaceAll(LINK_SCHEME, LINK_SCHEME_SAFE);    if (s.includes(WWW) || s.includes("WWW.")) s = s.replace(/www\./gi, (m) => `${m.slice(0, 3)}\u2060.`);  }  return s;}

Frame saat idle tidak mencatat apa pun

Setiap baris buffer dihitung hash-nya. Jika tidak ada hash yang berubah sejak frame terakhir, driver berhenti sebelum membangun string dan tidak menyentuh konsol. Aplikasi yang sedang menunggu input tidak membuat panggilan apa pun.

Ketika ada baris yang berubah, driver membersihkan konsol dan mencatat frame baru:

const { frame, dirtyRows } = this.encode(buffer, options);if (frame === null) return SKIPPED;if (this.clearEachFrame) target.clear();target.log(frame.format, ...frame.args);

Hanya baris yang hash-nya berubah yang dikodekan ulang. Baris lainnya menggunakan kembali span dari frame sebelumnya.

Input

Konsol tidak menerima penekanan tombol, sehingga halamanlah yang mengumpulkannya. <textarea> tersembunyi menerima penekanan tombol, penempelan teks, dan komposisi IME, lalu mengubahnya menjadi event tombol untuk aplikasi. Mengeklik halaman akan memfokuskan input tersebut.

Selanjutnya

Prototipe @textjs/core 0.0.x di npm memuat semuanya dalam satu paket. Text.js 0.1 memindahkan renderer yang dijelaskan di sini ke @textjs/core, reconciler React ke @textjs/react, dan menambahkan App Router.