mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge pull request #2873 from node-red/function-modules
Function node external modules
This commit is contained in:
@@ -1,60 +1,318 @@
|
||||
|
||||
<script type="text/html" data-template-name="function">
|
||||
<style>
|
||||
.func-tabs-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#node-input-libs-container-row .red-ui-editableList-container {
|
||||
padding: 0px;
|
||||
}
|
||||
#node-input-libs-container-row .red-ui-editableList-container li {
|
||||
padding:5px;
|
||||
}
|
||||
#node-input-libs-container-row .red-ui-editableList-item-remove {
|
||||
right: 5px;
|
||||
}
|
||||
.node-libs-entry {
|
||||
display: flex;
|
||||
}
|
||||
.node-libs-entry .node-input-libs-var, .node-libs-entry .red-ui-typedInput-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.node-libs-entry > code,.node-libs-entry > span {
|
||||
line-height: 30px;
|
||||
}
|
||||
.node-libs-entry > input[type=text] {
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 2px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.node-libs-entry > span > i {
|
||||
display: none;
|
||||
}
|
||||
.node-libs-entry > span.input-error > i {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
</style>
|
||||
<input type="hidden" id="node-input-func">
|
||||
<input type="hidden" id="node-input-noerr">
|
||||
<input type="hidden" id="node-input-finalize">
|
||||
<input type="hidden" id="node-input-initialize">
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
|
||||
<div class="form-row func-tabs-row">
|
||||
<ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul>
|
||||
</div>
|
||||
|
||||
<div id="func-tabs-content" style="min-height: calc(100% - 95px);">
|
||||
|
||||
<div id="func-tab-init" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-initialize" autofocus="autofocus">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-init-editor" ></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="func-tab-body" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-func" autofocus="autofocus">
|
||||
<input type="hidden" id="node-input-noerr">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
<div style="height: 220px; min-height:120px; margin-top: 30px;" class="node-text-editor" id="node-input-func-editor" ></div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" style="margin-bottom: 0px">
|
||||
<div id="func-tab-config" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
|
||||
<input id="node-input-outputs" style="width: 60px;" value="1">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-input-libs-row hide" style="margin-bottom: 0px;">
|
||||
<label><i class="fa fa-cubes"></i> <span>Modules</span></label>
|
||||
</div>
|
||||
<div class="form-row node-input-libs-row hide" id="node-input-libs-container-row">
|
||||
<ol id="node-input-libs-container"></ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="func-tab-init" style="display:none">
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
|
||||
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="func-tab-body" style="display:none">
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
|
||||
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="func-tab-finalize" style="display:none">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<input type="hidden" id="node-input-finalize" autofocus="autofocus">
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row" style="position:relative">
|
||||
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
|
||||
<div style="position: absolute; right:0; bottom: calc(100% - 20px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
(function() {
|
||||
|
||||
var invalidModuleVNames = [
|
||||
'console',
|
||||
'util',
|
||||
'Buffer',
|
||||
'Date',
|
||||
'RED',
|
||||
'node',
|
||||
'__node__',
|
||||
'context',
|
||||
'flow',
|
||||
'global',
|
||||
'env',
|
||||
'setTimeout',
|
||||
'clearTimeout',
|
||||
'setInterval',
|
||||
'clearInterval',
|
||||
'promisify'
|
||||
]
|
||||
|
||||
var knownFunctionNodes = {};
|
||||
RED.events.on("nodes:add", function(n) {
|
||||
if (n.type === "function") {
|
||||
knownFunctionNodes[n.id] = n;
|
||||
}
|
||||
})
|
||||
RED.events.on("nodes:remove", function(n) {
|
||||
if (n.type === "function") {
|
||||
delete knownFunctionNodes[n.id];
|
||||
}
|
||||
})
|
||||
|
||||
var missingModules = [];
|
||||
var missingModuleReasons = {};
|
||||
RED.events.on("runtime-state", function(event) {
|
||||
if (event.error === "missing-modules") {
|
||||
missingModules = event.modules.map(function(m) { missingModuleReasons[m.module] = m.error; return m.module });
|
||||
for (var id in knownFunctionNodes) {
|
||||
if (knownFunctionNodes.hasOwnProperty(id) && knownFunctionNodes[id].libs && knownFunctionNodes[id].libs.length > 0) {
|
||||
RED.editor.validateNode(knownFunctionNodes[id])
|
||||
}
|
||||
}
|
||||
} else if (!event.text) {
|
||||
missingModuleReasons = {};
|
||||
missingModules = [];
|
||||
for (var id in knownFunctionNodes) {
|
||||
if (knownFunctionNodes.hasOwnProperty(id) && knownFunctionNodes[id].libs && knownFunctionNodes[id].libs.length > 0) {
|
||||
RED.editor.validateNode(knownFunctionNodes[id])
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
var installAllowList = ['*'];
|
||||
var installDenyList = [];
|
||||
|
||||
var modulesEnabled = true;
|
||||
if (RED.settings.get('externalModules.modules.allowInstall', true) === false) {
|
||||
modulesEnabled = false;
|
||||
}
|
||||
var settingsAllowList = RED.settings.get("externalModules.modules.allowList")
|
||||
var settingsDenyList = RED.settings.get("externalModules.modules.denyList")
|
||||
if (settingsAllowList || settingsDenyList) {
|
||||
installAllowList = settingsAllowList;
|
||||
installDenyList = settingsDenyList
|
||||
}
|
||||
installAllowList = RED.utils.parseModuleList(installAllowList);
|
||||
installDenyList = RED.utils.parseModuleList(installDenyList);
|
||||
|
||||
|
||||
// object that maps from library name to its descriptor
|
||||
var allLibs = [];
|
||||
|
||||
function moduleName(module) {
|
||||
var match = /^([^@]+)@(.+)/.exec(module);
|
||||
if (match) {
|
||||
return [match[1], match[2]];
|
||||
}
|
||||
return [module, undefined];
|
||||
}
|
||||
|
||||
function getAllUsedModules() {
|
||||
var moduleSet = new Set();
|
||||
for (var id in knownFunctionNodes) {
|
||||
if (knownFunctionNodes.hasOwnProperty(id)) {
|
||||
if (knownFunctionNodes[id].libs) {
|
||||
for (var i=0, l=knownFunctionNodes[id].libs.length; i<l; i++) {
|
||||
if (RED.utils.checkModuleAllowed(knownFunctionNodes[id].libs[i].module,null,installAllowList,installDenyList)) {
|
||||
moduleSet.add(knownFunctionNodes[id].libs[i].module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var modules = Array.from(moduleSet);
|
||||
modules.sort();
|
||||
return modules;
|
||||
}
|
||||
|
||||
function prepareLibraryConfig(node) {
|
||||
$(".node-input-libs-row").show();
|
||||
var usedModules = getAllUsedModules();
|
||||
var typedModules = usedModules.map(function(l) {
|
||||
return {icon:"fa fa-cube", value:l,label:l,hasValue:false}
|
||||
})
|
||||
typedModules.push({
|
||||
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
|
||||
})
|
||||
|
||||
var libList = $("#node-input-libs-container").css('min-height','100px').css('min-width','450px').editableList({
|
||||
addItem: function(container,i,opt) {
|
||||
var parent = container.parent();
|
||||
var row0 = $("<div/>").addClass("node-libs-entry").appendTo(container);
|
||||
var fieldWidth = "260px";
|
||||
$('<code>const </code>').appendTo(row0);
|
||||
var fvar = $("<input/>", {
|
||||
class: "node-input-libs-var red-ui-font-code",
|
||||
placeholder: RED._("node-red:function.require.var"),
|
||||
type: "text"
|
||||
}).css({
|
||||
width: "120px",
|
||||
"margin-left": "5px"
|
||||
}).appendTo(row0).val(opt.var);
|
||||
var vnameWarning = $('<span style="display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(row0);
|
||||
RED.popover.tooltip(vnameWarning.find("i"),function() {
|
||||
var val = fvar.val();
|
||||
if (invalidModuleVNames.indexOf(val) !== -1) {
|
||||
return RED._("node-red:function.error.moduleNameReserved",{name:val})
|
||||
} else {
|
||||
return RED._("node-red:function.error.moduleNameError",{name:val})
|
||||
}
|
||||
})
|
||||
|
||||
$('<code> = require(</code>').appendTo(row0);
|
||||
var fmodule = $("<input/>", {
|
||||
class: "node-input-libs-val",
|
||||
placeholder: RED._("node-red:function.require.module"),
|
||||
type: "text"
|
||||
}).css({
|
||||
width: "180px",
|
||||
}).appendTo(row0).typedInput({
|
||||
types: typedModules,
|
||||
default: usedModules.indexOf(opt.module) > -1 ? opt.module : "_custom_"
|
||||
});
|
||||
if (usedModules.indexOf(opt.module) === -1) {
|
||||
fmodule.typedInput('value', opt.module);
|
||||
}
|
||||
|
||||
$('<code>)</code>').appendTo(row0);
|
||||
|
||||
var moduleWarning = $('<span style="display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(row0);
|
||||
RED.popover.tooltip(moduleWarning.find("i"),function() {
|
||||
var val = fmodule.typedInput("type");
|
||||
if (val === "_custom_") {
|
||||
val = fmodule.val();
|
||||
}
|
||||
var errors = [];
|
||||
|
||||
if (!RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList)) {
|
||||
return RED._("node-red:function.error.moduleNotAllowed",{module:val});
|
||||
} else {
|
||||
return RED._("node-red:function.error.moduleLoadError",{module:val,error:missingModuleReasons[val]});
|
||||
}
|
||||
})
|
||||
|
||||
fvar.on("change keyup paste", function (e) {
|
||||
var v = $(this).val().trim();
|
||||
if (v === "" || / /.test(v) || invalidModuleVNames.indexOf(v) !== -1) {
|
||||
fvar.addClass("input-error");
|
||||
vnameWarning.addClass("input-error");
|
||||
} else {
|
||||
fvar.removeClass("input-error");
|
||||
vnameWarning.removeClass("input-error");
|
||||
}
|
||||
});
|
||||
|
||||
fmodule.on("change keyup paste", function (e) {
|
||||
var val = $(this).typedInput("type");
|
||||
if (val === "_custom_") {
|
||||
val = $(this).val();
|
||||
}
|
||||
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/]./g, function(v) { return v[1].toUpperCase() });
|
||||
fvar.val(varName);
|
||||
fvar.trigger("change");
|
||||
|
||||
if (RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList) && (missingModules.indexOf(val) === -1)) {
|
||||
fmodule.removeClass("input-error");
|
||||
moduleWarning.removeClass("input-error");
|
||||
} else {
|
||||
fmodule.addClass("input-error");
|
||||
moduleWarning.addClass("input-error");
|
||||
}
|
||||
});
|
||||
if (RED.utils.checkModuleAllowed(opt.module,null,installAllowList,installDenyList) && (missingModules.indexOf(opt.module) === -1)) {
|
||||
fmodule.removeClass("input-error");
|
||||
moduleWarning.removeClass("input-error");
|
||||
} else {
|
||||
fmodule.addClass("input-error");
|
||||
moduleWarning.addClass("input-error");
|
||||
}
|
||||
if (opt.var) {
|
||||
fvar.trigger("change");
|
||||
}
|
||||
},
|
||||
removable: true
|
||||
});
|
||||
|
||||
var libs = node.libs || [];
|
||||
for (var i=0,l=libs.length;i<l; i++) {
|
||||
libList.editableList('addItem',libs[i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType('function',{
|
||||
color:"#fdd0a2",
|
||||
category: 'function',
|
||||
@@ -64,7 +322,26 @@
|
||||
outputs: {value:1},
|
||||
noerr: {value:0,required:true,validate:function(v) { return !v; }},
|
||||
initialize: {value:""},
|
||||
finalize: {value:""}
|
||||
finalize: {value:""},
|
||||
libs: {value: [], validate: function(v) {
|
||||
if (!v) { return true; }
|
||||
for (var i=0,l=v.length;i<l;i++) {
|
||||
var m = v[i];
|
||||
if (!RED.utils.checkModuleAllowed(m.module,null,installAllowList,installDenyList)) {
|
||||
return false
|
||||
}
|
||||
if (m.var === "" || / /.test(m.var)) {
|
||||
return false;
|
||||
}
|
||||
if (missingModules.indexOf(m.module) > -1) {
|
||||
return false;
|
||||
}
|
||||
if (invalidModuleVNames.indexOf(m.var) !== -1){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
@@ -85,6 +362,12 @@
|
||||
$("#" + tab.id).show();
|
||||
}
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "func-tab-config",
|
||||
iconClass: "fa fa-cog",
|
||||
label: that._("function.label.setup")
|
||||
});
|
||||
|
||||
tabs.addTab({
|
||||
id: "func-tab-init",
|
||||
label: that._("function.label.initialize")
|
||||
@@ -97,6 +380,7 @@
|
||||
id: "func-tab-finalize",
|
||||
label: that._("function.label.finalize")
|
||||
});
|
||||
|
||||
tabs.activateTab("func-tab-body");
|
||||
|
||||
$( "#node-input-outputs" ).spinner({
|
||||
@@ -205,7 +489,9 @@
|
||||
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
|
||||
RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand"));
|
||||
|
||||
|
||||
if (RED.settings.functionExternalModules !== false) {
|
||||
prepareLibraryConfig(that);
|
||||
}
|
||||
},
|
||||
oneditsave: function() {
|
||||
var node = this;
|
||||
@@ -237,7 +523,28 @@
|
||||
|
||||
$("#node-input-noerr").val(noerr);
|
||||
this.noerr = noerr;
|
||||
|
||||
if (RED.settings.functionExternalModules === true) {
|
||||
var libs = $("#node-input-libs-container").editableList("items");
|
||||
node.libs = [];
|
||||
libs.each(function(i) {
|
||||
var item = $(this);
|
||||
var v = item.find(".node-input-libs-var").val();
|
||||
var n = item.find(".node-input-libs-val").typedInput("type");
|
||||
if (n === "_custom_") {
|
||||
n = item.find(".node-input-libs-val").val();
|
||||
}
|
||||
if ((!v || (v === "")) ||
|
||||
(!n || (n === ""))) {
|
||||
return;
|
||||
}
|
||||
node.libs.push({
|
||||
var: v,
|
||||
module: n
|
||||
});
|
||||
});
|
||||
} else {
|
||||
node.libs = [];
|
||||
}
|
||||
},
|
||||
oneditcancel: function() {
|
||||
var node = this;
|
||||
@@ -259,18 +566,19 @@
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
$("#dialog-form .node-text-editor").css("height",height+"px");
|
||||
|
||||
var height = size.height;
|
||||
$("#node-input-init-editor").css("height", (height -105)+"px");
|
||||
$("#node-input-func-editor").css("height", (height -145)+"px");
|
||||
$("#node-input-finalize-editor").css("height", (height -105)+"px");
|
||||
$("#node-input-init-editor").css("height", (height -45-48)+"px");
|
||||
$("#node-input-func-editor").css("height", (height -45-48)+"px");
|
||||
$("#node-input-finalize-editor").css("height", (height -45-48)+"px");
|
||||
|
||||
this.initEditor.resize();
|
||||
this.editor.resize();
|
||||
this.finalizeEditor.resize();
|
||||
|
||||
$("#node-input-libs-container").css("height", (height - 185)+"px");
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
|
||||
var util = require("util");
|
||||
var vm = require("vm");
|
||||
|
||||
@@ -94,6 +95,11 @@ module.exports = function(RED) {
|
||||
node.func = n.func;
|
||||
node.ini = n.initialize ? n.initialize.trim() : "";
|
||||
node.fin = n.finalize ? n.finalize.trim() : "";
|
||||
node.libs = n.libs || [];
|
||||
|
||||
if (RED.settings.functionExternalModules !== true && node.libs.length > 0) {
|
||||
throw new Error("Function node not allowed to load external modules");
|
||||
}
|
||||
|
||||
var handleNodeDoneCall = true;
|
||||
|
||||
@@ -105,23 +111,23 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
var functionText = "var results = null;"+
|
||||
"results = (async function(msg,__send__,__done__){ "+
|
||||
"var __msgid__ = msg._msgid;"+
|
||||
"var node = {"+
|
||||
"id:__node__.id,"+
|
||||
"name:__node__.name,"+
|
||||
"log:__node__.log,"+
|
||||
"error:__node__.error,"+
|
||||
"warn:__node__.warn,"+
|
||||
"debug:__node__.debug,"+
|
||||
"trace:__node__.trace,"+
|
||||
"on:__node__.on,"+
|
||||
"status:__node__.status,"+
|
||||
"send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
|
||||
"done:__done__"+
|
||||
"};\n"+
|
||||
node.func+"\n"+
|
||||
"})(msg,__send__,__done__);";
|
||||
"results = (async function(msg,__send__,__done__){ "+
|
||||
"var __msgid__ = msg._msgid;"+
|
||||
"var node = {"+
|
||||
"id:__node__.id,"+
|
||||
"name:__node__.name,"+
|
||||
"log:__node__.log,"+
|
||||
"error:__node__.error,"+
|
||||
"warn:__node__.warn,"+
|
||||
"debug:__node__.debug,"+
|
||||
"trace:__node__.trace,"+
|
||||
"on:__node__.on,"+
|
||||
"status:__node__.status,"+
|
||||
"send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
|
||||
"done:__done__"+
|
||||
"};\n"+
|
||||
node.func+"\n"+
|
||||
"})(msg,__send__,__done__);";
|
||||
var finScript = null;
|
||||
var finOpt = null;
|
||||
node.topic = n.topic;
|
||||
@@ -266,34 +272,96 @@ module.exports = function(RED) {
|
||||
};
|
||||
sandbox.promisify = util.promisify;
|
||||
}
|
||||
|
||||
if (node.hasOwnProperty("libs")) {
|
||||
let moduleErrors = false;
|
||||
var modules = node.libs;
|
||||
modules.forEach(module => {
|
||||
var vname = module.hasOwnProperty("var") ? module.var : null;
|
||||
if (vname && (vname !== "")) {
|
||||
if (sandbox.hasOwnProperty(vname) || vname === 'node') {
|
||||
node.error(RED._("function.error.moduleNameError",{name:vname}))
|
||||
moduleErrors = true;
|
||||
return;
|
||||
}
|
||||
sandbox[vname] = null;
|
||||
try {
|
||||
var spec = module.module;
|
||||
if (spec && (spec !== "")) {
|
||||
var lib = RED.require(module.module);
|
||||
sandbox[vname] = lib;
|
||||
}
|
||||
} catch (e) {
|
||||
//TODO: NLS error message
|
||||
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:e.toString()}))
|
||||
moduleErrors = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (moduleErrors) {
|
||||
throw new Error("Function node failed to load external modules");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const RESOLVING = 0;
|
||||
const RESOLVED = 1;
|
||||
const ERROR = 2;
|
||||
var state = RESOLVING;
|
||||
var messages = [];
|
||||
var processMessage = (() => {});
|
||||
|
||||
node.on("input", function(msg,send,done) {
|
||||
if(state === RESOLVING) {
|
||||
messages.push({msg:msg, send:send, done:done});
|
||||
}
|
||||
else if(state === RESOLVED) {
|
||||
processMessage(msg, send, done);
|
||||
}
|
||||
});
|
||||
|
||||
var context = vm.createContext(sandbox);
|
||||
try {
|
||||
var iniScript = null;
|
||||
var iniOpt = null;
|
||||
if (node.ini && (node.ini !== "")) {
|
||||
var iniText = `
|
||||
(async function(__send__) {
|
||||
var node = {
|
||||
id:__node__.id,
|
||||
name:__node__.name,
|
||||
log:__node__.log,
|
||||
error:__node__.error,
|
||||
warn:__node__.warn,
|
||||
debug:__node__.debug,
|
||||
trace:__node__.trace,
|
||||
status:__node__.status,
|
||||
send: function(msgs, cloneMsg) {
|
||||
__node__.send(__send__, RED.util.generateId(), msgs, cloneMsg);
|
||||
}
|
||||
};
|
||||
`+ node.ini +`
|
||||
})(__initSend__);`;
|
||||
(async function(__send__) {
|
||||
var node = {
|
||||
id:__node__.id,
|
||||
name:__node__.name,
|
||||
log:__node__.log,
|
||||
error:__node__.error,
|
||||
warn:__node__.warn,
|
||||
debug:__node__.debug,
|
||||
trace:__node__.trace,
|
||||
status:__node__.status,
|
||||
send: function(msgs, cloneMsg) {
|
||||
__node__.send(__send__, RED.util.generateId(), msgs, cloneMsg);
|
||||
}
|
||||
};
|
||||
`+ node.ini +`
|
||||
})(__initSend__);`;
|
||||
iniOpt = createVMOpt(node, " setup");
|
||||
iniScript = new vm.Script(iniText, iniOpt);
|
||||
}
|
||||
node.script = vm.createScript(functionText, createVMOpt(node, ""));
|
||||
if (node.fin && (node.fin !== "")) {
|
||||
var finText = "(function () {\n"+node.fin +"\n})();";
|
||||
var finText = `(function () {
|
||||
var node = {
|
||||
id:__node__.id,
|
||||
name:__node__.name,
|
||||
log:__node__.log,
|
||||
error:__node__.error,
|
||||
warn:__node__.warn,
|
||||
debug:__node__.debug,
|
||||
trace:__node__.trace,
|
||||
status:__node__.status,
|
||||
send: function(msgs, cloneMsg) {
|
||||
__node__.error("Cannot send from close function");
|
||||
}
|
||||
};
|
||||
`+node.fin +`})();`;
|
||||
finOpt = createVMOpt(node, " cleanup");
|
||||
finScript = new vm.Script(finText, finOpt);
|
||||
}
|
||||
@@ -303,7 +371,7 @@ module.exports = function(RED) {
|
||||
promise = iniScript.runInContext(context, iniOpt);
|
||||
}
|
||||
|
||||
function processMessage(msg, send, done) {
|
||||
processMessage = function (msg, send, done) {
|
||||
var start = process.hrtime();
|
||||
context.msg = msg;
|
||||
context.__send__ = send;
|
||||
@@ -363,20 +431,6 @@ module.exports = function(RED) {
|
||||
});
|
||||
}
|
||||
|
||||
const RESOLVING = 0;
|
||||
const RESOLVED = 1;
|
||||
const ERROR = 2;
|
||||
var state = RESOLVING;
|
||||
var messages = [];
|
||||
|
||||
node.on("input", function(msg,send,done) {
|
||||
if(state === RESOLVING) {
|
||||
messages.push({msg:msg, send:send, done:done});
|
||||
}
|
||||
else if(state === RESOLVED) {
|
||||
processMessage(msg, send, done);
|
||||
}
|
||||
});
|
||||
node.on("close", function() {
|
||||
if (finScript) {
|
||||
try {
|
||||
@@ -422,7 +476,12 @@ module.exports = function(RED) {
|
||||
node.error(err);
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("function",FunctionNode);
|
||||
RED.nodes.registerType("function",FunctionNode, {
|
||||
dynamicModuleList: "libs",
|
||||
settings: {
|
||||
functionExternalModules: { value: false, exportable: true }
|
||||
}
|
||||
});
|
||||
RED.library.register("functions");
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user