From a8414d2e6cf66a7268ac5c1672f974a2531a77dc Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 29 Mar 2026 09:55:51 -0700 Subject: [PATCH] module: runtime-deprecate module.register() (DEP0205) PR-URL: https://github.com/nodejs/node/pull/62401 --- doc/api/deprecations.md | 14 ++++- doc/api/module.md | 3 + lib/internal/modules/esm/loader.js | 14 ++++- .../test-esm-register-deprecation.mjs | 60 +++++++++++++++++++ ...ync-loader-hooks-use-hooks-require-esm.mjs | 1 + 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 test/es-module/test-esm-register-deprecation.mjs diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 0bfe9a3fa3a463..864846efaad26b 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -4527,16 +4527,27 @@ deprecated and will throw an error in a future version. -Type: Documentation-only +Type: Runtime [`module.register()`][] is deprecated. Use [`module.registerHooks()`][] instead. +The `module.register()` API provides off-thread async hooks for customizing ES modules; +the `module.registerHooks()` API provides similar hooks that are synchronous, in-thread, and +work for all types of modules. +Supporting async hooks has proven to be complex, involving worker threads orchestration, and there are issues +that have proven unresolveable. See [caveats of asynchronous customization hooks][]. Please migrate to +`module.registerHooks()` as soon as possible as `module.register()` will be +removed in a future version of Node.js. + [DEP0142]: #dep0142-repl_builtinlibs [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 @@ -4696,6 +4707,7 @@ instead. [`zlib.bytesWritten`]: zlib.md#zlibbyteswritten [alloc]: buffer.md#static-method-bufferallocsize-fill-encoding [alloc_unsafe_size]: buffer.md#static-method-bufferallocunsafesize +[caveats of asynchronous customization hooks]: module.md#caveats-of-asynchronous-customization-hooks [from_arraybuffer]: buffer.md#static-method-bufferfromarraybuffer-byteoffset-length [from_string_encoding]: buffer.md#static-method-bufferfromstring-encoding [legacy URL API]: url.md#legacy-url-api diff --git a/doc/api/module.md b/doc/api/module.md index 465749f28731c0..e0f1abad62d03e 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -180,6 +180,9 @@ added: - v18.19.0 deprecated: REPLACEME changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/62401 + description: Runtime deprecation (DEP0205). - version: REPLACEME pr-url: https://github.com/nodejs/node/pull/62395 description: Documentation-only deprecation (DEP0205). Use diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 04c374c00cfc3e..8ae6761fba571a 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -31,7 +31,10 @@ const { } = require('internal/errors').codes; const { getOptionValue } = require('internal/options'); const { isURL, pathToFileURL } = require('internal/url'); -const { kEmptyObject } = require('internal/util'); +const { + getDeprecationWarningEmitter, + kEmptyObject, +} = require('internal/util'); const { compileSourceTextModule, SourceTextModuleTypes: { kUser }, @@ -955,7 +958,16 @@ function isCascadedLoaderInitialized() { * }); * ``` */ +const emitRegisterDeprecation = getDeprecationWarningEmitter( + 'DEP0205', + '`module.register()` is deprecated. Use `module.registerHooks()` instead.', + undefined, + false, +); + function register(specifier, parentURL = undefined, options) { + emitRegisterDeprecation(); + if (parentURL != null && typeof parentURL === 'object' && !isURL(parentURL)) { options = parentURL; parentURL = options.parentURL; diff --git a/test/es-module/test-esm-register-deprecation.mjs b/test/es-module/test-esm-register-deprecation.mjs new file mode 100644 index 00000000000000..171e5971a091fe --- /dev/null +++ b/test/es-module/test-esm-register-deprecation.mjs @@ -0,0 +1,60 @@ +import { spawnPromisified } from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; + +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import { describe, it } from 'node:test'; + +const urlToRegister = fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'); +const urlToRegisterEscaped = JSON.stringify(urlToRegister.href); + + +describe('module.register() deprecation (DEP0205)', { concurrency: !process.env.TEST_PARALLEL }, () => { + it('emits DEP0205 when module.register() is called', async () => { + const { code, stderr } = await spawnPromisified(execPath, [ + '--input-type=module', + '--eval', + `import { register } from 'node:module'; register(${urlToRegisterEscaped});`, + ]); + + assert.match(stderr, /\[DEP0205\]/); + assert.strictEqual(code, 0); + }); + + it('only emits the warning once per process', async () => { + const { code, stderr } = await spawnPromisified(execPath, [ + '--input-type=module', + '--eval', + `import { register } from 'node:module'; + register(${urlToRegisterEscaped}); + register(${urlToRegisterEscaped});`, + ]); + + assert.strictEqual(stderr.split('[DEP0205]').length - 1, 1); + assert.strictEqual(code, 0); + }); + + it('does not emit when --no-deprecation is set', async () => { + const { code, stderr } = await spawnPromisified(execPath, [ + '--no-deprecation', + '--input-type=module', + '--eval', + `import { register } from 'node:module'; register(${urlToRegisterEscaped});`, + ]); + + assert.doesNotMatch(stderr, /DEP0205/); + assert.strictEqual(code, 0); + }); + + it('throws when --throw-deprecation is set', async () => { + const { code, stderr } = await spawnPromisified(execPath, [ + '--throw-deprecation', + '--input-type=module', + '--eval', + `import { register } from 'node:module'; register(${urlToRegisterEscaped});`, + ]); + + assert.match(stderr, /DEP0205/); + assert.notStrictEqual(code, 0); + }); +}); diff --git a/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs b/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs index a86b0607246fa9..1eee6a6014aa15 100644 --- a/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs +++ b/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs @@ -7,6 +7,7 @@ import { spawnSyncAndAssert } from '../common/child_process.js'; spawnSyncAndAssert( execPath, [ + '--no-deprecation', '--no-experimental-require-module', '--import', fixtures.fileURL('es-module-loaders/builtin-named-exports.mjs'),