Skip to content

esm: fix register hook chaining behavior #51878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions lib/internal/modules/esm/hooks.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

const {
ArrayPrototypePush,
ArrayPrototypePushApply,
ArrayPrototypeSplice,
AtomicsLoad,
AtomicsWait,
AtomicsWaitAsync,
Expand Down Expand Up @@ -48,6 +48,7 @@ const {
kEmptyObject,
} = require('internal/util');

const { createModuleLoader } = require('internal/modules/esm/loader');
const {
defaultResolve,
throwIfInvalidParentURL,
Expand Down Expand Up @@ -146,7 +147,7 @@ class Hooks {
* loader (user-land) to the worker.
*/
async register(urlOrSpecifier, parentURL, data) {
const moduleLoader = require('internal/process/esm_loader').esmLoader;
const moduleLoader = createModuleLoader();
Comment on lines -149 to +150
Copy link
Member

Choose a reason for hiding this comment

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

Was this change necessary for it to work? The existing code was done this way for a reason (to reduce node's startup time by loading the dependency just in time). You'll see that done in many places within this file.

const keyedExports = await moduleLoader.import(
urlOrSpecifier,
parentURL,
Expand All @@ -171,12 +172,14 @@ class Hooks {
} = pluckHooks(exports);

if (resolve) {
const next = this.#chains.resolve[this.#chains.resolve.length - 1];
ArrayPrototypePush(this.#chains.resolve, { __proto__: null, fn: resolve, url, next });
spliceHookChain(this.#chains.resolve, this.#chains.resolve.length - 1, {
__proto__: null, fn: resolve, url,
});
}
if (load) {
const next = this.#chains.load[this.#chains.load.length - 1];
ArrayPrototypePush(this.#chains.load, { __proto__: null, fn: load, url, next });
spliceHookChain(this.#chains.load, this.#chains.load.length - 1, {
__proto__: null, fn: load, url,
});
}
return initialize?.(data);
}
Expand Down Expand Up @@ -232,8 +235,7 @@ class Hooks {
);
}
};

const nextResolve = nextHookFactory(chain[chain.length - 1], meta, { validateArgs, validateOutput });
const nextResolve = nextHookFactory(chain[0], meta, { validateArgs, validateOutput });

const resolution = await nextResolve(originalSpecifier, context);
const { hookErrIdentifier } = meta; // Retrieve the value after all settled
Expand Down Expand Up @@ -378,7 +380,7 @@ class Hooks {
}
};

const nextLoad = nextHookFactory(chain[chain.length - 1], meta, { validateArgs, validateOutput });
const nextLoad = nextHookFactory(chain[0], meta, { validateArgs, validateOutput });

const loaded = await nextLoad(url, defineImportAssertionAlias(context));
const { hookErrIdentifier } = meta; // Retrieve the value after all settled
Expand Down Expand Up @@ -684,6 +686,27 @@ function pluckHooks({
return acceptedHooks;
}

/**
* A utility function to splice new hook into chains
* @param {KeyedHook[]} chain
* @param {number} position Position to splice hook into
* @param {KeyedHook} hook Hook to splice into chain
*/
function spliceHookChain(chain, position, hook) {
if (position < 0 || position >= chain.length) {
throw new ERR_INTERNAL_ASSERTION(`
Position ${position} not valid for chain of length ${chain}
`);
}

const previous = chain[position - 1];
hook.next = chain[position];
ArrayPrototypeSplice(chain, position, 0, hook);

if (previous) {
previous.next = hook;
}
}

/**
* A utility function to iterate through a hook chain, track advancement in the
Expand Down