Skip to content
Permalink
Browse files
esm,loader: tidy ESMLoader internals
PR-URL: #44701
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
JakobJingleheimer authored and RafaelGSS committed Sep 26, 2022
1 parent 97d2ed7 commit 9b3b7d6978bbda570b85bc6367f27d4fbc9bb664
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 77 deletions.
@@ -1,8 +1,6 @@
'use strict';
const {
RegExpPrototypeExec,
ObjectAssign,
ObjectCreate,
ObjectPrototypeHasOwnProperty,
PromisePrototypeThen,
PromiseResolve,
@@ -25,13 +23,14 @@ const { getPackageType, getPackageScopeConfig } = require('internal/modules/esm/
const { URL, fileURLToPath } = require('internal/url');
const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes;

const protocolHandlers = ObjectAssign(ObjectCreate(null), {
const protocolHandlers = {
'__proto__': null,
'data:': getDataProtocolModuleFormat,
'file:': getFileProtocolModuleFormat,
'http:': getHttpProtocolModuleFormat,
'https:': getHttpProtocolModuleFormat,
'node:'() { return 'builtin'; },
});
};

/**
* @param {URL} parsed
@@ -179,39 +179,38 @@ function nextHookFactory(chain, meta, { validateArgs, validateOutput }) {
* the main module and everything in its dependency graph.
*/
class ESMLoader {
/**
* Prior to ESM loading. These are called once before any modules are started.
* @private
* @property {KeyedHook[]} globalPreloaders Last-in-first-out
* list of preload hooks.
*/
#globalPreloaders = [];

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

/**
* Phase 1 of 2 in ESM loading.
* @private
* @property {KeyedHook[]} resolvers Last-in-first-out
* collection of resolver hooks.
*/
#resolvers = [
{
fn: defaultResolve,
url: 'node:internal/modules/esm/resolve',
},
];
#hooks = {
/**
* 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 2 of 2 in ESM loading (phase 1 is below).
* @private
* @property {KeyedHook[]} load Last-in-first-out collection of loader hooks.
*/
load: [
{
fn: defaultLoad,
url: 'node:internal/modules/esm/load',
},
],

/**
* Phase 1 of 2 in ESM loading.
* @private
* @property {KeyedHook[]} resolve Last-in-first-out collection of resolve hooks.
*/
resolve: [
{
fn: defaultResolve,
url: 'node:internal/modules/esm/resolve',
},
],
};

#importMetaInitializer = initializeImportMeta;

@@ -305,13 +304,13 @@ class ESMLoader {
);

if (globalPreload) {
acceptedHooks.globalPreloader = globalPreload;
acceptedHooks.globalPreload = globalPreload;
}
if (resolve) {
acceptedHooks.resolver = resolve;
acceptedHooks.resolve = resolve;
}
if (load) {
acceptedHooks.loader = load;
acceptedHooks.load = load;
}

return acceptedHooks;
@@ -333,34 +332,34 @@ class ESMLoader {
url,
} = customLoaders[i];
const {
globalPreloader,
resolver,
loader,
globalPreload,
resolve,
load,
} = ESMLoader.pluckHooks(exports);

if (globalPreloader) {
if (globalPreload) {
ArrayPrototypePush(
this.#globalPreloaders,
this.#hooks.globalPreload,
{
fn: globalPreloader,
fn: globalPreload,
url,
},
);
}
if (resolver) {
if (resolve) {
ArrayPrototypePush(
this.#resolvers,
this.#hooks.resolve,
{
fn: resolver,
fn: resolve,
url,
},
);
}
if (loader) {
if (load) {
ArrayPrototypePush(
this.#loaders,
this.#hooks.load,
{
fn: loader,
fn: load,
url,
},
);
@@ -411,14 +410,14 @@ class ESMLoader {
async getModuleJob(specifier, parentURL, importAssertions) {
let importAssertionsForResolve;

// By default, `this.#loaders` contains just the Node default load hook
if (this.#loaders.length !== 1) {
// By default, `this.#hooks.load` contains just the Node default load hook
if (this.#hooks.load.length !== 1) {
// We can skip cloning if there are no user-provided loaders because
// the Node.js default resolve hook does not use import assertions.
importAssertionsForResolve = ObjectAssign(
ObjectCreate(null),
importAssertions,
);
importAssertionsForResolve = {
__proto__: null,
...importAssertions,
};
}

const { format, url } =
@@ -529,11 +528,11 @@ class ESMLoader {
if (!wasArr) { return namespaces[0]; } // We can skip the pairing below

for (let i = 0; i < count; i++) {
const namespace = ObjectCreate(null);
namespace.url = specifiers[i];
namespace.exports = namespaces[i];

namespaces[i] = namespace;
namespaces[i] = {
__proto__: null,
url: specifiers[i],
exports: namespaces[i],
};
}

return namespaces;
@@ -551,7 +550,7 @@ class ESMLoader {
* @returns {{ format: ModuleFormat, source: ModuleSource }}
*/
async load(url, context = {}) {
const chain = this.#loaders;
const chain = this.#hooks.load;
const meta = {
chainFinished: null,
context,
@@ -680,7 +679,7 @@ class ESMLoader {
}

preload() {
for (let i = this.#globalPreloaders.length - 1; i >= 0; i--) {
for (let i = this.#hooks.globalPreload.length - 1; i >= 0; i--) {
const channel = new MessageChannel();
const {
port1: insidePreload,
@@ -691,19 +690,19 @@ class ESMLoader {
insideLoader.unref();

const {
fn: preloader,
fn: preload,
url: specifier,
} = this.#globalPreloaders[i];
} = this.#hooks.globalPreload[i];

const preload = preloader({
const preloaded = preload({
port: insideLoader,
});

if (preload == null) { return; }
if (preloaded == null) { return; }

const hookErrIdentifier = `${specifier} globalPreload`;

if (typeof preload !== 'string') { // [2]
if (typeof preloaded !== 'string') { // [2]
throw new ERR_INVALID_RETURN_VALUE(
'a string',
hookErrIdentifier,
@@ -712,7 +711,7 @@ class ESMLoader {
}
const { compileFunction } = require('vm');
const preloadInit = compileFunction(
preload,
preloaded,
['getBuiltin', 'port', 'setImportMetaCallback'],
{
filename: '<preload>',
@@ -785,7 +784,7 @@ class ESMLoader {
async resolve(
originalSpecifier,
parentURL,
importAssertions = ObjectCreate(null)
importAssertions = ObjectCreate(null),
) {
const isMain = parentURL === undefined;

@@ -800,7 +799,7 @@ class ESMLoader {
parentURL,
);
}
const chain = this.#resolvers;
const chain = this.#hooks.resolve;
const context = {
conditions: DEFAULT_CONDITIONS,
importAssertions,
@@ -12,10 +12,10 @@ const { ESMLoader } = esmLoaderModule;
const esmLoader = new ESMLoader();

const originalSpecifier = 'foo/bar';
const importAssertions = Object.assign(
Object.create(null),
{ type: 'json' },
);
const importAssertions = {
__proto__: null,
type: 'json',
};
const parentURL = 'file:///entrypoint.js';
const resolvedURL = 'file:///foo/bar.js';
const suggestedFormat = 'test';

0 comments on commit 9b3b7d6

Please sign in to comment.