fix(node): prefer Module#registerHooks over Module#register (#20028)

<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

Fix this warning when building apps with TailwindCSS with Node 26+:
```
(node:25346) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
    at node:internal/util:129:11
    at Module.register (node:internal/modules/esm/loader:969:3)
    at file:///Users/antoine/Developer/my-app/node_modules/.pnpm/@tailwindcss+node@4.3.0/node_modules/@tailwindcss/node/dist/index.mjs:18:214
    at ...
```

This PR:
- Correctly prefers using `Module#registerHooks` instead of
`Module#register` by checking its availability at runtime
- Adjusts the exports of the hooks’ file by creating a synchronous
version for the new API
- Remove now unused exports from the `package.json`, relying on the
[recommended usage in the
docs](https://nodejs.org/docs/latest/api/module.html#registration-of-asynchronous-customization-hooks)
for the `Module#register` calls

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

I'd be happy to ensure this works correctly on my end before merging
this, but it's not as trivial to test locally as a logic fix. Can you
guide me through what I need to do to test this?

I extensively based my change on the docs, following those guides:
- [Fixing imports for
`Module#register`](https://nodejs.org/docs/latest/api/module.html#registration-of-asynchronous-customization-hooks)
- [Using the new `Module#registerHooks`
function](https://nodejs.org/docs/latest/api/module.html#registration-of-synchronous-customization-hooks)

(and other more detailed sections of the same docs page)

---

Fixes #19893
Closes #19907

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This commit is contained in:
Antoine Lethimonnier 2026-05-11 16:25:28 +02:00 • committed by GitHub
parent ae96721fc5
commit fc432e0fd5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 34 additions and 4 deletions

View file

@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Nothing yet!
### Fixed
- Remove deprecation warnings by using `Module#registerHooks` instead of `Module#register` on Node 26+ ([#20028](https://github.com/tailwindlabs/tailwindcss/pull/20028))
## [4.3.0] - 2026-05-08

View file

@ -1,8 +1,22 @@
import { isBuiltin, type ResolveHook } from 'node:module'
import {
isBuiltin,
type ResolveFnOutput,
type ResolveHook,
type ResolveHookContext,
type ResolveHookSync,
} from 'node:module'
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

View file

@ -1,6 +1,7 @@
import * as Module from 'node:module'
import { pathToFileURL } from 'node:url'
import * as env from './env'
import { resolveSync } from './esm-cache.loader.mjs'
export * from './compile'
export * from './instrumentation'
export * from './normalize-path'
@ -12,8 +13,14 @@ export { env }
// not necessary.
if (!process.versions.bun) {
// `Module#register` was added in Node v18.19.0 and v20.6.0
// `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0,
// runtime-deprecating `Module#register` since v26
//
// Not calling it means that while ESM dependencies don't get reloaded, the
// actual included files will because they cache bust directly via `?id=…`
Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader')))
if (Module.registerHooks) {
Module.registerHooks({ resolve: resolveSync })
} else {
Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader')))
}
}

View file

@ -1,6 +1,7 @@
import * as Module from 'node:module'
import { pathToFileURL } from 'node:url'
import * as env from './env'
import { resolveSync } from './esm-cache.loader.mjs'
export * from './compile'
export * from './instrumentation'
export * from './normalize-path'
@ -14,8 +15,14 @@ if (!process.versions.bun) {
let localRequire = Module.createRequire(import.meta.url)
// `Module#register` was added in Node v18.19.0 and v20.6.0
// `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0,
// runtime-deprecating `Module#register` since v26
//
// Not calling it means that while ESM dependencies don't get reloaded, the
// actual included files will because they cache bust directly via `?id=…`
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')))
}
}