DevTools での CSS インフラストラクチャのモダナイゼーション

DevTools アーキテクチャの更新: DevTools の CSS インフラストラクチャのモダナイゼーション

この記事は、DevTools のアーキテクチャとその構築方法に対する変更について説明する一連のブログ投稿の一部です。DevTools で CSS がどのように機能してきたか、そして、JavaScript ファイルで CSS を読み込むためのウェブ標準ソリューションに(最終的に)移行する準備として、DevTools で CSS をモダナイズした方法について説明します。

DevTools の CSS の以前の状態

DevTools では、CSS が 2 つの異なる方法で実装されています。1 つは DevTools のレガシー部分で使用される CSS ファイル用で、もう 1 つは DevTools で使用されているモダン ウェブ コンポーネント用です。

DevTools の CSS 実装は数年前に定義されたもので、現在は古くなっています。DevTools では module.json パターンが使用され続けており、これらのファイルを削除するために多大な労力がかかっています。これらのファイルを削除する最後の障害は、CSS ファイルの読み込みに使用される resources セクションです。

最終的に CSS モジュール スクリプトに変換できるさまざまなソリューションの候補を検討することにしました。目的は、レガシー システムによって生じた技術的負債を解消することと、CSS モジュール スクリプトへの移行プロセスを容易にすることでした。

DevTools に存在する CSS ファイルは、module.json ファイルを使用して読み込まれるため、「以前の」ファイルと見なされ、削除の対象となっています。すべての CSS ファイルは、CSS ファイルと同じディレクトリにある module.json ファイルの resources に登録する必要がありました。

残りの module.json ファイルの例:

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

これらの CSS ファイルは、パスからコンテンツへのマッピングとして、Root.Runtime.cachedResources というグローバル オブジェクトマップに格納されます。DevTools にスタイルを追加するには、読み込むファイルの正確なパスを指定して registerRequiredCSS を呼び出す必要があります。

registerRequiredCSS 呼び出しの例:

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

これにより、CSS ファイルの内容が取得され、appendStyle 関数を使用して <style> 要素としてページに挿入されます。

インライン スタイル要素を使用して CSS を追加する appendStyle 関数:

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);

モダン ウェブ コンポーネント(カスタム要素を使用)を導入した際、当初はコンポーネント ファイル自体でインライン <style> タグを使用して CSS を使用することを決定しました。これには独自の課題がありました。

  • 構文のハイライト表示がサポートされていない。インライン CSS の構文のハイライト表示を提供するプラグインは、.css ファイルで記述された CSS の構文のハイライト表示機能や自動補完機能ほど優れているわけではありません。
  • ビルドのパフォーマンス オーバーヘッド。インライン CSS の場合、CSS ファイル用とインライン CSS 用の 2 つのリンティング パスが必要でした。これは、すべての CSS がスタンドアロンの CSS ファイルに記述されていれば削除できるパフォーマンス オーバーヘッドでした。
  • 圧縮に関する課題インライン CSS を簡単に圧縮できなかったため、CSS は圧縮されませんでした。また、同じウェブ コンポーネントの複数のインスタンスで導入された重複する CSS により、DevTools のリリースビルドのファイルサイズも増加しました。

インターン プロジェクトの目標は、レガシー インフラストラクチャと DevTools で使用されている新しいウェブ コンポーネントの両方で機能する CSS インフラストラクチャのソリューションを見つけることでした。

考えられるソリューションの調査

この問題は 2 つの部分に分けることができます。

  • ビルドシステムが CSS ファイルを処理する方法の把握。
  • CSS ファイルが DevTools によってインポートされ、使用される仕組みを把握する。

各部分について、考えられる解決策を検討しました。以下に概要を示します。

CSS ファイルのインポート

TypeScript ファイルに CSS をインポートして使用する目的は、ウェブ標準にできるだけ忠実に従い、DevTools 全体で一貫性を保ち、HTML で CSS の重複を回避することでした。また、変更を CSS モジュール スクリプトなどの新しいウェブ プラットフォーム標準に移行できるソリューションを選択することも必要でした。

