Skip to content
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

esm, loader: tidy ESMLoader internals #44701

Merged
merged 5 commits into from Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 3 additions & 4 deletions lib/internal/modules/esm/get_format.js
@@ -1,8 +1,6 @@
'use strict';
const {
RegExpPrototypeExec,
ObjectAssign,
ObjectCreate,
ObjectPrototypeHasOwnProperty,
PromisePrototypeThen,
PromiseResolve,
Expand All @@ -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
Expand Down
137 changes: 68 additions & 69 deletions lib/internal/modules/esm/loader.js
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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,
},
);
Expand Down Expand Up @@ -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 } =
Expand Down Expand Up @@ -533,11 +532,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;
Expand All @@ -555,7 +554,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,
Expand Down Expand Up @@ -684,7 +683,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,
Expand All @@ -695,19 +694,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,
Expand All @@ -716,7 +715,7 @@ class ESMLoader {
}
const { compileFunction } = require('vm');
const preloadInit = compileFunction(
preload,
preloaded,
['getBuiltin', 'port', 'setImportMetaCallback'],
{
filename: '<preload>',
Expand Down Expand Up @@ -789,7 +788,7 @@ class ESMLoader {
async resolve(
originalSpecifier,
parentURL,
importAssertions = ObjectCreate(null)
importAssertions = ObjectCreate(null),
) {
const isMain = parentURL === undefined;

Expand All @@ -804,7 +803,7 @@ class ESMLoader {
parentURL,
);
}
const chain = this.#resolvers;
const chain = this.#hooks.resolve;
const context = {
conditions: DEFAULT_CONDITIONS,
importAssertions,
Expand Down
8 changes: 4 additions & 4 deletions test/es-module/test-esm-loader-hooks.mjs
Expand Up @@ -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';
Expand Down