削除されたファイルが原因の Vite HMR クラッシュを防止

tailwindlabs/tailwindcss

Tailwind CSS の @tailwindcss/vite プラグインが、ファイル削除時に Vite の Hot Module Replacement (HMR) がクラッシュする問題を解消し、削除されたパスが再度監視対象になることを防ぎました。

背景

ViteaddWatchFile API を通じて Tailwind のスキャナが検出したファイルを監視しますが、削除されたファイルがスキャナ内部の集合に残ると、次回の HMR サイクルでそのパスを再度開こうとして ENOENT エラーが発生し、開発サーバーが停止します。Issue #17532 で報告されたこの現象は、SVG 等のアセットを削除しただけで再現され、開発フローを著しく阻害していました。

この根本原因は、スキャナがファイルシステムの再走査時に削除されたエントリを除去しない点にありました。したがって、スキャナが最新のファイル集合を正しく反映できるように状態をリセットする必要がありました。

技術的な変更

Scanner の再走査ロジックに、走査開始前に内部コレクションをクリアする処理が追加されました。具体的には crates/oxide/src/scanner/mod.rsdiscover_sources メソッド冒頭で以下のコードが挿入されています。

@@
-        let mut seen_files: FxHashSet<PathBuf> = FxHashSet::default();
+        // Fresh state for each scan
+        self.files.clear();
+        self.dirs.clear();
+        self.extensions.clear();
+        self.globs = None;

同時に、重複除去の手段が seen_files から self.files への直接挿入へ置き換えられ、拡張子情報も同時に格納されるようになっています。

@@
-                if !seen_files.insert(path.clone()) {
+                if !self.files.insert(path.clone()) {
                     continue;
                 }
+                self.extensions.insert(extension.clone());

さらに、変更検知のロジックが path.metadata().ok().and_then(|m| m.modified().ok()) を用いて mtime を取得し、スキャンが初回でない場合にのみ未変更ファイルをスキップするよう整理されています。これにより、削除されたファイルは再走査時にコレクションから除外され、addWatchFile に渡されることがなくなります。

設計判断

今回の実装は 状態リセット に重点を置き、スキャナが保持するファイル集合・ディレクトリ集合・拡張子集合・glob 設定を走査毎に完全に再構築する方針を採用しました。これにより、削除検知ロジックを個別に追跡・除去する複雑性を回避し、既存 API(addWatchFile への呼び出し)への影響を最小限に抑えています。

一方で candidates は新しいコンパイラインスタンスが生成されるまで append‑only のまま保持され、スキャンのリセット対象外としています。この設計は、ビルドパイプラインの安定性を保ちつつ、削除されたファイルが再度ウォッチされるリスクだけを解消するというトレードオフを明示しています。

まとめ

スキャナの内部集合を走査毎にクリアし、削除済みパスを除去する変更により、@tailwindcss/vite が Vite の HMR 中にクラッシュする問題が解消されました。変更は Scanner の状態管理 に限定され、既存の挙動や API への影響は最小限に抑えられています。

記事メタデータ

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

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文・背景・技術的な変更・設計判断・まとめの5つの要素が揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライトは ` ```rust:crates/oxide/src/scanner/mod.rs ` の形式で正しく記述され、GitHubリンクは #付きかつURLでリンク化されています。

対象読者への適合性 ✓ PASS

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

エンジニア向けの専門的な内容で、初心者向けの余計な説明は含まれていません。

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

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

各セクションが「総論→各論→結論」の段落構成になっており、段落はトピックセンテンスで始まり1段落1トピック、長さも適切です。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは提供されたDiffの変更点と一致しており、ファイルパスや変更内容に相違はありません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Scanner」「addWatchFile」「mtime」などの用語は PR で使われているものと一致し、誤用は見られません。

説明の技術的正確性 ✓ PASS

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

技術的な説明は Diff と PR の記述に裏付けられており、因果関係も論理的に正しいです。

事実の突合 ✓ PASS

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

すべての主張は PR のタイトル・説明・Diff で裏付けられており、推測や捏造はありません。

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

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

Issue 番号、PR 番号、行番号などの数値は正確です。

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

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

記事タイトルは PR タイトル「Deleting files should not crash Vite HMR」の意味を日本語で適切に表現しています。

外部知識の正確性 ✓ PASS

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

LTS やリリース日程など PR に記載のない外部情報は含まれていません。

時間表現の正確性 ✓ PASS

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

時間に関する表現はなく、PR の記述と食い違う箇所もありません。