mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Support npm subflow modules
This commit is contained in:
committed by
Nick O'Leary
parent
6e1466e411
commit
9a660f3fe9
@@ -99,6 +99,8 @@ module.exports = {
|
||||
*/
|
||||
get: registry.getNodeConstructor,
|
||||
|
||||
registerSubflow: registry.registerSubflow,
|
||||
|
||||
/**
|
||||
* Get a node's set information.
|
||||
*
|
||||
|
@@ -158,13 +158,10 @@ async function loadNodeTemplate(node) {
|
||||
}
|
||||
return node
|
||||
}).catch(err => {
|
||||
node.types = [];
|
||||
if (err.code === 'ENOENT') {
|
||||
if (!node.types) {
|
||||
node.types = [];
|
||||
}
|
||||
node.err = "Error: "+node.template+" does not exist";
|
||||
} else {
|
||||
// ENOENT means no html file. We can live with that. But any other error
|
||||
// should be fatal
|
||||
// node.err = "Error: "+node.template+" does not exist";
|
||||
if (err.code !== 'ENOENT') {
|
||||
node.types = [];
|
||||
node.err = err.toString();
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ var fs = require("fs");
|
||||
|
||||
var library = require("./library");
|
||||
const {events} = require("@node-red/util")
|
||||
var subflows = require("./subflow");
|
||||
var settings;
|
||||
var loader;
|
||||
|
||||
@@ -27,6 +28,8 @@ var nodeConfigCache = {};
|
||||
var moduleConfigs = {};
|
||||
var nodeList = [];
|
||||
var nodeConstructors = {};
|
||||
var subflowModules = {};
|
||||
|
||||
var nodeTypeToId = {};
|
||||
var moduleNodes = {};
|
||||
|
||||
@@ -36,6 +39,7 @@ function init(_settings,_loader) {
|
||||
moduleNodes = {};
|
||||
nodeTypeToId = {};
|
||||
nodeConstructors = {};
|
||||
subflowModules = {};
|
||||
nodeList = [];
|
||||
nodeConfigCache = {};
|
||||
}
|
||||
@@ -225,6 +229,7 @@ function removeNode(id) {
|
||||
config.types.forEach(function(t) {
|
||||
var typeId = nodeTypeToId[t];
|
||||
if (typeId === id) {
|
||||
delete subflowModules[t];
|
||||
delete nodeConstructors[t];
|
||||
delete nodeTypeToId[t];
|
||||
}
|
||||
@@ -393,6 +398,27 @@ function registerNodeConstructor(nodeSet,type,constructor) {
|
||||
events.emit("type-registered",type);
|
||||
}
|
||||
|
||||
function registerSubflow(nodeSet, subflow) {
|
||||
var nodeSetInfo = getFullNodeInfo(nodeSet);
|
||||
|
||||
const result = subflows.register(nodeSet,subflow);
|
||||
|
||||
if (subflowModules.hasOwnProperty(result.type)) {
|
||||
throw new Error(result.type+" already registered");
|
||||
}
|
||||
|
||||
if (nodeSetInfo) {
|
||||
if (nodeSetInfo.types.indexOf(result.type) === -1) {
|
||||
nodeSetInfo.types.push(result.type);
|
||||
nodeTypeToId[result.type] = nodeSetInfo.id;
|
||||
}
|
||||
nodeSetInfo.config = result.config;
|
||||
}
|
||||
subflowModules[result.type] = result;
|
||||
events.emit("type-registered",result.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAllNodeConfigs(lang) {
|
||||
if (!nodeConfigCache[lang]) {
|
||||
var result = "";
|
||||
@@ -447,7 +473,7 @@ function getNodeConstructor(type) {
|
||||
}
|
||||
|
||||
if (!config || (config.enabled && !config.err)) {
|
||||
return nodeConstructors[type];
|
||||
return nodeConstructors[type] || subflowModules[type];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -457,6 +483,7 @@ function clear() {
|
||||
moduleConfigs = {};
|
||||
nodeList = [];
|
||||
nodeConstructors = {};
|
||||
subflowModules = {};
|
||||
nodeTypeToId = {};
|
||||
}
|
||||
|
||||
@@ -613,6 +640,7 @@ var registry = module.exports = {
|
||||
registerNodeConstructor: registerNodeConstructor,
|
||||
getNodeConstructor: getNodeConstructor,
|
||||
|
||||
registerSubflow: registerSubflow,
|
||||
|
||||
addModule: addModule,
|
||||
|
||||
|
117
packages/node_modules/@node-red/registry/lib/subflow.js
vendored
Normal file
117
packages/node_modules/@node-red/registry/lib/subflow.js
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
function getSubflowType(subflow) {
|
||||
return "sf-"+subflow.id
|
||||
}
|
||||
|
||||
function generateSubflowConfig(subflow) {
|
||||
|
||||
const subflowType = getSubflowType(subflow)
|
||||
const label = subflow.name || subflowType;
|
||||
const category = subflow.category || "function";
|
||||
const color = subflow.color || "#C0DEED";
|
||||
const inputCount = subflow.in?subflow.in.length:0;
|
||||
const outputCount = subflow.out?subflow.out.length:0;
|
||||
const icon = subflow.icon || "arrow-in.svg";
|
||||
|
||||
const defaults = {
|
||||
name: {value: ""}
|
||||
}
|
||||
|
||||
const credentials = {}
|
||||
|
||||
if (subflow.env) {
|
||||
subflow.env.forEach(prop => {
|
||||
var defaultValue;
|
||||
|
||||
switch(prop.type) {
|
||||
case "cred": defaultValue = ""; break;
|
||||
case "str": defaultValue = prop.value||""; break;
|
||||
case "bool": defaultValue = (typeof prop.value === 'boolean')?prop.value:prop.value === "true" ; break;
|
||||
case "num": defaultValue = (typeof prop.value === 'number')?prop.value:Number(prop.value); break;
|
||||
default:
|
||||
defaultValue = {
|
||||
type: prop.type,
|
||||
value: prop.value||""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
defaults[prop.name] = {
|
||||
value: defaultValue,
|
||||
ui: prop.ui
|
||||
}
|
||||
if (prop.type === 'cred') {
|
||||
defaults[prop.name].ui.type = "cred";
|
||||
credentials[prop.name] = {type:"password"}
|
||||
}
|
||||
})
|
||||
}
|
||||
const defaultString = JSON.stringify(defaults);
|
||||
const credentialsString = JSON.stringify(credentials);
|
||||
|
||||
return `<script type="text/javascript">
|
||||
RED.nodes.registerType("${subflowType}",{
|
||||
category: "${category}",
|
||||
color: "${color}",
|
||||
defaults: ${defaultString},
|
||||
credentials: ${credentialsString},
|
||||
inputs:${inputCount},
|
||||
outputs:${outputCount},
|
||||
icon: "${icon}",
|
||||
paletteLabel: "${label}",
|
||||
label: function() {
|
||||
return this.name||"${label}";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
RED.subflow.buildEditForm('subflow', this);
|
||||
},
|
||||
oneditsave: function() {
|
||||
var props = RED.subflow.exportSubflowInstanceEnv(this);
|
||||
var i=0,l=props.length;
|
||||
for (;i<l;i++) {
|
||||
var prop = props[i];
|
||||
if (this._def.defaults[prop.name].ui && this._def.defaults[prop.name].ui.type === "cred") {
|
||||
this[prop.name] = "";
|
||||
this.credentials[prop.name] = prop.value || "";
|
||||
this.credentials['has_' + prop.name] = (this.credentials[prop.name] !== "");
|
||||
} else {
|
||||
switch(prop.type) {
|
||||
case "str": this[prop.name] = prop.value||""; break;
|
||||
case "bool": this[prop.name] = (typeof prop.value === 'boolean')?prop.value:prop.value === "true" ; break;
|
||||
case "num": this[prop.name] = (typeof prop.value === 'number')?prop.value:Number(prop.value); break;
|
||||
default:
|
||||
this[prop.name] = {
|
||||
type: prop.type,
|
||||
value: prop.value||""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script type="text/x-red" data-template-name="${subflowType}">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]editor:common.label.name">
|
||||
</div>
|
||||
<div id="subflow-input-ui"></div>
|
||||
</script>
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
function register(id,subflow) {
|
||||
return {
|
||||
subflow: subflow,
|
||||
type: getSubflowType(subflow),
|
||||
config: generateSubflowConfig(subflow)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
register: register
|
||||
}
|
@@ -83,6 +83,9 @@ function createNodeApi(node) {
|
||||
red.nodes.registerType = function(type,constructor,opts) {
|
||||
runtime.nodes.registerType(node.id,type,constructor,opts);
|
||||
}
|
||||
red.nodes.registerSubflow = function(subflowDef) {
|
||||
runtime.nodes.registerSubflow(node.id,subflowDef)
|
||||
}
|
||||
copyObjectProperties(log,red.log,null,["init"]);
|
||||
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
|
||||
if (runtime.adminApi) {
|
||||
|
Reference in New Issue
Block a user