scalra
Version:
node.js framework to prototype and scale rapidly
161 lines (127 loc) • 4.59 kB
JavaScript
//
// script.js
//
// dyanmic script loading functions
//
// functions:
// monitor(name, fullpath) // see if a script file is modified, and re-load if so
//
//
var l_name = 'SR.Script';
// record of recently modified file(s), to be reloaded by require
var l_modified_scripts = {};
// seconds to reload the file
var l_reloadTime = SR.Settings.TIMEOUT_RELOAD_SCRIPT;
// store script loading arguments
var l_args = {};
// loaded scripts
var l_loaded = exports;
// script loader, will check periodically if modified script queue is non-empty
var l_loadScript = function (fullpath, publname) {
var curr_time = new Date();
// store script if modified for first time
if (fullpath !== undefined && publname !== undefined) {
if (l_modified_scripts.hasOwnProperty(fullpath) === false) {
LOG.warn('script modified: ' + fullpath, l_name);
l_modified_scripts[fullpath] = {
time: new Date(curr_time.getTime() + l_reloadTime * 1000),
name: publname
};
}
// if already stored, ignore this request
else
return;
} else {
// check queue for scripts that can be safely reloaded
for (var path in l_modified_scripts) {
// check if wait time has expired
if (curr_time - l_modified_scripts[path].time > 0) {
// get public name
var name = l_modified_scripts[path].name;
var notify_msg = 'reloading [' + name + '] from: ' + path;
LOG.warn(notify_msg, l_name);
// send e-mail notify to project admin (only if specified)
if (SR.Settings.NOTIFY_SCRIPT_RELOAD === true)
UTIL.notifyAdmin('script reloading', notify_msg);
// save current script in cache as backup
var backup_script = require.cache[path];
// NOTE: if 'path' is incorrect, may not delete successfully, and new script won't load
delete require.cache[path];
// NOTE: this will show false
//LOG.warn('after delete, has path: ' + require.cache.hasOwnProperty(path), l_name);
// NOTE: something can go wrong if the script is corrupt
try {
// re-require
if (l_args.hasOwnProperty(path)) {
LOG.warn('args exist..', l_name);
require(path)(l_args[path]);
l_loaded[name] = require.cache[path];
} else {
l_loaded[name] = require(path);
SR.Handler.add(l_loaded[name]);
}
} catch (e) {
LOG.error('reload error: ', l_name);
LOG.error(UTIL.dumpError(e), l_name);
LOG.warn('restoring old script...', l_name);
require.cache[path] = backup_script;
l_loaded[name] = require.cache[path];
// this will show 'true'
//LOG.warn('after restore, has path: ' + require.cache.hasOwnProperty(path), l_name);
}
// remove file record
delete l_modified_scripts[path];
}
}
}
// reload myself to check later if there are scripts to be loaded
if (Object.keys(l_modified_scripts).length > 0) {
var timeout = l_reloadTime * 1.5 * 1000;
LOG.sys('automatic reloading after: ' + timeout + ' ms', l_name);
setTimeout(l_loadScript, timeout);
}
};
// see if a script file is modified, and re-load if so
// returns loaded module's script or undefined for failure
exports.monitor = function (name, fullpath, args) {
//LOG.warn(name + ': ' + fullpath, l_name);
// check if file exists
if (SR.fs.existsSync(fullpath) === false) {
LOG.error('script does not exist: ' + fullpath, l_name);
LOG.stack();
return undefined;
}
LOG.sys('requiring: ' + fullpath, l_name);
// perform require, pass in optional arguments as well
try {
if (typeof args !== 'undefined') {
LOG.warn('argument provided, will pass to file when performing require', l_name);
require(fullpath)(args);
l_loaded[name] = require.cache[fullpath];
// store arguments (for later reloading)
l_args[fullpath] = args;
} else {
l_loaded[name] = require(fullpath);
}
} catch (e) {
LOG.error('load error:', l_name);
LOG.error(e, l_name);
LOG.error(e.stack, l_name);
return undefined;
}
if (l_loaded.hasOwnProperty(name))
LOG.sys('[' + name + '] is accessible via SR.Script[\'' + name + '\']', l_name);
else {
LOG.error('script [' + name + '] fail to load at: ' + fullpath, l_name);
return undefined;
}
SR.fs.watchFile(fullpath, function (curr, prev) {
// check if file has updated
if (curr.mtime !== prev.mtime) {
// record to modified file & trigger periodic check
l_loadScript(fullpath, name);
}
});
LOG.sys('type of loaded script: ' + typeof l_loaded[name], l_name);
return l_loaded[name];
};