How to create utility function in node red

I have many functions that are used in common in multiple nodes, so I would like to write a function node that contains all of those shared functions.

Below is a string format function like C/C++ or C #, ...

I intend to create a function node containing common functions.
Sorry if that's a stupid handle, trying to execute this code inside a function node:

After executing the below command, there is an error "TypeError: Cannot read property 'format' of undefined"

var util = global.get('util');
msg.payload = ("{0} {1}".util.format(str1, str2));

What do I need to change in order to be able to execute this code inside a function node?
Thank you in advance.

This is my code:

[{"id":"28f09b25.216ca4","type":"inject","z":"aa40b590.ff1888","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"","payloadType":"str","x":1570,"y":3060,"wires":[["2056fed4.bc5862"]]},{"id":"74d3302b.5591c","type":"debug","z":"aa40b590.ff1888","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1890,"y":3060,"wires":[]},{"id":"2056fed4.bc5862","type":"function","z":"aa40b590.ff1888","name":"","func":"/*\n// First, check if it's not implemented yet\nif (!String.prototype.format) {\n  String.prototype.format = function() {\n    var args = arguments;\n    return this.replace(/{(\\d+)}/g, function(match, number) { \n      return typeof args[number] != 'undefined' ? args[number] : match;\n    });\n  };\n}\n*/\n\nvar str1 = 'Hello';\nvar str2 = 'Node-RED';\n//msg.payload = (\"{0} {1}\".format(str1, str2));\n\nvar util = global.get('util');\nmsg.payload = (\"{0} {1}\".util.format(str1, str2));\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1720,"y":3060,"wires":[["74d3302b.5591c"]]},{"id":"a6f5a726.d648c8","type":"inject","z":"aa40b590.ff1888","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1590,"y":2980,"wires":[["df10beb2.75551"]]},{"id":"df10beb2.75551","type":"function","z":"aa40b590.ff1888","name":"Functions Utility","func":"const util = (function () {\n    'use strict';\n    \n    // First, check if it's not implemented yet\n    if (!String.prototype.format) {\n      String.prototype.format = function() {\n        var args = arguments;\n        return this.replace(/{(\\d+)}/g, function(match, number) { \n          return typeof args[number] != 'undefined' ? args[number] : match;\n        });\n      };\n    }\n    \n    return {\n        format:format\n    };\n    \n}());\n\nglobal.set('util', util);","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1780,"y":2980,"wires":[[]]}]

I'm not sure but this might be a limitation of the sandboxed execution environment. Also in general modifying the prototypes is not considered a good practice. What you could do is try to understand what the modified prototype function does and modify it to be a plain function that takes a string as a parameter. Then you'll be able to store it using global.set. Alternatively you could do this in your settings.js file in which globalContext can be initialised with external libraries and such.

More info here: https://nodered.org/docs/user-guide/writing-functions#loading-additional-modules

2 Likes

If I use settings.js, do I need to modify the modified prototype function to be a plain function that takes a string as a parameter? Or i can keep the modified prototype function and use it too...

I would guess you could use the modified prototype but I have no idea if it'd work for all strings inside function nodes. Test and find out!

BTW, since the function you're using is just to add a string format, are you aware that nowadays JavaScript has template strings? An example:

// using modified String prototype
msg.payload = ("{0} {1}".util.format(str1, str2));

// using template strings 
msg.payload = `${str1} ${str2}`:

Both should have the same end result.

1 Like

@minhhn0205 Note that I edited my previous reply if you already got notified and read it before my edit. (Usually the edit marker doesn't appear if editing right away so I assume someone read the message in between)

1 Like

@ristomatti Thank you for your enthusiastic support!
I will think more about how to do with the modified prototype function...

1 Like

The function node runs inside a Node.js VM so mostly, externally defined functions are not available except the global variables from settings.js and other objects pre-defined in the core and attached to the VM.

One thing to note though is the danger of changing the prototype of a standard object. Bugs in such code can be horrible to deal with and may not appear to come from your code. Worse, if your function name clashes with someone else who also did something similar, who wins depends on the order of execution which, in Node-RED, can be hard to work out. I've already been hit by this problem in uibuilder and it took a long time to track down to a function attached to a prototype in a sub-sub-sub-dependency.

So really, it is much better to avoid changing the prototype of common objects and data types.

1 Like

@TotallyInformation Thanks for the guidance!
I will take note when changing the prototype of a standard object.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.