Skip to content

node:module register hook chaining order behavior is incorrect #51876

Closed
@yklcs

Description

@yklcs

Version

v21.6.1

Platform

Darwin mbpll.local 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct 9 21:28:12 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T8103 arm64

Subsystem

module

What steps will reproduce the bug?

Chaining hooks via multiple register calls: https://gist.github.com/yklcs/9b4182e365aead6bad32e637621fd6fb

How often does it reproduce? Is there a required condition?

No response

What is the expected behavior? Why is that the expected behavior?

Loader hooks should get executed in registration order (FIFO). This is the behavior consistent with documentation:

The chaining section for node:module states:

In this example, the registered hooks will form chains. If both first.mjs and second.mjs define a resolve hook, both will be called, in the order they were registered. The same applies to all the other hooks.

The hooks section for node:module states:

Hooks are part of a chain, even if that chain consists of only one custom (user-provided) hook and the default hook, which is always present. Hook functions nest: each one must always return a plain object, and chaining happens as a result of each function calling next<hookName>(), which is a reference to the subsequent loader's hook.

loader 1 initialize
loader 2 initialize
loader 3 initialize

loader 1 resolve
loader 2 resolve
loader 3 resolve

loader 1 load
loader 2 load
loader 3 load

What do you see instead?

Recursive calls to next<hookName> are executed in LIFO order, with the first calls happening in FIFO order:

loader 1 initialize
loader 1 resolve
loader 1 load

loader 2 initialize
loader 2 resolve
loader 1 resolve
loader 2 load
loader 1 load

loader 3 initialize
loader 3 resolve
loader 2 resolve
loader 1 resolve
loader 3 load
loader 2 load
loader 1 load

This appears to be incorrect because:

1. The same hook is executed multiple times (n^2 behavior)

2. The dependency chain between hooks is broken. For example, if we tried to import by chaining https-loader and typescript-loader (as is the intended use case for chaining hooks), https-loader must run before typescript-loader. However, the current behavior gives us the following loading order:

https-loader load

typescript-loader load
https-loader load

Or, when chained in reverse:

typescript-loader load

https-loader load
typescript-loader load

Where neither version can function as intended.

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions