コードハイライト処理をスコープ化・冪等化し、単一要素ハイライトをエクスポート

basecamp/lexxy

@37signals/lexxy のコードハイライトヘルパーが、スコープ指定と冪等性ガードを追加し、さらに単一 <pre> 要素向け API を公開しました。これにより、ページ内の多数のコードブロックに対する不要な再走査が排除され、レンダリングパフォーマンスが大幅に改善されます。

背景

従来の highlightCode() はドキュメント全体を走査し、すべての <pre data-language> 要素に対して Prism を再適用していました。ページ内に多数のコードブロックが存在し、同一ブロックが複数回ハイライトされると、コントローラ数 × コードブロック数 の二乗オーダーの計算が走り、メインスレッドが数秒間フリーズするケースが報告されていました。さらに、highlightElement が内部で innerHTML を書き換えるたびに、ホストアプリの MutationObserver が駆動され、余計なコストが累積しました。

この問題は、Turbo の morph や Stimulus の connect() が繰り返し呼び出された際に顕在化し、スケーラビリティのボトルネックとなっていました。

技術的な変更

スコープ指定: highlightCode にオプション引数 root = document を導入し、デフォルトは従来通り全体走査です。指定されたルート以下の要素だけを対象にすることで、highlightCode(this.element) のようにコンポーネント単位で安全に呼び出せます。

@@
-export function highlightCode() {
-  const elements = document.querySelectorAll("pre[data-language]")
+export function highlightCode(root = document) {
+  const elements = root.querySelectorAll("pre[data-language]:not([data-highlighted])")

冪等性ガード: <pre>data-highlighted="true" 属性が付与されている場合はスキップし、再実行時の重複処理を回避します。highlightElement 内でも同属性チェックを行い、二度目以降は即リターンします。

@@
-export function highlightElement(preElement) {
+export function highlightElement(preElement) {
+  if (preElement.dataset.highlighted === "true") return
@@
-  preElement.dataset.highlighted = "true"
+  preElement.dataset.highlighted = "true"

単一要素 API の公開: highlightElement をモジュールエクスポートに加え、src/index.js からも再エクスポートしました。呼び出し側が既に <pre> 参照を保持している場合、余計な querySelectorAll が不要になります。

@@
-export { highlightCode } from "./helpers/code_highlighting_helper"
+export { highlightCode, highlightElement } from "./helpers/code_highlighting_helper"

テスト追加: test/javascript/unit/helpers/code_highlighting_helper.test.js にスコープ制限、冪等性、単一要素ハイライトの 3 つのユニットテストを追加し、全 30 件のヘルパーテストがパスすることを確認しました。テストは afterEach フックで DOM をリセットし、各ケースが相互に影響しないようにしています。

+test("highlightCode only walks pre elements within the given root", () => {
+  // ...
+})
+
+test("highlightCode is idempotent — already-highlighted blocks are skipped", () => {
+  // ...
+})
+
+test("highlightElement highlights a single pre element", () => {
+  // ...
+})

バージョン更新: lib/lexxy/version.rbpackage.json0.9.15.alpha.4 に更新され、リリースノート相当の変更として扱われます。

設計判断

ルート引数によるスコープ化 は、既存の API 互換性を保ちつつ機能拡張できる最小限の侵襲的変更です。新規キーを導入せず、デフォルト引数で全体走査を維持することで、既存コードの動作をそのまま保ちます。これは、広範囲に使用されている highlightCode() の呼び出し箇所を一括置換せずに済む設計です。

data-highlighted 属性の利用 は、DOM に状態を持たせるシンプルな手法です。外部フラグやキャッシュ構造を導入しないため、メモリ使用量が増えず、また CSS セレクタと組み合わせて :not([data-highlighted]) による高速フィルタリングが可能です。

highlightElement のエクスポート は、呼び出し側が要素参照を既に所有しているケース(例: Stimulus の element プロパティ)でのオーバーヘッド削減を意図しています。API の分離により、責務が明確化され、テストやドキュメントの記述が容易になります。

これらの判断は、バックワードコンパチビリティパフォーマンス最適化 の二軸でバランスを取った結果であり、コードベース全体への影響を最小限に抑えています。

まとめ

highlightCode にルートスコープと冪等性ガードを追加し、highlightElement を公開したことで、コードハイライト処理は局所的かつ一度だけ実行されるようになりました。結果として、ページロード時のメインスレッド負荷が大幅に低減し、開発者は既存 API をそのまま利用しつつ、必要に応じて細かい制御が可能です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
9bb07bcf

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文がタイトル直下にあり、背景(Context)、技術的な変更(Technical Detail)、設計判断(任意)、まとめ(Conclusion)という構成が明確に示されています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは `言語:ファイルパス` 形式で正しく記述され、PRリンクは正しいGitHubリンク記法で記載されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

専門的なエンジニア向けの内容で、初心者向けの冗長な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論の流れが保たれ、段落はトピックセンテンスで始まり、1段落1トピック、6文以下に収まっています。空行で区切りも適切です。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供されたDiffと一致しており、ファイル名・変更内容・行番号すべてが正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている技術用語(冪等性、スコープ指定、data-highlighted など)はPRで示されたものと一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明はPRの要旨と合致しており、主張は根拠があり論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全ての主張はPRのタイトル、説明、Diffで裏付けられており、捏造や推測は見当たりません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

バージョン番号(0.9.15.alpha.4)やテスト数(30 件)など、数値情報はDiffと一致しています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルはPRの主旨を日本語で的確に表現しており、内容と一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事はPR情報以外の外部知識(LTS、リリース日程等)を挿入していません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現に関する記述はなく、PRの記載と矛盾する表現もありません。