Node 26+の非推奨警告を解消するESMフック登録APIの切り替え
Node.js 26でModule#registerが非推奨となったことで発生する[DEP0205]警告を解消するため、@tailwindcss/nodeがModule#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#registerとModule#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.tsとindex.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の移行期を適切に乗り越える実装になっています。