diff --git a/red/nodes/index.js b/red/nodes/index.js index 84cb7dc8e..ec48b18d2 100644 --- a/red/nodes/index.js +++ b/red/nodes/index.js @@ -51,6 +51,26 @@ function init(_settings,storage) { registry.init(_settings); } + +function removeNode(info) { + var nodeInfo = registry.getNodeInfo(info); + var inUse = {}; + flows.each(function(n) { + inUse[n.type] = (inUse[n.type]||0)+1; + }); + var nodesInUse = []; + nodeInfo.types.forEach(function(t) { + if (inUse[t]) { + nodesInUse.push(t); + } + }); + if (nodesInUse.length > 0) { + var msg = nodesInUse.join(", "); + throw Error("Type in use: "+msg); + } + return registry.removeNode(nodeInfo.id); +} + module.exports = { // Lifecycle init: init, @@ -60,6 +80,9 @@ module.exports = { createNode: createNode, getNode: flows.get, + addNode: registry.addNode, + removeNode: removeNode, + // Node type registry registerType: registerType, getType: registry.get, diff --git a/red/nodes/registry.js b/red/nodes/registry.js index f3080c044..236ba61a0 100644 --- a/red/nodes/registry.js +++ b/red/nodes/registry.js @@ -46,14 +46,42 @@ var registry = (function() { var nodeConfigs = {}; var nodeList = []; var nodeConstructors = {}; + var nodeTypeToId = {}; return { addNodeSet: function(id,set) { + if (!set.err) { + set.types.forEach(function(t) { + nodeTypeToId[t] = id; + }); + } nodeConfigs[id] = set; nodeList.push(id); + nodeConfigCache = null; }, - getNodeSet: function(id) { - return nodeConfigs[id]; + removeNode: function(id) { + var config = nodeConfigs[id]; + if (config) { + delete nodeConfigs[id]; + var i = nodeList.indexOf(id); + if (i > -1) { + nodeList.splice(i,1); + } + config.types.forEach(function(t) { + delete nodeConstructors[t]; + delete nodeTypeToId[t]; + }); + nodeConfigCache = null; + } + return filterNodeInfo(config); + }, + getNodeInfo: function(typeOrId) { + if (nodeTypeToId[typeOrId]) { + return filterNodeInfo(nodeConfigs[nodeTypeToId[typeOrId]]); + } else if (nodeConfigs[typeOrId]) { + return filterNodeInfo(nodeConfigs[typeOrId]); + } + return null; }, getNodeList: function() { return nodeList.map(function(id) { @@ -65,9 +93,11 @@ var registry = (function() { if (nodeConstructors[type]) { throw new Error(type+" already registered"); } + //TODO: Ensure type is known - but doing so will break some tests + // that don't have a way to register a node template ahead + // of registering the constructor util.inherits(constructor,Node); nodeConstructors[type] = constructor; - events.emit("type-registered",type); }, @@ -87,9 +117,11 @@ var registry = (function() { script += config.script; } } - result += ''; + if (script.length > 0) { + result += ''; + } nodeConfigCache = result; } return nodeConfigCache; @@ -107,7 +139,11 @@ var registry = (function() { }, getNodeConstructor: function(type) { - return nodeConstructors[type]; + var config = nodeConfigs[nodeTypeToId[type]]; + if (!config || config.enabled) { + return nodeConstructors[type]; + } + return null; }, clear: function() { @@ -115,6 +151,31 @@ var registry = (function() { nodeConfigs = {}; nodeList = []; nodeConstructors = {}; + nodeTypeToId = {}; + }, + + getTypeId: function(type) { + return nodeTypeToId[type]; + }, + + enableNodeSet: function(id) { + var config = nodeConfigs[id]; + if (config) { + if (config.err) { + throw new Error("cannot enable node with error"); + } + config.enabled = true; + nodeConfigCache = null; + } + }, + + disableNodeSet: function(id) { + var config = nodeConfigs[id]; + if (config) { + config.enabled = false; + nodeConfigCache = null; + } + } } })(); @@ -220,7 +281,7 @@ function loadNodesFromModule(moduleDir,pkg) { for (var n in nodes) { if (nodes.hasOwnProperty(n)) { var file = path.join(moduleDir,nodes[n]); - results.push(loadNodeConfig(file,pkg.name+":"+n)); + results.push(loadNodeConfig(file,pkg.name,n)); var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons"); if (iconDirs.indexOf(iconDir) == -1) { if (fs.existsSync(iconDir)) { @@ -249,21 +310,28 @@ function loadNodesFromModule(moduleDir,pkg) { * types: an array of node type names in this file * } */ -function loadNodeConfig(file,name) { +function loadNodeConfig(file,module,name) { var id = crypto.createHash('sha1').update(file).digest("hex"); - if (registry.getNodeSet(id)) { + if (registry.getNodeInfo(id)) { throw new Error(file+" already loaded"); } var node = { id: id, file: file, - name: name||path.basename(file), template: file.replace(/\.js$/,".html"), enabled: true } + if (module) { + node.name = module+":"+name; + node.module = module; + } else { + node.name = path.basename(file) + } + + var content = fs.readFileSync(node.template,'utf8'); var $ = cheerio.load(content); @@ -293,6 +361,12 @@ function loadNodeConfig(file,name) { node.config = template; node.script = script; + for (var i=0;i + + + +

this should be filtered out

diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js new file mode 100644 index 000000000..d359fb3f2 --- /dev/null +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js @@ -0,0 +1,4 @@ +// A test node that exports a function +module.exports = function(RED) { + throw new Error("fail to load"); +} diff --git a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json index da483f645..8eba5b04b 100644 --- a/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json +++ b/test/red/nodes/resources/TestNodeModule/node_modules/TestNodeModule/package.json @@ -4,7 +4,8 @@ "description" : "A test node module", "node-red" : { "nodes": { - "TestNodeMod1": "TestNodeModule.js" + "TestNodeMod1": "TestNodeModule.js", + "TestNodeMod2": "TestNodeModule2.js" } } }