1- const { cwd } = require ( "process" ) ;
1+ const { cwd } = require ( "node: process" ) ;
22const ts = require ( "typescript" ) ;
33const {
4- cp,
54 readdirSync,
6- exists,
75 existsSync,
86 readFileSync,
9- writeFile,
107 writeFileSync,
118 mkdirSync,
129 realpathSync,
10+ copyFileSync,
1311} = require ( "node:fs" ) ;
14- const sourcePath = cwd ( ) + "/src" ;
15- const targetPath = cwd ( ) + "/dist" ;
12+
13+ // directory containing the sources for nodes; defaults to `src/nodes` within the current working directory
14+ const nodesSourceDir = cwd ( ) + "/src/nodes" ;
15+ // directory the generated files should be saved to; defaults to `dist/nodes` within the current working directory
16+ const nodesTargetDir = cwd ( ) + "/dist/nodes" ;
17+ // path to the typescript project configuration for building the nodes
1618const buildTsConfig = JSON . parse ( readFileSync ( "build.tsconfig.json" ) . toString ( ) ) ;
1719
1820/**
19- * @param node String
20- * @param source String
21- * @param target String
21+ * Creates a directory if it does not exist
22+ * @param {string } dir directory path to create
23+ */
24+ function makeDir ( dir ) {
25+ // bypass everything in case the directory already exists
26+ if ( ! existsSync ( dir ) ) {
27+ const parentDir = realpathSync ( `${ dir } ../` ) ;
28+
29+ // check whether parent directory exists and create it recursively if not
30+ if ( ! existsSync ( parentDir ) ) {
31+ makeDir ( parentDir ) ;
32+ }
33+
34+ // finally create the directory
35+ mkdirSync ( dir ) ;
36+ }
37+ }
38+
39+ /**
40+ * Builds the form for editing a node using the Node-RED Editor.
41+ *
42+ * @param {string } node name of the node the form belongs to
43+ * @param {string } sourcePath path of the directory containing the node's source files
44+ * @param {string } targetPath path of the directory the node's generated files should be saved to
2245 */
23- function buildNodeForm ( node , source , target ) {
46+ function buildForm ( node , sourcePath , targetPath ) {
2447 /**
2548 * @type {string[] }
2649 */
2750 const html = [ ] ;
28- const form = readFileSync ( `${ source } /form.html` ) . toString ( ) ;
29- const docs = existsSync ( `${ source } /docs.html` ) ? readFileSync ( `${ source } /docs.html` ) . toString ( ) : undefined ;
51+ const form = readFileSync ( `${ sourcePath } /form.html` ) . toString ( ) ;
52+ const docs = existsSync ( `${ sourcePath } /docs.html` ) ? readFileSync ( `${ sourcePath } /docs.html` ) . toString ( ) : undefined ;
3053
54+ // parse `form.html` and wrap it to the corresponding script-tag
3155 const formLines = form . split ( "\n" ) ;
3256 html . push ( `<script type="text/html" data-template-name="${ node } ">` ) ;
3357 formLines . forEach ( ( line ) => {
@@ -37,6 +61,7 @@ function buildNodeForm(node, source, target) {
3761 } ) ;
3862 html . push ( "</script>" ) ;
3963
64+ // parse `docs.html` and wrap it to the corresponding script-tag
4065 if ( docs ) {
4166 const docsLines = docs . split ( "\n" ) ;
4267 html . push ( `<script type="text/html" data-help-name="${ node } ">` ) ;
@@ -48,12 +73,14 @@ function buildNodeForm(node, source, target) {
4873 html . push ( "</script>" ) ;
4974 }
5075
51- const initTS = readFileSync ( `${ source } /init.ts` ) . toString ( ) ;
76+ // read and transpile the node's TypeScript
77+ const initTS = readFileSync ( `${ sourcePath } /init.ts` ) . toString ( ) ;
5278 let initJS = ts
5379 . transpileModule ( initTS , buildTsConfig )
5480 . outputText . replace ( / e x p o r t \{ (?: [ ^ \} ] + ) ? \} ; / gim, "" )
5581 . replace ( / R E D \. n o d e s \. r e g i s t e r T y p e \( " ( [ ^ \" ] + ) " / i, `RED.nodes.registerType("${ node } "` ) ;
5682
83+ // inject the node's init logic into the node's HTML
5784 const initJSLines = initJS . split ( "\n" ) ;
5885 html . push ( '<script type="text/javascript">' ) ;
5986 // wrap in an IIFE so top-level declarations (e.g. `const def`, helper functions)
@@ -70,40 +97,47 @@ function buildNodeForm(node, source, target) {
7097
7198 html . push ( "" ) ;
7299
73- mkdirSync ( `${ target } /` , {
100+ // create node's target directory
101+ mkdirSync ( `${ targetPath } /` , {
74102 recursive : true ,
75103 } ) ;
76104
77- writeFileSync ( `${ target } /${ node } .html` , html . join ( "\n" ) ) ;
105+ // save the node's editor formular
106+ writeFileSync ( `${ targetPath } /${ node } .html` , html . join ( "\n" ) ) ;
78107}
79108
80109/**
81- * @param node String
82- * @param source String
83- * @param target String
110+ * Copys the locale files of a node.
111+ *
112+ * @param {string } node name of the node the form belongs to
113+ * @param {string } sourcePath path of the directory containing the node's source files
114+ * @param {string } targetPath path of the directory the node's generated files should be saved to
84115 */
85- function copyNodeLocales ( node , source , target ) {
86- if ( ! existsSync ( `${ source } /locales` ) ) {
116+ function copyLocales ( node , sourcePath , targetPath ) {
117+ // skip in case directory `locales` does not exist within the nodes directory
118+ if ( ! existsSync ( `${ sourcePath } /locales` ) ) {
87119 return ;
88120 }
89121
90- readdirSync ( `${ source } /locales` , {
122+ readdirSync ( `${ sourcePath } /locales` , {
91123 recursive : false ,
92124 } )
93125 . filter ( ( language ) => {
126+ // filter for files named as valid language codes
94127 return language . match ( / ^ [ a - z ] { 2 , 2 } (?: - [ A - Z ] { 2 , 2 } ) ? \. j s o n $ / ) ;
95128 } )
96129 . forEach ( ( language ) => {
130+ // read the language code and the JSON
97131 const languageCode = language . match ( / ^ ( [ a - z ] { 2 , 2 } (?: - [ A - Z ] { 2 , 2 } ) ? ) \. j s o n $ / i) [ 1 ] ;
98- const content = readFileSync ( `${ source } /locales/${ language } ` ) . toString ( ) ;
132+ const content = readFileSync ( `${ sourcePath } /locales/${ language } ` ) . toString ( ) ;
99133
100134 /**
101135 * @type {string[] }
102136 */
103- const json = [ ] ;
137+ const json = [ "{" ] ;
104138
139+ // wrap locales content within a tag names like the node
105140 const contentLines = content . split ( "\n" ) ;
106- const lastElement = json . push ( "{" ) ;
107141 json . push ( ` "${ node } ": ${ contentLines [ 0 ] } ` ) ;
108142 contentLines . splice ( 1 ) . forEach ( ( line ) => {
109143 if ( line . length > 0 ) {
@@ -112,75 +146,62 @@ function copyNodeLocales(node, source, target) {
112146 } ) ;
113147 json . push ( "}" ) ;
114148
115- mkdirSync ( `${ target } /locales/${ languageCode } ` , {
149+ // create node locales target directory
150+ mkdirSync ( `${ targetPath } /locales/${ languageCode } ` , {
116151 recursive : true ,
117152 } ) ;
118153
119- writeFileSync ( `${ target } /locales/${ languageCode } /${ node } .json` , json . join ( "\n" ) ) ;
154+ // save the node's locales
155+ writeFileSync ( `${ targetPath } /locales/${ languageCode } /${ node } .json` , json . join ( "\n" ) ) ;
120156 } ) ;
121157}
122158
123- function buildNodes ( ) {
124- readdirSync ( `${ sourcePath } /nodes` , {
125- recursive : false ,
126- } )
127- . filter ( ( node ) => {
128- const path = `${ sourcePath } /nodes/${ node } ` ;
129-
130- return existsSync ( `${ path } /form.html` ) && existsSync ( `${ path } /${ node } .ts` ) ;
131- } )
132- . forEach ( ( node ) => {
133- console . info ( `Building node ${ node } ` ) ;
134-
135- const source = `${ sourcePath } /nodes/${ node } ` ;
136- const target = `${ targetPath } /nodes/${ node } ` ;
137-
138- buildNodeForm ( node , source , target ) ;
159+ /**
160+ * Copy icons of a node.
161+ *
162+ @param {string } node name of the node the form belongs to
163+ @param {string } sourcePath path of the directory containing the node's source files
164+ @param {string } targetPath path of the directory the node's generated files should be saved to
165+ */
166+ function copyIcons ( node , sourcePath , targetPath ) {
167+ // skip in case directory `icons` does not exist within the nodes directory
168+ if ( ! existsSync ( `${ sourcePath } /icons` ) ) {
169+ return ;
170+ }
139171
140- copyNodeLocales ( node , source , target ) ;
141- } ) ;
142- }
172+ // create node icons target directory
173+ mkdirSync ( `${ targetPath } /icons` , {
174+ recursive : true ,
175+ } ) ;
143176
144- function buildPlugins ( ) {
145- readdirSync ( `${ sourcePath } /plugins ` , {
177+ // copy the node's icons
178+ readdirSync ( `${ sourcePath } /icons ` , {
146179 recursive : false ,
147- } )
148- . filter ( ( plugin ) => {
149- const path = `${ sourcePath } /plugins/${ plugin } ` ;
150-
151- return existsSync ( `${ path } /${ plugin } .ts` ) ;
152- } )
153- . forEach ( ( plugin ) => {
154- console . info ( `Building plugin ${ plugin } ` ) ;
155-
156- // does nothing yet
157- } ) ;
180+ } ) . forEach ( ( icon ) => {
181+ copyFileSync ( `${ sourcePath } /icons/${ icon } ` , `${ targetPath } /icons/${ icon } ` ) ;
182+ } ) ;
158183}
159184
160- function buildThemes ( ) {
161- readdirSync ( `${ sourcePath } /themes` , {
162- recursive : false ,
185+ /*
186+ * Iterate over every directory
187+ */
188+ readdirSync ( nodesSourceDir , {
189+ recursive : false ,
190+ } )
191+ . filter ( ( node ) => {
192+ const path = `${ nodesSourceDir } /${ node } ` ;
193+
194+ // filter for directories containing at least a `form.html` and the nodes logic script `node.ts`
195+ return existsSync ( `${ path } /form.html` ) && existsSync ( `${ path } /${ node } .ts` ) ;
163196 } )
164- . filter ( ( theme ) => {
165- const path = `${ sourcePath } /themes/${ theme } ` ;
197+ . forEach ( ( node ) => {
198+ const sourcePath = `${ nodesSourceDir } /${ node } ` ;
199+ const targetPath = `${ nodesTargetDir } /${ node } ` ;
166200
167- return existsSync ( `${ path } /${ theme } .ts` ) ;
168- } )
169- . forEach ( ( theme ) => {
170- console . info ( `Building theme ${ theme } ` ) ;
171-
172- // does nothing yet
173- } ) ;
174- }
175-
176- if ( existsSync ( `${ sourcePath } /plugins` ) ) {
177- buildPlugins ( ) ;
178- }
179-
180- if ( existsSync ( `${ sourcePath } /nodes` ) ) {
181- buildNodes ( ) ;
182- }
201+ // build the nodes' form for Node-RED Editor
202+ buildForm ( node , sourcePath , targetPath ) ;
183203
184- if ( existsSync ( `${ sourcePath } /themes` ) ) {
185- buildThemes ( ) ;
186- }
204+ // copy/transfer optional assets
205+ copyLocales ( node , sourcePath , targetPath ) ;
206+ copyIcons ( node , sourcePath , targetPath ) ;
207+ } ) ;
0 commit comments