`--value(…)` を省略した関数ユーティリティ定義を無効化するバリデーション追加

tailwindlabs/tailwindcss

関数ユーティリティ(@utility foo-*)において --value(…) の使用を必須とするバリデーションが追加されました。これにより、値が実際に使われていないまま無限の候補クラスが生成されてしまうバグを防ぎます。

背景

@utility foo-* のような関数ユーティリティ定義では、--value(…) を使ってワイルドカード部分の値をプロパティ値に反映させることが意図された使い方です。しかしこれまでの実装では、--value(…) を記述しない定義もバリデーションエラーにならず、有効な CSS として扱われていました。

この状態では、以下のような定義が受理されていました。

@utility foo-* {
  color: red;
}

foo-afoo-bfoo-c のいずれのクラスも同一の color: red を生成し、ワイルドカード部分の値(abc)は何にも使われません。ユーザーの意図としては --value(…) を書き忘れているケースがほとんどであり、実質的なバグでした。なお、関連PR #19989 の実装中にこの問題が発見されています。

技術的な変更

utilities.ts 内の条件分岐を1行修正することで、--value(…) が未使用、または使用されたが解決されなかった場合の両方を弾くようになりました。

変更前:

// Used `--value(…)` but nothing resolved
if (usedValueFn && !resolvedValueFn) return null

変更後:

// Functional CSS utilities require `--value(…)`, and one of those
// branches must resolve for the candidate to be valid.
if (!usedValueFn || !resolvedValueFn) return null

変更前の条件は「--value(…) を使ったが解決できなかった場合のみ無効」でした。変更後は「--value(…) を使っていないか、解決できなかった場合に無効」となり、usedValueFnfalse のケース(そもそも --value(…) を書いていない)もカバーされます。

あわせて、ウォーク処理のローカル変数名が valueNode から fnNode にリネームされています。これは --modifier(…) の処理にも同じ変数が使われるため、「値に限らず関数ノードである」という実態を正確に表した命名への改善です。

テストは utilities.test.ts に2ケース追加されました。

  • --value(…) を持たない @utility tab-* に対して tabtab-foo を候補として渡した場合、空文字列を返すこと
  • --value(integer) を持つ @utility tab-* に対して、tab-1tab-2 は正常に解決され、tabtab-footab-2.5(整数でない)は空文字列を返すこと

設計判断

論理演算子の反転のみで対処する最小変更が採用されました。

usedValueFn && !resolvedValueFn!usedValueFn || !resolvedValueFn に変えることは、ド・モルガンの法則による等価変換ではなく意味的な拡張です。前者は「使ったが失敗」、後者は「使っていない OR 失敗」という異なる条件です。この変更によって既存の「解決に失敗した候補を破棄する」動作は維持しつつ、「そもそも --value(…) が存在しない」ケースを同一のリターンパスで処理できるようにしています。新しいフラグやコードパスを追加せず、1行の修正で仕様を正確にした判断です。

まとめ

--value(…) の必須化は、関数ユーティリティの「ワイルドカード部分を必ず何かに使う」という本来の契約をコードレベルで強制するものです。論理演算子1つの修正ながら、意図しない無限クラス生成という実用上の問題を根本から封じており、API の意味的な整合性を高める変更といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
0bb81cdc

この記事は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:packages/tailwindcss/src/utilities.ts)およびPR番号のリンク記法([#19989](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

「関数ユーティリティ」「@utility」「--value(...)」などの専門用語を前提としており、対象読者であるエンジニアに適した技術レベルと表現で書かれています。

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

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

各セクションの冒頭に要旨が述べられ、各段落はトピックセンテンスで始まっています。1段落1トピックの原則が守られ、段落長も適切です。

Diff内容との照合 ✓ PASS

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

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

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「関数ユーティリティ」「--value(...)」「--modifier(...)」といった用語が正確に使用されています。また、`valueNode`から`fnNode`への変数リネームの理由も的確に説明されており、技術的理解の深さを示しています。

説明の技術的正確性 ✓ PASS

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

論理演算子の変更(`usedValueFn && !resolvedValueFn` → `!usedValueFn || !resolvedValueFn`)がもたらす意味的な変化(「使ったが失敗」→「使っていない OR 失敗」)を正確に解説できています。テストケースの追加に関する説明もDiff内容と整合しています。

事実の突合 ⚠ WARNING

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

リード文の「無限の候補クラスが生成されてしまうバグ」という表現は、PR Descriptionの「making your CSS bigger」という記述よりも強い表現です。技術的な帰結としては妥当な解釈ですが、PRに直接の記述がないため警告とします。

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

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

PR番号(#20005, #19989)はすべて正確に記載されています。

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

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

記事タイトルはPRの主題「Ensure --value(…) is required in functional @utility definitions」を正確に要約しており、内容と一致しています。

外部知識の正確性 ✓ PASS

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

PR情報に記載のないバージョン情報やリリース予定などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

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

「これまでの実装では」といった時間表現は、PR Descriptionの「is not required right now」という記述と整合性が取れており、正確です。