allow‑list .gitignore で無視された @source ディレクトリを明示的にスキャンできるように修正
Tailwind CSS の @source ディレクティブが、リポジトリルートの allow‑list 形式 .gitignore によって除外されたフォルダでも、明示的に指定すれば走査されるようになり、クラス抽出の欠落が解消されました。
背景
従来、@source が指すパスが .gitignore の allow‑list(* で全体除外し ! で例外を許可)にマッチすると、スキャナはそのディレクトリ全体をスキップし、対象テンプレートから生成される CSS クラスが失われていました(Issue #19844)。この挙動は vendor/ のように Git で無視されても実装上は重要なリソースを含むケースで特に問題となります。Tailwind は Auto ソースに対してデフォルトで Git の無視規則を適用しており、明示的に @source で指定した場合でも同様に除外されていました。
技術的な変更
ソース種別の拡張: crates/oxide/src/scanner/sources.rs の SourceEntry::External のコメントを更新し、"gitignore で無視されるが明示的に追加されたフォルダ" を表す旨を明記しました。また、fxhash::FxHashMap と ignore::gitignore::Gitignore のインポートを追加し、gitignore の状態を高速に判定できる基盤を整えました。
Auto → External の昇格ロジック: public_source_entries_to_private_source_entries 内で、Auto と判定されたエントリが実際に gitignore により無視されているかをチェックし、対象が無視されていれば External に変換します。この変更により、明示的な @source が gitignore の除外をバイパスし、スキャン対象に加わります。
Walker の無視パターン調整: crates/oxide/src/scanner/mod.rs で emit(base, "!/**/*".to_owned()) に変更し、外部ソースが常に全ファイルを対象にするパターンを簡潔に記述しました。これに伴い、Pattern ソースの正規化ロジックも SourceEntry::Pattern { base, pattern } => Some((base.into(), pattern.into())) とシンプル化しました。
テストの追加: crates/oxide/tests/scanner.rs と integrations/cli/index.test.ts に、allow‑list .gitignore 環境下で明示的 @source が正しく走査されることを検証するテストケースを追加しました。これらのテストは、実際のリポジトリ構成をシミュレートし、期待通り External ソースとして検出されることを確認します。
設計判断
既存キー拡張で後方互換性を維持:新しい設定キーを導入せず、Auto → External の変換ロジックで機能追加を実現したため、既存の @source 設定やプラグインは変更不要です。これにより、利用者は既存の @source 文法をそのまま使い続けられます。
最小侵入的変更:walker のパターン文字列を一行にまとめ、Pattern の正規化ロジックを簡素化した点は、コードベース全体への影響を最小限に抑える意図が見えます。加えて、gitignore 判定を高速ハッシュマップで実装したことで、追加コストが最小化されています。
テスト駆動の安全性:新規テストが既存テストスイートに統合されたことで、将来的なリファクタリング時にこの振る舞いが壊れにくくなっています。テストは allow‑list と deny‑list の両シナリオを網羅し、設計選択の正当性を自動で保証します。
まとめ
この PR は、@source が .gitignore の allow‑list によって除外されたフォルダでも明示的にスキャンできるよう、Auto ソースを External に昇格させるロジックと walker のパターン調整を導入した点が核心です。既存インターフェースを保ちつつ、テストで裏付けられた安全な実装変更により、Tailwind のクラス抽出がより堅牢になりました。