Skip to content

Commit fc432e0

Browse files
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>
1 parent ae96721 commit fc432e0

4 files changed

Lines changed: 34 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Remove deprecation warnings by using `Module#registerHooks` instead of `Module#register` on Node 26+ ([#20028](https://github.com/tailwindlabs/tailwindcss/pull/20028))
1113

1214
## [4.3.0] - 2026-05-08
1315

packages/@tailwindcss-node/src/esm-cache.loader.mts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
import { isBuiltin, type ResolveHook } from 'node:module'
1+
import {
2+
isBuiltin,
3+
type ResolveFnOutput,
4+
type ResolveHook,
5+
type ResolveHookContext,
6+
type ResolveHookSync,
7+
} from 'node:module'
28

39
export let resolve: ResolveHook = async (specifier, context, nextResolve) => {
410
let result = await nextResolve(specifier, context)
11+
return processResolve(context, result)
12+
}
13+
14+
export let resolveSync: ResolveHookSync = (specifier, context, nextResolve) => {
15+
let result = nextResolve(specifier, context)
16+
return processResolve(context, result)
17+
}
518

19+
function processResolve(context: ResolveHookContext, result: ResolveFnOutput) {
620
if (result.url === import.meta.url) return result
721
if (isBuiltin(result.url)) return result
822
if (!context.parentURL) return result

packages/@tailwindcss-node/src/index.cts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as Module from 'node:module'
22
import { pathToFileURL } from 'node:url'
33
import * as env from './env'
4+
import { resolveSync } from './esm-cache.loader.mjs'
45
export * from './compile'
56
export * from './instrumentation'
67
export * from './normalize-path'
@@ -12,8 +13,14 @@ export { env }
1213
// not necessary.
1314
if (!process.versions.bun) {
1415
// `Module#register` was added in Node v18.19.0 and v20.6.0
16+
// `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0,
17+
// runtime-deprecating `Module#register` since v26
1518
//
1619
// Not calling it means that while ESM dependencies don't get reloaded, the
1720
// actual included files will because they cache bust directly via `?id=…`
18-
Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader')))
21+
if (Module.registerHooks) {
22+
Module.registerHooks({ resolve: resolveSync })
23+
} else {
24+
Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader')))
25+
}
1926
}

packages/@tailwindcss-node/src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as Module from 'node:module'
22
import { pathToFileURL } from 'node:url'
33
import * as env from './env'
4+
import { resolveSync } from './esm-cache.loader.mjs'
45
export * from './compile'
56
export * from './instrumentation'
67
export * from './normalize-path'
@@ -14,8 +15,14 @@ if (!process.versions.bun) {
1415
let localRequire = Module.createRequire(import.meta.url)
1516

1617
// `Module#register` was added in Node v18.19.0 and v20.6.0
18+
// `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0,
19+
// runtime-deprecating `Module#register` since v26
1720
//
1821
// Not calling it means that while ESM dependencies don't get reloaded, the
1922
// actual included files will because they cache bust directly via `?id=…`
20-
Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader')))
23+
if (Module.registerHooks) {
24+
Module.registerHooks({ resolve: resolveSync })
25+
} else {
26+
Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader')))
27+
}
2128
}

0 commit comments

Comments
 (0)