1'use strict'; 2 3const { 4 ArrayPrototypeJoin, 5 ArrayPrototypeMap, 6 ArrayPrototypeSort, 7 JSONStringify, 8 ObjectCreate, 9 ObjectKeys, 10 SafeMap, 11} = primordials; 12const { kImplicitAssertType } = require('internal/modules/esm/assert'); 13let debug = require('internal/util/debuglog').debuglog('esm', (fn) => { 14 debug = fn; 15}); 16const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; 17const { validateString } = require('internal/validators'); 18 19/** 20 * Cache the results of the `resolve` step of the module resolution and loading process. 21 * Future resolutions of the same input (specifier, parent URL and import attributes) 22 * must return the same result if the first attempt was successful, per 23 * https://tc39.es/ecma262/#sec-HostLoadImportedModule. 24 * This cache is *not* used when custom loaders are registered. 25 */ 26class ResolveCache extends SafeMap { 27 constructor(i) { super(i); } // eslint-disable-line no-useless-constructor 28 29 /** 30 * Generates the internal serialized cache key and returns it along the actual cache object. 31 * 32 * It is exposed to allow more efficient read and overwrite a cache entry. 33 * @param {string} specifier 34 * @param {Record<string,string>} importAttributes 35 * @returns {string} 36 */ 37 serializeKey(specifier, importAttributes) { 38 // To serialize the ModuleRequest (specifier + list of import attributes), 39 // we need to sort the attributes by key, then stringifying, 40 // so that different import statements with the same attributes are always treated 41 // as identical. 42 const keys = ObjectKeys(importAttributes); 43 44 if (keys.length === 0) { 45 return specifier + '::'; 46 } 47 48 return specifier + '::' + ArrayPrototypeJoin( 49 ArrayPrototypeMap( 50 ArrayPrototypeSort(keys), 51 (key) => JSONStringify(key) + JSONStringify(importAttributes[key])), 52 ','); 53 } 54 55 #getModuleCachedImports(parentURL) { 56 let internalCache = super.get(parentURL); 57 if (internalCache == null) { 58 super.set(parentURL, internalCache = { __proto__: null }); 59 } 60 return internalCache; 61 } 62 63 /** 64 * @param {string} serializedKey 65 * @param {string} parentURL 66 * @returns {import('./loader').ModuleExports | Promise<import('./loader').ModuleExports>} 67 */ 68 get(serializedKey, parentURL) { 69 return this.#getModuleCachedImports(parentURL)[serializedKey]; 70 } 71 72 /** 73 * @param {string} serializedKey 74 * @param {string} parentURL 75 * @param {{ format: string, url: URL['href'] }} result 76 */ 77 set(serializedKey, parentURL, result) { 78 this.#getModuleCachedImports(parentURL)[serializedKey] = result; 79 return this; 80 } 81 82 has(serializedKey, parentURL) { 83 return serializedKey in this.#getModuleCachedImports(parentURL); 84 } 85} 86 87/** 88 * Cache the results of the `load` step of the module resolution and loading process. 89 */ 90class LoadCache extends SafeMap { 91 constructor(i) { super(i); } // eslint-disable-line no-useless-constructor 92 get(url, type = kImplicitAssertType) { 93 validateString(url, 'url'); 94 validateString(type, 'type'); 95 return super.get(url)?.[type]; 96 } 97 set(url, type = kImplicitAssertType, job) { 98 validateString(url, 'url'); 99 validateString(type, 'type'); 100 101 const ModuleJob = require('internal/modules/esm/module_job'); 102 if (job instanceof ModuleJob !== true && 103 typeof job !== 'function') { 104 throw new ERR_INVALID_ARG_TYPE('job', 'ModuleJob', job); 105 } 106 debug(`Storing ${url} (${ 107 type === kImplicitAssertType ? 'implicit type' : type 108 }) in ModuleLoadMap`); 109 const cachedJobsForUrl = super.get(url) ?? ObjectCreate(null); 110 cachedJobsForUrl[type] = job; 111 return super.set(url, cachedJobsForUrl); 112 } 113 has(url, type = kImplicitAssertType) { 114 validateString(url, 'url'); 115 validateString(type, 'type'); 116 return super.get(url)?.[type] !== undefined; 117 } 118} 119 120module.exports = { 121 LoadCache, 122 ResolveCache, 123}; 124