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 では、lint チェックに 2 つのパス(CSS ファイル用とインライン CSS 用)が必要でした。これは、すべての CSS がスタンドアロンの CSS ファイルに記述されていれば削除できるパフォーマンス オーバーヘッドでした。
  • 圧縮に関する課題インライン CSS を簡単に圧縮できなかったため、CSS は圧縮されませんでした。DevTools のリリースビルドのファイルサイズも、同じウェブ コンポーネントの複数のインスタンスによって重複する CSS が導入されたことにより、大きくなりました。

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

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

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

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

各要素に対して考えられるさまざまなソリューションを検討しました。以下でそれらの概要を説明します。

CSS ファイルのインポート

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

このような理由から、@import ステートメントと タグは DevTools に適していないと判断されました。DevTools の他の部分でインポートが均一化されず、Flash Of Unstyled Content(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 のフィクサ API を使用して確実に自動的に修正できるようになりました。

次のステップ

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

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

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

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

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