`@custom-variant`のネスト時に発生する無限ループを解消

tailwindlabs/tailwindcss

@custom-variant内で@variantを使用し、その@variantが別の@custom-variantを参照する場合に発生していた無限ループが解消されました。この修正により、カスタムバリアントのネストが安全に動作するようになります。

背景

@custom-variantのネスト機能において、特定の構成で無限ループが発生する問題が報告されていました(#19618)。以下のような設定でb:バリアントを使用すると、アプリケーションがクラッシュする現象です:

@custom-variant a {
  @slot;
}

@custom-variant b {
  @variant a {
    @slot;
  }
}

この問題の原因は、@custom-variantが内部の@slotをASTノードに置換する際、置換後のノードに再び@slotが含まれていると、その@slotを再度置換しようとすることで無限ループに陥る設計にありました。

技術的な変更

packages/tailwindcss/src/variants.tssubstituteAtSlot関数が修正され、@slotの置換後にそのノードのウォーク処理をスキップするようになりました。

変更前:

walk(ast, (node) => {
  if (node.kind === 'at-rule' && node.name === '@slot') {
    return WalkAction.Replace(nodes)
  }
})

変更後:

walk(ast, (node) => {
  if (node.kind === 'at-rule' && node.name === '@slot') {
    return WalkAction.ReplaceSkip(nodes)
  }
})

WalkAction.ReplaceからWalkAction.ReplaceSkipへの変更により、置換されたノード群のトラバースがスキップされます。この結果、最終的なASTには@slotノードが残りますが、次の@custom-variantの処理時に置換されるため問題になりません。

設計判断

置換後のノードをスキップする方式が採用されました。

PR内の説明では、置換直後のノードに含まれる@slotは後続の処理で解決されることが明記されています。これは、複数の@custom-variantが連鎖する場合でも、各段階で必要な置換のみを実行し、残りは次の段階に委ねる設計です。

一度の走査で全ての@slotを解決しようとするのではなく、段階的な処理を前提とすることで、ネストの深さに関わらず安定した動作を実現しています。

まとめ

本PRは、ASTウォーク時のアクションをWalkAction.ReplaceからWalkAction.ReplaceSkipに変更する1行の修正で無限ループを解消しています。この変更により、@custom-variantのネスト構造が任意の深さで安全に動作し、a:b:flexb:a:flexのような複雑なバリアント組み合わせも正しく機能します。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

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

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```typescript:packages/tailwindcss/src/variants.ts)およびGitHubのIssue/PRへのリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

ASTやビルドプロセスの内部実装に関する話題であり、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

各セクションが総論→各論で構成され、各段落はトピックセンテンスで始まっています。段落の長さも適切で、可読性が高いです。

Diff内容との照合 ✓ PASS

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

`packages/tailwindcss/src/variants.ts`における`WalkAction.Replace`から`WalkAction.ReplaceSkip`への変更が、Diff内容と正確に一致しています。ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

AST、`@slot`、`@custom-variant`、`WalkAction`などの技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

無限ループの原因(`@slot`の再置換)と解決策(`ReplaceSkip`による置換後ノードのスキップ)に関する説明が、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張(問題の発生源、解決策、設計判断の背景)が、PRのDescriptionやDiff内容によって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#19633)と関連するIssue番号(#19618)が正確に記載されています。

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

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

記事のタイトル「`@custom-variant`のネスト時に発生する無限ループを解消」は、PRのタイトルと内容を的確に要約しています。

外部知識の正確性 ✓ PASS

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

PR情報に含まれない外部知識(バージョン情報、リリース予定など)の追記はなく、提供された情報に忠実です。

時間表現の正確性 ✓ PASS

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

「発生していました」「解消されました」といった過去形の表現が、完了した変更を正確に反映しており、時間表現に問題はありません。