From d8d3a1e899b0e4401fa4cce5eacc90fd48843312 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Sun, 15 Apr 2018 19:21:50 +0800 Subject: [PATCH 01/24] Allow require to pick up js files from process.cwd() --- .vscode/launch.json | 14 ++++++++++++ README.md | 7 ++++++ file-function/file-function.js | 40 +++++++++++++++++++++++++--------- package-lock.json | 5 +++++ package.json | 12 +++++----- 5 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 package-lock.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d9c888f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/file-function\\file-function.js" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 20ac954..6fe33b5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ # node-red-contrib-file-function +**Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions** + **This Node-RED node is just like the [core node "function"](http://nodered.org/docs/writing-functions.html), only that this node loads the script to be executed from an actual file on your drive.** This may help you developing for Node RED. Instead of having to write your Javascript code in that small textfield in your browser you can use your favorite editor/IDE. ![screenshot of settings](https://raw.githubusercontent.com/emiloberg/node-red-contrib-file-function/master/docs/screenshot-settings.png) +## Changes over original node-red-contrib-file-function + + - Make it compatible with node-red v0.18.4 + - FIX: ReferenceError: global is not defined + - FIX: node object is always undefined ## Status diff --git a/file-function/file-function.js b/file-function/file-function.js index e850895..468d047 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -15,9 +15,12 @@ module.exports = function(RED) { "use strict"; - var util = require("util"); - var vm = require("vm"); - var fs = require("fs"); + const util = require("util"); + const vm = require("vm"); + const fs = require("fs"); + const path = require("path"); + const process = require("process"); + function FunctionNode(n) { RED.nodes.createNode(this, n); @@ -78,14 +81,31 @@ module.exports = function(RED) { function runScript(node, msg, script) { var functionText = "var results = null; results = (function(msg){"+script+"\n})(msg);"; - var sandbox = { + + var sandbox = Object.assign(node.context(), { + process: process, + path: path, + util: util, + require: function(name) { + if (path.extname(name)) { + var fullpath = path.join(process.cwd(), name); + return require(fullpath); + } else { + return require(name); + } + }, console:console, util:util, - Buffer:Buffer, - context: { - global:RED.settings.functionGlobalContext || {} - } - }; + Buffer:Buffer, + + node: node, + context: node.context(), + + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }); var context = vm.createContext(sandbox); var vmScript = vm.createScript(functionText); @@ -122,7 +142,7 @@ module.exports = function(RED) { } } catch(err) { - node.warn(err); + node.error(err, msg); } } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0652840 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "node-red-contrib-file-function-ext", + "version": "1.1.2", + "lockfileVersion": 1 +} diff --git a/package.json b/package.json index 18fa93f..bfe9fc3 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "node-red-contrib-file-function", + "name": "node-red-contrib-file-function-ext", "version": "1.1.2", - "description": "Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", + "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", - "url": "https://github.com/emiloberg/node-red-contrib-file-function.git" + "url": "https://github.com/oazabir/node-red-contrib-file-function.git" }, "keywords": [ "node-red", @@ -19,10 +19,10 @@ "file-function": "file-function/file-function.js" } }, - "author": "Emil Oberg", + "author": "Omar AL Zabir", "license": "Apache", "bugs": { - "url": "https://github.com/emiloberg/node-red-contrib-file-function/issues" + "url": "https://github.com/oazabir/node-red-contrib-file-function/issues" }, - "homepage": "https://github.com/emiloberg/node-red-contrib-file-function" + "homepage": "https://github.com/oazabir/node-red-contrib-file-function" } From b90f7b8555ebca6ec66790f37faf0648cbefd4ac Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Sun, 15 Apr 2018 19:22:03 +0800 Subject: [PATCH 02/24] 1.2.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0652840..33651b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.1.2", + "version": "1.2.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index bfe9fc3..c2c0e28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.1.2", + "version": "1.2.0", "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", From bca69c946236ab0c874c6d910f3007f3a8735f36 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Sun, 15 Apr 2018 20:48:33 +0800 Subject: [PATCH 03/24] Translate all paths inside script relative to the script location --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 468d047..b52ecc4 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -88,7 +88,7 @@ module.exports = function(RED) { util: util, require: function(name) { if (path.extname(name)) { - var fullpath = path.join(process.cwd(), name); + var fullpath = path.join(path.dirname(path.resolve(this.filename)),name); return require(fullpath); } else { return require(name); From f45e38f893f704f87a97e6123a236b7cdb2ef0b5 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Sun, 15 Apr 2018 20:48:36 +0800 Subject: [PATCH 04/24] 1.3.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33651b3..c0df465 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index c2c0e28..a14ee0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.2.0", + "version": "1.3.0", "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", From 39fda013ca844c91e4548d9f7b3f0504eac12176 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Sun, 15 Apr 2018 20:50:43 +0800 Subject: [PATCH 05/24] fix path --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index b52ecc4..272adad 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -88,7 +88,7 @@ module.exports = function(RED) { util: util, require: function(name) { if (path.extname(name)) { - var fullpath = path.join(path.dirname(path.resolve(this.filename)),name); + var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); return require(fullpath); } else { return require(name); From 999149bfb95747c13106acacd61d2778ad8c47c3 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Thu, 26 Apr 2018 11:04:08 +0800 Subject: [PATCH 06/24] Wrap function code execution in try...catch which reports exceptions using node.error --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 272adad..136bc02 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -79,7 +79,7 @@ module.exports = function(RED) { function runScript(node, msg, script) { - var functionText = "var results = null; results = (function(msg){"+script+"\n})(msg);"; + var functionText = "try { var results = null; results = (function(msg){"+script+"\n})(msg); } catch(err) { node.error(err); }"; var sandbox = Object.assign(node.context(), { From d99aa82af26d00359070d6953a9897d52618d7b4 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Thu, 26 Apr 2018 11:04:28 +0800 Subject: [PATCH 07/24] 1.4.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c0df465..305a7e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index a14ee0d..4969cd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.3.0", + "version": "1.4.0", "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", From a693a999d2c4cf00828fb21d1d246434694414f5 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Thu, 26 Apr 2018 16:27:59 +0800 Subject: [PATCH 08/24] Handle error properly --- file-function/file-function.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 136bc02..a4d6090 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -79,7 +79,7 @@ module.exports = function(RED) { function runScript(node, msg, script) { - var functionText = "try { var results = null; results = (function(msg){"+script+"\n})(msg); } catch(err) { node.error(err); }"; + var functionText = "var results = null; results = (function(msg){"+script+"\n})(msg);"; var sandbox = Object.assign(node.context(), { @@ -142,7 +142,7 @@ module.exports = function(RED) { } } catch(err) { - node.error(err, msg); + node.error(err.toString(), msg); } } From 2f55b08a2654286b207f362c4fd1bb08588733b8 Mon Sep 17 00:00:00 2001 From: Al Zabir Date: Thu, 26 Apr 2018 16:28:06 +0800 Subject: [PATCH 09/24] 1.5.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 305a7e9..6366b6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index 4969cd3..64ac19b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function-ext", - "version": "1.4.0", + "version": "1.5.0", "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", From b88422bea9ef24d507f6ccd2910e12d3af143a61 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 16 Feb 2019 09:37:48 -0800 Subject: [PATCH 10/24] prevent crash if functionText has syntax error Due to a mistake I parsed the wrong file and node-red crashed: ``` 16 Feb 09:30:51 - evalmachine.:1 var results = null; results = (function(msg){{"payload":[22.38,null,null,null,-18,0.22,0,0,-62,29,-733,35,896],"hwid":"374f0e41","at":"2019-02-16T08:59:41.768462122-08:00","fmt":2,"_msgid":"2f0d373f.63fa68"} ^ SyntaxError: Unexpected token : at Object.createScript (vm.js:74:10) at runScript (/usr/src/node-red/node_modules/node-red-contrib-file-function-ext/file-function/file-function.js:111:21) at /usr/src/node-red/node_modules/node-red-contrib-file-function-ext/file-function/file-function.js:73:7 at tryToString (fs.js:512:3) at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:500:12) ``` --- file-function/file-function.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index a4d6090..c17e280 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -84,8 +84,8 @@ module.exports = function(RED) { var sandbox = Object.assign(node.context(), { process: process, - path: path, - util: util, + path: path, + util: util, require: function(name) { if (path.extname(name)) { var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); @@ -108,9 +108,9 @@ module.exports = function(RED) { }); var context = vm.createContext(sandbox); - var vmScript = vm.createScript(functionText); try { + var vmScript = vm.createScript(functionText); var start = process.hrtime(); context.msg = msg; vmScript.runInContext(context); From de0734d3cf535078d037f4b69e7ef63079419012 Mon Sep 17 00:00:00 2001 From: emiliobool Date: Tue, 16 Apr 2019 02:13:19 -0700 Subject: [PATCH 11/24] fix: Stop overriding the topic field --- file-function/file-function.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index c17e280..8a31a95 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -121,20 +121,6 @@ module.exports = function(RED) { results = [results]; } - if (msg.topic) { - for (var m in results) { - if (results[m]) { - if (util.isArray(results[m])) { - for (var n=0; n < results[m].length; n++) { - results[m][n].topic = msg.topic; - } - } else { - results[m].topic = msg.topic; - } - } - } - } - node.send(results); var duration = process.hrtime(start); if (process.env.NODE_RED_FUNCTION_TIME) { From 574cc6170bfa1782394b11015ec98792111e69d7 Mon Sep 17 00:00:00 2001 From: emiliobool Date: Mon, 22 Apr 2019 18:46:29 -0700 Subject: [PATCH 12/24] Remove require checks --- file-function/file-function.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 8a31a95..8d54955 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -87,12 +87,7 @@ module.exports = function(RED) { path: path, util: util, require: function(name) { - if (path.extname(name)) { - var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); - return require(fullpath); - } else { - return require(name); - } + return require(name); }, console:console, util:util, From 2b5281864cb2e7a9b799147a651c9631828eed14 Mon Sep 17 00:00:00 2001 From: emiliobool Date: Mon, 22 Apr 2019 19:19:10 -0700 Subject: [PATCH 13/24] Update file-function.js --- file-function/file-function.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 8d54955..8a31a95 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -87,7 +87,12 @@ module.exports = function(RED) { path: path, util: util, require: function(name) { - return require(name); + if (path.extname(name)) { + var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); + return require(fullpath); + } else { + return require(name); + } }, console:console, util:util, From 49ca2fa9746fac48262fdaaf8ec1f58ae2e8265e Mon Sep 17 00:00:00 2001 From: emiliobool Date: Mon, 22 Apr 2019 19:31:54 -0700 Subject: [PATCH 14/24] Fix relative paths when it starts with a dot --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 8a31a95..bc28e86 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -87,7 +87,7 @@ module.exports = function(RED) { path: path, util: util, require: function(name) { - if (path.extname(name)) { + if (path.startsWith('.')) { var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); return require(fullpath); } else { From 113d8a3d65639da03def2680082a1e2afe2dea8e Mon Sep 17 00:00:00 2001 From: emiliobool Date: Mon, 22 Apr 2019 19:39:02 -0700 Subject: [PATCH 15/24] Update file-function.js --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index bc28e86..60abccb 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -87,7 +87,7 @@ module.exports = function(RED) { path: path, util: util, require: function(name) { - if (path.startsWith('.')) { + if (name.startsWith('.')) { var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); return require(fullpath); } else { From ba356b7be93e17a55a191cbdbf3e0a5dcf42cd5e Mon Sep 17 00:00:00 2001 From: emiliobool Date: Mon, 22 Apr 2019 20:03:09 -0700 Subject: [PATCH 16/24] Fix: Use node.loadedFilename for dynamic msg.filename --- file-function/file-function.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 60abccb..c7274de 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -88,7 +88,7 @@ module.exports = function(RED) { util: util, require: function(name) { if (name.startsWith('.')) { - var fullpath = path.join(path.dirname(path.resolve(node.filename)),name); + var fullpath = path.join(path.dirname(path.resolve(node.loadedFilename)),name); return require(fullpath); } else { return require(name); From ab7dd93f6815185761fab7c4499322422d35eebf Mon Sep 17 00:00:00 2001 From: Emilio Bool Date: Mon, 29 Apr 2019 18:50:58 -0700 Subject: [PATCH 17/24] format prettier --- file-function/file-function.js | 269 ++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 119 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index c7274de..13cbbf0 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -14,124 +14,155 @@ **/ module.exports = function(RED) { - "use strict"; - const util = require("util"); - const vm = require("vm"); - const fs = require("fs"); - const path = require("path"); - const process = require("process"); - + "use strict" + const util = require("util") + const vm = require("vm") + const fs = require("fs") + const path = require("path") + const process = require("process") function FunctionNode(n) { - RED.nodes.createNode(this, n); - - this.filename = n.filename || ""; - this.loadedScript = ''; - this.loadedFilename = ''; - - var node = this; - - // Read and file when node is initialized, - // if user didn't check the "reload file every time"-checkbox, we'll be using - // this when node is invoked. - if (this.filename !== '') { - node.loadedFilename = this.filename; - fs.readFile(this.filename, {encoding: 'utf-8'}, function (err, fileContent) { - if (err) { - if (err.code === 'ENOENT') { - node.warn('Could not find file "' + err.path + '". Hint: File path is relative to "' + process.env.PWD + '"'); - } else { - node.warn(err); - } - } else { - node.loadedScript = fileContent; - } - }); - } - - - // On invocation - this.on("input", function (msg) { - var filename = msg.filename || this.filename; - - if (filename === '') { - node.warn('No filename specified'); - } else if (n.reloadfile === false && filename === node.loadedFilename && node.loadedScript !== ''){ // Run script from "cache" - runScript(node, msg, node.loadedScript); - } else { // Read script from disk and run - fs.readFile(filename, {encoding: 'utf-8'}, function (err, fileContent) { - if (err) { - if (err.code === 'ENOENT') { - node.warn('Could not find file "' + err.path + '". Hint: File path is relative to "' + process.env.PWD + '"'); - } else { - node.warn(err); - } - msg.error = err; - } else { - node.loadedScript = fileContent; - node.loadedFilename = filename; - runScript(node, msg, fileContent); - } - }); - } - }); - } - - - function runScript(node, msg, script) { - var functionText = "var results = null; results = (function(msg){"+script+"\n})(msg);"; - - - var sandbox = Object.assign(node.context(), { - process: process, - path: path, - util: util, - require: function(name) { - if (name.startsWith('.')) { - var fullpath = path.join(path.dirname(path.resolve(node.loadedFilename)),name); - return require(fullpath); - } else { - return require(name); - } - }, - console:console, - util:util, - Buffer:Buffer, - - node: node, - context: node.context(), - - setTimeout: setTimeout, - clearTimeout: clearTimeout, - setInterval: setInterval, - clearInterval: clearInterval - }); - - var context = vm.createContext(sandbox); - - try { - var vmScript = vm.createScript(functionText); - var start = process.hrtime(); - context.msg = msg; - vmScript.runInContext(context); - var results = context.results; - if (results == null) { - results = []; - } else if (results.length == null) { - results = [results]; - } - - node.send(results); - var duration = process.hrtime(start); - if (process.env.NODE_RED_FUNCTION_TIME) { - this.status({fill:"yellow",shape:"dot",text:""+Math.floor((duration[0]* 1e9 + duration[1])/10000)/100}); - } - - } catch(err) { - node.error(err.toString(), msg); - } - } - - RED.nodes.registerType("file function",FunctionNode); - RED.library.register("functions"); -}; + RED.nodes.createNode(this, n) + + this.filename = n.filename || "" + this.loadedScript = "" + this.loadedFilename = "" + + var node = this + + // Read and file when node is initialized, + // if user didn't check the "reload file every time"-checkbox, we'll be using + // this when node is invoked. + if (this.filename !== "") { + node.loadedFilename = this.filename + fs.readFile(this.filename, { encoding: "utf-8" }, function( + err, + fileContent + ) { + if (err) { + if (err.code === "ENOENT") { + node.warn( + 'Could not find file "' + + err.path + + '". Hint: File path is relative to "' + + process.env.PWD + + '"' + ) + } else { + node.warn(err) + } + } else { + node.loadedScript = fileContent + } + }) + } + + // On invocation + this.on("input", function(msg) { + var filename = msg.filename || this.filename + + if (filename === "") { + node.warn("No filename specified") + } else if ( + n.reloadfile === false && + filename === node.loadedFilename && + node.loadedScript !== "" + ) { + // Run script from "cache" + runScript(node, msg, node.loadedScript) + } else { + // Read script from disk and run + fs.readFile(filename, { encoding: "utf-8" }, function( + err, + fileContent + ) { + if (err) { + if (err.code === "ENOENT") { + node.warn( + 'Could not find file "' + + err.path + + '". Hint: File path is relative to "' + + process.env.PWD + + '"' + ) + } else { + node.warn(err) + } + msg.error = err + } else { + node.loadedScript = fileContent + node.loadedFilename = filename + runScript(node, msg, fileContent) + } + }) + } + }) + } + + function runScript(node, msg, script) { + var functionText = + "var results = null; results = (function(msg){" + + script + + "\n})(msg);" + + var sandbox = Object.assign(node.context(), { + process: process, + path: path, + util: util, + require: function(name) { + if (name.startsWith(".")) { + var fullpath = path.join( + path.dirname(path.resolve(node.loadedFilename)), + name + ) + return require(fullpath) + } else { + return require(name) + } + }, + console: console, + util: util, + Buffer: Buffer, + node: node, + context: node.context(), + + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }) + + var context = vm.createContext(sandbox) + + try { + var vmScript = vm.createScript(functionText) + var start = process.hrtime() + context.msg = msg + vmScript.runInContext(context) + var results = context.results + if (results == null) { + results = [] + } else if (results.length == null) { + results = [results] + } + + node.send(results) + var duration = process.hrtime(start) + if (process.env.NODE_RED_FUNCTION_TIME) { + this.status({ + fill: "yellow", + shape: "dot", + text: + "" + + Math.floor((duration[0] * 1e9 + duration[1]) / 10000) / + 100 + }) + } + } catch (err) { + node.error(err.toString(), msg) + } + } + + RED.nodes.registerType("file function", FunctionNode) + RED.library.register("functions") +} From f08fe167a935ffc785e98db33418d8a95ba57fc0 Mon Sep 17 00:00:00 2001 From: Emilio Bool Date: Mon, 29 Apr 2019 18:57:44 -0700 Subject: [PATCH 18/24] add require cache --- file-function/file-function.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 13cbbf0..4ec5830 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -57,6 +57,21 @@ module.exports = function(RED) { }) } + function requireFunction(name) { + if (name.startsWith(".")) { + var fullpath = path.join( + path.dirname(path.resolve(node.loadedFilename)), + name + ) + return require(fullpath) + } else { + return require(name) + } + } + requireFunction.main = require.main + requireFunction.cache = require.cache + requireFunction.resolve = require.resolve + // On invocation this.on("input", function(msg) { var filename = msg.filename || this.filename @@ -109,17 +124,7 @@ module.exports = function(RED) { process: process, path: path, util: util, - require: function(name) { - if (name.startsWith(".")) { - var fullpath = path.join( - path.dirname(path.resolve(node.loadedFilename)), - name - ) - return require(fullpath) - } else { - return require(name) - } - }, + require: requireFunction, console: console, util: util, Buffer: Buffer, From df8312b72302c549768a494abc844643e763a352 Mon Sep 17 00:00:00 2001 From: Emilio Bool Date: Mon, 29 Apr 2019 20:23:52 -0700 Subject: [PATCH 19/24] fix scope bug --- file-function/file-function.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 4ec5830..2877f4d 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -21,6 +21,21 @@ module.exports = function(RED) { const path = require("path") const process = require("process") + const requireFunction = function(name) { + if (name.startsWith(".")) { + var fullpath = path.join( + path.dirname(path.resolve(node.loadedFilename)), + name + ) + return require(fullpath) + } else { + return require(name) + } + } + requireFunction.main = require.main + requireFunction.cache = require.cache + requireFunction.resolve = require.resolve + function FunctionNode(n) { RED.nodes.createNode(this, n) @@ -57,21 +72,6 @@ module.exports = function(RED) { }) } - function requireFunction(name) { - if (name.startsWith(".")) { - var fullpath = path.join( - path.dirname(path.resolve(node.loadedFilename)), - name - ) - return require(fullpath) - } else { - return require(name) - } - } - requireFunction.main = require.main - requireFunction.cache = require.cache - requireFunction.resolve = require.resolve - // On invocation this.on("input", function(msg) { var filename = msg.filename || this.filename From 5d34997f39ef828dc79987a0375d33fe3404c925 Mon Sep 17 00:00:00 2001 From: Emilio Bool Date: Mon, 29 Apr 2019 20:55:07 -0700 Subject: [PATCH 20/24] fix require bug --- file-function/file-function.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index 2877f4d..4c35b89 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -21,21 +21,6 @@ module.exports = function(RED) { const path = require("path") const process = require("process") - const requireFunction = function(name) { - if (name.startsWith(".")) { - var fullpath = path.join( - path.dirname(path.resolve(node.loadedFilename)), - name - ) - return require(fullpath) - } else { - return require(name) - } - } - requireFunction.main = require.main - requireFunction.cache = require.cache - requireFunction.resolve = require.resolve - function FunctionNode(n) { RED.nodes.createNode(this, n) @@ -115,11 +100,27 @@ module.exports = function(RED) { } function runScript(node, msg, script) { - var functionText = + const functionText = "var results = null; results = (function(msg){" + script + "\n})(msg);" + const requireFunction = function(name) { + if (name.startsWith(".")) { + var fullpath = path.join( + path.dirname(path.resolve(node.loadedFilename)), + name + ) + return require(fullpath) + } else { + return require(name) + } + } + + requireFunction.main = require.main + requireFunction.cache = require.cache + requireFunction.resolve = require.resolve + var sandbox = Object.assign(node.context(), { process: process, path: path, @@ -130,7 +131,6 @@ module.exports = function(RED) { Buffer: Buffer, node: node, context: node.context(), - setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, From d06e38ed1f30a07021d6abfb38eda973ce35ae86 Mon Sep 17 00:00:00 2001 From: William Shostak Date: Sun, 31 Jul 2022 15:07:47 +0900 Subject: [PATCH 21/24] Verious updates to work with Node Red v2 + --- file-function/file-function.html | 146 +++++++++++--------- file-function/file-function.js | 227 +++++++++++++++---------------- package.json | 2 +- 3 files changed, 190 insertions(+), 185 deletions(-) diff --git a/file-function/file-function.html b/file-function/file-function.html index 821a56e..19438fa 100644 --- a/file-function/file-function.html +++ b/file-function/file-function.html @@ -12,77 +12,89 @@ limitations under the License. --> - - diff --git a/file-function/file-function.js b/file-function/file-function.js index e850895..a089ccb 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -12,120 +12,113 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - -module.exports = function(RED) { - "use strict"; - var util = require("util"); - var vm = require("vm"); - var fs = require("fs"); - - function FunctionNode(n) { - RED.nodes.createNode(this, n); - - this.filename = n.filename || ""; - this.loadedScript = ''; - this.loadedFilename = ''; - - var node = this; - - // Read and file when node is initialized, - // if user didn't check the "reload file every time"-checkbox, we'll be using - // this when node is invoked. - if (this.filename !== '') { - node.loadedFilename = this.filename; - fs.readFile(this.filename, {encoding: 'utf-8'}, function (err, fileContent) { - if (err) { - if (err.code === 'ENOENT') { - node.warn('Could not find file "' + err.path + '". Hint: File path is relative to "' + process.env.PWD + '"'); - } else { - node.warn(err); - } - } else { - node.loadedScript = fileContent; - } - }); - } - - - // On invocation - this.on("input", function (msg) { - var filename = msg.filename || this.filename; - - if (filename === '') { - node.warn('No filename specified'); - } else if (n.reloadfile === false && filename === node.loadedFilename && node.loadedScript !== ''){ // Run script from "cache" - runScript(node, msg, node.loadedScript); - } else { // Read script from disk and run - fs.readFile(filename, {encoding: 'utf-8'}, function (err, fileContent) { - if (err) { - if (err.code === 'ENOENT') { - node.warn('Could not find file "' + err.path + '". Hint: File path is relative to "' + process.env.PWD + '"'); - } else { - node.warn(err); - } - msg.error = err; - } else { - node.loadedScript = fileContent; - node.loadedFilename = filename; - runScript(node, msg, fileContent); - } - }); - } - }); - } - - - function runScript(node, msg, script) { - var functionText = "var results = null; results = (function(msg){"+script+"\n})(msg);"; - - var sandbox = { - console:console, - util:util, - Buffer:Buffer, - context: { - global:RED.settings.functionGlobalContext || {} - } - }; - - var context = vm.createContext(sandbox); - var vmScript = vm.createScript(functionText); - - try { - var start = process.hrtime(); - context.msg = msg; - vmScript.runInContext(context); - var results = context.results; - if (results == null) { - results = []; - } else if (results.length == null) { - results = [results]; - } - - if (msg.topic) { - for (var m in results) { - if (results[m]) { - if (util.isArray(results[m])) { - for (var n=0; n < results[m].length; n++) { - results[m][n].topic = msg.topic; - } - } else { - results[m].topic = msg.topic; - } - } - } - } - - node.send(results); - var duration = process.hrtime(start); - if (process.env.NODE_RED_FUNCTION_TIME) { - this.status({fill:"yellow",shape:"dot",text:""+Math.floor((duration[0]* 1e9 + duration[1])/10000)/100}); - } - - } catch(err) { - node.warn(err); - } - } - - RED.nodes.registerType("file function",FunctionNode); - RED.library.register("functions"); -}; +const util = require('util') +const vm = require('vm') +const fs = require('fs') +const path = require('path') + +module.exports = RED => { + function runScript(node, msg, script) { + const functionText = `const results = null; results = (function(msg){${script.replace(/return *msg;*[\s\r\n\t]/, '')}return msg})(msg);` + + const sandbox = { + console, + util, + Buffer, + context: { + global: RED.settings.functionGlobalContext || {} + } + } + + const context = vm.createContext(sandbox) + const vmScript = vm.createScript(functionText) + + try { + const start = process.hrtime() + context.msg = msg + vmScript.runInContext(context) + let { results } = context + results = Array.isArray(results) ? results : [results] + + if (msg.topic) { + results.forEach(item => { + const itemArray = Array.isArray(item) ? item : [item] + itemArray.forEach(result => { + if (result.topic) { + result.topic = msg.topic + } + }) + }) + } + + node.send(results) + const duration = process.hrtime(start) + if (process.env.NODE_RED_FUNCTION_TIME) { + this.status({ + fill: 'yellow', + shape: 'dot', + text: `${Math.floor((duration[0] * 1e9 + duration[1]) / 10000) / 100}` + }) + } + } catch (err) { + node.warn(err) + } + } + + function FunctionNode(config) { + const node = this + const { filename } = config + + RED.nodes.createNode(node, config) + node.filename = '' + const projects = RED.settings.get('projects') || {} + const paths = filename ? [ + path.join(process.env.PWD || '', filename), + path.join(process.cwd(),filename), + path.join(RED.settings.userDir, filename) + ] : [] + + if (paths.length && projects.activeProject) { + paths.push(path.join(RED.settings.userDir, 'projects', projects.activeProject, filename)) + } + + paths.forEach(filename => { + if (fs.existsSync(filename)) { + node.filename = filename + } + }) + + // Read and file when node is initialized, + // if user didn't check the "reload file every time"-checkbox, we'll be using + // this when node is invoked. + if (node.filename) { + node.loadedFilename = node.filename + node.loadedScript = fs.readFileSync(node.filename, { encoding: 'utf-8' }) + + // On invocation + node.on('input', msg => { + const filename = msg.filename || node.filename + + if (filename === '') { + node.warn('No file with that name found.') + } else if ( + config.reloadFile === false + && filename === node.loadedFilename + && node.loadedScript !== '' + ) { + // Run script from "cache" + runScript(node, msg, node.loadedScript) + } else { + node.loadedScript = fs.readFileSync(node.filename, { encoding: 'utf-8' }) + node.loadedFilename = filename + runScript(node, msg, node.loadedScript) + } + }) + } else { + node.warn('No file with that name found.') + } + } + + RED.nodes.registerType('file-function', FunctionNode) + RED.library.register('functions') +} diff --git a/package.json b/package.json index 18fa93f..5c0d91a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function", - "version": "1.1.2", + "version": "1.1.3", "description": "Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", From e0aaaaaa5c188cf516f77bd5a8cb1bbd27ddef78 Mon Sep 17 00:00:00 2001 From: William Shostak Date: Sun, 31 Jul 2022 15:36:07 +0900 Subject: [PATCH 22/24] Merge conflicts --- file-function/file-function.js | 161 --------------------------------- 1 file changed, 161 deletions(-) diff --git a/file-function/file-function.js b/file-function/file-function.js index e8c7953..a089ccb 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ -<<<<<<< HEAD const util = require('util') const vm = require('vm') const fs = require('fs') @@ -122,164 +121,4 @@ module.exports = RED => { RED.nodes.registerType('file-function', FunctionNode) RED.library.register('functions') -======= - -module.exports = function(RED) { - "use strict" - const util = require("util") - const vm = require("vm") - const fs = require("fs") - const path = require("path") - const process = require("process") - - function FunctionNode(n) { - RED.nodes.createNode(this, n) - - this.filename = n.filename || "" - this.loadedScript = "" - this.loadedFilename = "" - - var node = this - - // Read and file when node is initialized, - // if user didn't check the "reload file every time"-checkbox, we'll be using - // this when node is invoked. - if (this.filename !== "") { - node.loadedFilename = this.filename - fs.readFile(this.filename, { encoding: "utf-8" }, function( - err, - fileContent - ) { - if (err) { - if (err.code === "ENOENT") { - node.warn( - 'Could not find file "' + - err.path + - '". Hint: File path is relative to "' + - process.env.PWD + - '"' - ) - } else { - node.warn(err) - } - } else { - node.loadedScript = fileContent - } - }) - } - - // On invocation - this.on("input", function(msg) { - var filename = msg.filename || this.filename - - if (filename === "") { - node.warn("No filename specified") - } else if ( - n.reloadfile === false && - filename === node.loadedFilename && - node.loadedScript !== "" - ) { - // Run script from "cache" - runScript(node, msg, node.loadedScript) - } else { - // Read script from disk and run - fs.readFile(filename, { encoding: "utf-8" }, function( - err, - fileContent - ) { - if (err) { - if (err.code === "ENOENT") { - node.warn( - 'Could not find file "' + - err.path + - '". Hint: File path is relative to "' + - process.env.PWD + - '"' - ) - } else { - node.warn(err) - } - msg.error = err - } else { - node.loadedScript = fileContent - node.loadedFilename = filename - runScript(node, msg, fileContent) - } - }) - } - }) - } - - function runScript(node, msg, script) { - const functionText = - "var results = null; results = (function(msg){" + - script + - "\n})(msg);" - - const requireFunction = function(name) { - if (name.startsWith(".")) { - var fullpath = path.join( - path.dirname(path.resolve(node.loadedFilename)), - name - ) - return require(fullpath) - } else { - return require(name) - } - } - - requireFunction.main = require.main - requireFunction.cache = require.cache - requireFunction.resolve = require.resolve - - var sandbox = Object.assign(node.context(), { - process: process, - path: path, - util: util, - require: requireFunction, - console: console, - util: util, - Buffer: Buffer, - node: node, - context: node.context(), - setTimeout: setTimeout, - clearTimeout: clearTimeout, - setInterval: setInterval, - clearInterval: clearInterval - }) - - var context = vm.createContext(sandbox) - - try { - var vmScript = vm.createScript(functionText) - var start = process.hrtime() - context.msg = msg - vmScript.runInContext(context) - var results = context.results - if (results == null) { - results = [] - } else if (results.length == null) { - results = [results] - } - - node.send(results) - var duration = process.hrtime(start) - if (process.env.NODE_RED_FUNCTION_TIME) { - this.status({ - fill: "yellow", - shape: "dot", - text: - "" + - Math.floor((duration[0] * 1e9 + duration[1]) / 10000) / - 100 - }) - } - } catch (err) { - node.error(err.toString(), msg) - } - } - - RED.nodes.registerType("file function", FunctionNode) - RED.library.register("functions") ->>>>>>> 5d34997f39ef828dc79987a0375d33fe3404c925 } From 952141c2c140b43d1133494339ac5fb32e65ad48 Mon Sep 17 00:00:00 2001 From: William Shostak Date: Sun, 31 Jul 2022 15:36:20 +0900 Subject: [PATCH 23/24] Merge conflicts --- README.md | 4 ++-- package-lock.json | 5 ----- package.json | 14 ++++---------- 3 files changed, 6 insertions(+), 17 deletions(-) delete mode 100644 package-lock.json diff --git a/README.md b/README.md index 6fe33b5..aa1b41e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This may help you developing for Node RED. Instead of having to write your Javas ## Status What? | Status | What? | Status ------- | ------ | ------- | ------ -Code Climate GPA | [![Code Climate](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function/badges/gpa.svg)](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function) | Licence | [![Licence](https://img.shields.io/npm/l/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/blob/master/LICENSE) +Code Climate GPA | [![Code Climate](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function/badges/gpa.svg)](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function) | License | [![License](https://img.shields.io/npm/l/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/blob/master/LICENSE) Codacy | [![Codacy Badge](https://www.codacy.com/project/badge/f51ca088d01f4af6b83ed2e2529b51dd)](https://www.codacy.com/public/emiloberg/node-red-contrib-file-function) | Tag | [![Tag](https://img.shields.io/github/tag/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/tags) Issues | [![Issues](https://img.shields.io/github/issues/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/issues) | GitHub Forks | [![Forks](https://img.shields.io/github/forks/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/network) GitHub Version | [![GitHub version](https://badge.fury.io/gh/emiloberg%2Fnode-red-contrib-file-function.svg)](http://badge.fury.io/gh/emiloberg%2Fnode-red-contrib-file-function) | GitHub Followers | [![Followers](https://img.shields.io/github/followers/emiloberg.svg)](https://github.com/emiloberg/followers) @@ -33,7 +33,7 @@ Either set the filename in the configuration dialog of the node, or override it ## Cache By checking the _"Reread file from disk every time node is invoked?"_ checkbox the file will be read every time the node is called, so there's no need to redeploy or restart Node-RED. If the checkbox is unchecked, the file will be read on deploy/start. -If the checkbox is set to only read the file once (when flow is deployed/Node-RED is started) but another filename is sent in msg.filename, it will read the file from disk and cache it anyways. Only the last called file will be cached. If you're alternating between two different files you're better of creating two different nodes if you're looking for perfomance. +If the checkbox is set to only read the file once (when flow is deployed/Node-RED is started) but another filename is sent in msg.filename, it will read the file from disk and cache it anyways. Only the last called file will be cached. If you're alternating between two different files you're better of creating two different nodes if you're looking for performance. Unless you're working with functions called __very__ often or with __very__ large functions you can probably just leave it to reload the file every time it's invoked. diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 6366b6b..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "node-red-contrib-file-function-ext", - "version": "1.5.0", - "lockfileVersion": 1 -} diff --git a/package.json b/package.json index 94f1d15..5c0d91a 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,10 @@ { -<<<<<<< HEAD "name": "node-red-contrib-file-function", "version": "1.1.3", "description": "Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", -======= - "name": "node-red-contrib-file-function-ext", - "version": "1.5.0", - "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", ->>>>>>> 5d34997f39ef828dc79987a0375d33fe3404c925 "repository": { "type": "git", - "url": "https://github.com/oazabir/node-red-contrib-file-function.git" + "url": "https://github.com/emiloberg/node-red-contrib-file-function.git" }, "keywords": [ "node-red", @@ -25,10 +19,10 @@ "file-function": "file-function/file-function.js" } }, - "author": "Omar AL Zabir", + "author": "Emil Oberg", "license": "Apache", "bugs": { - "url": "https://github.com/oazabir/node-red-contrib-file-function/issues" + "url": "https://github.com/emiloberg/node-red-contrib-file-function/issues" }, - "homepage": "https://github.com/oazabir/node-red-contrib-file-function" + "homepage": "https://github.com/emiloberg/node-red-contrib-file-function" } From 24e68583c6cd7ec519b79e1a8aea0062e81cb9c7 Mon Sep 17 00:00:00 2001 From: William Shostak Date: Mon, 15 Aug 2022 21:46:20 +0900 Subject: [PATCH 24/24] Fixed TypeError: Assignment to constant variable. --- .vscode/launch.json | 14 -- README.md | 13 +- file-function/file-function.html | 146 ++++++++++--------- file-function/file-function.js | 233 ++++++++++++------------------- package.json | 14 +- 5 files changed, 180 insertions(+), 240 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index d9c888f..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceFolder}/file-function\\file-function.js" - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 6fe33b5..70cc411 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,17 @@ # node-red-contrib-file-function -**Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions** - **This Node-RED node is just like the [core node "function"](http://nodered.org/docs/writing-functions.html), only that this node loads the script to be executed from an actual file on your drive.** This may help you developing for Node RED. Instead of having to write your Javascript code in that small textfield in your browser you can use your favorite editor/IDE. ![screenshot of settings](https://raw.githubusercontent.com/emiloberg/node-red-contrib-file-function/master/docs/screenshot-settings.png) -## Changes over original node-red-contrib-file-function - - - Make it compatible with node-red v0.18.4 - - FIX: ReferenceError: global is not defined - - FIX: node object is always undefined ## Status What? | Status | What? | Status ------- | ------ | ------- | ------ -Code Climate GPA | [![Code Climate](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function/badges/gpa.svg)](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function) | Licence | [![Licence](https://img.shields.io/npm/l/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/blob/master/LICENSE) +Code Climate GPA | [![Code Climate](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function/badges/gpa.svg)](https://codeclimate.com/github/emiloberg/node-red-contrib-file-function) | License | [![License](https://img.shields.io/npm/l/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/blob/master/LICENSE) Codacy | [![Codacy Badge](https://www.codacy.com/project/badge/f51ca088d01f4af6b83ed2e2529b51dd)](https://www.codacy.com/public/emiloberg/node-red-contrib-file-function) | Tag | [![Tag](https://img.shields.io/github/tag/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/tags) Issues | [![Issues](https://img.shields.io/github/issues/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/issues) | GitHub Forks | [![Forks](https://img.shields.io/github/forks/emiloberg/node-red-contrib-file-function.svg)](https://github.com/emiloberg/node-red-contrib-file-function/network) GitHub Version | [![GitHub version](https://badge.fury.io/gh/emiloberg%2Fnode-red-contrib-file-function.svg)](http://badge.fury.io/gh/emiloberg%2Fnode-red-contrib-file-function) | GitHub Followers | [![Followers](https://img.shields.io/github/followers/emiloberg.svg)](https://github.com/emiloberg/followers) @@ -33,7 +26,7 @@ Either set the filename in the configuration dialog of the node, or override it ## Cache By checking the _"Reread file from disk every time node is invoked?"_ checkbox the file will be read every time the node is called, so there's no need to redeploy or restart Node-RED. If the checkbox is unchecked, the file will be read on deploy/start. -If the checkbox is set to only read the file once (when flow is deployed/Node-RED is started) but another filename is sent in msg.filename, it will read the file from disk and cache it anyways. Only the last called file will be cached. If you're alternating between two different files you're better of creating two different nodes if you're looking for perfomance. +If the checkbox is set to only read the file once (when flow is deployed/Node-RED is started) but another filename is sent in msg.filename, it will read the file from disk and cache it anyways. Only the last called file will be cached. If you're alternating between two different files you're better of creating two different nodes if you're looking for performance. Unless you're working with functions called __very__ often or with __very__ large functions you can probably just leave it to reload the file every time it's invoked. @@ -77,4 +70,4 @@ Import this flow (or add it manually by creating a simple [inject] > [file funct [{"id":"62efe026.9d102","type":"inject","name":"Inject","topic":"this is topic from the function","payload":"this data is feeded to the function","payloadType":"string","repeat":"","crontab":"","once":false,"x":303,"y":119,"z":"dd1ad5c3.22e528","wires":[["fd11ceda.02ee3"]]},{"id":"fd11ceda.02ee3","type":"file function","name":"","filename":"sample-file-function.js","outputs":"1","x":508,"y":119,"z":"dd1ad5c3.22e528","wires":[["7e85f5db.817a0c"]]},{"id":"7e85f5db.817a0c","type":"debug","name":"","active":true,"console":"false","complete":"true","x":723,"y":118,"z":"dd1ad5c3.22e528","wires":[]}] ``` -Deploy the changes and click the inject node - check the output in the debug sidebar! +Deploy the changes and click the inject node - check the output in the debug sidebar! \ No newline at end of file diff --git a/file-function/file-function.html b/file-function/file-function.html index 821a56e..19438fa 100644 --- a/file-function/file-function.html +++ b/file-function/file-function.html @@ -12,77 +12,89 @@ limitations under the License. --> - - diff --git a/file-function/file-function.js b/file-function/file-function.js index 4c35b89..1634051 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -12,162 +12,111 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +const util = require('util') +const vm = require('vm') +const fs = require('fs') +const path = require('path') -module.exports = function(RED) { - "use strict" - const util = require("util") - const vm = require("vm") - const fs = require("fs") - const path = require("path") - const process = require("process") - - function FunctionNode(n) { - RED.nodes.createNode(this, n) - - this.filename = n.filename || "" - this.loadedScript = "" - this.loadedFilename = "" - - var node = this - - // Read and file when node is initialized, - // if user didn't check the "reload file every time"-checkbox, we'll be using - // this when node is invoked. - if (this.filename !== "") { - node.loadedFilename = this.filename - fs.readFile(this.filename, { encoding: "utf-8" }, function( - err, - fileContent - ) { - if (err) { - if (err.code === "ENOENT") { - node.warn( - 'Could not find file "' + - err.path + - '". Hint: File path is relative to "' + - process.env.PWD + - '"' - ) - } else { - node.warn(err) - } - } else { - node.loadedScript = fileContent - } - }) - } +module.exports = RED => { + function runScript(node, msg, script) { + const functionText = `var results = null; results = (function(msg){${script}\nreturn msg})(msg);` + const sandbox = { + console, + util, + Buffer, + context: { + global: RED.settings.functionGlobalContext || {} + } + } + const context = vm.createContext(sandbox) + const vmScript = vm.createScript(functionText) - // On invocation - this.on("input", function(msg) { - var filename = msg.filename || this.filename + try { + const start = process.hrtime() + context.msg = msg + vmScript.runInContext(context) + let { results } = context + results = Array.isArray(results) ? results : [results] - if (filename === "") { - node.warn("No filename specified") - } else if ( - n.reloadfile === false && - filename === node.loadedFilename && - node.loadedScript !== "" - ) { - // Run script from "cache" - runScript(node, msg, node.loadedScript) - } else { - // Read script from disk and run - fs.readFile(filename, { encoding: "utf-8" }, function( - err, - fileContent - ) { - if (err) { - if (err.code === "ENOENT") { - node.warn( - 'Could not find file "' + - err.path + - '". Hint: File path is relative to "' + - process.env.PWD + - '"' - ) - } else { - node.warn(err) - } - msg.error = err - } else { - node.loadedScript = fileContent - node.loadedFilename = filename - runScript(node, msg, fileContent) - } - }) + if (msg.topic) { + results.forEach(item => { + const itemArray = Array.isArray(item) ? item : [item] + itemArray.forEach(result => { + if (result.topic) { + result.topic = msg.topic } + }) + }) + } + + node.send(results) + const [seconds, nanoseconds] = process.hrtime(start) + if (process.env.NODE_RED_FUNCTION_TIME) { + node.status({ + fill: 'yellow', + shape: 'dot', + text: `${Math.floor((seconds * 1e9 + nanoseconds) / 10000) / 100}` }) + } + } catch (err) { + node.warn(err) } + } - function runScript(node, msg, script) { - const functionText = - "var results = null; results = (function(msg){" + - script + - "\n})(msg);" + function FunctionNode(config) { + const node = this + const { filename } = config - const requireFunction = function(name) { - if (name.startsWith(".")) { - var fullpath = path.join( - path.dirname(path.resolve(node.loadedFilename)), - name - ) - return require(fullpath) - } else { - return require(name) - } - } + RED.nodes.createNode(node, config) + node.filename = '' + const projects = RED.settings.get('projects') || {} + const paths = filename ? [ + path.join(process.env.PWD || '', filename), + path.join(process.cwd(),filename), + path.join(RED.settings.userDir, filename) + ] : [] - requireFunction.main = require.main - requireFunction.cache = require.cache - requireFunction.resolve = require.resolve + if (paths.length && projects.activeProject) { + paths.push(path.join(RED.settings.userDir, 'projects', projects.activeProject, filename)) + } - var sandbox = Object.assign(node.context(), { - process: process, - path: path, - util: util, - require: requireFunction, - console: console, - util: util, - Buffer: Buffer, - node: node, - context: node.context(), - setTimeout: setTimeout, - clearTimeout: clearTimeout, - setInterval: setInterval, - clearInterval: clearInterval - }) + paths.forEach(filename => { + if (fs.existsSync(filename)) { + node.filename = filename + } + }) - var context = vm.createContext(sandbox) + // Read and file when node is initialized, + // if user didn't check the "reload file every time"-checkbox, we'll be using + // this when node is invoked. + if (node.filename) { + node.loadedFilename = node.filename + node.loadedScript = fs.readFileSync(node.filename, { encoding: 'utf-8' }) - try { - var vmScript = vm.createScript(functionText) - var start = process.hrtime() - context.msg = msg - vmScript.runInContext(context) - var results = context.results - if (results == null) { - results = [] - } else if (results.length == null) { - results = [results] - } + // On invocation + node.on('input', msg => { + const filename = msg.filename || node.filename - node.send(results) - var duration = process.hrtime(start) - if (process.env.NODE_RED_FUNCTION_TIME) { - this.status({ - fill: "yellow", - shape: "dot", - text: - "" + - Math.floor((duration[0] * 1e9 + duration[1]) / 10000) / - 100 - }) - } - } catch (err) { - node.error(err.toString(), msg) + if (filename === '') { + node.warn('No file with that name found.') + } else if ( + config.reloadFile === false + && filename === node.loadedFilename + && node.loadedScript !== '' + ) { + // Run script from "cache" + runScript(node, msg, node.loadedScript) + } else { + node.loadedScript = fs.readFileSync(node.filename, { encoding: 'utf-8' }) + node.loadedFilename = filename + runScript(node, msg, node.loadedScript) } + }) + } else { + node.warn('No file with that name found.') } + } - RED.nodes.registerType("file function", FunctionNode) - RED.library.register("functions") + RED.nodes.registerType('file-function', FunctionNode) + RED.library.register('functions') } diff --git a/package.json b/package.json index 64ac19b..5055fd7 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "node-red-contrib-file-function-ext", - "version": "1.5.0", - "description": "Bug fix and extension of original node-red-contrib-file-function. Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", + "name": "node-red-contrib-file-function", + "version": "1.1.4", + "description": "Read function javascript from file. This way you can use your favorite editor/IDE to develop Node-RED functions", "repository": { "type": "git", - "url": "https://github.com/oazabir/node-red-contrib-file-function.git" + "url": "https://github.com/emiloberg/node-red-contrib-file-function.git" }, "keywords": [ "node-red", @@ -19,10 +19,10 @@ "file-function": "file-function/file-function.js" } }, - "author": "Omar AL Zabir", + "author": "Emil Oberg", "license": "Apache", "bugs": { - "url": "https://github.com/oazabir/node-red-contrib-file-function/issues" + "url": "https://github.com/emiloberg/node-red-contrib-file-function/issues" }, - "homepage": "https://github.com/oazabir/node-red-contrib-file-function" + "homepage": "https://github.com/emiloberg/node-red-contrib-file-function" }