diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js index 6906adea9..8f00d6d9a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/js.js @@ -87,6 +87,9 @@ expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); } dialogForm.i18n(); + setTimeout(function() { + expressionEditor.focus(); + },300); }, close: function() { expressionEditor.destroy(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js index aa1cd8b6b..7abd559f7 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js @@ -68,6 +68,8 @@ RED.library = (function() { var field = activeLibrary.fields[i]; if (field == "name") { data.name = name; + } else if (typeof(field) === 'object') { + data[field.name] = field.get(); } else { data[field] = $("#"+elementPrefix+field).val(); } @@ -539,7 +541,13 @@ RED.library = (function() { var elementPrefix = activeLibrary.elementPrefix || "node-input-"; for (var i=0; i
-
- - - + +
+
    -
    -
    -
    -
    -
    - - + +
    + + + + + + +
    + diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 65a1b4a61..14c8e9d49 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -57,17 +57,50 @@ module.exports = function(RED) { } } + function createVMOpt(node, kind) { + var opt = { + filename: 'Function node'+kind+':'+node.id+(node.name?' ['+node.name+']':''), // filename for stack traces + displayErrors: true + // Using the following options causes node 4/6 to not include the line number + // in the stack output. So don't use them. + // lineOffset: -11, // line number offset to be used for stack traces + // columnOffset: 0, // column number offset to be used for stack traces + }; + return opt; + } + + function updateErrorInfo(err) { + if (err.stack) { + var stack = err.stack.toString(); + var m = /^([^:]+):([^:]+):(\d+).*/.exec(stack); + if (m) { + var line = parseInt(m[3]) -1; + var kind = "body:"; + if (/setup/.exec(m[1])) { + kind = "setup:"; + } + if (/cleanup/.exec(m[1])) { + kind = "cleanup:"; + } + err.message += " ("+kind+"line "+line+")"; + } + } + } + function FunctionNode(n) { RED.nodes.createNode(this,n); var node = this; - this.name = n.name; - this.func = n.func; + node.name = n.name; + node.func = n.func; + node.ini = n.initialize ? n.initialize : ""; + node.fin = n.finalize ? n.finalize : ""; var handleNodeDoneCall = true; + // Check to see if the Function appears to call `node.done()`. If so, // we will assume it is well written and does actually call node.done(). // Otherwise, we will call node.done() after the function returns regardless. - if (/node\.done\s*\(\s*\)/.test(this.func)) { + if (/node\.done\s*\(\s*\)/.test(node.func)) { handleNodeDoneCall = false; } @@ -87,11 +120,13 @@ module.exports = function(RED) { "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ "done:__done__"+ "};\n"+ - this.func+"\n"+ + node.func+"\n"+ "})(msg,send,done);"; - this.topic = n.topic; - this.outstandingTimers = []; - this.outstandingIntervals = []; + var finScript = null; + var finOpt = null; + node.topic = n.topic; + node.outstandingTimers = []; + node.outstandingIntervals = []; var sandbox = { console:console, util:util, @@ -182,12 +217,12 @@ module.exports = function(RED) { arguments[0] = function() { sandbox.clearTimeout(timerId); try { - func.apply(this,arguments); + func.apply(node,arguments); } catch(err) { node.error(err,{}); } }; - timerId = setTimeout.apply(this,arguments); + timerId = setTimeout.apply(node,arguments); node.outstandingTimers.push(timerId); return timerId; }, @@ -203,12 +238,12 @@ module.exports = function(RED) { var timerId; arguments[0] = function() { try { - func.apply(this,arguments); + func.apply(node,arguments); } catch(err) { node.error(err,{}); } }; - timerId = setInterval.apply(this,arguments); + timerId = setInterval.apply(node,arguments); node.outstandingIntervals.push(timerId); return timerId; }, @@ -226,35 +261,46 @@ module.exports = function(RED) { sandbox.setTimeout(function(){ resolve(value); }, after); }); }; + sandbox.promisify = util.promisify; } var context = vm.createContext(sandbox); try { - this.script = vm.createScript(functionText, { - filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces - displayErrors: true - // Using the following options causes node 4/6 to not include the line number - // in the stack output. So don't use them. - // lineOffset: -11, // line number offset to be used for stack traces - // columnOffset: 0, // column number offset to be used for stack traces - }); - this.on("input", function(msg,send,done) { + var iniScript = null; + var iniOpt = null; + if (node.ini && (node.ini !== "")) { + var iniText = "(async function () {\n"+node.ini +"\n})();"; + 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})();"; + finOpt = createVMOpt(node, " cleanup"); + finScript = new vm.Script(finText, finOpt); + } + var promise = Promise.resolve(); + if (iniScript) { + promise = iniScript.runInContext(context, iniOpt); + } + + function processMessage(msg, send, done) { try { var start = process.hrtime(); context.msg = msg; context.send = send; context.done = done; - this.script.runInContext(context); - sendResults(this,send,msg._msgid,context.results,false); + node.script.runInContext(context); + sendResults(node,send,msg._msgid,context.results,false); if (handleNodeDoneCall) { done(); } var duration = process.hrtime(start); var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100; - this.metric("duration", msg, converted); + node.metric("duration", msg, converted); if (process.env.NODE_RED_FUNCTION_TIME) { - this.status({fill:"yellow",shape:"dot",text:""+converted}); + node.status({fill:"yellow",shape:"dot",text:""+converted}); } } catch(err) { if ((typeof err === "object") && err.hasOwnProperty("stack")) { @@ -295,22 +341,66 @@ module.exports = function(RED) { done(JSON.stringify(err)); } } + } + + 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); + } }); - this.on("close", function() { + node.on("close", function() { + if (finScript) { + try { + finScript.runInContext(context, finOpt); + } + catch (err) { + node.error(err); + } + } while (node.outstandingTimers.length > 0) { clearTimeout(node.outstandingTimers.pop()); } while (node.outstandingIntervals.length > 0) { clearInterval(node.outstandingIntervals.pop()); } - this.status({}); + node.status({}); }); - } catch(err) { + + promise.then(function (v) { + var msgs = messages; + messages = []; + while (msgs.length > 0) { + msgs.forEach(function (s) { + processMessage(s.msg, s.send, s.done); + }); + msgs = messages; + messages = []; + } + state = RESOLVED; + }).catch((error) => { + messages = []; + state = ERROR; + node.error(error); + }); + + } + catch(err) { // eg SyntaxError - which v8 doesn't include line number information // so we can't do better than this - this.error(err); + updateErrorInfo(err); + node.error(err); } } RED.nodes.registerType("function",FunctionNode); RED.library.register("functions"); }; + diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html index 28bc76f3a..93a1d6f51 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/function/10-function.html @@ -15,12 +15,14 @@ -->