Viteプラグインがサーバー専用モジュールで誤って全リロードを引き起こす問題を修正

tailwindlabs/tailwindcss

SSRフレームワークが管理するサーバー専用モジュールの変更時に、@tailwindcss/viteプラグインが誤ってクライアントの全リロードを強制していた問題を修正しました。これにより、React RouterのHDR(Hot Data Revalidation)やWakuなどのSSRフレームワークにおけるHMRが正常に動作するようになります。

背景

#19670で導入されたhandleHotUpdateフックが、SSRフレームワーク固有のサーバー専用モジュールを考慮していませんでした。このフックはVite 7.1+での外部ファイル変更時に全リロードを正しくトリガーするために実装されましたが、SSRフレームワークが管理するサーバー専用モジュールまで誤って全リロードの対象としていました。

具体的には、クライアント側のCSSスキャン時にサーバー専用モジュールが走査された場合、そのモジュールへの変更がクライアントの全リロードを引き起こしていました。React RouterのHDRのように、サーバー側のローダー依存ファイルを変更した際にクライアント状態を保ちながらデータのみを再取得する仕組みが、全リロードによって破壊される状況でした(#19744)。同様の問題は@vitejs/plugin-rscwakuでも確認されており、複数のSSRフレームワークに影響を与えていました。

この問題の核心は、ViteのEnvironment APIが提供する複数の環境(clientssrなど)にまたがるモジュールグラフの扱いにあります。SSRフレームワークはサーバー専用モジュールに対して独自のHMR・リロード機構を持っており、クライアント側が介入すべきではありません。

技術的な変更

packages/@tailwindcss-vite/src/index.tshandleHotUpdateフック内に、他の環境のモジュールグラフを参照するチェックが追加されました。

変更前は、変更されたファイルに対応するモジュールがすべてasset型かid未定義であれば、外部ファイルと判断して全リロードを実行していました。

変更前:

let isExternalFile = modules.every((mod) => mod.type === 'asset' || mod.id === undefined)
if (!isExternalFile) return

for (let env of new Set([this.environment.name, 'client'])) {
  // ... full reload logic
}

変更後:

let isExternalFile =
  modules.length > 0 &&
  modules.every((mod) => mod.type === 'asset' || mod.id === undefined)
if (!isExternalFile) return

// Skip if the module exists in other environments. SSR framework has
// its own server side hmr/reload mechanism when handling server
// only modules.
for (let environment of Object.values(server.environments)) {
  if (environment.name === this.environment.name) continue

  let modules = environment.moduleGraph.getModulesByFile(file)
  if (modules) {
    for (let module of modules) {
      if (module.type !== 'asset') {
        return
      }
    }
  }
}

for (let env of new Set([this.environment.name, 'client'])) {
  // ... full reload logic
}

変更は2点あります。第一に、modules.length > 0のチェックが追加されました。これにより、モジュールが空の場合(addWatchFile経由ではないファイル)は外部ファイル判定をスキップします。第二に、現在の環境以外の全環境のmoduleGraph.getModulesByFileを参照し、変更ファイルが他の環境でも非asset型モジュールとして管理されている場合は全リロードを中止します。

検証として、React RouterのHDR(サーバーローダーのHMR)を対象とした統合テストがintegrations/vite/react-router.test.tsに追加されています。サーバー専用のdirect-hdr-dep.tsを変更した際に、クライアント入力状態を保ちつつHDRが発火することを確認するテストで、修正前はmainブランチで失敗します。

設計判断

他環境のモジュールグラフを参照して全リロードをスキップするアプローチが採用されました。

ViteのEnvironment APIでは、server.environmentsに各環境(clientssr、RSCフレームワーク固有の環境など)のモジュールグラフが独立して管理されています。あるファイルが現在の環境(クライアント)ではasset型として見えても、別の環境(SSR)では通常のJSモジュールとして管理されている場合、そのファイルの更新はSSR環境側のHMR機構に委ねるべきです。この判断はVite公式ドキュメントの「SSR専用モジュールへの更新はクライアントの全リロードをトリガーしない」という原則と一致しています。

このアプローチにより、Tailwindプラグインは自身が担当すべき範囲(TailwindのCSSに影響する外部ファイルの変更)に処理を限定し、SSRフレームワーク固有のモジュール管理には介入しない設計になっています。

まとめ

本修正は、マルチ環境構成を持つVite向けSSRフレームワークとの協調動作を改善する変更です。全リロードを実行する前に他環境のモジュールグラフを参照するという1つの判断が、React Router、@vitejs/plugin-rsc、Wakuといった複数のSSRフレームワークで発生していたHMR破壊を解消しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
71e2d7d8

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

「総論→各論→結論」の構成が明確です。リード文、背景、技術的な変更、設計判断、まとめの各要素が論理的に配置されており、記事の全体像を把握しやすいです。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きのシンタックスハイライト(```typescript:ファイルパス)や、PR・Issue番号のリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

ViteやSSRフレームワークに関する専門知識を持つエンジニアを対象としており、技術レベルや表現が適切です。冗長な初心者向けの説明はありません。

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

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

各セクションが総論から各論へ展開され、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が遵守されています。非常に読みやすい構造です。

Diff内容との照合 ✓ PASS

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

記事で引用されているコードブロックは、提供されたDiffの内容と正確に一致しています。ファイル名も正しく記載されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Environment API」「moduleGraph」「HMR」など、関連する技術用語が正確かつ適切な文脈で使用されています。

説明の技術的正確性 ✓ PASS

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

コード変更の目的(他環境のモジュールグラフを参照して全リロードをスキップする)に関する説明は、Diffのコードロジックと完全に整合しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内の主張はすべて、PRのタイトル、Description、Diff内のコメントで裏付けられています。根拠のない推測や憶測(ハルシネーション)は見られません。

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

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

PR番号(#19745)、関連Issue番号(#19744)、関連PR番号(#19670)など、記事に含まれる数値や固有名詞はすべて正確です。

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

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

記事のタイトルは、PRのタイトル「fix(vite): skip full reload for server only modules scanned by client css」の内容を分かりやすく要約しており、主題と一致しています。

外部知識の正確性 ✓ PASS

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

PR情報に含まれていない外部知識(バージョンのサポート状況、リリース日程など)の追記はなく、記事の信頼性が担保されています。

時間表現の正確性 ✓ PASS

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

時間表現の歪曲は見られず、事実関係が正確に記述されています。