@gregoriusrippenstein/node-red-contrib-nodedev 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -165,62 +165,137 @@ module.exports = function (RED) {
165
165
  });
166
166
  }
167
167
 
168
- function convertToPkgFileNodes(msg, jsCtnt, htmlCtnt) {
169
- var allNodes = [];
170
-
171
- var secondId = RED.util.generateId();
172
-
173
- allNodes.push({
174
- id: RED.util.generateId(),
175
- type: "PkgFile",
176
- name: msg.node.name + ".js",
177
- filename: "nodes/" + msg.node.name.toLowerCase() + ".js",
178
- template: jsCtnt,
179
- syntax: "mustache",
180
- format: "javascript",
181
- output: "str",
182
- x: 100,
183
- y: 50,
184
- wires: [[secondId]]
168
+ function createPluginNodeDefintions(msg, node) {
169
+ var sidebarHtmlPath = path.join(__dirname, 'templates', 'tmplsidebar.html');
170
+ var nodeJsPath = path.join(__dirname, 'templates', 'tmplsidebarnode.js');
171
+ var nodeHtmlPath = path.join(__dirname, 'templates', 'tmplsidebarnode.html');
172
+
173
+ var content = {};
174
+
175
+ var promises = [
176
+ handleTemplate(msg, node, fs.readFileSync(sidebarHtmlPath, 'utf8')).then((c) => { content["sbht"] = c }),
177
+ handleTemplate(msg, node, fs.readFileSync(nodeJsPath, 'utf8')).then((c) => { content["ndjs"] = c }),
178
+ handleTemplate(msg, node, fs.readFileSync(nodeHtmlPath, 'utf8')).then((c) => { content["ndht"] = c }),
179
+ ];
180
+
181
+ return Promise.all(promises).then(() => {
182
+ var secondId = RED.util.generateId();
183
+ var thirdId = RED.util.generateId();
184
+
185
+ return [{
186
+ id: RED.util.generateId(),
187
+ type: "PkgFile",
188
+ name: msg.node.name + "Cfg.js",
189
+ filename: "nodes/" + msg.node.namelwr + ".js",
190
+ template: content["ndjs"],
191
+ syntax: "mustache",
192
+ format: "javascript",
193
+ output: "str",
194
+ x: 100,
195
+ y: 50,
196
+ wires: [[secondId]]
197
+ },
198
+
199
+ {
200
+ id: secondId,
201
+ type: "PkgFile",
202
+ name: "Sidebar: " + msg.node.name + ".html",
203
+ filename: "plugins/" + msg.node.namelwr + ".html",
204
+ template: content["sbht"],
205
+ syntax: "mustache",
206
+ format: "html",
207
+ output: "str",
208
+ x: 100,
209
+ y: 100,
210
+ wires: [[thirdId]]
211
+ },
212
+
213
+ {
214
+ id: thirdId,
215
+ type: "PkgFile",
216
+ name: msg.node.name + "Cfg.html",
217
+ filename: "nodes/" + msg.node.namelwr + ".html",
218
+ template: content["ndht"],
219
+ syntax: "mustache",
220
+ format: "html",
221
+ output: "str",
222
+ x: 100,
223
+ y: 150,
224
+ wires: [[]]
225
+ }]
185
226
  })
227
+ }
228
+
229
+ function createSimpleNodeDefintions(msg,node) {
230
+ var htmlPath = path.join(__dirname, 'templates', 'tmpl.html');
231
+ var jsPath = path.join(__dirname, 'templates', 'tmpl.js');
232
+
233
+ var content = {};
186
234
 
187
- allNodes.push({
188
- id: secondId,
189
- type: "PkgFile",
190
- name: msg.node.name + ".html",
191
- filename: "nodes/" + msg.node.name.toLowerCase() + ".html",
192
- template: htmlCtnt,
193
- syntax: "mustache",
194
- format: "html",
195
- output: "str",
196
- x: 100,
197
- y: 100,
198
- wires: [[]]
235
+ var promises = [
236
+ handleTemplate(msg, node, fs.readFileSync(htmlPath, 'utf8')).then((c) => { content["html"] = c }),
237
+ handleTemplate(msg, node, fs.readFileSync(jsPath, 'utf8')).then((c) => { content["jasc"] = c }),
238
+ ];
239
+
240
+ return Promise.all(promises).then(() => {
241
+ var secondId = RED.util.generateId();
242
+
243
+ return [{
244
+ id: RED.util.generateId(),
245
+ type: "PkgFile",
246
+ name: msg.node.name + ".js",
247
+ filename: "nodes/" + msg.node.namelwr + ".js",
248
+ template: content["jasc"],
249
+ syntax: "mustache",
250
+ format: "javascript",
251
+ output: "str",
252
+ x: 100,
253
+ y: 50,
254
+ wires: [[secondId]]
255
+ },
256
+
257
+ {
258
+ id: secondId,
259
+ type: "PkgFile",
260
+ name: msg.node.name + ".html",
261
+ filename: "nodes/" + msg.node.namelwr + ".html",
262
+ template: content["html"],
263
+ syntax: "mustache",
264
+ format: "html",
265
+ output: "str",
266
+ x: 100,
267
+ y: 100,
268
+ wires: [[]]
269
+ }]
199
270
  })
271
+ }
200
272
 
201
- return allNodes;
273
+ function createNodeDefintions(msg, node) {
274
+ if ( msg.node.isplugin ) {
275
+ return createPluginNodeDefintions(msg, node)
276
+ } else {
277
+ return createSimpleNodeDefintions(msg, node)
278
+ }
202
279
  }
203
280
 
204
281
  function createManifestFiles(msg, node, nodeDefinitions) {
205
- /* ASSUMPTION: assume the .js file is defined first and
206
- then the .html file in the node definitions */
207
-
208
282
  var packageJsonPath = path.join(__dirname, 'templates', 'tmplpackage.json');
209
283
  var readmePath = path.join(__dirname, 'templates', 'tmplreadme.md');
210
284
  var licensePath = path.join(__dirname, 'templates', 'tmpllicense');
211
285
 
212
- var pkjsTmpl = fs.readFileSync(packageJsonPath, 'utf8');
213
- var rdmeTmpl = fs.readFileSync(readmePath, 'utf8');
214
- var lcnsTmpl = fs.readFileSync(licensePath, 'utf8');
215
-
216
- spcRepDict["nodestanza"] = " \"" + msg.node.name.toLowerCase() + "\": \"" + nodeDefinitions[0].filename + "\""
286
+ /* ASSUMPTION: assume the .js file is defined first and
287
+ then the .html file in the node definitions */
288
+ spcRepDict["nodestanza"] = " \"" + msg.node.namelwr + "\": \"" + nodeDefinitions[0].filename + "\""
289
+ if ( msg.node.isplugin ) {
290
+ spcRepDict["pluginstanza"] = " \"sidebar-plugin\": \"" + nodeDefinitions[1].filename + "\""
291
+ }
217
292
 
218
293
  var content = {};
219
294
 
220
295
  var promises = [
221
- handleTemplate(msg, node, pkjsTmpl).then((c) => { content["pkjs"] = c }),
222
- handleTemplate(msg, node, rdmeTmpl).then((c) => { content["rdme"] = c }),
223
- handleTemplate(msg, node, lcnsTmpl).then((c) => { content["lcns"] = c }),
296
+ handleTemplate(msg, node, fs.readFileSync(packageJsonPath, 'utf8')).then((c) => { content["pkjs"] = c }),
297
+ handleTemplate(msg, node, fs.readFileSync(readmePath, 'utf8')).then((c) => { content["rdme"] = c }),
298
+ handleTemplate(msg, node, fs.readFileSync(licensePath, 'utf8')).then((c) => { content["lcns"] = c }),
224
299
  ];
225
300
 
226
301
  return Promise.all(promises).then( () => {
@@ -284,43 +359,15 @@ module.exports = function (RED) {
284
359
  });
285
360
 
286
361
  node.on("input", function (msg, send, done) {
362
+ send({ payload: msg.node })
363
+
287
364
  if (msg.node && msg.node.__task == "generate_from_templates") {
288
365
  try {
289
- var htmlPath = path.join(__dirname, 'templates', 'tmpl.html');
290
- var jsPath = path.join(__dirname, 'templates', 'tmpl.js');
291
-
292
- var jsonTmpl = fs.readFileSync(jsPath, 'utf8');
293
- var htmlTmpl = fs.readFileSync(htmlPath, 'utf8');
294
-
295
- handleTemplate(msg, node, jsonTmpl).then(function (data) {
296
- var jsData = data;
297
-
298
- handleTemplate(msg, node, htmlTmpl).then(function (data) {
299
- var nodeDef = convertToPkgFileNodes(msg, jsData, data)
300
-
301
- if (msg.node.createmanifest ) {
302
- /* create the package.json, readme and license files */
303
- createManifestFiles(msg, node, nodeDef).then( (data) => {
304
- var nodeImpStr = JSON.stringify(data);
305
-
306
- send({ payload: nodeImpStr })
307
-
308
- RED.comms.publish(
309
- "nodedev:perform-autoimport-nodes",
310
- RED.util.encodeObject({
311
- msg: "autoimport",
312
- payload: nodeImpStr,
313
- topic: msg.topic,
314
- nodeid: node.id,
315
- _msg: msg
316
- })
317
- );
318
- }).catch( (err) => {
319
- msg.error = err
320
- done(err.message, msg)
321
- })
322
- } else {
323
- var nodeImpStr = JSON.stringify(nodeDef);
366
+ createNodeDefintions(msg,node).then( (nodeDef) => {
367
+ if (msg.node.createmanifest ) {
368
+ /* create the package.json, readme and license files */
369
+ createManifestFiles(msg, node, nodeDef).then( (data) => {
370
+ var nodeImpStr = JSON.stringify(data);
324
371
 
325
372
  send({ payload: nodeImpStr })
326
373
 
@@ -334,17 +381,31 @@ module.exports = function (RED) {
334
381
  _msg: msg
335
382
  })
336
383
  );
337
- }
338
- done()
339
- }).catch((err) => {
340
- msg.error = err
341
- done(err.message, msg)
342
- })
384
+ }).catch( (err) => {
385
+ msg.error = err
386
+ done(err.message, msg)
387
+ })
388
+ } else {
389
+ var nodeImpStr = JSON.stringify(nodeDef);
390
+
391
+ send({ payload: nodeImpStr })
392
+
393
+ RED.comms.publish(
394
+ "nodedev:perform-autoimport-nodes",
395
+ RED.util.encodeObject({
396
+ msg: "autoimport",
397
+ payload: nodeImpStr,
398
+ topic: msg.topic,
399
+ nodeid: node.id,
400
+ _msg: msg
401
+ })
402
+ );
403
+ }
404
+ done()
343
405
  }).catch((err) => {
344
406
  msg.error = err
345
407
  done(err.message, msg)
346
408
  })
347
- //send({ payload: jsonString })
348
409
  } catch (err) {
349
410
  msg.error = err
350
411
  done(err.message, msg)
@@ -145,6 +145,6 @@
145
145
  </script>
146
146
 
147
147
  <script type="text/html" data-help-name="{{ node.name }}">
148
- <p>{{ node.summary }}</p>
149
- {{ node.description }}
148
+ <p>{{{ node.summary }}}</p>
149
+ {{{ node.description }}}
150
150
  </script>
@@ -1,5 +1,5 @@
1
1
  module.exports = function(RED) {
2
- function {{ node.name }}Functionality(config) {
2
+ function Core{{ node.name }}Functionality(config) {
3
3
  RED.nodes.createNode(this,config);
4
4
 
5
5
  var node = this;
@@ -24,7 +24,7 @@ module.exports = function(RED) {
24
24
  });
25
25
  }
26
26
 
27
- RED.nodes.registerType("{{ node.name }}", {{ node.name }}Functionality);
27
+ RED.nodes.registerType("{{ node.name }}", Core{{ node.name }}Functionality);
28
28
 
29
29
  {{ #node.frt2bakcomm }}
30
30
  RED.httpAdmin.post("/{{ node.name }}/:id",
@@ -1,28 +1,33 @@
1
1
  {
2
2
  "name" : "{{__.ocb3}} pname {{__.ccb3}}",
3
- "version": "{{__.ocb2}} pversion {{__.ccb2}}",
3
+ "version": "{{__.ocb3}} pversion {{__.ccb3}}",
4
4
  "dependencies": {
5
5
  },
6
6
 
7
+ "description": "{{__.ocb3}} pdescription {{__.ccb3}}",
7
8
  "keywords": [
8
9
  "node-red"
9
10
  ],
10
11
 
11
- "homepage": "https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}#readme",
12
12
  "license": "Don't do evil.",
13
- "author": "Joe Blog <joe.blog@spreads-the.love>",
13
+ "homepage": "https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}#readme",
14
+ "author": "{{__.ocb3}} pauthorname {{__.ccb3}} <{{__.ocb3}} pauthoremail {{__.ccb3}}>",
14
15
  "engines": {
15
16
  "node": ">=14"
16
17
  },
17
18
 
18
19
  "node-red" : {
19
20
  "version": ">=3.0.0",
21
+ {{#node.isplugin}}
22
+ "plugins": {
23
+ {{{__.pluginstanza}}}
24
+ },
25
+ {{/node.isplugin}}
20
26
  "nodes": {
21
27
  {{{__.nodestanza}}}
22
28
  }
23
29
  },
24
30
 
25
- "description": "Description of what these nodes do.",
26
31
  "repository": {
27
32
  "type": "github",
28
33
  "url": "git+https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}.git"
@@ -0,0 +1,138 @@
1
+ <script type="text/javascript">
2
+ (function() {
3
+ var globalYourConfigNode = null;
4
+
5
+ function ensureYourConfigNodeExists() {
6
+ // This function makes sure there is 1 instance of your config node is available, and that the globalYourConfigNode variable refers to it.
7
+ // Explained in the next step of this tutorial... --> https://discourse.nodered.org/t/tutorial-create-a-sidebar-plugin-and-persist-the-data-in-a-config-node/82020
8
+
9
+ // If we had found it previously, check if it has been deleted by the user behind our back
10
+ if (globalYourConfigNode !== null) {
11
+ var configNode = RED.nodes.node(globalYourConfigNode.id);
12
+ if (configNode === null) { globalYourConfigNode = null; }
13
+ }
14
+
15
+ // If not found previously, let's go find it
16
+ if (globalYourConfigNode === null) {
17
+ var configNodes = [];
18
+ RED.nodes.eachConfig(function(configNode) {
19
+ if (configNode.type === '{{ node.name }}') {
20
+ configNodes.push(configNode);
21
+ }
22
+ });
23
+
24
+ // Make sure we only have 1 config node
25
+ while (configNodes.length > 1) {
26
+ var configNode = configNodes.pop();
27
+ RED.nodes.remove(configNode.id);
28
+ RED.nodes.dirty(true);
29
+ }
30
+
31
+ // When we found a config node, let's use that one
32
+ if (configNodes.length === 1) { globalYourConfigNode = configNodes[0]; }
33
+ }
34
+
35
+ // When it doesn't exist yet, create it if required
36
+ if (globalYourConfigNode === null) {
37
+ // Remark: since this config node is dynamically created (and only used in this sidebar which isn't another node), the config
38
+ // node is in fact "unused". But since we don't want it to appear "unused" in the "config nodes" panel, we need to set hasUsers
39
+ // to false (see https://github.com/node-red/node-red/blob/master/CHANGELOG.md#0161-maintenance-release).
40
+ // The hasUsers needs also to be specified in the RED.nodes.registerType statement!
41
+ globalYourConfigNode = {
42
+ id: RED.nodes.id(), // on the server side, this is called RED.util.generateId()
43
+ _def: RED.nodes.getType("{{ node.name }}Cfg"),
44
+ type: "{{ node.name }}Cfg",
45
+ hasUsers: false,
46
+ users: [],
47
+ name: "{{ node.name }}",
48
+ label: function() { return this.name || "{{ node.name }}"},
49
+ /* values and data defined by this config node */
50
+ data: "some default value", // Default data
51
+ }
52
+
53
+ // Add the new config node to the collection of Node-RED nodes
54
+ RED.nodes.add(globalYourConfigNode);
55
+
56
+ // Make sure the "Deploy" button becomes active
57
+ RED.nodes.dirty(true);
58
+ }
59
+ }
60
+
61
+ // Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started
62
+ var initialiseConfigNodeOnce = () => {
63
+ RED.events.off('runtime-state', initialiseConfigNodeOnce);
64
+
65
+ // The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
66
+ var content = $($('script[type="text/x-red"][data-template-name="{{ node.name }}"]').i18n().html());
67
+
68
+ // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed
69
+ // --> more details: https://nodered.org/docs/api/ui/sidebar/
70
+ RED.sidebar.addTab({
71
+ id: "{{ node.name }}",
72
+ label: "{{ node.namelwr }}", // short name for the tab
73
+ name: "{{ node.name }}", // long name for the menu
74
+ content: content,
75
+ closeable: true,
76
+ // disableOnEdit: true,
77
+ enableOnEdit: true,
78
+ iconClass: "{{{ node.iconclass }}}" // your fontawesome icon
79
+ });
80
+
81
+ ensureYourConfigNodeExists();
82
+
83
+ {{#node.hasbutton}}
84
+ var doSomething = (e) => {
85
+ if (e) { e.preventDefault(); }
86
+ RED.notify("Button pressed in the side or action "+
87
+ "triggered. Current value: <b>" + globalYourConfigNode.data + "</b>",
88
+ "success");
89
+ }
90
+
91
+ $('#node-input-button').on('click', (e) => {
92
+ doSomething(e);
93
+ })
94
+
95
+ /* name of action will become "My Sidebar Action", label is generated
96
+ from action name - see https://nodered.org/docs/api/ui/actions/ */
97
+ RED.actions.add("{{ node.namelwr }}:my-sidebar-action",function() {
98
+ doSomething();
99
+ });
100
+ {{/node.hasbutton}}
101
+
102
+ // At startup load your config node data into the plugin sidebar html elements
103
+ $("#node-input-data").val(globalYourConfigNode.data);
104
+
105
+ // When the user has entered new data in the sidebar, then store it into the config node
106
+ $("#node-input-data").on("change", function() {
107
+ ensureYourConfigNodeExists();
108
+
109
+ let data = $(this).val();
110
+
111
+ if (globalYourConfigNode.data != data) {
112
+ globalYourConfigNode.data = data;
113
+ // Since the config node has been updated, the 'Deploy' button should become active
114
+ RED.nodes.dirty(true);
115
+ }
116
+ })
117
+ };
118
+ RED.events.on('runtime-state', initialiseConfigNodeOnce);
119
+ })();
120
+ </script>
121
+
122
+ <!-- The html for the right sidebar plugin screen -->
123
+ <script type="text/x-red" data-template-name="{{ node.name }}">
124
+ <div class="form-row">
125
+ <div style="position: relative; height: 100%; margin: 15px;">
126
+ <label for="node-input-data" style="margin-top: 15px"><i class="fa fa-cog"></i> Data</label>
127
+ <input type="text" id="node-input-data" style="width: 100%; margin-top: 30px">
128
+ </div>
129
+ </div>
130
+
131
+ {{#node.hasbutton}}
132
+ <div class="form-row">
133
+ <div style="position: relative; height: 100%; margin: 15px;">
134
+ <button id="node-input-button" class="success" style="width: 100%; margin-top: 30px">Do something</button>
135
+ </div>
136
+ </div>
137
+ {{/node.hasbutton}}
138
+ </script>
@@ -0,0 +1,22 @@
1
+ <script type="text/javascript">
2
+ (function ($) {
3
+ RED.nodes.registerType('{{ node.name }}Cfg', {
4
+ category: 'config',
5
+ hasUsers: false,
6
+ defaults: {
7
+ name: { value: "" },
8
+ data: { value: "" }
9
+ },
10
+ paletteLabel: '{{ node.name }}Cfg',
11
+ label: function () {
12
+ return this.name;
13
+ }
14
+ });
15
+ })(jQuery);
16
+ </script>
17
+
18
+ <!-- The html for the config node info panel (in right sidebar) -->
19
+ <script type="text/x-red" data-template-name="{{ node.name }}Cfg">
20
+ <p>{{{ node.summary }}}</p>
21
+ {{{ node.description }}}
22
+ </script>
@@ -0,0 +1,6 @@
1
+ module.exports = function (RED) {
2
+ function Config{{ node.name }}Functionality(config) {
3
+ RED.nodes.createNode(this, config)
4
+ }
5
+ RED.nodes.registerType('{{ node.name }}Cfg', Config{{ node.name }}Functionality);
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "@gregoriusrippenstein/node-red-contrib-nodedev",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "dependencies": {
5
5
  "pako": "latest",
6
6
  "tar-stream": "latest",
@@ -13,7 +13,7 @@
13
13
  "node-red", "node development", "introspection", "bootstrapping", "recursion"
14
14
  ],
15
15
 
16
- "homepage": "https://github.com/gorenje/node-red-contrib-nodedev#README.md",
16
+ "homepage": "https://github.com/gorenje/node-red-contrib-nodedev#readme",
17
17
  "license": "Don't do evil.",
18
18
  "author": "Gerrit Riessen <does.anyone@spread-the.love> (https://spread-the.love)",
19
19
  "engines": {