Saturday, December 12, 2015

Least Astonishment: Node.js mutable module cache

Node.js' require cache's modules after the initial call to `require`, so that subsequent calls to require return the cached module.  If a module exports an object, and that object is mutated then subsequent calls to require that module will import the mutated object.  Below are 3 files.  One exporting an object, one requiring that object and mutating it, and another re-requiring the object, which loads the mutated object from the cache:


module.exports = {
one: 'one'
};
view raw shared.js hosted with ❤ by GitHub

shared.js exports an object, which will be required and mutated.

// first import of shared
var shared = require('./shared');
// mutate the required module
shared.mutated = 'mutated';
module.exports = null;
mutate-require.js imports the object from shared.js.  This is the first import so it is loading the object that shared.js exports fresh and stores it in the cache.  It then mutates that object, and exports null.

// load module that loads shared.js
var mutateRequired = require('./mutate-require');
// load shared, which will hit cache
var cached = require('./shared');
console.log(cached);
/*
$ node -v
v5.2.0
$ node index.js
{ one: 'one', mutated: 'mutated' }
*/
view raw index.js hosted with ❤ by GitHub
Finally index.js imports mutate-require.js, which triggers the require of shared.js, the imports shared.js directly, which results from a cache load.  The comment at the bottom of index.js shows the output of when index.js is executed.

Is this the behavior you'd expect?  I was surprised, when I saw this.  I could imagine a more sane default being: having `require` copy the exported value to the cache and returning the clean copy on each require. But then what about modules that export objects that mutate themselves??  Thoughts?