-
-
Notifications
You must be signed in to change notification settings - Fork 35.2k
module: add clearCache for CJS and ESM #61767
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
Open
anonrig
wants to merge
15
commits into
nodejs:main
Choose a base branch
from
anonrig:yagiz/node-module-clear-cache
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,780
−3
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
b7788b4
module: add clearCache for CJS and ESM
anonrig 357917f
fixup! module: add clearCache for CJS and ESM
anonrig f7c826e
fixup! module: add clearCache for CJS and ESM
anonrig 723300c
fixup! module: add clearCache for CJS and ESM
anonrig 2496f25
fixup! module: add clearCache for CJS and ESM
anonrig e2a581d
fixup! module: add clearCache for CJS and ESM
anonrig d21982c
fixup! module: add clearCache for CJS and ESM
anonrig 35d207e
fixup! module: add clearCache for CJS and ESM
anonrig 105ff7e
fixup! module: add clearCache for CJS and ESM
anonrig 7e9a7c5
fixup! module: add clearCache for CJS and ESM
anonrig 1410c00
fixup! module: add clearCache for CJS and ESM
anonrig 933b744
fixup! module: add clearCache for CJS and ESM
anonrig 0b472a6
fixup! module: add clearCache for CJS and ESM
anonrig ddde84e
fixup! module: add clearCache for CJS and ESM
anonrig be1f4a5
fixup! module: add clearCache for CJS and ESM
anonrig File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -66,6 +66,115 @@ const require = createRequire(import.meta.url); | |||||||||||
| const siblingModule = require('./sibling-module'); | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ### `module.clearCache(specifier, options)` | ||||||||||||
|
|
||||||||||||
| <!-- YAML | ||||||||||||
| added: REPLACEME | ||||||||||||
| --> | ||||||||||||
|
|
||||||||||||
| > Stability: 1.0 - Early development | ||||||||||||
|
|
||||||||||||
| * `specifier` {string|URL} The module specifier, as it would have been passed to | ||||||||||||
| `import()` or `require()`. | ||||||||||||
| * `options` {Object} | ||||||||||||
| * `parentURL` {string|URL} The parent URL used to resolve the specifier. Parent identity | ||||||||||||
| is part of the resolution cache key. For CommonJS, pass `pathToFileURL(__filename)`. | ||||||||||||
| For ES modules, pass `import.meta.url`. | ||||||||||||
| * `resolver` {string} Specifies how resolution should be performed. Must be either | ||||||||||||
| `'import'` or `'require'`. | ||||||||||||
| * `caches` {string} Specifies which caches to clear. Must be one of: | ||||||||||||
| * `'resolution'` — only clear the resolution cache entry for this specifier. | ||||||||||||
| * `'module'` — clear the cached module everywhere in Node.js (not counting | ||||||||||||
| JS-level references). | ||||||||||||
| * `'all'` — clear both resolution and module caches. | ||||||||||||
| * `importAttributes` {Object} Optional import attributes. Only meaningful when | ||||||||||||
| `resolver` is `'import'`. | ||||||||||||
|
|
||||||||||||
| Clears module resolution and/or module caches for a module. This enables | ||||||||||||
| reload patterns similar to deleting from `require.cache` in CommonJS, and is useful for | ||||||||||||
| hot module reload. | ||||||||||||
|
|
||||||||||||
| When `caches` is `'module'` or `'all'`, the specifier is resolved using the chosen `resolver` | ||||||||||||
| and the resolved module is removed from all internal caches (CommonJS `require` cache, ESM | ||||||||||||
| load cache, and ESM translators cache). When a `file:` URL is resolved, cached module jobs for | ||||||||||||
| the same file path are cleared even if they differ by search or hash. This means clearing | ||||||||||||
| `'./mod.mjs?v=1'` will also clear `'./mod.mjs?v=2'` and any other query/hash variants that | ||||||||||||
| resolve to the same file. | ||||||||||||
|
|
||||||||||||
| When `caches` is `'resolution'` or `'all'` with `resolver` set to `'import'`, the ESM | ||||||||||||
| resolution cache entry for the given `(specifier, parentURL, importAttributes)` tuple is | ||||||||||||
| cleared. When `resolver` is `'require'`, internal CJS resolution caches (including the | ||||||||||||
| relative resolve cache and path cache) are also cleared for the resolved filename. | ||||||||||||
| When `importAttributes` are provided for `'import'` resolution, they are used to construct the cache key; if a module | ||||||||||||
| was loaded with multiple different import attribute combinations, only the matching entry | ||||||||||||
| is cleared from the resolution cache. The module cache itself (`caches: 'module'`) clears | ||||||||||||
| all attribute variants for the URL. | ||||||||||||
|
|
||||||||||||
| Clearing a module does not clear cached entries for its dependencies. When using | ||||||||||||
| `resolver: 'import'`, resolution cache entries for other specifiers that resolve to the | ||||||||||||
| same target are not cleared — only the exact `(specifier, parentURL, importAttributes)` | ||||||||||||
| entry is removed. The module cache itself is cleared by resolved file path, so all | ||||||||||||
| specifiers pointing to the same file will see a fresh execution on next import. | ||||||||||||
|
|
||||||||||||
| #### ECMA-262 spec considerations | ||||||||||||
|
|
||||||||||||
| Re-importing the exact same `(specifier, parentURL, importAttribtues)` tuple after clearing the module cache | ||||||||||||
| technically violates the idempotency invariant of the ECMA-262 | ||||||||||||
| [`HostLoadImportedModule`][] host hook, which expects that the same module request always | ||||||||||||
| returns the same Module Record for a given referrer. For spec-compliant usage, use | ||||||||||||
| cache-busting search parameters so that each reload uses a distinct module request: | ||||||||||||
|
Comment on lines
+124
to
+125
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use stronger language to clarify "violate the spec at your own risk", in case V8 start to CHECK on it.
Suggested change
|
||||||||||||
|
|
||||||||||||
| ```mjs | ||||||||||||
| import { clearCache } from 'node:module'; | ||||||||||||
| import { watch } from 'node:fs'; | ||||||||||||
|
|
||||||||||||
| let version = 0; | ||||||||||||
| const base = new URL('./app.mjs', import.meta.url); | ||||||||||||
|
|
||||||||||||
| watch(base, async () => { | ||||||||||||
| // Clear the module cache for the previous version. | ||||||||||||
| clearCache(new URL(`${base.href}?v=${version}`), { | ||||||||||||
| parentURL: import.meta.url, | ||||||||||||
| resolver: 'import', | ||||||||||||
| caches: 'all', | ||||||||||||
| }); | ||||||||||||
| version++; | ||||||||||||
| // Re-import with a new search parameter — this is a distinct module request | ||||||||||||
| // and does not violate the ECMA-262 invariant. | ||||||||||||
| const mod = await import(`${base.href}?v=${version}`); | ||||||||||||
| console.log('reloaded:', mod); | ||||||||||||
| }); | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| #### Examples | ||||||||||||
|
|
||||||||||||
| ```mjs | ||||||||||||
| import { clearCache } from 'node:module'; | ||||||||||||
|
|
||||||||||||
| await import('./mod.mjs'); | ||||||||||||
|
|
||||||||||||
| clearCache('./mod.mjs', { | ||||||||||||
| parentURL: import.meta.url, | ||||||||||||
| resolver: 'import', | ||||||||||||
| caches: 'module', | ||||||||||||
| }); | ||||||||||||
| await import('./mod.mjs'); // re-executes the module | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ```cjs | ||||||||||||
| const { clearCache } = require('node:module'); | ||||||||||||
|
|
||||||||||||
| require('./mod.js'); | ||||||||||||
|
|
||||||||||||
| clearCache('./mod.js', { | ||||||||||||
| parentURL: __filename, | ||||||||||||
| resolver: 'require', | ||||||||||||
| caches: 'module', | ||||||||||||
| }); | ||||||||||||
| require('./mod.js'); // eslint-disable-line node-core/no-duplicate-requires | ||||||||||||
| // re-executes the module | ||||||||||||
| ``` | ||||||||||||
anonrig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
|
|
||||||||||||
| ### `module.findPackageJSON(specifier[, base])` | ||||||||||||
|
|
||||||||||||
| <!-- YAML | ||||||||||||
|
|
@@ -2010,6 +2119,7 @@ returned object contains the following keys: | |||||||||||
| [`--enable-source-maps`]: cli.md#--enable-source-maps | ||||||||||||
| [`--import`]: cli.md#--importmodule | ||||||||||||
| [`--require`]: cli.md#-r---require-module | ||||||||||||
| [`HostLoadImportedModule`]: https://tc39.es/ecma262/#sec-HostLoadImportedModule | ||||||||||||
| [`NODE_COMPILE_CACHE=dir`]: cli.md#node_compile_cachedir | ||||||||||||
| [`NODE_COMPILE_CACHE_PORTABLE=1`]: cli.md#node_compile_cache_portable1 | ||||||||||||
| [`NODE_DISABLE_COMPILE_CACHE=1`]: cli.md#node_disable_compile_cache1 | ||||||||||||
|
|
||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need this feature, or could we leave this customization out for now?
To fully understand the resolve cache case - this means we clear the resolution for
specifier, parentURL, attributesonly right? It seems to me like that has to be expected here?