mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00

So that if there are circular dependencies in the synchronous module graph, they could be resolved using the cached jobs. In case linking fails and the error gets caught, reset the cache right after linking. If it succeeds, the caller will cache it again. Otherwise the error bubbles up to users, and since we unset the cache for the unlinkable module the next attempt would still fail. PR-URL: https://github.com/nodejs/node/pull/52868 Fixes: https://github.com/nodejs/node/issues/52864 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
128 lines
3.9 KiB
JavaScript
128 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayPrototypeJoin,
|
|
ArrayPrototypeMap,
|
|
ArrayPrototypeSort,
|
|
JSONStringify,
|
|
ObjectKeys,
|
|
SafeMap,
|
|
} = primordials;
|
|
const { kImplicitAssertType } = require('internal/modules/esm/assert');
|
|
let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
|
|
debug = fn;
|
|
});
|
|
const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes;
|
|
const { validateString } = require('internal/validators');
|
|
|
|
/**
|
|
* Cache the results of the `resolve` step of the module resolution and loading process.
|
|
* Future resolutions of the same input (specifier, parent URL and import attributes)
|
|
* must return the same result if the first attempt was successful, per
|
|
* https://tc39.es/ecma262/#sec-HostLoadImportedModule.
|
|
* This cache is *not* used when custom loaders are registered.
|
|
*/
|
|
class ResolveCache extends SafeMap {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
|
|
/**
|
|
* Generates the internal serialized cache key and returns it along the actual cache object.
|
|
*
|
|
* It is exposed to allow more efficient read and overwrite a cache entry.
|
|
* @param {string} specifier
|
|
* @param {Record<string,string>} importAttributes
|
|
* @returns {string}
|
|
*/
|
|
serializeKey(specifier, importAttributes) {
|
|
// To serialize the ModuleRequest (specifier + list of import attributes),
|
|
// we need to sort the attributes by key, then stringifying,
|
|
// so that different import statements with the same attributes are always treated
|
|
// as identical.
|
|
const keys = ObjectKeys(importAttributes);
|
|
|
|
if (keys.length === 0) {
|
|
return specifier + '::';
|
|
}
|
|
|
|
return specifier + '::' + ArrayPrototypeJoin(
|
|
ArrayPrototypeMap(
|
|
ArrayPrototypeSort(keys),
|
|
(key) => JSONStringify(key) + JSONStringify(importAttributes[key])),
|
|
',');
|
|
}
|
|
|
|
#getModuleCachedImports(parentURL) {
|
|
let internalCache = super.get(parentURL);
|
|
if (internalCache == null) {
|
|
super.set(parentURL, internalCache = { __proto__: null });
|
|
}
|
|
return internalCache;
|
|
}
|
|
|
|
/**
|
|
* @param {string} serializedKey
|
|
* @param {string} parentURL
|
|
* @returns {import('./loader').ModuleExports | Promise<import('./loader').ModuleExports>}
|
|
*/
|
|
get(serializedKey, parentURL) {
|
|
return this.#getModuleCachedImports(parentURL)[serializedKey];
|
|
}
|
|
|
|
/**
|
|
* @param {string} serializedKey
|
|
* @param {string} parentURL
|
|
* @param {{ format: string, url: URL['href'] }} result
|
|
*/
|
|
set(serializedKey, parentURL, result) {
|
|
this.#getModuleCachedImports(parentURL)[serializedKey] = result;
|
|
return this;
|
|
}
|
|
|
|
has(serializedKey, parentURL) {
|
|
return serializedKey in this.#getModuleCachedImports(parentURL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cache the results of the `load` step of the module resolution and loading process.
|
|
*/
|
|
class LoadCache extends SafeMap {
|
|
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
|
|
get(url, type = kImplicitAssertType) {
|
|
validateString(url, 'url');
|
|
validateString(type, 'type');
|
|
return super.get(url)?.[type];
|
|
}
|
|
set(url, type = kImplicitAssertType, job) {
|
|
validateString(url, 'url');
|
|
validateString(type, 'type');
|
|
|
|
const { ModuleJobBase } = require('internal/modules/esm/module_job');
|
|
if (job instanceof ModuleJobBase !== true &&
|
|
typeof job !== 'function') {
|
|
throw new ERR_INVALID_ARG_TYPE('job', 'ModuleJob', job);
|
|
}
|
|
debug(`Storing ${url} (${
|
|
type === kImplicitAssertType ? 'implicit type' : type
|
|
}) in ModuleLoadMap`);
|
|
const cachedJobsForUrl = super.get(url) ?? { __proto__: null };
|
|
cachedJobsForUrl[type] = job;
|
|
return super.set(url, cachedJobsForUrl);
|
|
}
|
|
has(url, type = kImplicitAssertType) {
|
|
validateString(url, 'url');
|
|
validateString(type, 'type');
|
|
return super.get(url)?.[type] !== undefined;
|
|
}
|
|
delete(url, type = kImplicitAssertType) {
|
|
const cached = super.get(url);
|
|
if (cached) {
|
|
cached[type] = undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
LoadCache,
|
|
ResolveCache,
|
|
};
|