`--value(…)` を省略した関数ユーティリティ定義を無効化するバリデーション追加
関数ユーティリティ(@utility foo-*)において --value(…) の使用を必須とするバリデーションが追加されました。これにより、値が実際に使われていないまま無限の候補クラスが生成されてしまうバグを防ぎます。
背景
@utility foo-* のような関数ユーティリティ定義では、--value(…) を使ってワイルドカード部分の値をプロパティ値に反映させることが意図された使い方です。しかしこれまでの実装では、--value(…) を記述しない定義もバリデーションエラーにならず、有効な CSS として扱われていました。
この状態では、以下のような定義が受理されていました。
@utility foo-* {
color: red;
}
foo-a・foo-b・foo-c のいずれのクラスも同一の color: red を生成し、ワイルドカード部分の値(a・b・c)は何にも使われません。ユーザーの意図としては --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(…) を使っていないか、解決できなかった場合に無効」となり、usedValueFn が false のケース(そもそも --value(…) を書いていない)もカバーされます。
あわせて、ウォーク処理のローカル変数名が valueNode から fnNode にリネームされています。これは --modifier(…) の処理にも同じ変数が使われるため、「値に限らず関数ノードである」という実態を正確に表した命名への改善です。
テストは utilities.test.ts に2ケース追加されました。
-
--value(…)を持たない@utility tab-*に対してtab・tab-fooを候補として渡した場合、空文字列を返すこと -
--value(integer)を持つ@utility tab-*に対して、tab-1・tab-2は正常に解決され、tab・tab-foo・tab-2.5(整数でない)は空文字列を返すこと
設計判断
論理演算子の反転のみで対処する最小変更が採用されました。
usedValueFn && !resolvedValueFn を !usedValueFn || !resolvedValueFn に変えることは、ド・モルガンの法則による等価変換ではなく意味的な拡張です。前者は「使ったが失敗」、後者は「使っていない OR 失敗」という異なる条件です。この変更によって既存の「解決に失敗した候補を破棄する」動作は維持しつつ、「そもそも --value(…) が存在しない」ケースを同一のリターンパスで処理できるようにしています。新しいフラグやコードパスを追加せず、1行の修正で仕様を正確にした判断です。
まとめ
--value(…) の必須化は、関数ユーティリティの「ワイルドカード部分を必ず何かに使う」という本来の契約をコードレベルで強制するものです。論理演算子1つの修正ながら、意図しない無限クラス生成という実用上の問題を根本から封じており、API の意味的な整合性を高める変更といえます。