Skip to content

module: expose requestType to user hooks#1

Draft
privatenumber wants to merge 2 commits into
mainfrom
hiroki/module-hooks-request-type
Draft

module: expose requestType to user hooks#1
privatenumber wants to merge 2 commits into
mainfrom
hiroki/module-hooks-request-type

Conversation

@privatenumber
Copy link
Copy Markdown
Owner

@privatenumber privatenumber commented May 21, 2026

User-supplied module hooks cannot reliably tell whether the current resolve or load is on behalf of a require() call or an import. The async load hook also receives no context.conditions at all. Loaders (tsx, ts-node, swc-node) work around this today by inspecting Error().stack for internal frames like loadAndTranslateForRequireInImportedCJS, which is brittle and breaks under composed loaders such as Yarn PnP.

This patch:

  • Adds context.requestType: 'import' | 'require' to both resolve and load hook contexts, in sync (module.registerHooks) and async (module.register) modes. 'require' covers require(), the require(esm) bridge, and require.resolve() in imported CJS; 'import' covers import statements, import() expressions, import.meta.resolve(), and the kImportInRequiredESM case.
  • Selects context.conditions from requestType. Sync hooks for require-bridge loads now see CJS conditions instead of ESM defaults; async hooks receive conditions for the first time.
  • Extracts toRequestTypeString() in esm/utils.js and getConditionsForRequestType() in modules/helpers.js so the mapping lives in one place.
  • Constructs a fresh context in AsyncLoaderHooksOnLoaderHookWorker.load instead of mutating the IPC-arrived one, matching the sibling resolve.
  • Closes the TODO at customization_hooks.js:336.

The field is added to both module.registerHooks and the runtime-deprecated module.register so today's module.register users have parity while they migrate. The docs and the stability banner steer new users toward module.registerHooks.

Refs: nodejs#51327

Add `context.requestType` ('import' | 'require') to both resolve and load
hook contexts, in sync (`module.registerHooks`) and async (`module.register`)
modes. The value is 'require' for `require()` calls (including the
`require(esm)` bridge) and 'import' for `import` statements, dynamic
`import()`, and `import.meta.resolve()`.

The async load hook also now receives `context.conditions` (previously
absent), and conditions are computed from the request type so sync hooks
no longer see ESM defaults when the load is on behalf of a `require()`.

This lets loaders distinguish a `require()` from an `import` without
inspecting the call stack, and gives them the correct package-exports
conditions for the request.

Refs: nodejs#51327
Pre-submission cleanup based on reviewer-style anticipation:

* Extract `toRequestTypeString(requestType)` in esm/utils.js so the
  `kRequireInImportedCJS` -> `'require'` mapping lives in one place
  instead of being inlined at three call sites.

* Extract `getConditionsForRequestType(requestType, defaultEsmConditions)`
  in modules/helpers.js so the request-type -> conditions selection logic
  is shared between sync and async hooks.

* In `AsyncLoaderHooksOnLoaderHookWorker.load`, construct a fresh
  context object rather than mutating the one delivered over IPC. This
  matches the pattern used by the sibling `resolve` method and avoids
  surprising callers that pass a context through `nextLoad`.

* Add tests covering the kImportInRequiredESM case (import inside a
  require()'d ESM stays `requestType: 'import'`), `import.meta.resolve()`,
  chained hooks where an intermediate hook drops the context, and the
  async-loader CJS bridge regression (the original repro for nodejs#51327).

* Document `context.requestType` and the new `context.conditions` on the
  async load hook with YAML `changes:` entries, and cross-link
  `module.register` to its replacement `module.registerHooks`.

Refs: nodejs#51327
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant