A more comfortable way to edit the code for creating custom nodes

Published: | Updated: | by Julian Knight Reading time ~4 min.
📖 Kb | 📎 Development | 🔖 node-red

Node-RED allows us to create custom nodes to extend the features. While this is a great feature, actually writing the code can be tricky. In part because the code for the admin configuration panel is contained in an html file containing 3 script entries that are Node-RED specific. This post shows you a relatively easy way to split that into 3 files and combine them with a build step using an npm cross-platform script.

When defining a new node in Node-RED, the code is contained in two files. An html file and a JavaScript file.

The JavaScript file contains the core code that is loaded into the server and does most of the hard work. As this is a standard js file, nothing special is needed to edit it, your favourite code editor or IDE will do the job nicely. My preference is VScode from Microsoft.

However, the html file isn’t very comfortable to edit because any HTML it actually contains is wrapped in some special <script> tags that are only recognised by Node-RED and they throw out your code editor making things really hard to read and process.

<script type="text/javascript">
... js that is run in the admin ui when the configuration panel is opened ...
</script>

<script type="text/x-red" data-template-name="uibuilder">
... html that is used in the admin node configuration panel ...
</script>

<script type="text/x-red" data-help-name="uibuilder">
... html that is used in the information sidebar ...
</script>

To get round this, I’ve started creating a separate ./node-src folder and splitting the content for this file into three source files: script.js, template.html and help.html.

Of course, you can call them whatever you like & will probably want to include the node-name if you are creating multiple nodes.

There is then a cross-platform executable that replaces the empty master with the contents of the three files.

Now all you have to do is to remember to run the build before pushing your changes to your repo and before publishing a new version to npm. That can also be automated as long as you are using npm to manage git. Look at the prepublishOnly script which will automatically allow you to run the build before publishing. You can do similar things with git by setting up the appropriate scripts.

Installation 🔗︎

To get this all working, you will need to firstly install a package called replace-in-file. Install this to the root of the folder holding you node.

npm install replace-in-file -D

Note that I install it as a devDependency because it is only needed for the node’s editors and not for end users.

Next, add the following to the package.json file, also in the root folder of your node.

    ...
    "scripts": {
        "build": "node ./bin/mergehtml",
        "prepublishOnly": "npm run build"
    },
    "bin": {
        "mergehtml": "./bin/mergehtml"
    }
    ...

This lets you run the build on any platform supported by npm, including Windows:

npm run build

Now, create folders ./bin and ./node-src. Into node-src, put the three files and your master template. Into bin put the mergehtml.js file shown below.

./bin/mergehtml.js 🔗︎

This is the code that does the magic. I overwrites the nodes html file (./nodes/node.html in this example) with the blank template then replaces the three <script> tags with the contents of the files from node-src.

#!/usr/bin/env node

/** Merge the various sections of nodes/uibuilder.html from the files in node-src/
 * Note that this is run from `npm run mergehtml` which puts the working directory
 * to the root, not `./bin`
*/

const replace = require('replace-in-file')
const fs = require('fs')
//const path = require('path')

// nodes\lib\uibuilder-help.html
const myhelp = fs.readFileSync('./node-src/help.html')
const mytemplate = fs.readFileSync('./node-src/template.html')
const myscript = fs.readFileSync('./node-src/script.js')

// Copy template - overwrite the nodes html file
fs.copyFileSync('./node-src/node.html', './nodes/node.html')

var options = {
    files: './nodes/uibuilder.html',
    from: [
        /(<script type="text\/javascript">)(.|\n)*?(<\/script>)/gmi,
        /(<script type="text\/x-red" data-template-name="uibuilder">)(.|\n)*?(<\/script>)/gmi,
        /(<script type="text\/x-red" data-help-name="uibuilder">)(.|\n)*?(<\/script>)/gmi,
    ],
    to: [
        `$1\n${myscript}\n$3`,
        `$1\n${mytemplate}\n$3`,
        `$1\n${myhelp}\n$3`,
    ],
}

replace(options)
    .then(changes => {
        console.log('MERGEHELP: data-help-name Modified files:', changes.join(', '));
    })
    .catch(error => {
        console.error('MERGEHELP: Error occurred:', error);
    })

console.log('MERGEHELP: Completed')

./node-src/node.html 🔗︎

Here is the content of the node.html file - you can use this for any project. You can put text between the tags if you like, even change the order of them if you prefer, the code should still work just fine. Obviously, any text you put between the open/close tags will be replaced.

<script type="text/javascript"></script>

<script type="text/x-red" data-template-name="uibuilder"></script>

<script type="text/x-red" data-help-name="uibuilder"></script>

comments powered by Disqus