`@tailwindcss/cli` に `--poll` オプションを再導入

tailwindlabs/tailwindcss

Tailwind CSS v4 の CLI で失われていた --poll オプションが復活し、ファイルシステムイベントが取得できない環境でもインクリメンタルなビルドが可能になりました。

背景

--poll は v3 で提供されていたが、v4 で @parcel/watcher に切り替えた際に削除され、Docker などの bind‑mount 環境で --watch が機能しなくなったことが問題となっていました(#18109#18540)。

ファイル変更の検知に依存する開発フローが途絶えると、手作業でビルドコマンドを走らせるしかなく、フルビルドのコストが増大します。この PR はそのギャップを埋め、Docker でも快適に --watch を利用できるようにすることを目的としています。

技術的な変更

CLI オプション定義packages/@tailwindcss-cli/src/commands/build/index.ts に追加され、--pollboolean | number 型で受け取ります。デフォルトは falsetrue が指定された場合は内部定数 DEFAULT_POLL_INTERVAL_MS(250 ms)を使用します。

+const DEFAULT_POLL_INTERVAL_MS = 250
@@
+    '--poll': {
+      type: 'boolean | number',
+      description: 'Use polling instead of filesystem events when watching',
+      default: false,
+      values: ['ms'],
+    },

オプションのバリデーションロジックも同ファイルに実装され、--poll が未指定の場合はエラーメッセージを出してプロセスを終了します。また、数値が 0 以下の場合もエラーとし、正のミリ秒数だけが受け入れられます。

+  if (args['--poll'] === undefined) {
+    eprintln(`Use --poll with a non-zero value in milliseconds.`)
+    process.exit(1)
+  }
+  let pollInterval = args['--poll'] === true ? DEFAULT_POLL_INTERVAL_MS : args['--poll']
+  if (pollInterval !== false && pollInterval <= 0) {
+    eprintln(`Specified polling interval must be a positive number.`)
+    process.exit(1)
+  }

内部スキャナcrates/oxide/src/scanner/mod.rs に拡張され、スキャン毎に走査したファイルパスを scanned_files ベクタに格納し、get_scanned_files アクセサで取得できるようになりました。これにより、ポーリング時に前回と変化がなかったファイルを高速にスキップできます。

+    /// Files that were scanned during the last `scan()` call.
+    scanned_files: Vec<String>,
@@
+        self.scanned_files = files;
@@
+    pub fn get_scanned_files(&self) -> Vec<String> {
+        self.scanned_files.clone()
+    }

Node バインディング側 (crates/node/src/lib.rs) でも同様に scanned_files の getter が公開され、JavaScript からも走査結果を参照できるようになっています。

+  #[napi(getter)]
+  pub fn scanned_files(&self) -> Vec<String> {
+    self.scanner.get_scanned_files()
+  }

テストは integrations/cli/index.test.tswatch mode with polling が追加され、--poll=50 でファイル変更が正しく検知され、インクリメンタルに CSS が更新されることを確認しています。

+  test(
+    'watch mode with polling',
+    {
+      fs: { ... },
+    },
+    async ({ fs, spawn }) => {
+      let process = await spawn(`${command} --input src/index.css --output dist/out.css --watch --poll=50`)
+      await process.onStderr((m) => m.includes('Done in'))
+      await fs.expectFileToContain('dist/out.css', [candidate`underline`])
+      await fs.write('src/index.html', html`<div class="underline flex"></div>`)
+      await fs.expectFileToContain('dist/out.css', [candidate`flex`])
+    },
+  )

設計判断

後方互換性の維持 が最優先され、既存の --watch 動作は変更せず --poll をオプションとして追加しました。true が指定された場合は既定の 250 ms が適用されるため、ユーザーはコマンドラインの変更だけで即座に利用できます。

boolean | number の型選択は、シンプルに「デフォルトでポーリング」か「カスタム間隔」を明示できる点が評価されました。実装は CLI のオプションパースに数行追加するだけで済み、コアのビルドロジックに大きな侵入を行っていません。

内部スキャナに scanned_files を導入したのは、ポーリング時の インクリメンタルビルド を実現するための最小限の情報提供です。走査対象を正確に把握できるため、変更が無いファイルを再パースせずに済み、パフォーマンス低下を抑えられます。

まとめ

--poll オプションの復活により、Docker などファイルシステムイベントが取得できない環境でも安全に --watch が利用でき、従来と同等のインクリメンタルビルド体験が提供されます。実装は CLI のオプション拡張とスキャナの軽微な状態追跡に留め、後方互換性とパフォーマンスのバランスを保った設計判断がなされています。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

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

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きコードブロックは `言語:ファイルパス` 形式で正しく記述され、GitHubリンクは #番号 と URL の組み合わせで正しくリンク化されています。

対象読者への適合性 ✓ PASS

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

対象は Tailwind CSS の CLI 開発者・高度なエンジニア向けで、専門用語以外の余計な解説はありません。

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

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

各セクションが総論パラグラフで要旨を示し、続く段落で具体的事実・コードを示し、最後に結論的まとめがある。段落はトピックセンテンスで始まり、1段落1トピック・適切な長さで区切られています。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは提供された Diff と一致しており、追加・変更箇所が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

CLI、watcher、polling、incremental build などの用語は正しく使用され、PR の記述と整合しています。

説明の技術的正確性 ✓ PASS

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

デフォルト 250 ms、`boolean | number` 型の説明、後方互換性の方針など、全て PR の記述に基づく正確な説明です。

事実の突合 ✓ PASS

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

全ての事実(背景、目的、実装内容、テスト追加)は PR の情報と一致しており、根拠のない推測はありません。

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

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

PR 番号 #20297、Issue 番号 #18109・#18540、デフォルト間隔 250 ms など数値は正確です。

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

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

記事タイトルは PR タイトル「Add `--poll` option to `@tailwindcss/cli`」の日本語訳として適切です。

外部知識の正確性 ✓ PASS

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

バージョンサポートやリリース予定など、PR に記載されていない外部情報は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現のずれはなく、PR の記述と一致しています。