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