From ea2e3f25d8d505a939d97a1d65535e1db9153bd3 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 8 Jan 2021 14:19:12 +0000 Subject: [PATCH] Implement node property typing See https://github.com/node-red/designs/pull/37 --- .../@node-red/editor-client/src/js/history.js | 30 ++-- .../@node-red/editor-client/src/js/nodes.js | 133 ++++++++++++++---- .../@node-red/editor-client/src/js/red.js | 1 + .../editor-client/src/js/ui/editor.js | 18 +-- .../editor-client/src/js/ui/tab-info.js | 2 +- .../editor-client/src/sass/palette.scss | 4 +- .../nodes/core/common/24-complete.html | 2 +- .../@node-red/nodes/core/common/25-catch.html | 2 +- .../nodes/core/common/25-status.html | 2 +- .../@node-red/nodes/core/common/60-link.html | 4 +- 10 files changed, 145 insertions(+), 53 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index a3392fd26..7fedf84be 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -343,17 +343,29 @@ RED.history = (function() { if (ev.changes.hasOwnProperty(i)) { inverseEv.changes[i] = ev.node[i]; if (ev.node._def.defaults && ev.node._def.defaults[i] && ev.node._def.defaults[i].type) { - // This is a config node property - var currentConfigNode = RED.nodes.node(ev.node[i]); - if (currentConfigNode) { - currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); - RED.events.emit("nodes:change",currentConfigNode); + // This property is a reference to another node or nodes. + var nodeList = ev.node[i]; + if (!Array.isArray(nodeList)) { + nodeList = [nodeList]; } - var newConfigNode = RED.nodes.node(ev.changes[i]); - if (newConfigNode) { - newConfigNode.users.push(ev.node); - RED.events.emit("nodes:change",newConfigNode); + nodeList.forEach(function(id) { + var currentConfigNode = RED.nodes.node(id); + if (currentConfigNode && currentConfigNode._def.category === "config") { + currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); + RED.events.emit("nodes:change",currentConfigNode); + } + }); + nodeList = ev.changes[i]; + if (!Array.isArray(nodeList)) { + nodeList = [nodeList]; } + nodeList.forEach(function(id) { + var newConfigNode = RED.nodes.node(id); + if (newConfigNode && newConfigNode._def.category === "config") { + newConfigNode.users.push(ev.node); + RED.events.emit("nodes:change",newConfigNode); + } + }); } ev.node[i] = ev.changes[i]; } diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 0d664ce3c..9e5464354 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -164,6 +164,21 @@ RED.nodes = (function() { // TODO: too tightly coupled into palette UI } + if (def.defaults) { + for (var d in def.defaults) { + if (def.defaults.hasOwnProperty(d)) { + if (def.defaults[d].type) { + try { + def.defaults[d]._type = parseNodePropertyTypeString(def.defaults[d].type) + } catch(err) { + console.warn(err); + } + } + } + } + } + + RED.events.emit("registry:node-type-added",nt); }, removeNodeType: function(nt) { @@ -193,6 +208,59 @@ RED.nodes = (function() { return (1+Math.random()*4294967295).toString(16); } + function parseNodePropertyTypeString(typeString) { + typeString = typeString.trim(); + var c; + var pos = 0; + var isArray = /\[\]$/.test(typeString); + if (isArray) { + typeString = typeString.substring(0,typeString.length-2); + } + + var l = typeString.length; + var inBrackets = false; + var inToken = false; + var currentToken = ""; + var types = []; + while (pos < l) { + c = typeString[pos]; + if (inToken) { + if (c === "|") { + types.push(currentToken.trim()) + currentToken = ""; + inToken = false; + } else if (c === ")") { + types.push(currentToken.trim()) + currentToken = ""; + inBrackets = false; + inToken = false; + } else { + currentToken += c; + } + } else { + if (c === "(") { + if (inBrackets) { + throw new Error("Invalid character '"+c+"' at position "+pos) + } + inBrackets = true; + } else if (c !== " ") { + inToken = true; + currentToken = c; + } + } + pos++; + } + currentToken = currentToken.trim(); + if (currentToken.length > 0) { + types.push(currentToken) + } + return { + types: types, + array: isArray + } + } + + function addNode(n) { if (n.type.indexOf("subflow") !== 0) { n["_"] = n._def._; @@ -787,16 +855,29 @@ RED.nodes = (function() { if (node.type !== "subflow") { var convertedNode = RED.nodes.convertNode(node); for (var d in node._def.defaults) { - if (node._def.defaults[d].type && node[d] in configNodes) { - var confNode = configNodes[node[d]]; - var exportable = registry.getNodeType(node._def.defaults[d].type).exportable; - if ((exportable == null || exportable)) { - if (!(node[d] in exportedConfigNodes)) { - exportedConfigNodes[node[d]] = true; - set.push(confNode); + if (node._def.defaults[d].type) { + var nodeList = node[d]; + if (!Array.isArray(nodeList)) { + nodeList = [nodeList]; + } + nodeList = nodeList.filter(function(id) { + if (id in configNodes) { + var confNode = configNodes[id]; + if (confNode._def.exportable !== false) { + if (!(id in exportedConfigNodes)) { + exportedConfigNodes[id] = true; + set.push(confNode); + return true; + } + } + return false; } + return true; + }) + if (nodeList.length === 0) { + convertedNode[d] = Array.isArray(node[d])?[]:"" } else { - convertedNode[d] = ""; + convertedNode[d] = Array.isArray(node[d])?nodeList:nodeList[0] } } } @@ -1587,15 +1668,6 @@ RED.nodes = (function() { } } } - // TODO: make this a part of the node definition so it doesn't have to - // be hardcoded here - var nodeTypeArrayReferences = { - "catch":"scope", - "status":"scope", - "complete": "scope", - "link in":"links", - "link out":"links" - } // Remap all wires and config node references for (i=0;i').appendTo(tableBody); $(propRow.children()[0]).text(n); - if (defaults[n].type) { + if (defaults[n].type && !defaults[n]._type.array) { var configNode = RED.nodes.node(val); if (!configNode) { RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss index 7a2b53eb2..385ad761f 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/palette.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/palette.scss @@ -131,10 +131,10 @@ width: 120px; background-size: contain; position: relative; - &:not(.red-ui-palette-node-config):first-child { + &:not(.red-ui-palette-node-config):not(.red-ui-palette-node-small):first-child { margin-top: 15px; } - &:not(.red-ui-palette-node-config):last-child { + &:not(.red-ui-palette-node-config):not(.red-ui-palette-node-small):first-child { margin-bottom: 15px; } } diff --git a/packages/node_modules/@node-red/nodes/core/common/24-complete.html b/packages/node_modules/@node-red/nodes/core/common/24-complete.html index 9c49648e3..a6a7a2a45 100644 --- a/packages/node_modules/@node-red/nodes/core/common/24-complete.html +++ b/packages/node_modules/@node-red/nodes/core/common/24-complete.html @@ -18,7 +18,7 @@ color:"#c0edc0", defaults: { name: {value:""}, - scope: {value:[]}, + scope: {value:[], type:"*[]"}, uncaught: {value:false} }, inputs:0, diff --git a/packages/node_modules/@node-red/nodes/core/common/25-catch.html b/packages/node_modules/@node-red/nodes/core/common/25-catch.html index 1fa94258f..0b976ea78 100644 --- a/packages/node_modules/@node-red/nodes/core/common/25-catch.html +++ b/packages/node_modules/@node-red/nodes/core/common/25-catch.html @@ -30,7 +30,7 @@ color:"#e49191", defaults: { name: {value:""}, - scope: {value:null}, + scope: {value:null, type:"*[]"}, uncaught: {value:false} }, inputs:0, diff --git a/packages/node_modules/@node-red/nodes/core/common/25-status.html b/packages/node_modules/@node-red/nodes/core/common/25-status.html index dc2f8893d..47a3192e4 100644 --- a/packages/node_modules/@node-red/nodes/core/common/25-status.html +++ b/packages/node_modules/@node-red/nodes/core/common/25-status.html @@ -26,7 +26,7 @@ color:"#94c1d0", defaults: { name: {value:""}, - scope: {value:null} + scope: {value:null, type:"*[]"} }, inputs:0, outputs:1, diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html index 7953c9979..c9207a345 100644 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.html +++ b/packages/node_modules/@node-red/nodes/core/common/60-link.html @@ -187,7 +187,7 @@ color:"#ddd",//"#87D8CF", defaults: { name: {value:""}, - links: { value: [] } + links: { value: [], type:"link out[]" } }, inputs:0, outputs:1, @@ -216,7 +216,7 @@ color:"#ddd",//"#87D8CF", defaults: { name: {value:""}, - links: { value: []} + links: { value: [], type:"link in[]"} }, align:"right", inputs:1,