diff --git a/README.md b/README.md index 20ac954..70cc411 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,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) @@ -26,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. @@ -70,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 e850895..1634051 100644 --- a/file-function/file-function.js +++ b/file-function/file-function.js @@ -12,120 +12,111 @@ * 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 = `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) + + 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 [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 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..5055fd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-file-function", - "version": "1.1.2", + "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",