Memodernisasi infrastruktur CSS di DevTools

Pembaruan arsitektur DevTools: Memodernisasi infrastruktur CSS di DevTools

Postingan ini adalah bagian dari serangkaian postingan blog yang menjelaskan perubahan yang kami lakukan pada arsitektur DevTools dan cara membangunnya. Kami akan menjelaskan cara kerja CSS di DevTools secara historis dan cara kami memodernisasi CSS di DevTools sebagai persiapan untuk (pada akhirnya) bermigrasi ke solusi standar web untuk memuat CSS dalam file JavaScript.

Status CSS Sebelumnya di DevTools

DevTools menerapkan CSS dengan dua cara berbeda: satu untuk file CSS yang digunakan di bagian lama DevTools, satu untuk komponen web modern yang digunakan di DevTools.

Implementasi CSS di DevTools ditentukan bertahun-tahun yang lalu dan sekarang sudah tidak berlaku lagi. DevTools tetap menggunakan pola module.json dan telah dilakukan upaya besar untuk menghapus file ini. Pemblokir terakhir untuk penghapusan file ini adalah bagian resources, yang digunakan untuk memuat file CSS.

Kami ingin meluangkan waktu untuk mempelajari berbagai solusi potensial yang pada akhirnya dapat berubah menjadi Skrip Modul CSS. Tujuannya adalah untuk menghilangkan utang teknis yang disebabkan oleh sistem lama, serta mempermudah proses migrasi ke Skrip Modul CSS.

Setiap file CSS yang ada di DevTools dianggap sebagai 'lama' karena dimuat menggunakan file module.json, yang sedang dalam proses penghapusan. Semua file CSS harus dicantumkan di bagian resources dalam file module.json di direktori yang sama dengan file CSS.

Contoh file module.json yang tersisa:

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

File CSS ini kemudian akan mengisi peta objek global yang disebut Root.Runtime.cachedResources sebagai pemetaan dari jalur ke kontennya. Untuk menambahkan gaya ke DevTools, Anda harus memanggil registerRequiredCSS dengan jalur yang sama persis ke file yang ingin Anda muat.

Contoh panggilan registerRequiredCSS:

constructor() {
  
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  
}

Tindakan ini akan mengambil konten file CSS dan memasukkannya sebagai elemen <style> ke dalam halaman menggunakan fungsi appendStyle:.

Fungsi appendStyle yang menambahkan CSS menggunakan elemen gaya inline:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

Saat memperkenalkan komponen web modern (menggunakan elemen kustom), kami awalnya memutuskan untuk menggunakan CSS melalui tag <style> inline di file komponen itu sendiri. Hal ini menimbulkan tantangan tersendiri:

  • Kurangnya dukungan sorotan sintaksis. Plugin yang menyediakan penyorotan sintaksis untuk CSS inline cenderung tidak sebagus fitur penyorotan sintaksis dan pelengkapan otomatis untuk CSS yang ditulis dalam file .css.
  • Membuat overhead performa. CSS inline juga berarti bahwa diperlukan dua penerusan untuk analisis lint: satu untuk file CSS dan satu untuk CSS inline. Ini adalah overhead performa yang dapat kita hapus jika semua CSS ditulis dalam file CSS mandiri.
  • Tantangan dalam minifikasi. CSS inline tidak dapat diminifikasi dengan mudah, sehingga tidak ada CSS yang diminifikasi. Ukuran file build rilis DevTools juga ditingkatkan oleh CSS duplikat yang diperkenalkan oleh beberapa instance komponen web yang sama.

Tujuan project magang saya adalah menemukan solusi untuk infrastruktur CSS yang berfungsi dengan infrastruktur lama dan komponen web baru yang digunakan di DevTools.

Meneliti solusi potensial

Masalah ini dapat dibagi menjadi dua bagian yang berbeda:

  • Mencari tahu bagaimana sistem build menangani file CSS.
  • Mencari tahu cara file CSS diimpor dan digunakan oleh DevTools.

Kami telah melihat berbagai solusi potensial untuk setiap bagian dan solusi ini diuraikan di bawah.

Mengimpor File CSS

Tujuan mengimpor dan memanfaatkan CSS dalam file TypeScript adalah untuk selalu mematuhi standar web, menerapkan konsistensi di seluruh DevTools, dan menghindari CSS duplikat di HTML kita. Kami juga ingin dapat memilih solusi yang memungkinkan migrasi perubahan ke standar platform web baru, seperti Skrip Modul CSS.

Karena alasan ini, pernyataan @import dan tag tampaknya tidak cocok untuk DevTools. Hal ini tidak akan seragam dengan impor di seluruh DevTools dan akan menghasilkan Flash Of Unstyled Content (FOUC). Migrasi ke Skrip Modul CSS akan lebih sulit karena impor harus ditambahkan secara eksplisit dan ditangani secara berbeda dengan tag <link>.

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

Solusi yang mungkin menggunakan @import atau <link>.

Sebagai gantinya, kita memilih untuk menemukan cara mengimpor file CSS sebagai objek CSSStyleSheet sehingga kita dapat menambahkannya ke Shadow Dom (DevTools telah menggunakan Shadow DOM selama beberapa tahun) menggunakan properti adoptedStyleSheets-nya.

Opsi paket

Kita memerlukan cara untuk mengonversi file CSS menjadi objek CSSStyleSheet sehingga kita dapat dengan mudah memanipulasinya dalam file TypeScript. Kami mempertimbangkan Rollup dan webpack sebagai pemaket potensial yang akan melakukan transformasi ini untuk kami. DevTools sudah menggunakan Rollup dalam build produksinya, tetapi menambahkan salah satu bundler ke build produksi dapat memiliki potensi masalah performa saat menggunakan sistem build kami saat ini. Integrasi kami dengan sistem build GN Chromium mempersulit proses bundling sehingga penggabungan cenderung tidak terintegrasi dengan baik dengan sistem build Chromium saat ini.

Sebagai gantinya, kami mempelajari opsi untuk menggunakan sistem build GN saat ini untuk melakukan transformasi ini.

Infrastruktur baru penggunaan CSS di DevTools

Solusi baru ini melibatkan penggunaan adoptedStyleSheets untuk menambahkan gaya ke Shadow DOM tertentu saat menggunakan sistem build GN untuk menghasilkan objek CSSStyleSheet yang dapat diadopsi oleh document atau ShadowRoot.

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

Menggunakan adoptedStyleSheets memiliki beberapa manfaat, termasuk:

  • Teknologi ini sedang dalam proses untuk menjadi standar web modern
  • Mencegah CSS duplikat
  • Menerapkan gaya hanya ke Shadow DOM dan ini menghindari masalah yang disebabkan oleh nama class atau pemilih ID duplikat dalam file CSS
  • Mudah dimigrasikan ke standar web mendatang seperti Skrip Modul CSS dan Import Assertion

Satu-satunya pengecualian untuk solusi ini adalah pernyataan import mengharuskan file .css.js diimpor. Agar GN dapat membuat file CSS selama proses build, kita menulis skrip generate_css_js_files.js. Sistem build kini memproses setiap file CSS dan mengubahnya menjadi file JavaScript yang secara default mengekspor objek CSSStyleSheet. Ini bagus karena kita bisa mengimpor {i>file<i} CSS dan mengadopsinya dengan mudah. Selain itu, sekarang kita juga dapat meminifikasi build produksi dengan mudah, sehingga menghemat ukuran file:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

Contoh iconButton.css.js yang dihasilkan dari skrip.

Memigrasikan kode lama menggunakan aturan ESLint

Meskipun komponen web dapat dimigrasikan secara manual dengan mudah, proses untuk memigrasikan penggunaan lama registerRequiredCSS lebih rumit. Dua fungsi utama yang mendaftarkan gaya lama adalah registerRequiredCSS dan createShadowRootWithCoreStyles. Kami memutuskan bahwa karena langkah-langkah untuk memigrasikan panggilan ini cukup mekanis, kami dapat menggunakan aturan ESLint untuk menerapkan perbaikan dan memigrasikan kode lama secara otomatis. DevTools sudah menggunakan sejumlah aturan kustom khusus untuk codebase DevTools. Hal ini berguna karena ESLint telah mengurai kode menjadi Abstract Syntax Tree(singkatan dari AST) dan kita dapat membuat kueri node panggilan tertentu yang merupakan panggilan untuk mendaftarkan CSS.

Masalah terbesar yang kami hadapi saat menulis Aturan ESLint migrasi adalah menangkap kasus ekstrem. Kami ingin memastikan bahwa kami mendapatkan keseimbangan yang tepat antara mengetahui kasus ekstrem mana yang layak diambil dan mana yang harus dimigrasikan secara manual. Kita juga ingin memastikan bahwa kita dapat memberi tahu pengguna saat file .css.js yang diimpor tidak dibuat secara otomatis oleh sistem build karena hal ini akan mencegah error file tidak ditemukan selama runtime.

Salah satu kelemahan menggunakan aturan ESLint untuk migrasi adalah kita tidak dapat mengubah file build GN yang diperlukan dalam sistem. Perubahan ini harus dilakukan secara manual oleh pengguna di setiap direktori. Meskipun memerlukan lebih banyak pekerjaan, ini adalah cara yang baik untuk mengonfirmasi bahwa setiap file .css.js yang diimpor sebenarnya dihasilkan oleh sistem build.

Secara keseluruhan, penggunaan aturan ESLint untuk migrasi ini sangat membantu karena kami dapat dengan cepat memigrasikan kode lama ke infrastruktur baru. Selain itu, dengan memiliki AST yang siap digunakan, kami juga dapat menangani beberapa kasus ekstrem dalam aturan dan memperbaikinya secara otomatis dengan andal menggunakan API Fixer ESLint.

Apa selanjutnya?

Sejauh ini, semua komponen web di Chromium DevTools telah dimigrasikan untuk menggunakan infrastruktur CSS baru, bukan menggunakan gaya inline. Sebagian besar penggunaan lama registerRequiredCSS juga telah dimigrasikan untuk menggunakan sistem baru. Yang tersisa hanyalah menghapus file module.json sebanyak mungkin, lalu memigrasikan infrastruktur saat ini untuk menerapkan Skrip Modul CSS di masa mendatang.

Mendownload saluran pratinjau

Pertimbangkan untuk menggunakan Chrome Canary, Dev, atau Beta sebagai browser pengembangan default Anda. Saluran pratinjau ini memberi Anda akses ke fitur DevTools terbaru, memungkinkan Anda menguji API platform web yang mutakhir, dan membantu menemukan masalah di situs sebelum pengguna melakukannya.

Hubungi tim Chrome DevTools

Gunakan opsi berikut untuk membahas fitur baru, update, atau hal lain yang terkait dengan DevTools.