ChatGPT解决这个技术问题 Extra ChatGPT

node.js require() cache - possible to invalidate?

From the node.js documentation:

Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.

Is there a way to invalidate this cache? i.e. for unit testing, I'd like each test to be working on a fresh object.

Another NPM module with a watcher: npmjs.com/package/updated-require
It's possible to cache the file contents without using require and eval it for different scopes stackoverflow.com/questions/42376161/…

J
José Cabo

You can always safely delete an entry in require.cache without a problem, even when there are circular dependencies. Because when you delete, you just delete a reference to the cached module object, not the module object itself, the module object will not be GCed because in case of circular dependencies, there is still a object referencing this module object.

Suppose you have:

script a.js:

var b=require('./b.js').b;
exports.a='a from a.js';
exports.b=b;

and script b.js:

var a=require('./a.js').a;
exports.b='b from b.js';
exports.a=a;

when you do:

var a=require('./a.js')
var b=require('./b.js')

you will get:

> a
{ a: 'a from a.js', b: 'b from b.js' }
> b
{ b: 'b from b.js', a: undefined }

now if you edit your b.js:

var a=require('./a.js').a;
exports.b='b from b.js. changed value';
exports.a=a;

and do:

delete require.cache[require.resolve('./b.js')]
b=require('./b.js')

you will get:

> a
{ a: 'a from a.js', b: 'b from b.js' }
> b
{ b: 'b from b.js. changed value',
  a: 'a from a.js' }

===

The above is valid if directly running node.js. However, if using tools that have their own module caching system, such as jest, the correct statement would be:

jest.resetModules();

could you please explain why { ... a: undefined} when requiring b.js for the first time ? I would expect to equal 'a from a.js'. Thank you
why is a undefined?
Late reply, but from what I gather b[a] is undefined the first time since there's a circular dependency. a.js requires b.js which in turn requires a.js. a.js is not yet fully loaded and exports.a is yet to be defined, so b.js get's nothing.
any way to do this if I'm using require.main.require(path) as described here? stackoverflow.com/questions/10860244/…
Thank you for adding the last paragraph with 'jest.resetModules();', could be really helpful as most tutorials I came accross used Mocha!
l
luff

If you always want to reload your module, you could add this function:

function requireUncached(module) {
    delete require.cache[require.resolve(module)];
    return require(module);
}

and then use requireUncached('./myModule') instead of require.


This is perfect in combination with the fs.watch method which listens for file changes.
what is the risk?
Same question I have, what is the risk of using this solution and not the accepted answer?
It's the same really. Depending on how the code is structured, things might crash when you try to initialize it again. Ex. if the module starts a server, and listens to a port. The next time you requireUncached the module, it will fail since that port is already opened, and so on.
it bemuses me that this function isn't called reacquire(), but that's just me.
B
Ben Barkay

Yes, you can access the cache via require.cache[moduleName] where moduleName is the name of the module you wish to access. Deleting an entry by calling delete require.cache[moduleName] will cause require to load the actual file.

This is how you would remove all cached files associated with the module:

/**
 * Removes a module from the cache
 */
function purgeCache(moduleName) {
    // Traverse the cache looking for the files
    // loaded by the specified module name
    searchCache(moduleName, function (mod) {
        delete require.cache[mod.id];
    });

    // Remove cached paths to the module.
    // Thanks to @bentael for pointing this out.
    Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
        if (cacheKey.indexOf(moduleName)>0) {
            delete module.constructor._pathCache[cacheKey];
        }
    });
};

/**
 * Traverses the cache to search for all the cached
 * files of the specified module name
 */
function searchCache(moduleName, callback) {
    // Resolve the module identified by the specified name
    var mod = require.resolve(moduleName);

    // Check if the module has been resolved and found within
    // the cache
    if (mod && ((mod = require.cache[mod]) !== undefined)) {
        // Recursively go over the results
        (function traverse(mod) {
            // Go over each of the module's children and
            // traverse them
            mod.children.forEach(function (child) {
                traverse(child);
            });

            // Call the specified callback providing the
            // found cached module
            callback(mod);
        }(mod));
    }
};

Usage would be:

// Load the package
var mypackage = require('./mypackage');

// Purge the package from cache
purgeCache('./mypackage');

Since this code uses the same resolver require does, just specify whatever you would for require.

