スタンドアロンビルドで `NODE_PATH` をサポート

tailwindlabs/tailwindcss

Tailwind CSS のスタンドアロン CLI が NODE_PATH 環境変数を認識するようになりました。これにより、標準の node_modules 以外の場所にあるモジュールを、Node.js をインストールせずに解決できます。

背景

スタンドアロン CLI は、Node.js なしで Tailwind CSS を実行できる配布形態ですが、カスタムの NODE_PATH を使ったモジュール解決には対応していませんでした。Node.js 経由で実行する場合はこの制約がなく、同じ機能を利用できていました。

この問題の発端は、#19391 におけるビルド時の意図しない動作にあります。スタンドアロンビルドでは一部の環境変数をビルド時にインライン展開していましたが、CI 環境(macOS)上で実行されたため、NODE_PATH がランナー固有のパス(/Users/runner/work/.../node_modules)として埋め込まれてしまっていました。この問題を修正する際に NODE_PATH のサポートそのものが無効化され、スタンドアロン CLI では明示的に NODE_PATH が空文字列として定義されるようになりました。

一方、#16274 では Node.js 版の @tailwindcss/node パッケージに対して NODE_PATH のサポートが追加されており、スタンドアロン版との間に機能差が生じていました。本 PR はその差を解消します。具体的なユースケースとして挙げられているのが Phoenix LiveView のコロケーション機能で、テンプレートから抽出されたスクリプトを通常の node_modules とは異なるディレクトリに配置し、NODE_PATH を通じて解決する仕組みです。CSS のコロケーションでも同様のアプローチを採用したいが、スタンドアロン CLI がそれを妨げているという課題がありました。

技術的な変更

今回の変更は2ファイルにまたがっており、いずれも最小限の修正で目的を達成しています。

build.ts からの NODE_PATH の無効化を削除:

packages/@tailwindcss-standalone/scripts/build.ts では、NODE_PATH を空文字列にインライン化する define エントリが削除されました。

変更前:

define: {
  'process.env.NAPI_RS_NATIVE_LIBRARY_PATH': JSON.stringify(''),

  // No need to support additional NODE_PATHs in the standalone build
  'process.env.NODE_PATH': JSON.stringify(''),
},

変更後:

define: {
  'process.env.NAPI_RS_NATIVE_LIBRARY_PATH': JSON.stringify(''),
},

これにより、ビルド時に NODE_PATH が空文字列として固定されなくなり、実行時の環境変数がそのまま参照されるようになります。

compile.ts での複数パス対応:

packages/@tailwindcss-node/src/compile.ts では、NODE_PATH に複数のパスが含まれる場合を正しく処理するよう修正されました。

変更前:

const modules = ['node_modules', ...(process.env.NODE_PATH ? [process.env.NODE_PATH] : [])]

変更後:

const modules = ['node_modules', ...(process.env.NODE_PATH ? [...process.env.NODE_PATH.split(path.delimiter)] : [])]

path.delimiter を使って NODE_PATH の値を分割することで、Unix では : 区切り、Windows では ; 区切りという OS 標準のセパレータに自動対応しています。変更前の実装では NODE_PATH 全体を単一パスとして扱っていたため、複数パスが指定された場合に正しく動作しない問題もありました。

設計判断

path.delimiter による OS 依存の吸収が本変更の要点です。NODE_PATH の仕様では複数のパスをOSのパスセパレータで区切って指定できますが、変更前のコードはこの仕様に対応していませんでした。path.delimiter は Node.js 標準モジュール path が提供する定数であり、Unix/Windows の差異を明示的に吸収しています。

ビルド時の define による無効化を「削除する」という形で変更を実装したことも注目に値します。NODE_PATH を空にするコードを追加した経緯が、ビルド環境固有の問題への暫定対処だったため、その対処ごと取り除くことで、他の環境変数と同様に実行時解決へと戻しています。これは「無効化のための特別処理を除去する」という最小変更の方針と一致しています。

まとめ

スタンドアロン CLI が NODE_PATH を正しく扱えるようになったことで、Node.js 版との機能差が解消されました。Phoenix LiveView のような、標準の node_modules 以外のディレクトリからのモジュール解決を必要とするユースケースで、Node.js のインストールを強いることなくスタンドアロン CLI をそのまま活用できます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
2bfa994d

この記事は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リンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)とGitHubのPR番号リンク記法([#123](URL))が、ガイドラインに沿って正しく使用されています。

対象読者への適合性 ✓ PASS

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

内容はスタンドアロンCLIやビルドプロセスに関する専門的なもので、対象読者であるエンジニアに適しています。過度な初心者向けの説明はなく、簡潔で専門的な記述が維持されています。

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

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

各セクションは総論から各論へと展開され、各段落はトピックセンテンスで始まっています。1段落1トピックの原則が守られ、段落長も適切であるため、高い可読性を確保しています。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiff情報と完全に一致しています。ファイルパスの指定も正確で、変更点が忠実に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「NODE_PATH」「スタンドアロン CLI」「path.delimiter」などの技術用語が、文脈に応じて正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

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

コード変更がもたらす影響(`define`からの削除、`path.delimiter`による複数パス対応など)についての説明は、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張(背景、ユースケース、変更理由)は、PRのDescriptionやDiff情報によって裏付けられており、ハルシネーション(捏造)は見られません。「設計判断」セクションもコードの意図を汲んだ妥当な解説です。

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

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

PR番号(#19617, #19391, #16274)が正確に記載・リンクされています。

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

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

記事のタイトル「スタンドアロンビルドで `NODE_PATH` をサポート」は、PRのタイトル「support NODE_PATH in standalone build」と完全に一致しており、内容を的確に表現しています。

外部知識の正確性 ✓ PASS

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

PR情報に記載のない外部知識(バージョンのサポート状況、リリース日程など)は含まれておらず、記事内容は提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

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

「~ようになりました」「~していました」といった時間表現は、PRによる変更の前後関係を正確に反映しており、誤解を招く表現はありません。