Skip to content

Commit b3bd79a

Browse files
committed
fixup! module: add clearCache for CJS and ESM
1 parent 1d0accc commit b3bd79a

File tree

8 files changed

+110
-4
lines changed

8 files changed

+110
-4
lines changed

β€Ždoc/api/module.mdβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Clears the CommonJS `require` cache and/or the ESM module cache for a module. Th
8989
reload patterns similar to deleting from `require.cache` in CommonJS, and is useful for HMR.
9090
When `mode` is `'all'`, resolution failures for one module system do not throw; check the
9191
returned flags to see what was cleared.
92+
This also clears internal resolution caches for the resolved module.
9293
9394
```mjs
9495
import { clearCache } from 'node:module';

β€Žlib/internal/modules/cjs/loader.jsβ€Ž

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,6 +2153,43 @@ function resolveClearCacheURL(specifier, parentURL, importAttributes) {
21532153
return cascadedLoader.resolveSync(parentURL, request).url;
21542154
}
21552155

2156+
/**
2157+
* Remove path cache entries that resolve to a filename.
2158+
* @param {string} filename
2159+
* @returns {boolean} true if any entries were deleted.
2160+
*/
2161+
function deletePathCacheEntries(filename) {
2162+
const cache = Module._pathCache;
2163+
const keys = ObjectKeys(cache);
2164+
let deleted = false;
2165+
for (let i = 0; i < keys.length; i++) {
2166+
const key = keys[i];
2167+
if (cache[key] === filename) {
2168+
delete cache[key];
2169+
deleted = true;
2170+
}
2171+
}
2172+
return deleted;
2173+
}
2174+
2175+
/**
2176+
* Remove relative resolve cache entries that resolve to a filename.
2177+
* @param {string} filename
2178+
* @returns {boolean} true if any entries were deleted.
2179+
*/
2180+
function deleteRelativeResolveCacheEntries(filename) {
2181+
const keys = ObjectKeys(relativeResolveCache);
2182+
let deleted = false;
2183+
for (let i = 0; i < keys.length; i++) {
2184+
const key = keys[i];
2185+
if (relativeResolveCache[key] === filename) {
2186+
delete relativeResolveCache[key];
2187+
deleted = true;
2188+
}
2189+
}
2190+
return deleted;
2191+
}
2192+
21562193
/**
21572194
* Clear CommonJS and/or ESM module cache entries.
21582195
* @param {string|URL} specifier
@@ -2192,9 +2229,19 @@ function clearCache(specifier, options = kEmptyObject) {
21922229
if (mode !== 'esm') {
21932230
try {
21942231
const filename = resolveClearCacheFilename(specifier, parentPath);
2195-
if (filename && Module._cache[filename] !== undefined) {
2196-
delete Module._cache[filename];
2197-
result.cjs = true;
2232+
if (filename) {
2233+
let deleted = false;
2234+
if (Module._cache[filename] !== undefined) {
2235+
delete Module._cache[filename];
2236+
deleted = true;
2237+
}
2238+
if (deletePathCacheEntries(filename)) {
2239+
deleted = true;
2240+
}
2241+
if (deleteRelativeResolveCacheEntries(filename)) {
2242+
deleted = true;
2243+
}
2244+
result.cjs = deleted;
21982245
}
21992246
} catch (err) {
22002247
if (mode === 'cjs') {
@@ -2208,7 +2255,9 @@ function clearCache(specifier, options = kEmptyObject) {
22082255
const url = resolveClearCacheURL(specifier, parentURL, importAttributes);
22092256
const cascadedLoader =
22102257
require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
2211-
result.esm = cascadedLoader.loadCache.deleteAll(url);
2258+
const loadDeleted = cascadedLoader.loadCache.deleteAll(url);
2259+
const resolveDeleted = cascadedLoader.deleteResolveCache(url);
2260+
result.esm = loadDeleted || resolveDeleted;
22122261
} catch (err) {
22132262
if (mode === 'esm') {
22142263
throw err;

β€Žlib/internal/modules/esm/loader.jsβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ class ModuleLoader {
181181
*/
182182
loadCache = newLoadCache();
183183

184+
/**
185+
* Delete cached resolutions that resolve to a URL.
186+
* @param {string} url
187+
* @returns {boolean} true if any entries were deleted.
188+
*/
189+
deleteResolveCache(url) {
190+
return this.#resolveCache.deleteByResolvedURL(url);
191+
}
192+
184193
/**
185194
* Methods which translate input code or other information into ES modules
186195
*/

β€Žlib/internal/modules/esm/module_map.jsβ€Ž

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,31 @@ class ResolveCache extends SafeMap {
8585
has(serializedKey, parentURL) {
8686
return serializedKey in this.#getModuleCachedImports(parentURL);
8787
}
88+
89+
/**
90+
* Delete cached resolutions that resolve to a URL.
91+
* @param {string} url
92+
* @returns {boolean} true if any entries were deleted.
93+
*/
94+
deleteByResolvedURL(url) {
95+
validateString(url, 'url');
96+
let deleted = false;
97+
for (const [parentURL, entries] of this) {
98+
const keys = ObjectKeys(entries);
99+
for (let i = 0; i < keys.length; i++) {
100+
const key = keys[i];
101+
if (entries[key]?.url === url) {
102+
delete entries[key];
103+
deleted = true;
104+
}
105+
}
106+
107+
if (ObjectKeys(entries).length === 0) {
108+
super.delete(parentURL);
109+
}
110+
}
111+
return deleted;
112+
}
88113
}
89114

90115
/**

β€Žtest/es-module/test-module-clear-cache.mjsβ€Ž

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,16 @@ assert.strictEqual(result.esm, true);
1919
const third = await import(specifier);
2020
assert.strictEqual(third.count, 2);
2121

22+
const nested = new URL('../fixtures/module-cache/esm-nested-a.mjs', import.meta.url);
23+
const nestedFirst = await import(`${nested.href}?v=1`);
24+
assert.strictEqual(nestedFirst.value, 1);
25+
26+
const nestedResult = clearCache(new URL('../fixtures/module-cache/esm-nested-b.mjs', import.meta.url));
27+
assert.strictEqual(nestedResult.cjs, false);
28+
assert.strictEqual(nestedResult.esm, true);
29+
30+
const nestedSecond = await import(`${nested.href}?v=2`);
31+
assert.strictEqual(nestedSecond.value, 2);
32+
2233
delete globalThis.__module_cache_esm_counter;
34+
delete globalThis.__module_cache_esm_nested_c_counter;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { value } from './esm-nested-b.mjs';
2+
3+
export { value };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { count } from './esm-nested-c.mjs';
2+
3+
export const value = count;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
globalThis.__module_cache_esm_nested_c_counter =
2+
(globalThis.__module_cache_esm_nested_c_counter ?? 0) + 1;
3+
4+
export const count = globalThis.__module_cache_esm_nested_c_counter;

0 commit comments

Comments
Β (0)