SelectorParserにおける疑似要素セレクタの二重コロン解析バグを修正

tailwindlabs/tailwindcss

::before などの疑似要素セレクタが :::before に誤って分割されていたパースバグを修正しました。後続の機能開発を妨げていた問題を、独立したPRとして切り出して解決しています。

背景

別機能の開発中に発覚した SelectorParser のバグが、今回の修正の発端です。.foo::before のような疑似要素セレクタをパースした際、期待される2ノード構成ではなく、余分な : ノードを含む3ノード構成で返されていました。

これまでの実装では ::before の1文字目のコロンが単独の : ノードとして切り出され、残りの ::before が別ノードになる挙動を取っていました。PR本文によれば、この誤りはこれまで実用上の問題にはなっていなかったものの、後続のPRで実際の障害になることが判明したため、独立したPRとして先行修正されています。

技術的な変更

selector-parser.tsCOLON ケース処理に、二重コロンを検出して buffer に結合する早期リターンを追加しました。

変更前は、コロン文字に到達するたびに直前の buffer を新しいセレクタノードとして切り出す処理が走っていました。.foo::before の場合、最初のコロンで .foo が切り出され、次の文字処理で : 単独のノードが生成され、さらに before の手前で ::before が切り出されるという3段階の分割が発生していました。

変更後:

case FULL_STOP:
case COLON:
case NUMBER_SIGN: {
  if (currentChar === COLON && buffer === ':') {
    buffer += input[i]
    break
  }

  // Handle everything before the combinator as a selector and
  // start a new selector
  if (buffer.length > 0) {

追加されたガード節は「現在の文字がコロン(:)であり、かつ buffer の内容がすでに : である」という条件を評価します。この条件が真の場合、2文字目のコロンを buffer に追記して即座に break し、ノード分割処理をスキップします。結果として :: がひとまとまりで保持され、続く before と結合されて ::before という正しい単一ノードが生成されます。

テストケースも selector-parser.test.ts に追加され、.foo::before が2ノード構成(.foo::before)で返されることが明示的に検証されています。

設計判断

最小限の変更で問題を局所的に修正する方針が取られています。

修正箇所はパーサーの COLON ケースに5行を追加するのみです。buffer の状態を確認するという既存の設計パターンを活かし、二重コロンを特別扱いするロジックを既存の分岐の入口に挿入しています。セレクタ分割の主処理には手を加えず、その手前で二重コロンを透過的に処理することで、変更範囲が最小化されています。

また、PR本文に「後続PRのために分離して修正した」と明記されている点も注目に値します。バグ修正と機能追加を混在させず、変更の目的を単一に保つ方針が読み取れます。

まとめ

バッファの状態チェックという5行の早期リターンで、疑似要素セレクタの誤分割という構造的なバグを解消しました。これまでは問題が顕在化していなかったものの、セレクタノードの構造に依存する処理を追加しようとしたことで問題が発覚した経緯は、パーサー出力の正確性が下流の機能品質に直結することを示す好例といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
07966241

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の3部構成が明確に適用されており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```typescript:path/to/file.ts)とGitHubのPRリンク記法([#20090](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

「SelectorParser」「ノード」「ガード節」などの専門用語を適切に使用しており、専門知識を持つエンジニアという対象読者に適合した内容になっています。

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

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

各セクションが総論→各論の構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されています。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiffの内容と完全に一致しており、ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「疑似要素セレクタ」「パーサー」「バッファ」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

「二重コロンを検出してbufferに結合する」というコード変更の解説は、実際のDiffの内容と完全に一致しており、技術的に正確です。

事実の突合 ✓ PASS

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

「後続PRのために分離して修正した」という背景や、パーサーの誤動作の具体的な内容は、すべてPRのDescriptionで裏付けられており、ハルシネーションは認められません。

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

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

PR番号(#20090)やファイル名(selector-parser.ts)などの固有名詞はすべて正確に記載されています。

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

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

記事のタイトルは、PRのタイトル「Fix parsing bug in SelectorParser」の内容をより具体的に、かつ正確に表現しており、主題と一致しています。

外部知識の正確性 ✓ PASS

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

記事の内容はすべて提供されたPR情報とDiffに基づいており、バージョンサポート状況やリリース予定など、PR外の知識を持ち込んでいません。

時間表現の正確性 ✓ PASS

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

「これまでは問題にならなかった」「後続のPR」といった時間的な表現は、PR Descriptionの「So far this hasn't been a real issue」「a follow-up PR」と正確に一致しています。