Skip to content

Commit

Permalink
module: initialize hook returns load, resolve
Browse files Browse the repository at this point in the history
This commit allows the `initialize()` hook to optionally return an
object having the `resolve()` and `load()` hooks as properties.
This allows state passed into `initialize()` to be shared with the
`resolve()` and `load()` hooks either via closure or class
instance.

In addition to developer ergonomics, supporting this model will
make it easier to write tests against a loader module. The
existing design forces state to be shared at the module level
which puts the burden of invalidating the ESM module cache
on anyone hoping to write isolated tests against a loader
module.

Fixes: #50042
  • Loading branch information
ggoodman committed Oct 24, 2023
1 parent 52fcf14 commit d27cce8
Showing 1 changed file with 26 additions and 2 deletions.
28 changes: 26 additions & 2 deletions lib/internal/modules/esm/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
AtomicsLoad,
AtomicsWait,
AtomicsWaitAsync,
FunctionPrototypeBind,
Int32Array,
ObjectAssign,
ObjectDefineProperty,
Expand Down Expand Up @@ -162,7 +163,7 @@ class Hooks {
* to the worker.
* @returns {any | Promise<any>} User data, ignored unless it's a promise, in which case it will be awaited.
*/
addCustomLoader(url, exports, data) {
async addCustomLoader(url, exports, data) {
const {
initialize,
resolve,
Expand All @@ -177,7 +178,30 @@ class Hooks {
const next = this.#chains.load[this.#chains.load.length - 1];
ArrayPrototypePush(this.#chains.load, { __proto__: null, fn: load, url, next });
}
return initialize?.(data);

const hooks = await initialize?.(data);

if (hooks?.resolve) {
if (resolve) {
throw new ERR_INTERNAL_ASSERTION(
`ESM custom loader '${url}' exposed a 'resolve' hook and returned an object with a 'resolve' hook.`,
);
}
const next = this.#chains.resolve[this.#chains.resolve.length - 1];
const boundResolve = FunctionPrototypeBind(hooks.resolve, hooks);
ArrayPrototypePush(this.#chains.resolve, { __proto__: null, fn: boundResolve, url, next });
}

if (hooks?.load) {
if (load) {
throw new ERR_INTERNAL_ASSERTION(
`ESM custom loader '${url}' exposed a 'load' hook and returned an object with a 'load' hook.`,
);
}
const next = this.#chains.load[this.#chains.load.length - 1];
const boundLoad = FunctionPrototypeBind(hooks.load, hooks);
ArrayPrototypePush(this.#chains.load, { __proto__: null, fn: boundLoad, url, next });
}
}

/**
Expand Down

0 comments on commit d27cce8

Please sign in to comment.