"Unix was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things." – Doug Gwyn

I think that there should have been a way for performing an explicit uncached module loading.


+1 just for Doug's quote. I needed someone to phrase out what I also believed in :)
Excellent answer! If you would like to start a node repl with reloading enabled check out this gist.
awesome. I would add this to the require.uncache function. ``` // see github.com/joyent/node/issues/8266 Object.keys(module.constructor._pathCache).forEach(function(k) { if (k.indexOf(moduleName)>0) delete module.constructor._pathCache[k]; }); ``` Say you've required a module, then uninstalled it, then reinstalled the same module but used a different version that has a different main script in its package.json, the next require will fail because that main script does not exists because it's cached in Module._pathCache
crap. my comment is terrible. I couldn't neatly add code in this comment and it's too late to edit, so I answered. @Ben Barkay if you could edit your question to add the little snippet of code to your require.uncache
Thanks @bentael, I've added this to my answer.
G
Glorfindel

There's a Simple Module for that (with tests)

We had this exact issue while testing our code (delete cached modules so they can be re-required in a fresh state) so we reviewed all the suggestions of people on the various StackOverflow Questions & Answers and put together a simple node.js module (with tests):

https://www.npmjs.com/package/decache

As you would expect, works for both published npm packages and locally defined modules. Windows, Mac, Linux, etc.

https://img.shields.io/travis/dwyl/decache/master.svg?style=flat-square

How? (usage)

Usage is pretty simple:

install

Install the module from npm:

npm install decache --save-dev

Use it in your code:

// require the decache module:
const decache = require('decache');

// require a module that you wrote"
let mymod = require('./mymodule.js');

// use your module the way you need to:
console.log(mymod.count()); // 0   (the initial state for our counter is zero)
console.log(mymod.incrementRunCount()); // 1

// delete the cached module:
decache('./mymodule.js');

//
mymod = require('./mymodule.js'); // fresh start
console.log(mymod.count()); // 0   (back to initial state ... zero)

If you have any questions or need more examples, please create a GitHub issue: https://github.com/dwyl/decache/issues


I've been looking at this and it looks really great for me to use when testing so that I can unload and reload a module under specific conditions, but unfortunately I'm at work and my company shies away from GPL licenses. I only want to use it for testing so I am still considering it because it looks so helpful.
@Matt_JD thanks for your feedback. which licence would you prefer?
@Matt_JD We've updated the license to MIT. Good luck with your work! :-)
this worked amazingly! Starring this repo and upvoting this answer.
highly recommend, running fine on latest v14.2.0 as of today
T
Tim Malone

For anyone coming across this who is using Jest, because Jest does its own module caching, there's a built-in function for this - just make sure jest.resetModules runs eg. after each of your tests:

afterEach( function() {
  jest.resetModules();
});

Found this after trying to use decache like another answer suggested. Thanks to Anthony Garvan.

Function documentation here.


Thank you so much for this note!
god how long did I experiment before I found this....thank you!
C
Community

The solutions is to use:

delete require.cache[require.resolve(<path of your script>)]

Find here some basic explanations for those who, like me, are a bit new in this:

Suppose you have a dummy example.js file in the root of your directory:

exports.message = "hi";
exports.say = function () {
  console.log(message);
}

Then you require() like this:

$ node
> require('./example.js')
{ message: 'hi', say: [Function] }

If you then add a line like this to example.js:

exports.message = "hi";
exports.say = function () {
  console.log(message);
}

exports.farewell = "bye!";      // this line is added later on

And continue in the console, the module is not updated:

> require('./example.js')
{ message: 'hi', say: [Function] }

That's when you can use delete require.cache[require.resolve()] indicated in luff's answer:

> delete require.cache[require.resolve('./example.js')]
true
> require('./example.js')
{ message: 'hi', say: [Function], farewell: 'bye!' }

So the cache is cleaned and the require() captures the content of the file again, loading all the current values.


IMHO This is most appropriate answer
S
SavoryBytes

rewire is great for this use case, you get a new instance with each call. Easy dependency injection for node.js unit testing.

rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing. You may

inject mocks for other modules or globals like process leak private variables override variables within the module. rewire does not load the file and eval the contents to emulate node's require mechanism. In fact it uses node's own require to load the module. Thus your module behaves exactly the same in your test environment as under regular circumstances (except your modifications).