このような理由から、@import ステートメントと タグは DevTools に適していないと判断されました。他の DevTools のインポートと統一されず、スタイル設定されていないコンテンツのフラッシュ(FOUC)が発生します。CSS モジュール スクリプトへの移行は、インポートを明示的に追加し、<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>`

@import または <link> を使用した考えられる解決策。

代わりに、CSS ファイルを CSSStyleSheet オブジェクトとしてインポートし、adoptedStyleSheets プロパティを使用して Shadow DOM(DevTools では数年前から Shadow DOM を使用)に追加する方法を探しました。

バンドル オプション

TypeScript ファイルで簡単に操作できるように、CSS ファイルを CSSStyleSheet オブジェクトに変換する方法が必要でした。Google では、この変換を行うバンドルツールとして、Rollupwebpack の両方を検討しました。DevTools では、本番環境ビルドですでに Rollup が使用されていますが、いずれかのバンドルを本番環境ビルドに追加すると、現在のビルドシステムでパフォーマンスの問題が発生する可能性があります。Chromium の GN ビルドシステムとの統合によりバンドルが難しくなるため、バンドラは現在の Chromium ビルドシステムと適切に統合されない傾向があります。

代わりに、現在の GN ビルドシステムを使用してこの変換を行う方法を検討しました。

DevTools で CSS を使用する新しいインフラストラクチャ

新しいソリューションでは、adoptedStyleSheets を使用して特定の Shadow DOM にスタイルを追加し、GN ビルドシステムを使用して、document または ShadowRoot で採用できる CSSStyleSheet オブジェクトを生成します。

// 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];
  }
}

adoptedStyleSheets を使用すると、次のようなメリットがあります。

  • 最新のウェブ標準になりつつある
  • CSS の重複を防ぐ
  • スタイルは Shadow DOM にのみ適用されるため、CSS ファイル内のクラス名や ID セレクタの重複による問題を回避できます。
  • CSS モジュール スクリプトやインポート アサーションなどの将来のウェブ標準への移行が容易

この解決策の唯一の注意点は、import ステートメントで .css.js ファイルをインポートする必要があることです。GN がビルド中に CSS ファイルを生成できるように、generate_css_js_files.js スクリプトを作成しました。ビルドシステムはすべての CSS ファイルを処理し、デフォルトで CSSStyleSheet オブジェクトをエクスポートする JavaScript ファイルに変換します。CSS ファイルをインポートして簡単に適用できるため、これは便利です。さらに、本番環境ビルドを簡単に圧縮してファイルサイズを節約することもできます。

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;

スクリプトから生成された iconButton.css.js の例。

ESLint ルールを使用したレガシー コードの移行

ウェブ コンポーネントは手動で簡単に移行できましたが、registerRequiredCSS の従来の使用方法を移行するプロセスはより複雑でした。以前のスタイルを登録する 2 つの主な関数は registerRequiredCSScreateShadowRootWithCoreStyles でした。これらの呼び出しを移行する手順は非常に機械的であるため、ESLint ルールを使用して修正を適用し、レガシー コードを自動的に移行することにしました。DevTools では、DevTools コードベースに固有のカスタムルールがすでに使用されています。これは、ESLint がすでにコードを解析して抽象構文ツリー(AST)に変換しているため、役に立ちました。AST)を取得し、CSS の登録呼び出しだった特定の呼び出しノードをクエリできます。

移行 ESLint ルールの作成時に直面した最大の問題は、エッジケースをキャプチャすることでした。キャプチャする価値のあるエッジケースと手動で移行する必要があるエッジケースのバランスを適切に取ることが重要でした。また、インポートされた .css.js ファイルがビルドシステムによって自動生成されていない場合は、ユーザーに通知できるようにしました。これにより、実行時にファイルが見つからないエラーを防ぐことができます。

移行に ESLint ルールを使用することの欠点の 1 つは、システムで必要な GN ビルドファイルを変更できなかったことです。これらの変更は、各ディレクトリでユーザーが手動で行う必要がありました。これはより多くの作業が必要でしたが、インポートされるすべての .css.js ファイルが実際にビルドシステムによって生成されていることを確認するのに適した方法でした。

全体的に、この移行に ESLint ルールを使用したことは非常に有益でした。レガシー コードを新しいインフラストラクチャに迅速に移行できただけでなく、AST をすぐに利用できるおかげで、ルールで複数のエッジケースを処理し、ESLint の fixer API を使用してそれらを自動的に確実に修正できました。

次のステップ

これまでに、Chromium DevTools のすべてのウェブ コンポーネントが、インライン スタイルではなく新しい CSS インフラストラクチャを使用するように移行されています。registerRequiredCSS の従来の用途のほとんども、新しいシステムを使用するように移行されています。残す作業は、できるだけ多くの module.json ファイルを削除し、この現在のインフラストラクチャを移行して、将来的に CSS モジュール スクリプトを実装することだけです。

プレビュー チャネルをダウンロードする

デフォルトの開発用ブラウザとして Chrome の CanaryDevBeta のいずれかを使用することを検討してください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたりできます。また、ユーザーよりも早くサイトの問題を見つけることもできます。

Chrome DevTools チームに問い合わせる

次のオプションを使用して、DevTools の新機能、アップデート、その他のトピックについて話し合います。