JS プラグインの theme() が DEFAULT 値を返すように修正

tailwindlabs/tailwindcss

Tailwind CSS の JavaScript プラグイン内で theme() を呼び出した際、接頭辞が重なる CSS カスタムプロパティ(例: --color-foo--color-foo-bar)が存在すると、内部的な曖昧解消オブジェクトが返ってしまい、生成される CSS が破壊的になる問題を解消しました。今回の PR では、同様のケースで DEFAULT キーを自動的に展開し、期待通りの文字列を返すように変更しています。

背景

theme() 関数はプラグイン開発者がテーマ値を取得するための中心的な API ですが、CSS カスタムプロパティ名だけでは「foo」が単独キーか「foo-bar」のサブキーかを判別できません。この判別失敗により、theme('colors.foo'){ DEFAULT: 'red', bar: 'blue', __CSS_VALUES__: {...} } のような合成オブジェクトを返し、プラグインが期待する文字列ではなくオブジェクトが渡されました。その結果、addUtilities で生成された CSS に -d-e-f-a-u-l-t という不要なプロパティ名が混入し、__CSS_VALUES__ が漏出するという具体的な破壊が発生していました。

この曖昧さは、既に CSS 内部で使用される theme() 関数(#19097)では DEFAULT キーの展開で修正されていましたが、JS プラグインが直接呼び出す createThemeFn の実装には同様のロジックが欠如していたため、プラグイン側で同様の不具合が残っていました。

技術的な変更

packages/tailwindcss/src/compat/plugin-functions.tscreateThemeFn 内に、取得した CSS 値がオブジェクトでかつ DEFAULT プロパティを持つ場合にその DEFAULT 値を返す条件分岐を追加しました。

@@ -100,6 +100,23 @@ export function createThemeFn(
       // If `--color-foo` and `--color-foo-bar` are both defined, `colors.foo`
       // produces a synthetic object:
       //
       // ```ts
       // { DEFAULT: 'red', bar: 'blue', __CSS_VALUES__: { DEFAULT: 0, bar: 0 } }
       // ```
+      // Prefer `DEFAULT` instead of exposing the object to plugin code.
+      if (
+        cssValue !== null &&
+        typeof cssValue === 'object' &&
+        !Array.isArray(cssValue) &&
+        'DEFAULT' in cssValue
+      ) {
+        return cssValue.DEFAULT
+      }

このロジックは CSS から取得した値null でなく、オブジェクトであり配列でなく、DEFAULT キーが存在する場合にのみ適用されます。既存の cssValue ?? configValue の戻り値ロジックはそのまま残すことで、従来の挙動や設定ファイルからの値優先順位に影響を与えません。

同時に packages/tailwindcss/src/compat/plugin-api.test.ts に回帰テストが追加され、theme('colors.foo') が正しく red を返すことを自動的に検証します。テストは run ヘルパーでプラグイン実行をシミュレートし、インラインスナップショットで期待出力を確認しています。

設計判断

今回の修正は 既存 API の拡張 という方針で行われました。theme() 関数自体のシグネチャや呼び出し方法は変更せず、内部で返却する値だけを調整することで、プラグイン側のコードに対する破壊的変更を回避しています。DEFAULT キーの検出はシンプルな型チェックとプロパティ存在確認で実装され、余計な依存関係やパフォーマンスコストを導入しない設計です。

また、後方互換性 を重視し、theme('colors.foo') がオブジェクトを期待している既存プラグインがあった場合でも、DEFAULT キーが存在しない限り元のオブジェクトが返ります。これにより、今回の変更は安全にデプロイでき、既存エコシステムへの影響を最小限に抑えています。

まとめ

theme() が CSS カスタムプロパティの接頭辞衝突で返す内部オブジェクトを DEFAULT 値に自動変換 することで、JS プラグイン内でのテーマ取得が正しく行えるようになり、破損した CSS の生成を防止しました。この実装は既存 API と互換性を保ちつつ、先行の CSS 対応修正と同様の挙動をプラグイン側にも提供する設計判断のもと実装されています。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的な変更、設計判断(任意)、まとめが明確に存在し、まとめはリード文の単なる繰り返しではなく意義を追加している。

カスタムMarkdown構文 ✓ PASS

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

コードブロックは正しい diff 形式で示され、ファイル名付きシンタックスハイライトは使用していないが必要な形式は守られている。GitHubリンクは [#19097] 等の形式で正しくリンク化されている。

対象読者への適合性 ✓ PASS

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

エンジニア向けに技術的詳細が記載されており、初心者向けの過度な説明はない。

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

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

各セクションは総論→各論→結論の構成で、各段落はトピックセンテンスで始まり、1段落1トピック、長さも適切に分割されている。

Diff内容との照合 ✓ PASS

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

記事内の diff コードは PR が提供した diff と内容が一致しており、ファイル名・行番号も正確に反映されている。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている技術用語(DEFAULT、cssValue、plugin、addUtilities など)は PR と一致し、誤用はない。

説明の技術的正確性 ✓ PASS

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

技術的説明は PR の内容と合致しており、因果関係も論理的に正しい。

事実の突合 ✓ PASS

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

すべての主張は PR のタイトル・説明・Diff で裏付けられており、推測や憶測は含まれていない。

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

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

PR 番号 #20299、[#19097] などの数値は正確に記載されている。

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

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

記事タイトルは PR タイトルの内容を日本語で適切に要約しており、一致している。

外部知識の正確性 ✓ PASS

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

外部知識(LTS、リリース日程等)は一切記載されておらず、PR に基づいた情報のみで構成されている。

時間表現の正確性 ✓ PASS

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

時間表現は使用されておらず、PR の記述と食い違う表現もない。