https://github.com/franckfreiburger/module-invalidate
Invalidate node.js modules loaded through require()
https://github.com/franckfreiburger/module-invalidate
invalidation module nodejs reload require
Last synced: 12 months ago
JSON representation
Invalidate node.js modules loaded through require()
- Host: GitHub
- URL: https://github.com/franckfreiburger/module-invalidate
- Owner: FranckFreiburger
- License: mit
- Created: 2017-03-01T15:30:13.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2021-07-23T23:53:35.000Z (almost 5 years ago)
- Last Synced: 2024-11-09T12:19:43.774Z (over 1 year ago)
- Topics: invalidation, module, nodejs, reload, require
- Language: JavaScript
- Size: 60.5 KB
- Stars: 19
- Watchers: 4
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# module-invalidate
Invalidate node.js modules loaded through `require()`
## Description
`module-invalidate` allows you to invalidate a given module (or all modules) and make it automatically reloaded on further access, no need to call `require()` again.
## Install
`npm install --save module-invalidate`
## Examples
##### Example: simple case
###### module `./myModule.js`
```JavaScript
module.invalidable = true;
var count = 0;
exports.count = function() {
return count++;
}
```
###### main module `./index.js`
```JavaScript
require('module-invalidate');
var myModule = require('./myModule.js');
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
module.constructor.invalidateByExports(myModule);
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
```
##### Example: invalidate module on modification
```JavaScript
const fs = require('fs');
var myModule = require('./myModule.js');
fs.watch(require.resolve('./myModule.js'), function() {
module.invalidateByPath('./myModule.js');
});
setInterval(function() {
console.log(myModule.count());
}, 1000);
```
##### Example:
```JavaScript
require('module-invalidate');
var tmp_modulePath = require('path').join(__dirname, 'tmp_module.js');
require('fs').writeFileSync(tmp_modulePath, `
module.invalidable = true;
exports.a = 1;
`);
var tmp_module = require('./tmp_module.js');
console.log(tmp_module.a); // 1
require('fs').writeFileSync(tmp_modulePath, `
module.invalidable = true;
exports.a = 2;
`);
module.constructor.invalidateByExports(tmp_module);
console.log(tmp_module.a); // 2
require('fs').unlinkSync(tmp_modulePath);
```
## API
In the following API, `Module` refers to the Module constructor, available with `module.constructor` or `require('Module')`.
And `module` refers to a module instance, available in each module with `module`.
#### `require('module-invalidate')`
Enable the module-invalidate mechanism.
Any nodejs-non-internal module loaded after this call can be handled by this library.
#### `module.invalidable`
This property controls whether the module can be invalidated. By default, modules are not invalidable.
##### Example:
###### module `./myModule.js`
```JavaScript
module.invalidable = true;
module.exports = {
foo: function() {}
}
```
#### `module.invalidateByPath(path)`
Invalidates the specified module by its path (same syntax and context than `require()`). The module should have been flagged as invalidable using `module.invalidable`.
##### Example:
```JavaScript
require('module-invalidate');
var myModule = require('./myModule.js');
module.invalidateByPath('./myModule.js');
```
#### `Module.invalidateByExports(exports)`
Invalidates the module by giving its exported object. The module should have been flagged as invalidable using `module.invalidable`.
##### Example:
```JavaScript
require('module-invalidate');
var myModule = require('./myModule.js');
module.constructor.invalidateByExports(myModule);
```
`invalidateByExports()` only invalidates one module.
###### module `B.js`
```
module.invalidable = true;
console.log('load B');
module.exports = {
foo: 123
}
```
###### module `A.js`
```
module.invalidable = true;
console.log('load A');
module.exports = require('./B.js');
```
###### main module `index.js`
```
require('module-invalidate');
var a = require('./A.js');
console.log('invalidate');
module.constructor.invalidateByExports(a);
var tmp = a.foo;
```
output:
```
load A
load B
invalidate
load A
```
#### `Module.invalidate()`
Invalidates all nodejs-non-internal modules. Only process modules that have been flagged as invalidable using `module.invalidable`.
##### Example:
```JavaScript
require('module-invalidate');
module.constructor.invalidate();
```
#### `module.invalidate()`
Invalidates the module `module`. The module should have been flagged as invalidable using `module.invalidable`.
##### Example:
```JavaScript
module.invalidate();
```
#### `module.unload()`
Definitely unloads the module `module`.
#### `module.unloadByPath(path)`
Definitely unloads the module by its path (same syntax and context than `require()`).
#### `Module.unloadByExports(exports)`
Definitely unloads the module by giving its exported object.
#### `module.onInvalidate(callback)`
callback: `function(immutable_exports)`
Register a callback that will be called when the module is invalidated. The `immutable_exports` is a permanent reference to the current `module.exports`.
`onInvalidate` returns a function that unregisters the callback.
Gives you the opportunity to free resources created in the module.
eg. temporary files, timers, web routes, ...
The `callback` can return function that is called after the module is reloaded. This can help you restore your module state.
##### Example:
```JavaScript
module.invalidable = true;
this.connectedUsers = [];
exports.connectUser = function(name) {
this.connectedUsers.push(name);
}
exports.getConnectedUsers = function() {
return this.connectedUsers;
}
module.onInvalidate(function(oldExports) {
return function(newExports) {
newExports.connectedUsers = oldExports.connectedUsers;
}
});
```
## How it works
1. `Module.prototype.exports` is overridden by a No-op forwarding ES6 Proxy that handle all accesses to module exports.
1. When a module is invalidated, it is marked as *invalidated* and is then reloaded on the next access (lazily).
## Caveat
#### `typeof module.exports` is always `'function'`
Because the library is unable to know in advance what type of value will be assigned to `module.export`, it choose the most generic one as ES6 Proxy target.
However, `(function(){}) instanceof Object === true`.
As workaround, you can use `instanceof` against the `module.exports`, this will always retuns the expected result.
##### Example:
###### module `./myModule.js`
```
module.invalidable = true;
module.exports = new Date;
```
###### main module `index.js`
```
require('module-invalidate');
var myModule = require('./myModule.js');
console.log(myModule instanceof Date); // true
```
#### Only direct variable access is handled
##### Example:
```
var foo = require('foo.js');
var bar = foo.bar;
```
In this case, `bar` will always refers to the initial `foo.bar` value. To avoid this, always refer `bar` using `foo.bar`.
#### Invalidated modules will survive with the new child-module version
In a module, `module.exports` will always refers to the latest version of the module.
##### Example:
###### module `./child.js`
```
module.invalidable = true;
module.exports = {};
setInterval(function() {
console.log(module.exports.foo);
}, 1000);
```
###### main module `index.js`
```
require('module-invalidate');
var child = require('./child.js');
child.foo = 1;
module.constructor.invalidateByExports(child);
child.foo = 2;
```
###### output
```
2
2
2
2
2
...
```
## How can you help?
Give me some [feedback](https://github.com/FranckFreiburger/module-invalidate/issues/new?labels=feedback) and report [bugs](https://github.com/FranckFreiburger/module-invalidate/issues/new?labels=bug&body=Node.js%20version:%20%0Amodule-invalidate%20version:%20%0AIssue%20description:%20%0A).
## Credits
[Franck Freiburger](https://www.franck-freiburger.com)