Skip to content

Conversation

joyeecheung
Copy link
Member

@joyeecheung joyeecheung commented Oct 16, 2025

Paving the way for #59666, this patch does not introduce observable changes, mostly clarifying which paths are sync/async, or run on the async loader hook thread/on the non-async-loader-hook thread, and updating the comments so that it's easier to sync-ify the paths later.

  • This updates the comments that assume loader hooks must be async
  • Differentiate the sync/async loader hook paths in naming #customizations is now #asyncLoaderHooks to make it clear it's from the async APIs.
  • Differentiate the paths running on the loader hook thread (affects the loading of async other loader hooks and are async) v.s. paths on the main thread calling out to code on the loader hook thread (do not handle loading of other async loader hooks, and can be sync by blocking).
    • Hooks is now AsyncLoaderHooksOnLoaderHookWorker
    • CustomizedModuleLoader is now AsyncLoaderHooksProxiedToLoaderHookWorker and moved into lib/internal/modules/esm/hooks.js as it implements the same interface as AsyncLoaderHooksOnLoaderHookWorker
    • HooksProxy is now AsyncLoaderHookWorker
    • Adjust the JSDoc accordingly
  • Clarify the "loader worker" as the "async loader hook worker" i.e. when there's no async loader hook registered, there won't be this worker, to avoid the misconception that this worker is spawned unconditionally.
  • The code run on the loader hook worker to process --experimental-loader is moved into lib/internal/modules/esm/worker.js for clarity.
  • The initialization configuration forceDefaultLoader is split into shouldSpawnLoaderHookWorker and shouldPreloadModules as those can be separate.
  • --experimental-vm-modules is now processed during pre-execution and no longer part of the initialization of the built-in ESM loader, as it only exposes the vm APIs of ESM, and is unrelated to built-in ESM loading.

- This updates the comments that assume loader hooks must be async
- Differentiate the sync/async loader hook paths in naming
  `#customizations` is now `#asyncLoaderHooks` to make it clear
  it's from the async APIs.
- Differentiate the paths running on the loader hook thread
  (affects the loading of async other loader hooks and are async)
  v.s. paths on the main thread calling out to code on the loader
  hook thread (do not handle loading of other async loader hooks, and
  can be sync by blocking).
  - `Hooks` is now `AsyncLoaderHooksOnLoaderHookWorker`
  - `CustomizedModuleLoader` is now
    `AsyncLoaderHooksProxiedToLoaderHookWorker` and moved into
    `lib/internal/modules/esm/hooks.js` as it implements the same
    interface as `AsyncLoaderHooksOnLoaderHookWorker`
  - `HooksProxy` is now `AsyncLoaderHookWorker`
  - Adjust the JSDoc accordingly
- Clarify the "loader worker" as the "async loader hook worker"
  i.e. when there's no _async_ loader hook registered, there won't
  be this worker, to avoid the misconception that this worker
  is spawned unconditionally.
- The code run on the loader hook worker to process
  `--experimental-loader` is moved into
  `lib/internal/modules/esm/worker.js` for clarity.
- The initialization configuration `forceDefaultLoader` is split
  into `shouldSpawnLoaderHookWorker` and `shouldPreloadModules`
  as those can be separate.
- `--experimental-vm-modules` is now processed during pre-execution
  and no longer part of the initialization of the built-in ESM
  loader, as it only exposes the vm APIs of ESM, and is unrelated
  to built-in ESM loading.
@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/loaders
  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Oct 16, 2025
switch (doEval) {
case 'internal': {
// Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
internalBinding('module_wrap').callbackMap = new SafeWeakMap();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is removed as a drive-by, the callbackMap is no longer used after #48510

Copy link

codecov bot commented Oct 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.58%. Comparing base (a520a39) to head (f585876).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #60278      +/-   ##
==========================================
+ Coverage   88.56%   88.58%   +0.01%     
==========================================
  Files         704      704              
  Lines      208323   208369      +46     
  Branches    40033    40038       +5     
==========================================
+ Hits       184501   184581      +80     
+ Misses      15854    15823      -31     
+ Partials     7968     7965       -3     
Files with missing lines Coverage Δ
lib/internal/main/worker_thread.js 94.44% <100.00%> (+0.03%) ⬆️
lib/internal/modules/esm/hooks.js 92.00% <100.00%> (+1.32%) ⬆️
lib/internal/modules/esm/loader.js 95.79% <100.00%> (-0.39%) ⬇️
lib/internal/modules/esm/utils.js 100.00% <100.00%> (ø)
lib/internal/modules/esm/worker.js 92.78% <100.00%> (+1.34%) ⬆️
lib/internal/modules/run_main.js 97.64% <100.00%> (ø)
lib/internal/process/pre_execution.js 97.33% <100.00%> (-0.78%) ⬇️

... and 44 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot
Copy link
Collaborator

/**
* Interface for classes that implement asynchronous loader hooks that can be attached to the ModuleLoader
* via `ModuleLoader.#setAsyncLoaderHooks()`.
* @typedef {object} AsyncLoaderHooks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the reason for the “customization“ and “customization hooks“ names is to avoid confusion where some might think that these hooks only affect loading, when they also impact resolution and other things. It hasn’t been a priority to update the code, but as long as we’re adding new code and defining new names, I think the “customization“ name is more informative and correct.

Suggested change
* @typedef {object} AsyncLoaderHooks
* @typedef {object} AsyncCustomizationHooks

And elsewhere that the code uses “loader hook“ that this branch touches.

Copy link
Member Author

@joyeecheung joyeecheung Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case should AsyncLoaderHooksOnLoaderHookWorker be AsyncCustomizationHooksOnCustomizationHookWorker? That feels rather lengthy..

I feel that "loader hooks" contain already ample information about it: it's loader, which implies it's not just "loading" phase but everything you'd see in loader.js and the ModuleLoader class. I can see that "load" would be ambuiguous, but "loader" isn't, and "hooks" implies customizations (or the hooks aren't necessarily for customizations - they can also be for observations).

Copy link
Member

@GeoffreyBooth GeoffreyBooth Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a nit, do with it as you will. AsyncCustomizationHooksOnCustomizationHookWorker is the same number of words as with Loader, it's just a longer word. It's a mouthful in either version. Perhaps just AsyncOffThreadCustomizationHooks?

Another name instead of “customization hooks“ is “module hooks“ which is just as descriptive and the same number of letters as “loader hooks”.

Copy link
Member Author

@joyeecheung joyeecheung Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the brainstorm ;) though to me "foo hooks" means "it's hooking into foo/hook placed in foo", so I'd have some questions when I see "module hooks" - is it hooking into the code of user modules/hook placed in the modules? While it can be used in such a way that inject code into user modules, that is more of the output, instead of the process. "loader hooks" seem more intuitive because, there's a ModuleLoader in loader.js next to it, which either new AsyncLoaderHooksProxiedToLoaderHookWorker itself, or get a new AsyncLoaderHooksOnLoaderHookWorker passed into it, and the two classes have methods that are hooking into the methods of ModuleLoader with the same name, so that makes a lot of sense.

(I thought about just calling it async hooks but then unfortunately that combination might be a bit too well known as something completely unrelated in Node.js..)

@JakobJingleheimer JakobJingleheimer self-requested a review October 16, 2025 16:38
@nodejs-github-bot
Copy link
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants