Repository navigation
node:module register hook chaining order behavior is incorrect #51876
Description
Activity
- added a commit that references this issue
on Feb 26, 2024 I'm not sure anymore what the intended behavior of
registerhook chaining is.
Public facing documentation describes a FIFO approach where hooks registered first are ran first, andnext<hookName>()function calls the next registered function.In this example, the registered hooks will form chains. If both
first.mjsandsecond.mjsdefine aresolvehook, both will be called, in the order they were registered.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.Meanwhile, #42623 shows that LIFO is intended, and tests are written in a way that the n^2 calling behavior above is intended.
I drafted a PR that shows how FIFO would work at #51878.
The intended behaviour is LIFO:
A is registered first and is called last (assuming nothing special).
B is registered last and is called first.
Subsequently, C is registered dynamically, making it "last" so next chain run, it is called first.
(Node's default hook) ← A ← B ← C
I see. Appears to be a documentation issue if LIFO is intended, since https://nodejs.org/api/module.html is pretty clear that hooks are ran FIFO.
Unless something changed that I'm forgetting (and I use this at my job, on node 20, so I'm pretty sure it hasn't changed), the chain is a stack.
From
[the example in the docs](https://nodejs.org/api/module.html#chaining)
// entrypoint.mjs import { register } from 'node:module'; register('./first.mjs', import.meta.url); register('./second.mjs', import.meta.url); await import('./my-app.mjs');
first.mjsis registered first, andsecond.mjsis registered second. These names are misleading (when I wrote the original docs, I specifically chose non-ordinal names to avoid this confusion).The subsequent dynamic import of
my-app.mjsis loaded via: (node's default) ←first←second.I'll update the docs today to restore the less confusing names and specifically mention LIFO (I'm pretty sure they previously did).
Reacted by Lucas Yunkyu Lee and Alex YangLooks same as #50886 which also confusing me
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
registercalls: https://gist.github.com/yklcs/9b4182e365aead6bad32e637621fd6fbHow 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:modulestates:The hooks section for
node:modulestates:What do you see instead?
Recursive calls to
next<hookName>are executed in LIFO order, with the first calls happening in FIFO order: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-loaderandtypescript-loader(as is the intended use case for chaining hooks),https-loadermust run beforetypescript-loader. However, the current behavior gives us the following loading order:Or, when chained in reverse:
Where neither version can function as intended.
Additional information
No response