Node 26+の非推奨警告を解消するESMフック登録APIの切り替え

tailwindlabs/tailwindcss

Node.js 26でModule#registerが非推奨となったことで発生する[DEP0205]警告を解消するため、@tailwindcss/nodeModule#registerHooksを優先的に使用するよう変更されました。

背景

Node.js v26以降でModule#registerが非推奨となり、TailwindCSSを使用するとビルド時に[DEP0205] DeprecationWarningが出力される問題が#19893で報告されました。

(node:25346) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
    at file:///.../node_modules/@tailwindcss/node/dist/index.mjs:18:214

この問題の根本原因は、@tailwindcss/nodeがESMモジュールのキャッシュ制御のためにカスタムリゾルバーを登録する際にModule#registerを使用していた点にあります。Module#registerはNode v18.19.0とv20.6.0で追加されたAPIですが、Node v22.15.0とv23.5.0で導入されたModule#registerHooksがv25.9.0以降で推奨APIとなり、v26からは旧APIがランタイム警告を発するようになりました。

技術的な変更

この変更は2つのファイルにまたがり、新しいAPIへの切り替えと、それに対応する同期バージョンのフック実装で構成されています。

Module#registerModule#registerHooksには重要なAPIの違いがあります。Module#registerは外部ファイルのURLを渡してワーカースレッドで非同期に実行されるフックを登録するのに対し、Module#registerHooksはフック関数を直接オブジェクトとして渡す同期APIです。そのため、esm-cache.loader.mtsに同期版のresolveSync関数を追加する必要がありました。

packages/@tailwindcss-node/src/esm-cache.loader.mtsの変更:

// 変更前: 非同期の resolve のみ
export let resolve: ResolveHook = async (specifier, context, nextResolve) => {
  let result = await nextResolve(specifier, context)
  // ...
}

// 変更後: 共通処理を processResolve に切り出し、同期版を追加
export let resolve: ResolveHook = async (specifier, context, nextResolve) => {
  let result = await nextResolve(specifier, context)
  return processResolve(context, result)
}

export let resolveSync: ResolveHookSync = (specifier, context, nextResolve) => {
  let result = nextResolve(specifier, context)
  return processResolve(context, result)
}

function processResolve(context: ResolveHookContext, result: ResolveFnOutput) {
  if (result.url === import.meta.url) return result
  if (isBuiltin(result.url)) return result
  if (!context.parentURL) return result
  // ...
}

packages/@tailwindcss-node/src/index.tsindex.ctsの変更:

// 変更前
Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader')))

// 変更後
if (Module.registerHooks) {
  Module.registerHooks({ resolve: resolveSync })
} else {
  Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader')))
}

ランタイムでModule.registerHooksの存在を確認することで、Node v22.15.0/v23.5.0以降では新しいAPIを使用しつつ、それ以前のバージョンでは従来のModule#registerへフォールバックする後方互換性が確保されています。

設計判断

ランタイムでのAPI存在確認という方式が採用されました。

コンパイル時の条件分岐やバージョン番号の比較ではなく、Module.registerHooksプロパティの存在を直接チェックする方式は、Node.jsコアチームが推奨するフィーチャーデテクションのパターンです。これにより、APIが利用可能になった正確なバージョンを管理する複雑さを避けつつ、将来的なAPIの変化にも柔軟に対応できます。

また、共通ロジックをprocessResolve関数に切り出した点も重要です。非同期版と同期版でロジックの重複を避けることで、キャッシュ制御の挙動を一箇所でメンテナンスできる構造になっています。

まとめ

この変更はNode.js 26以降で発生する[DEP0205]非推奨警告を排除しつつ、旧バージョンのNode.jsとの後方互換性を維持します。ランタイムのフィーチャーデテクションと同期フックの追加という最小限の変更で、Node.jsのESMフックAPIの移行期を適切に乗り越える実装になっています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
4142bba1

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という3部構成が明確で、非常に分かりやすい記事構成です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```typescript:path```)、GitHubのIssue/PRリンク記法([#123](URL))がすべて正しく使用されています。

対象読者への適合性 ✓ PASS

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

Node.jsのESMフックに関する専門的な内容を、専門知識を持つエンジニアを対象として適切に記述しています。冗長な説明がなく、簡潔です。

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

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

各セクションが「総論→各論」で構成され、各段落はトピックセンテンスで始まり、1段落1トピックの原則が守られています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは、提供されたDiffの内容を正確に反映しています。説明に必要な箇所を適切に抜粋・比較しており、理解を助けます。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`Module#registerHooks`, `ESMフック`, `同期API`などの技術用語が、PRの文脈と一般の用法に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

APIの非同期/同期の違い、後方互換性の確保方法など、技術的な説明がDiffのコードとPRの意図に完全に一致しており、正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張(非推奨警告の解消、APIの切り替え、後方互換性の維持など)は、PRのタイトル、Description、Diff内のコードやコメントによって裏付けられています。ハルシネーションは見られません。

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

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

PR番号(#20028)、Issue番号(#19893)、ファイルパス、Node.jsのバージョン番号など、すべての数値と固有名詞が正確です。

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

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

記事のタイトル「Node 26+の非推奨警告を解消するESMフック登録APIの切り替え」は、PRの主題を読者にとって分かりやすく要約しており、内容と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事に記載されているNode.jsのバージョン番号やAPIの変遷に関する情報は、すべてPRのDiff内のコメントに基づいています。PR外の情報を持ち込むことなく、正確な記述ができています。

時間表現の正確性 ✓ PASS

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

「非推奨となった」「推奨APIとなり」「v26からは...発するようになりました」といった時間表現は、PR内の情報と正確に一致しています。