New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

hot-reload

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hot-reload - npm Package Compare versions

Comparing version 1.2.1 to 1.2.2

598

lib/hot-reload/hot-reload.js

@@ -8,305 +8,487 @@ // third-party dependencies

function resolveFilename(filename) {
if (filename.constructor !== String) {
return filename;
}
var simpleRegExpReplacements = {
"*": ".*?",
"?": ".?"
};
var simpleRegExpTest = /[\?\*]/;
function isSimpleRegExp(str) {
return simpleRegExpTest.test(str);
}
function escapeRegExpStr(str) {
return str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
function createSimpleRegExp(str) {
var _this = this;
if ((filename.charAt(0) !== '.') && (filename.charAt(0) !== '/')) {
return filename;
} else {
return path.resolve(filename);
}
return new RegExp("^" + str.replace(/[\*\?]|[^\*\?]*/g, function(match) {
return simpleRegExpReplacements[match] || escapeRegExpStr(match);
}) + "$");
}
function HotReloader(config) {
var self = this;
events.EventEmitter.call(this);
function createSimpleRegExpFilter(str, matchResult) {
var simpleRegExp = createSimpleRegExp(str);
var lastReloadTimestamp = Date.now();
return function(str) {
//console.error('str ', str, simpleRegExp);
return simpleRegExp.test(str) ? matchResult || true : false;
}
}
if (config) {
if (config.include) {
this.include(config.include);
console.log('[Hot Reload] Watched directories/files: ' + JSON.stringify(this._includes));
function getMatch(filters, path) {
for (var i=0, len=filters.length; i<len; i++) {
var result = filters[i](path);
if (result !== false) {
return result;
}
}
return undefined;
}
if (config.exclude) {
this.exclude(config.exclude);
console.log('[Hot Reload] Exclude from watched directories: ' + JSON.stringify(this._excludes));
function getMatches(filters, path) {
var matches = [];
for (var i=0, len=filters.length; i<len; i++) {
var result = filters[i](path);
if (result !== false) {
matches.push(result);
}
}
return matches;
}
if (config.alwaysReload) {
this.alwaysReload(config.alwaysReload);
console.log('[Hot Reload] Always reload: ' + JSON.stringify(this._alwaysReload));
}
function hasMatch(filters, path) {
return getMatch(filters, path) !== undefined ? true : false;
}
if (config.neverReload) {
this.neverReload(config.neverReload);
console.log('[Hot Reload] Never reload: ' + JSON.stringify(this._neverReload));
function HotReloader(require) {
events.EventEmitter.call(this);
var _this = this;
this._require = require;
this._uncacheIncludes = [];
this._uncacheExcludes = [];
this._watchIncludes = {};
this._watchExcludeFilters = [];
this._reloadIncludes = [];
this._reloadExcludes = [];
this._specialReloadIncludes = [];
this._specialReloadExcludes = [];
this._pending = 0;
this._reloadDelay = 2000;
this._lastReloadTime = null;
this._handleComplete = function() {
if (--_this._pending === 0) {
_this.emit('ready');
}
}
}
/**
* Reloads all reloadable modules when a watched file is changed.
*/
this.reload = function() {
self._reloadTimeout = null;
util.inherits(HotReloader, events.EventEmitter);
var now = Date.now();
var diff = now - lastReloadTimestamp;
if (diff <= 500) {
HotReloader.prototype._createFilterFunc = function(filter) {
}
HotReloader.prototype._addModuleFilters = function(target, args) {
for (var i=0, len=args.length; i<len; i++) {
var arg = args[i];
if (Array.isArray(arg)) {
this._addModuleFilters(target, arg);
return;
}
else {
self.emit('beforeReload');
var reloadableModules = [];
var filter = arg;
var filterFunc;
// FIRST PASS: loop through the module cache and remove entries within directories that we are watching
for (var key in require.cache) {
var module = require.cache[key];
if (typeof filter === 'string') {
if (isSimpleRegExp(filter)) {
filterFunc = createSimpleRegExpFilter(filter);
}
else {
if (self.isModuleReloadable(module)) {
//console.log('[Hot Reload] Unloading ' + module.filename);
// delete the cache entry only in first pass
delete require.cache[key];
var moduleUri = this._require.resolve(filter);
filterFunc = function(input) {
return moduleUri === input;
}
}
}
else if (filter.constructor === RegExp) {
filterFunc = function(testModule) {
return testModule.test(filter);
}
}
else if (typeof filter === 'function') {
filterFunc = filter;
}
else {
throw new Error("Invalid module filter: " + filter);
}
// keep track of the modules that
reloadableModules.push(module);
} else {
//console.log('[Hot Reload] Not unloading ' + module.filename);
}
target.push(filterFunc);
}
}
}
console.log('[Hot Reload] ' + reloadableModules.length + ' modules removed from cache.');
HotReloader.prototype.uncache = function(filter) {
this._addModuleFilters(this._uncacheIncludes, arguments);
return this;
}
if (!config || (config.autoReload !== false)) {
// SECOND PASS: Now trigger a reload of all of the modules since their cache entry was removed
for (var i = 0; i < reloadableModules.length; i++) {
// reload the module
self.reloadModule(reloadableModules[i]);
}
}
HotReloader.prototype.uncacheExclude = function(filter) {
this._addModuleFilters(this._uncacheExcludes, arguments);
return this;
}
self.emit('afterReload');
HotReloader.prototype.reload = function(filter) {
this._addModuleFilters(this._reloadIncludes, arguments);
return this;
}
lastReloadTimestamp = now;
}
HotReloader.prototype.reloadExclude = function(filter) {
this._addModuleFilters(this._reloadExcludes, arguments);
return this;
}
util.inherits(HotReloader, events.EventEmitter);
HotReloader.prototype.watch = function(dir, recursive) {
var _this = this;
var watchIncludes = this._watchIncludes;
function matches(filter, name) {
if (filter.constructor === RegExp) {
// see if name matches regex
if (filter.test(name)) {
return true;
}
} else {
// see if name starts with given filter value
if (name.indexOf(filter) === 0) {
return true;
}
function callback(path, eventArgs) {
watchIncludes[path] = {path: path, stat: eventArgs.stat};
}
}
HotReloader.prototype.isModuleReloadable = function(module) {
this._pending++;
var filename = module.filename;
directoryWalker.create()
.recursive(recursive)
.onDirectory(callback)
.onRoot(callback)
.onError(function(e) {
console.error('Directory walk error: ', e);
})
.onComplete(this._handleComplete)
.walk(dir);
if (this._excludes) {
for (var i = 0; i < this._excludes.length; i++) {
var exclude = this._excludes[i];
if (matches(exclude, filename)) {
return false;
}
return this;
}
HotReloader.prototype._addPathFilters = function(target, filter, recursive, matchResult) {
if (Array.isArray(filter)) {
var filters = filter;
for (var i=0, len=filters.length; i<filters; i++) {
this._addPathFilters(target, filters[i], recursive, matchResult);
}
return;
}
if (this._neverReload) {
for (var i = 0; i < this._neverReload.length; i++) {
var exclude = this._neverReload[i];
if (matches(exclude, filename)) {
return false;
var filterFunc;
if (typeof filter === 'string') {
if (isSimpleRegExp(filter)) {
filterFunc = createSimpleRegExpFilter(filter, matchResult);
}
else {
var path = fs.realpathSync(filter);
if (fs.existsSync(path)) {
var stat = fs.statSync(path);
if (stat.isFile()) {
recursive = false;
}
}
recursive = recursive !== false;
filterFunc = function(testPath) {
var match = recursive ? testPath.startsWith(path) : testPath === path;
return match ? matchResult || true : false;
};
}
}
if (this._alwaysReload) {
for (var i = 0; i < this._alwaysReload.length; i++) {
var include = this._alwaysReload[i];
if (matches(include, filename)) {
return true;
}
else if (filter.constructor === RegExp) {
filterFunc = function(testPath) {
return testPath.test(filter) ? matchResult || true : false;
}
}
if (this._includes) {
for (var i = 0; i < this._includes.length; i++) {
var include = this._includes[i];
if (matches(include, filename)) {
return true;
else if (typeof filter === 'function') {
if (matchResult) {
filterFunc = function(path) {
var result = filter(path);
return result ? matchResult : false;
}
}
else {
filterFunc = filter;
}
}
else {
throw new Error("Invalid filter: " + filter + " (" + (typeof filter) + ")");
}
return false;
target.push(filterFunc);
return this;
}
/**
* Reload a given module.
* @param {Module} module a module
* @param {EventEmitter} an event emitter
*/
HotReloader.prototype.reloadModule = function(module) {
console.log('[Hot Reload] Reloading module: ' + module.filename);
this.emit('beforeModuleReload', module);
HotReloader.prototype.watchExclude = function(filter, recursive) {
this._addPathFilters(this._watchExcludeFilters, filter, recursive);
return this;
}
if (module.exports.unloadModule) {
module.exports.unloadModule.call(exports);
HotReloader.prototype.specialReload = function(filter, recursive, handlerFunc) {
if (arguments.length === 2) {
handlerFunc = arguments[1];
recursive = true;
}
delete require.cache[module.filename];
var newModule = require(module.filename);
this._addPathFilters(this._specialReloadIncludes, filter, recursive, handlerFunc);
return this;
}
// copy properties from new module to old module in case their are some
// references to old module
for (var key in newModule) {
if (newModule.hasOwnProperty(key)) {
module.exports[key] = newModule[key];
}
}
this.emit('afterModuleReload', module);
HotReloader.prototype.specialReloadExclude = function(filter, recursive) {
this._addPathFilters(this._specialReloadExcludes, filter, recursive);
return this;
}
HotReloader.prototype.watch = function(options) {
if (options.include) {
this.include(options.include);
}
HotReloader.prototype.onBeforeReload = function(func) {
this.on('beforeReload', func);
return this;
}
if (options.exclude) {
this.exclude(options.exclude);
}
HotReloader.prototype.onAfterReload = function(func) {
this.on('afterReload', func);
return this;
}
/**
* Identify directories/files that will be watched
* for changes. Also, all Node.js modules within these directories
* will automatically be reloaded if a file is changed
* (unless they are explicitly excluded).
*/
HotReloader.prototype.include = function(includes) {
HotReloader.prototype._shouldUncacheModule = function(moduleName) {
if (!this._uncacheIncludes.length && !this._uncacheExcludes.length) {
return true;
}
if (!Array.isArray(includes)) {
includes = [includes];
if (hasMatch(this._uncacheExcludes, moduleName)) {
return false;
}
this._includes = this._includes || [];
if (this._uncacheExcludes.length && !this._uncacheIncludes.length) {
return true;
}
for (var i = 0; i < includes.length; i++) {
var dir = resolveFilename(includes[i]);
this._includes.push(dir);
if (hasMatch(this._uncacheIncludes, moduleName)) {
return true;
}
return this;
return false;
}
/**
* Identify directories/files that will not be watched.
* Exclusions take precedence over inclusions.
*
* @param {String[]} list of paths that will not be watched
*/
HotReloader.prototype.exclude = function(excludes) {
this._excludes = this._excludes || [];
this._excludesMap = this._excludesMap || {};
HotReloader.prototype._shouldReloadModule = function(moduleName) {
if (!this._reloadIncludes.length && !this._reloadExcludes.length) {
return false;
}
for ( var i = 0; i < excludes.length; i++) {
this._excludes.push(filename = resolveFilename(excludes[i]));
this._excludesMap[filename] = true;
if (hasMatch(this._reloadExcludes, moduleName)) {
return false;
}
return this;
if (this._reloadExcludes.length && !this._reloadIncludes.length) {
return true;
}
if (hasMatch(this._reloadIncludes, moduleName)) {
return true;
}
return false;
}
/**
* Identify all of the modules (and, implicitly, their submodules)
* that will be automatically reloaded if any watched file/directory
* changes.
*/
HotReloader.prototype.alwaysReload = function(modulePaths) {
this._alwaysReload = this._alwaysReload || [];
for (var i = 0; i < modulePaths.length; i++) {
var modulePath = modulePaths[i];
HotReloader.prototype.getRequire = function() {
return this._require;
};
// store the file path of the modulePaths that should always be reloaded
this._alwaysReload.push(resolveFilename(modulePath));
HotReloader.prototype._reload = function(path) {
console.log('[hot-reload] Beginning reload...');
var specialReloadHandlers;
if (!hasMatch(this._specialReloadExcludes, path)) {
specialReloadHandlers = getMatches(this._specialReloadIncludes, path);
}
var eventArgs = {path: path};
var i;
return this;
}
if (specialReloadHandlers.length !== 0) {
this.emit('beforeSpecialReload', eventArgs);
/**
* Identify all of the modules (and, implicitly, their submodules)
* that will never be reloaded. Exclusions given to this method
* call take precedence over includes provided via "alwaysReload".
*/
HotReloader.prototype.neverReload = function(modulePaths) {
this._neverReload = this._neverReload || [];
for (var i = 0; i < modulePaths.length; i++) {
var modulePath = modulePaths[i];
for (i=0; i<specialReloadHandlers.length; i++) {
var specialReloadHandler = specialReloadHandlers[i];
var result = specialReloadHandler(path);
if (result === false) {
break;
}
}
this.emit('afterSpecialReload', eventArgs);
}
else {
this.emit('beforeReload', eventArgs);
// store the file path of the modulePaths that should never be reloaded
this._neverReload.push(resolveFilename(modulePath));
var modulesToReload = [];
// FIRST PASS: loop through the module cache and remove entries within directories that we are watching
for (var key in require.cache) {
var module = require.cache[key];
if (this._shouldUncacheModule(module.filename)) {
if (require.cache.hasOwnProperty(key)) {
// delete the cache entry only in first pass
delete require.cache[key];
console.log('[hot-reload] Uncached module: ' + module.filename);
// keep track of the modules that
if (this._shouldReloadModule(module.filename)) {
modulesToReload.push(module);
}
}
} else {
//console.log('[hot-reload] Not uncaching ' + module.filename);
}
}
for (i = 0; i < modulesToReload.length; i++) {
var module = modulesToReload[i];
this._reloadModule(module);
}
this.emit('afterReload', eventArgs);
}
return this;
console.log('[hot-reload] Reload complete');
}
HotReloader.prototype.start = function() {
HotReloader.prototype._reloadModule = function(module) {
console.log('[hot-reload] Reloading module "' + module.filename + '"...');
var self = this;
this.emit('beforeModuleReload', module);
var walker = directoryWalker.createDirectoryWalker({
excludes : this._excludes,
delete require.cache[module.filename];
try {
var newModule = require(module.filename);
onDirectory : function(directory) {
console.log('[Hot Reload] Watching directory: ' + directory);
fs.watch(directory,
function(event, file) {
console.log('[Hot Reload] Changed: ' + file);
self._scheduleReload();
});
},
// copy properties from new module to old module in case their are some
// references to old module
for (var key in newModule) {
if (newModule.hasOwnProperty(key)) {
module.exports[key] = newModule[key];
}
}
listeners : {
'error' : function(err) {
console.error(err);
console.log('[hot-reload] Reloaded module: ' + module.filename);
}
catch(e) {
console.error('[hot-reload] ERROR: Unable to reload module "' + module.filename + '". Exception: ' + e, e.stack);
}
this.emit('afterModuleReload', module);
}
HotReloader.prototype.start = function(func) {
var watchIncludes = this._watchIncludes,
watchExcludes = this._watchExcludes,
_this = this;
function startWatching() {
function handleModified(event, path) {
var now = Date.now();
if (_this._lastReloadTime === null || now - _this._lastReloadTime > _this._reloadDelay) {
if (hasMatch(_this._watchExcludeFilters, path)) {
console.log('[hot-reload] Modified file ignored since it is excluded: ' + path + ' ');
// The file excluded from being watched so ignore the event
return;
}
console.log('[hot-reload] File modified: ' + path + ' (' + event + ')');
_this._reload(path);
_this._lastReloadTime = now;
}
}
});
if (this._includes) {
for (var i = 0; i < this._includes.length; i++) {
walker.walk(this._includes[i]);
function createWatcherFunc(watchInclude) {
return function(event, filename) {
if (watchInclude.stat.isDirectory()) {
if (!filename) {
handleModified(event, watchInclude.path);
}
else {
handleModified(event, require('path').join(watchInclude.path, filename));
}
}
else {
handleModified(event, watchInclude.path);
}
};
}
for (var path in watchIncludes) {
if (watchIncludes.hasOwnProperty(path)) {
if (hasMatch(_this._watchExcludeFilters, path)) {
console.log('[hot-reload] Not watching "' + path + '" since it is excluded.');
// The path is excluded from being watched...skip it
continue;
}
var watchInclude = watchIncludes[path];
var watcher = fs.watch(
fs.realpathSync(path),
createWatcherFunc(watchInclude));
console.log('[hot-reload] Watching ' + (watchInclude.stat.isDirectory() ? 'directory' : 'file') + ': ' + watchInclude.path);
watchInclude.watcher = watcher;
}
}
}
if (this._pending) {
this.on('ready', startWatching);
}
else {
startWatching();
}
return this;
}
HotReloader.prototype._scheduleReload = function() {
if (this._reloadTimeout) {
clearTimeout(this._reloadTimeout);
exports.create = function(require) {
if (!require) {
throw new Error("require argument is required");
}
this._reloadTimeout = setTimeout(this.reload, 1500);
return new HotReloader(require);
}
exports.createHotReloader = function(config) {
return new HotReloader(config);
}
exports.HotReloader = HotReloader;
{
"name": "hot-reload",
"description": "Triggers reloading of Node.js modules",
"version": "1.2.1",
"homepage": "https://github.com/philidem/node-hot-reload",
"authors": [
{
"name": "Phil Gates-Idem",
"email": "phillip.gates-idem@gmail.com"
}
],
"main": "./lib/hot-reload/hot-reload",
"dependencies": {
"directory-walker" : "1.2.x"
"name": "hot-reload",
"description": "Triggers reloading of Node.js modules",
"version": "1.2.2",
"homepage": "https://github.com/philidem/node-hot-reload",
"authors": [
{
"name": "Phil Gates-Idem",
"email": "phillip.gates-idem@gmail.com"
},
{
"name": "Patrick Steele-Idem",
"email": "pnidem@gmail.com"
}
}
],
"main": "./lib/hot-reload/hot-reload",
"dependencies": {
"directory-walker": "1.2.x"
}
}
node-hot-reload
===============
Utility code for watching for source file and changes and reloading modules
Utility code for watching for source file and changes and reloading modules
# Installation:
```
npm install hot-reload
```
# Usage
Example Usage:
```javascript
require('hot-reload').create(require)
.uncache("*")
.uncacheExclude(__filename)
.specialReload(path.join(__dirname, 'optimizer-config.xml'), initApp)
.specialReload(path.join(__dirname, 'routes.js'), function(path) {
delete require.cache[path];
initApp();
})
.watch(path.join(__dirname, 'modules'))
.watch(path.join(__dirname, 'optimizer-config.xml'))
.watch(path.join(__dirname, 'routes.js'))
.watchExclude("*.css")
.onBeforeReload(function() {
})
.onAfterReload(function() {
})
.start();
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc