Skip to content
Open
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions lib/internal/modules/esm/default_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const { defaultLoad } = require('internal/modules/esm/load');
const { defaultResolve } = require('internal/modules/esm/resolve');

exports.resolve = defaultResolve;
exports.load = defaultLoad;
99 changes: 45 additions & 54 deletions lib/internal/modules/esm/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const {
} = require('internal/util');

const {
defaultResolve,
throwIfInvalidParentURL,
} = require('internal/modules/esm/resolve');
const {
Expand Down Expand Up @@ -87,45 +86,40 @@ let importMetaInitializer;
// [2] `validate...()`s throw the wrong error

class Hooks {
#chains = {
/**
* Prior to ESM loading. These are called once before any modules are started.
* @private
* @property {KeyedHook[]} globalPreload Last-in-first-out list of preload hooks.
*/
globalPreload: [],

/**
* Phase 1 of 2 in ESM loading.
* The output of the `resolve` chain of hooks is passed into the `load` chain of hooks.
* @private
* @property {KeyedHook[]} resolve Last-in-first-out collection of resolve hooks.
*/
resolve: [
{
fn: defaultResolve,
url: 'node:internal/modules/esm/resolve',
},
],

/**
* Phase 2 of 2 in ESM loading.
* @private
* @property {KeyedHook[]} load Last-in-first-out collection of loader hooks.
*/
load: [
{
fn: require('internal/modules/esm/load').defaultLoad,
url: 'node:internal/modules/esm/load',
},
],
};
#loaderInstances = [];
#chains = {};

// Cache URLs we've already validated to avoid repeated validation
#validatedUrls = new SafeSet();

allowImportMetaResolve = false;

constructor() {
const defaultLoader = 'internal/modules/esm/default_loader';
this.addCustomLoader(`node:${defaultLoader}`, require(defaultLoader));
}

#rebuildChain(name) {
const chain = this.#chains[name] = [];
let i = 0;
for (const instance of this.#loaderInstances) {
if (typeof instance[name] !== 'function') {
continue;
}
chain.push({
loader: instance,
fn: instance[name],
next: chain[i++ - 1],
});
}
}

#rebuildChains() {
this.#rebuildChain('globalPreload');
this.#rebuildChain('resolve');
this.#rebuildChain('load');
}

/**
* Import and register custom/user-defined module loader hook(s).
* @param {string} urlOrSpecifier
Expand Down Expand Up @@ -164,25 +158,26 @@ class Hooks {
emitExperimentalWarning(
'`globalPreload` is planned for removal in favor of `initialize`. `globalPreload`',
);
ArrayPrototypePush(this.#chains.globalPreload, { __proto__: null, fn: globalPreload, url });
}
if (resolve) {
const next = this.#chains.resolve[this.#chains.resolve.length - 1];
ArrayPrototypePush(this.#chains.resolve, { __proto__: null, fn: resolve, url, next });
}
if (load) {
const next = this.#chains.load[this.#chains.load.length - 1];
ArrayPrototypePush(this.#chains.load, { __proto__: null, fn: load, url, next });
}
return initialize?.(data);
const instance = {
__proto__: null,
url,
globalPreload,
initialize,
resolve,
load,
};
ArrayPrototypePush(this.#loaderInstances, instance);
this.#rebuildChains();
return initialize?.(data, { __proto__: null, id: instance.id, url });
}

/**
* Initialize `globalPreload` hooks.
*/
initializeGlobalPreload() {
const preloadScripts = [];
for (let i = this.#chains.globalPreload.length - 1; i >= 0; i--) {
for (const chainEntry of this.#chains.globalPreload) {
const { MessageChannel } = require('internal/worker/io');
const channel = new MessageChannel();
const {
Expand All @@ -193,10 +188,8 @@ class Hooks {
insidePreload.unref();
insideLoader.unref();

const {
fn: preload,
url: specifier,
} = this.#chains.globalPreload[i];
const preload = chainEntry.fn;
const specifier = chainEntry.loader.url;

const preloaded = preload({
port: insideLoader,
Expand Down Expand Up @@ -789,11 +782,9 @@ function pluckHooks({
function nextHookFactory(current, meta, { validateArgs, validateOutput }) {
// First, prepare the current
const { hookName } = meta;
const {
fn: hook,
url: hookFilePath,
next,
} = current;

const { next, fn: hook, loader } = current;
const { url: hookFilePath } = loader;

// ex 'nextResolve'
const nextHookName = `next${
Expand Down