Repository navigation
module.register() in the prerender loader prints DEP0205 on Node 26 #34358
Description
Command
build
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
@angular/build registers its in-memory ESM loader with module.register(), which Node runtime-deprecated in Node 26. The call is in src/utils/server-rendering/esm-in-memory-loader/register-hooks.js:
register('./loader-hooks.js', { parentURL: pathToFileURL(__filename), data: workerData });esm-in-memory-loader/utils.js points IMPORT_EXEC_ARGV at that file, and server-rendering/prerender.js passes it as the execArgv of every prerender worker, so each worker prints DEP0205 to stderr during ng build.
Node runtime-deprecated the API in nodejs/node#62401, merged 2026-04-01 and labelled semver-major, so the warning starts at Node 26. @angular/build 22.2.2 declares engines.node as ^22.22.3 || ^24.15.0 || >=26.0.0, so this is a supported Node version.
This is not an Angular regression, the code did not change, Node did.
Minimal Reproduction
Any app with prerendering, built on Node 26:
ng new repro --ssrand keep prerendering enabled.ng buildon Node 26.- DEP0205 appears on stderr, once per prerender worker.
Measured on our app with Angular CLI 22.2.2, @angular/build 22.2.2, Node 26.5.0 on Windows, and on our CI agent (Linux, Node 26) in the production build step.
Also reproduced without a build, which isolates the loader from the rest of the pipeline. A worker created the way prerender.js creates one:
const { Worker } = require('node:worker_threads');
const { IMPORT_EXEC_ARGV } = require('@angular/build/src/utils/server-rendering/esm-in-memory-loader/utils.js');
new Worker('./stay-alive.cjs', {
execArgv: [IMPORT_EXEC_ARGV],
workerData: { outputFiles: {}, workspaceRoot: process.cwd() },
});with stay-alive.cjs holding the worker open for a moment (setTimeout(() => {}, 700)). Without the flag it prints DEP0205 in 3 of 3 runs, with NODE_OPTIONS=--disable-warning=DEP0205 it is clean in 3 of 3 runs.
Worth knowing when reproducing: DEP0205 is emitted asynchronously, so a worker whose body is process.exit(0) exits before the warning flushes and the run looks clean.
Exception or Error
(node:24372) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Your Environment
Angular CLI : 22.2.2
Angular : 22.2.1
Node.js : 26.5.0
Package Manager : pnpm 12.3.1
Operating System : win32 x64
@angular-devkit/build-angular 22.2.2
@angular-devkit/core 22.2.2
@angular-devkit/schematics 22.2.2
@angular/build 22.2.2
@angular/cli 22.2.2
@angular/core 22.2.1
@angular/platform-server 22.2.1
@angular/ssr 22.2.2
rxjs 7.8.2
typescript 6.0.3
zone.js 0.16.3
Anything else relevant?
A possible fix, from reading the two hook files. module.registerHooks() takes synchronous hooks, and loader-hooks.js is almost there already: initialize and resolve are synchronous, and load has exactly one await, await readFile(filePath), which readFileSync covers. The data: workerData channel is not needed either, because the hooks already run inside a worker that can read workerData directly.
The one real difference to weigh is that registerHooks runs the hooks on the calling thread rather than on a separate loader thread, so the synchronous read would block it.
The same Node 26 warning reported elsewhere, in case it helps: payloadcms/payload#18361 and Julien-R44/hot-hook#18.
What we do meanwhile: NODE_OPTIONS=--disable-warning=DEP0205 on the build step, which silences only this code.
Activity
fix(@angular/build): use module.registerHooks() for in-memory ESM loader
fix(@angular/build): use module.registerHooks() for in-memory ESM loader
fix(@angular/build): use module.registerHooks() for in-memory ESM loader