Good news to all caffeine-addicts: rewire works also with Coffee-Script. Note that in this case CoffeeScript needs to be listed in your devDependencies.


K
Krzysztof Wende

I'd add to luff's answer one more line and change the parameter name:

function requireCached(_module){
    var l = module.children.length;
    for (var i = 0; i < l; i++)
    {
        if (module.children[i].id === require.resolve(_module))
        {
            module.children.splice(i, 1);
            break;
        }
    }
    delete require.cache[require.resolve(_module)];
    return require(_module)
}

So this is to make the function work in submodules? Nice! A shorter way to remove the module from module.children array is by using a filter function: module.children = module.children.filter(function(child){ return child.id !== require.resolve(_module); });
f
fedorqui

Yes, you can invalidate cache.

The cache is stored in an object called require.cache which you can access directly according to filenames (e.g. - /projects/app/home/index.js as opposed to ./home which you would use in a require('./home') statement).

delete require.cache['/projects/app/home/index.js'];

Our team has found the following module useful. To invalidate certain groups of modules.

https://www.npmjs.com/package/node-resource


a
atomh33ls

I am not 100% certain of what you mean by 'invalidate', but you can add the following above the require statements to clear the cache:

Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })

Taken from @Dancrumb's comment here


Y
YairTawil

requireUncached with relative path: 🔥

const requireUncached = require => module => {
  delete require.cache[require.resolve(module)];
  return require(module);
};

module.exports = requireUncached;

invoke requireUncached with relative path:

const requireUncached = require('../helpers/require_uncached')(require);
const myModule = requireUncached('./myModule');

L
Lux

I couldn't neatly add code in an answer's comment. But I would use @Ben Barkay's answer then add this to the require.uncache function.

    // see https://github.com/joyent/node/issues/8266
    // use in it in @Ben Barkay's require.uncache function or along with it. whatever
    Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
        if ( cacheKey.indexOf(moduleName) > -1 ) {
            delete module.constructor._pathCache[ cacheKey ];
        }
    }); 

Say you've required a module, then uninstalled it, then reinstalled the same module but used a different version that has a different main script in its package.json, the next require will fail because that main script does not exists because it's cached in Module._pathCache


K
Kyuuhachi

If you want a module to simply never be cached (sometimes useful for development, but remember to remove it when done!) you can just put delete require.cache[module.id]; inside the module.


where do I add this? my js is not loading in the browser with hot reload...
C
Community

Following two step procedure is working perfectly for me.

After changing Model file i-e 'mymodule.js' dynamically, you need to Delete precompiled model in mongoose model first then reload it using require-reload

Example:
        // Delete mongoose model
        delete mongoose.connection.models[thisObject.singular('mymodule')]

        // Reload model
        var reload = require('require-reload')(require);
        var entityModel = reload('./mymodule.js');

O
OrangeDog

The documentation says:

Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module. This does not apply to native addons, for which reloading will result in an error.


u
unsynchronized

here's my version of this answer, which handles not loading a file if it has (for example) syntax errors

function reacquire(module) {
const fullpath  = require.resolve(module);
const backup = require.cache[fullpath];
delete require.cache[fullpath];

 try {
   const newcopy = require(module);
   console.log("reqcquired:",module,typeof newcopy);
   return newcopy;
 } catch (e) {
    console.log("Can't reqcquire",module,":",e.message);
    require.cache[fullpath] = backup;
    return backup;
 }

}

C
Comtaler

If it's for unit tests, another good tool to use is proxyquire. Everytime you proxyquire the module, it will invalidate the module cache and cache a new one. It also allows you to modify the modules required by the file that you are testing.


g
gleb bahmutov

I made a small module to delete module from the cache after loading. This forces reevaluation of the module next time it is required. See https://github.com/bahmutov/require-and-forget

// random.js
module.exports = Math.random()
const forget = require('require-and-forget')
const r1 = forget('./random')
const r2 = forget('./random')
// r1 and r2 will be different
// "random.js" will not be stored in the require.cache

PS: you can also put "self-destruct" into the module itself. See https://github.com/bahmutov/unload-me

PSS: more tricks with Node require in my https://glebbahmutov.com/blog/hacking-node-require/