Skip to content

Commit

Permalink
Fix node html compression and refactor registry
Browse files Browse the repository at this point in the history
  • Loading branch information
knolleary committed Jul 28, 2014
1 parent 3d31a0a commit e07a523
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 97 deletions.
2 changes: 1 addition & 1 deletion nodes/core/core/20-inject.html
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@
},
button: {
onclick: function() {
var label = (this.name||this.payload).replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;");
var label = (this.name||this.payload).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
d3.xhr("inject/"+this.id).post(function(err,resp) {
if (err) {
if (err.status == 404) {
Expand Down
6 changes: 3 additions & 3 deletions nodes/core/core/58-debug.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@
}

};
var name = (o.name?o.name:o.id).toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;");
var topic = (o.topic||"").toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;");
var payload = (o.msg||"").toString().replace(/&/g,"&amp;amp;").replace(/</g,"&amp;lt;").replace(/>/g,"&amp;gt;");
var name = (o.name?o.name:o.id).toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var topic = (o.topic||"").toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var payload = (o.msg||"").toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'')
msg.innerHTML = '<span class="debug-message-date">'+getTimestamp()+'</span>'+
'<span class="debug-message-name">['+name+']</span>'+
Expand Down
221 changes: 129 additions & 92 deletions red/nodes/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ var settings;

var node_types = {};
var node_configs = [];
var node_scripts = [];

//TODO: clear this cache whenever a node type is added/removed
var node_config_cache = null;

/**
* Synchronously walks the directory looking for node files.
Expand Down Expand Up @@ -63,7 +65,7 @@ function getNodeFiles(dir) {
* Scans the node_modules path for nodes
* @return a list of node modules: {dir,package}
*/
function scanTreeForNodesModules() {
function scanTreeForNodesModules(moduleName) {
var dir = __dirname+"/../../nodes";
var results = [];
var up = path.resolve(path.join(dir,".."));
Expand All @@ -72,16 +74,18 @@ function scanTreeForNodesModules() {
try {
var files = fs.readdirSync(pm);
files.forEach(function(fn) {
var pkgfn = path.join(pm,fn,"package.json");
try {
var pkg = require(pkgfn);
if (pkg['node-red']) {
var moduleDir = path.join(pm,fn);
results.push({dir:moduleDir,package:pkg});
}
} catch(err) {
if (err.code != "MODULE_NOT_FOUND") {
// TODO: handle unexpected error
if (!moduleName || fn == moduleName) {
var pkgfn = path.join(pm,fn,"package.json");
try {
var pkg = require(pkgfn);
if (pkg['node-red']) {
var moduleDir = path.join(pm,fn);
results.push({dir:moduleDir,package:pkg});
}
} catch(err) {
if (err.code != "MODULE_NOT_FOUND") {
// TODO: handle unexpected error
}
}
}
});
Expand All @@ -94,14 +98,55 @@ function scanTreeForNodesModules() {
return results;
}

/**
* Loads the nodes provided in an npm package.
* @param moduleDir the root directory of the package
* @param pkg the module's package.json object
* @return an array of promises returned by loadNode
*/
function loadNodesFromModule(moduleDir,pkg) {
var nodes = pkg['node-red'].nodes||{};
var promises = [];
var iconDirs = [];
for (var n in nodes) {
if (nodes.hasOwnProperty(n)) {
promises.push(loadNode(path.join(moduleDir,nodes[n]),pkg.name,n));
var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
if (iconDirs.indexOf(iconDir) == -1) {
if (fs.existsSync(iconDir)) {
events.emit("node-icon-dir",iconDir);
iconDirs.push(iconDir);
}
}
}
}
return promises;
}


/**
* Loads the specified node into the registry.
* @param nodeDir the directory containing the node
* @param nodeFn the node file
* @param nodeLabel the name of the node (npm nodes only)
* @return a promise that resolves to either {fn,path,err}
* @param nodeFile the fully qualified path of the node's .js file
* @param nodeModule the name of the module (npm nodes only)
* @param nodeName the name of the node (npm nodes only)
* @return a promise that resolves to a node info object
* {
* name: the name of the node file, or label from the npm module
* module: the name of the node npm module (npm nodes only)
* path: the fully qualified path to the node's .js file
* template: the fully qualified path to the node's .html file
* config: the non-script parts of the node's .html file
* script: the script part of the node's .html file
* err: any error encountered whilst loading the node
* }
* The node info object must be added to the node_config array by the caller.
* This allows nodes to be added in a defined order, regardless of how async
* their loading becomes.
*/
function loadNode(nodeDir, nodeFn, nodeLabel) {
function loadNode(nodeFile, nodeModule, nodeName) {
var nodeDir = path.dirname(nodeFile);
var nodeFn = path.basename(nodeFile);

if (settings.nodesExcludes) {
for (var i=0;i<settings.nodesExcludes.length;i++) {
if (settings.nodesExcludes[i] == nodeFn) {
Expand All @@ -110,63 +155,74 @@ function loadNode(nodeDir, nodeFn, nodeLabel) {
}
}
var nodeFilename = path.join(nodeDir,nodeFn);
var nodeInfo = {name:nodeFn, path:nodeFilename};
if (nodeModule) {
nodeInfo.name = nodeModule+":"+nodeName;
nodeInfo.module = nodeModule;
}
try {
var loadPromise = null;
var r = require(nodeFilename);
if (typeof r === "function") {
var promise = r(require('../red'));
if (promise != null && typeof promise.then === "function") {
loadPromise = promise.then(function() {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename});
nodeInfo = loadTemplate(nodeInfo);
return when.resolve(nodeInfo);
}).otherwise(function(err) {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename,err:err});
nodeInfo.err = err;
return when.resolve(nodeInfo);
});
}
}
if (loadPromise == null) {
loadPromise = when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename});
nodeInfo = loadTemplate(nodeInfo);
loadPromise = when.resolve(nodeInfo);
}
return loadPromise;
} catch(err) {
return when.resolve({fn:nodeLabel||nodeFn,path:nodeFilename,err:err});
nodeInfo.err = err;
return when.resolve(nodeInfo);
}
}

/**
* Loads the html template file for a node
* @param templateFilanem
*/
function loadTemplate(templateFilename) {
var content = fs.readFileSync(templateFilename,'utf8');
registerConfig(content);
}
function loadTemplate(nodeInfo) {

var templateFilename = nodeInfo.path.replace(/\.js$/,".html");

/**
* Loads the nodes provided in an npm package.
* @param moduleDir the root directory of the package
* @param pkg the module's package.json object
* @return an array of promises returned by loadNode
*/
function loadNodesFromModule(moduleDir,pkg) {
var nodes = pkg['node-red'].nodes||{};
var promises = [];
var iconDirs = [];
for (var n in nodes) {
if (nodes.hasOwnProperty(n)) {
promises.push(loadNode(moduleDir,nodes[n],pkg.name+":"+n));
var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
if (iconDirs.indexOf(iconDir) == -1) {
if (fs.existsSync(iconDir)) {
events.emit("node-icon-dir",iconDir);
iconDirs.push(iconDir);
var content = fs.readFileSync(templateFilename,'utf8');

$ = cheerio.load(content);
var template = "";
var script = "";
$("*").each(function(i,el) {
if (el.type == "script" && el.attribs.type == "text/javascript") {
script += el.children[0].data;
} else if (el.name == "script" || el.name == "style") {
var openTag = "<"+el.name;
var closeTag = "</"+el.name+">";
if (el.attribs) {
for (var j in el.attribs) {
if (el.attribs.hasOwnProperty(j)) {
openTag += " "+j+'="'+el.attribs[j]+'"';
}
}
}
openTag += ">";
template += openTag+$(el).text()+closeTag;
}
}
return promises;
});

nodeInfo.template = templateFilename;
nodeInfo.config = template;
nodeInfo.script = script;
return nodeInfo;
}


function init(_settings) {
Node = require("./Node");
settings = _settings;
Expand Down Expand Up @@ -197,9 +253,7 @@ function load() {
// Load all of the nodes in the order they were discovered
var loadPromises = [];
nodeFiles.forEach(function(file) {
var dir = path.dirname(file);
var fn = path.basename(file);
loadPromises.push(loadNode(dir,fn));
loadPromises.push(loadNode(file));
});

moduleFiles.forEach(function(file) {
Expand All @@ -213,64 +267,40 @@ function load() {
// Store the error to pass up
errors.push(result.value);
} else {
// Load the node template
var templateFilename = result.value.path.replace(/\.js$/,".html");
loadTemplate(templateFilename);
node_configs.push(result.value);
}
});
// Trigger a load of the configs to get it precached
getNodeConfigs();

resolve(errors);
});
});
}

/**
* Registers a node's html configuration.
*/
function registerConfig(config) {
$ = cheerio.load(config);
var template = "";
$("*").each(function(i,el) {
if (el.type == "script" && el.attribs.type == "text/javascript") {
var content = el.children[0].data;
el.children[0].data = UglifyJS.minify(content, {fromString: true}).code;
node_scripts.push($(this).text());
} else if (el.name == "script" || el.name == "style") {
var openTag = "<"+el.name;
var closeTag = "</"+el.name+">";
if (el.attribs) {
for (var j in el.attribs) {
if (el.attribs.hasOwnProperty(j)) {
openTag += " "+j+'="'+el.attribs[j]+'"';
}
}
}
openTag += ">";

template += openTag+$(el).text()+closeTag;
}
});
node_configs.push(template);
}

/**
* Gets all of the node template configs
* @return all of the node templates in a single string
*/
function getNodeConfigs() {
var result = "";
for (var i=0;i<node_configs.length;i++) {
result += node_configs[i];
}
result += '<script type="text/javascript">';
for (var j=0;j<node_scripts.length;j++) {
result += node_scripts[j];
if (!node_config_cache) {
var result = "";
var script = "";
for (var i=0;i<node_configs.length;i++) {
var config = node_configs[i];
result += config.config||"";
script += config.script||"";
}
result += '<script type="text/javascript">';
result += UglifyJS.minify(script, {fromString: true}).code;
result += '</script>';
node_config_cache = result;
}
result += '</script>';
return result;
return node_config_cache;
}


var typeRegistry = module.exports = {
module.exports = {
init:init,
load:load,
registerType: function(type,node) {
Expand All @@ -281,6 +311,13 @@ var typeRegistry = module.exports = {
get: function(type) {
return node_types[type];
},
getNodeConfigs: getNodeConfigs
getNodeConfigs: getNodeConfigs,

loadNode: function(filename) {

},
removeNode: function(filename) {

}
}

2 changes: 1 addition & 1 deletion red/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function start() {
util.log("------------------------------------------");
if (settings.verbose) {
for (var i=0;i<nodeErrors.length;i+=1) {
util.log("["+nodeErrors[i].fn+"] "+nodeErrors[i].err);
util.log("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
}
} else {
util.log("[red] Failed to register "+nodeErrors.length+" node type"+(nodeErrors.length==1?"":"s"));
Expand Down

0 comments on commit e07a523

Please sign in to comment.