@gregoriusrippenstein/node-red-contrib-nodedev 0.0.9 → 0.1.0
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.
- package/README.md +8 -0
- package/nodes/05-node-dev-ops.html +286 -0
- package/nodes/05-node-dev-ops.js +51 -0
- package/nodes/10-node-factory.html +35 -4
- package/nodes/10-node-factory.js +134 -23
- package/nodes/30-npm-tarball.html +1 -1
- package/nodes/50-npm-publish.js +14 -14
- package/nodes/templates/tmpl.html +70 -0
- package/nodes/templates/tmpl.js +41 -0
- package/nodes/templates/tmpllicense +4 -0
- package/nodes/templates/tmplpackage.json +33 -0
- package/nodes/templates/tmplreadme.md +4 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -45,6 +45,14 @@ Normally when developing nodes for Node-RED, a restart is necessary to flush old
|
|
|
45
45
|
|
|
46
46
|
Since all this happens in the browser, there is no leaving Node-RED, making it more efficient to test and modify nodes. Renaming of packages can be done automagically as demonstrated by the [flow](https://flowhub.org/f/b92be5062203ff69) that maintains these nodes.
|
|
47
47
|
|
|
48
|
+
### Artifacts
|
|
49
|
+
|
|
50
|
+
- [Flow that maintains](https://flowhub.org/f/b92be5062203ff69) this codebase
|
|
51
|
+
- [GitHub repo](https://flows.nodered.org/node/@gregoriusrippenstein/node-red-contrib-nodedev)
|
|
52
|
+
- [NPMjs node package](https://www.npmjs.com/package/@gregoriusrippenstein/node-red-contrib-nodedev)
|
|
53
|
+
- [Node-RED node package](https://flows.nodered.org/node/@gregoriusrippenstein/node-red-contrib-nodedev)
|
|
54
|
+
|
|
48
55
|
### Outlook
|
|
49
56
|
|
|
50
57
|
Wherever the road shall lead, there it will go.
|
|
58
|
+
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(function(){
|
|
3
|
+
|
|
4
|
+
function sendToBackend(node,data = {}) {
|
|
5
|
+
|
|
6
|
+
$.ajax({
|
|
7
|
+
url: "NodeDevOps/" + node.id,
|
|
8
|
+
type: "POST",
|
|
9
|
+
contentType: "application/json; charset=utf-8",
|
|
10
|
+
|
|
11
|
+
data: JSON.stringify(data),
|
|
12
|
+
|
|
13
|
+
success: function (resp) {
|
|
14
|
+
RED.notify("Node Developer Operation trigger", {
|
|
15
|
+
type: "success",
|
|
16
|
+
id: "NodeDevOps",
|
|
17
|
+
timeout: 2000
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
error: function (jqXHR, textStatus, errorThrown) {
|
|
22
|
+
if (jqXHR.status == 404) {
|
|
23
|
+
RED.notify("Node has not yet been deployed, please deploy.", "error");
|
|
24
|
+
} else if (jqXHR.status == 405) {
|
|
25
|
+
RED.notify("Not Allowed.", "error");
|
|
26
|
+
} else if (jqXHR.status == 500) {
|
|
27
|
+
RED.notify(node._("common.notification.error", {
|
|
28
|
+
message: node._("inject.errors.failed")
|
|
29
|
+
}), "error");
|
|
30
|
+
} else if (jqXHR.status == 0) {
|
|
31
|
+
RED.notify(node._("common.notification.error", {
|
|
32
|
+
message: node._("common.notification.errors.no-response")
|
|
33
|
+
}), "error");
|
|
34
|
+
} else {
|
|
35
|
+
RED.notify(node._("common.notification.error", {
|
|
36
|
+
message: node._("common.notification.errors.unexpected", {
|
|
37
|
+
status: jqXHR.status, message: textStatus }) }), "error");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
RED.nodes.registerType('NodeDevOps',{
|
|
44
|
+
color: '#e5e4ef',
|
|
45
|
+
icon: "font-awesome/fa-terminal",
|
|
46
|
+
category: 'nodedev',
|
|
47
|
+
defaults: {
|
|
48
|
+
name: { value:"" },
|
|
49
|
+
|
|
50
|
+
pname: { value: "", required: true },
|
|
51
|
+
pversion: { value: "", required: true },
|
|
52
|
+
|
|
53
|
+
noderedinstall: { value: false },
|
|
54
|
+
randompackagename: { value: false },
|
|
55
|
+
|
|
56
|
+
gitcommit: { value: false },
|
|
57
|
+
gitcheckforchange: { value: false },
|
|
58
|
+
githubowner: { value: ""},
|
|
59
|
+
githubrepo: { value: ""},
|
|
60
|
+
githubbranch: { value: "main"},
|
|
61
|
+
githubauthor: { value: ""},
|
|
62
|
+
githubauthoremail: { value: "" },
|
|
63
|
+
githubmessage: { value: "" },
|
|
64
|
+
|
|
65
|
+
npmpublish: { value: false },
|
|
66
|
+
npmunpublish: { value: false },
|
|
67
|
+
npmotp: { value: ""},
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
inputs: 0,
|
|
71
|
+
outputs: 1,
|
|
72
|
+
|
|
73
|
+
label: function() {
|
|
74
|
+
return (this.name || this._def.paletteLabel);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
labelStyle: function() {
|
|
78
|
+
return this.name?"node_label_italic":"";
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
onpaletteadd: function() {
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
oneditprepare: function() {
|
|
85
|
+
|
|
86
|
+
$('#node-input-noderedinstall').on('change', () => {
|
|
87
|
+
if ( $('#node-input-noderedinstall').is(":checked") ) {
|
|
88
|
+
$("#noderedinstall-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
89
|
+
} else {
|
|
90
|
+
$("#noderedinstall-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
$('#node-input-gitcommit').on('change', () => {
|
|
95
|
+
if ( $('#node-input-gitcommit').is(":checked") ) {
|
|
96
|
+
$("#github-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
97
|
+
$("#gitcommit-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
98
|
+
$('#node-input-gitcheckforchange').prop('checked',false)
|
|
99
|
+
} else {
|
|
100
|
+
$("#gitcommit-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
101
|
+
if ( !$('#node-input-gitcheckforchange').is(":checked") ) {
|
|
102
|
+
$("#github-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
$('#node-input-gitcheckforchange').on('change', () => {
|
|
108
|
+
if ( $('#node-input-gitcheckforchange').is(":checked") ) {
|
|
109
|
+
$("#github-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
110
|
+
$("#gitcommit-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
111
|
+
$('#node-input-gitcommit').prop('checked',false)
|
|
112
|
+
} else {
|
|
113
|
+
if ( !$('#node-input-gitcommit').is(":checked") ) {
|
|
114
|
+
$("#github-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
115
|
+
$("#gitcommit-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
$('#node-input-npmpublish').on('change', () => {
|
|
121
|
+
if ( $('#node-input-npmpublish').is(":checked") ) {
|
|
122
|
+
$("#npmpublish-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
123
|
+
$('#node-input-npmunpublish').prop('checked',false)
|
|
124
|
+
} else {
|
|
125
|
+
if ( !$('#node-input-npmunpublish').is(":checked") ) {
|
|
126
|
+
$("#npmpublish-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
$('#node-input-npmunpublish').on('change', () => {
|
|
132
|
+
if ( $('#node-input-npmunpublish').is(":checked") ) {
|
|
133
|
+
$("#npmpublish-options").animate({opacity: 'show', height: 'show'}, 450);
|
|
134
|
+
$('#node-input-npmpublish').prop('checked',false)
|
|
135
|
+
} else {
|
|
136
|
+
if ( !$('#node-input-npmpublish').is(":checked") ) {
|
|
137
|
+
$("#npmpublish-options").animate({opacity: 'hide', height: 'hide'}, 450);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
oneditcancel: function() {
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
oneditsave: function() {
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
onpaletteremove: function() {
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
button: {
|
|
155
|
+
enabled: function() {
|
|
156
|
+
return !this.changed
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
onclick: function () {
|
|
160
|
+
if (this.changed) {
|
|
161
|
+
return RED.notify(RED._("notification.warning", {
|
|
162
|
+
message: RED._("notification.warnings.undeployedChanges")
|
|
163
|
+
}), "warning");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
var that = this;
|
|
167
|
+
var data = {}
|
|
168
|
+
|
|
169
|
+
Object.keys(that._def.defaults).forEach( kname => {
|
|
170
|
+
data[kname] = that[kname]
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
sendToBackend(that, data)
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
});
|
|
178
|
+
})();
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<script type="text/html" data-template-name="NodeDevOps">
|
|
182
|
+
<div class="form-row">
|
|
183
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
184
|
+
<input type="text" id="node-input-name" placeholder="Name"/>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<hr/>
|
|
188
|
+
|
|
189
|
+
<div class="form-row">
|
|
190
|
+
<label for="node-input-pname" style="min-width: 150px;"><i class="fa fa-tag"></i> Package Name</label>
|
|
191
|
+
<input type="text" id="node-input-pname" placeholder="@username/node-red-contrib-somename"/>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div class="form-row">
|
|
195
|
+
<label for="node-input-pversion" style="min-width: 150px;"><i class="fa fa-tag"></i> Package Version</label>
|
|
196
|
+
<input type="text" id="node-input-pversion" placeholder="0.0.1"/>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<hr/>
|
|
200
|
+
|
|
201
|
+
<div class="form-row">
|
|
202
|
+
<label for="node-input-noderedinstall" style="min-width: 120px;">
|
|
203
|
+
<span>Install locally?</span>
|
|
204
|
+
</label>
|
|
205
|
+
<input type="checkbox" id="node-input-noderedinstall" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div id="noderedinstall-options">
|
|
209
|
+
<div class="form-row">
|
|
210
|
+
<label for="node-input-randompackagename" style="min-width: 180px;">
|
|
211
|
+
<span>Randomise Package Name?</span>
|
|
212
|
+
</label>
|
|
213
|
+
<input type="checkbox" id="node-input-randompackagename" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<hr/>
|
|
218
|
+
<div class="form-row">
|
|
219
|
+
<label for="node-input-npmpublish" style="min-width: 120px;">
|
|
220
|
+
<span>NPMjs: Publish</span>
|
|
221
|
+
</label>
|
|
222
|
+
<input type="checkbox" id="node-input-npmpublish" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
223
|
+
<label for="node-input-npmunpublish" style="margin-left: 30px; min-width: 100px;">
|
|
224
|
+
<span>Unpublish</span>
|
|
225
|
+
</label>
|
|
226
|
+
<input type="checkbox" id="node-input-npmunpublish" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div id="npmpublish-options">
|
|
230
|
+
<div class="form-row">
|
|
231
|
+
<label for="node-input-npmotp" style="min-width: 150px;"><i class="fa fa-tag"></i> One Time Password</label>
|
|
232
|
+
<input type="text" id="node-input-npmotp" placeholder="111999"/>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<hr />
|
|
237
|
+
<div class="form-row">
|
|
238
|
+
<label for="node-input-gitcommit" style="min-width: 120px;">
|
|
239
|
+
<span>GitHub: Commit?</span>
|
|
240
|
+
</label>
|
|
241
|
+
<input type="checkbox" id="node-input-gitcommit" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
242
|
+
<label for="node-input-gitcheckforchange" style="margin-left: 30px; min-width: 100px;">
|
|
243
|
+
<span>What changed?</span>
|
|
244
|
+
</label>
|
|
245
|
+
<input type="checkbox" id="node-input-gitcheckforchange" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
<div id="github-options">
|
|
250
|
+
<div class="form-row">
|
|
251
|
+
<label for="node-input-githubowner" style="min-width: 150px;"><i class="fa fa-tag"></i> GitHub Username</label>
|
|
252
|
+
<input type="text" id="node-input-githubowner" placeholder="username"/>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="form-row">
|
|
255
|
+
<label for="node-input-githubrepo" style="min-width: 150px;"><i class="fa fa-tag"></i> Repository</label>
|
|
256
|
+
<input type="text" id="node-input-githubrepo" placeholder=""/>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="form-row">
|
|
259
|
+
<label for="node-input-githubbranch" style="min-width: 150px;"><i class="fa fa-tag"></i> Branch</label>
|
|
260
|
+
<input type="text" id="node-input-githubbranch" placeholder="main"/>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div id="gitcommit-options">
|
|
265
|
+
<div class="form-row">
|
|
266
|
+
<label for="node-input-githubmessage" style="min-width: 150px;"><i class="fa fa-tag"></i> Commit Message</label>
|
|
267
|
+
<input type="text" id="node-input-githubmessage" placeholder="Initial Commit"/>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="form-row">
|
|
270
|
+
<label for="node-input-githubauthor" style="min-width: 150px;"><i class="fa fa-tag"></i> Author Name</label>
|
|
271
|
+
<input type="text" id="node-input-githubauthor" placeholder="Joe Blog"/>
|
|
272
|
+
</div>
|
|
273
|
+
<div class="form-row">
|
|
274
|
+
<label for="node-input-githubauthoremail" style="min-width: 150px;"><i class="fa fa-tag"></i> Author Email</label>
|
|
275
|
+
<input type="text" id="node-input-githubauthoremail" placeholder="joe@spreads-the.love"/>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</script>
|
|
279
|
+
|
|
280
|
+
<script type="text/html" data-help-name="NodeDevOps">
|
|
281
|
+
<p>NodeDevOps is the operations node that controals what happens with the package.</p>
|
|
282
|
+
Possible actions that can happen are GitHub commits and diffs, NPM publish or unpublish and installing the package
|
|
283
|
+
locally for local testing.
|
|
284
|
+
|
|
285
|
+
Local installations can be uninstalled so that changes can tested and modified quickly.
|
|
286
|
+
</script>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
function NodeDevOpsFunctionality(config) {
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
|
|
5
|
+
var node = this;
|
|
6
|
+
var cfg = config;
|
|
7
|
+
|
|
8
|
+
node.on('close', function () {
|
|
9
|
+
node.status({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
node.on("input", function (msg, send, done) {
|
|
13
|
+
msg.commit_message = msg.githubmessage;
|
|
14
|
+
|
|
15
|
+
if (msg.randompackagename) {
|
|
16
|
+
msg.pname += Math.random().toString().substring(2)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (msg.randompackagename && (msg.gitcommit || msg.npmpublish || msg.npmunpublish) ) {
|
|
20
|
+
done("Cannot randomise package and perform GitHub or NPM operation", msg)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
send(msg);
|
|
24
|
+
done();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
RED.nodes.registerType("NodeDevOps", NodeDevOpsFunctionality);
|
|
29
|
+
|
|
30
|
+
RED.httpAdmin.post("/NodeDevOps/:id",
|
|
31
|
+
RED.auth.needsPermission("NodeDevOps.write"),
|
|
32
|
+
(req, res) => {
|
|
33
|
+
var node = RED.nodes.getNode(req.params.id);
|
|
34
|
+
if (node != null) {
|
|
35
|
+
try {
|
|
36
|
+
if (req.body && node.type == "NodeDevOps") {
|
|
37
|
+
node.receive(req.body);
|
|
38
|
+
res.status(200).send({ status: "ok" })
|
|
39
|
+
} else {
|
|
40
|
+
res.sendStatus(404);
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(err);
|
|
44
|
+
res.status(500).send(err.toString());
|
|
45
|
+
node.error("NodeDevOps: Submission failed: " + err.toString())
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
res.sendStatus(404);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -62,6 +62,26 @@
|
|
|
62
62
|
</div>
|
|
63
63
|
</div>
|
|
64
64
|
|
|
65
|
+
<div class="form-row">
|
|
66
|
+
<label for="node-input-frt2bakcomm" style="min-width: 250px;">
|
|
67
|
+
<span>Frontend to Backend communication?</span>
|
|
68
|
+
</label>
|
|
69
|
+
<input type="checkbox" id="node-input-frt2bakcomm" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="form-row">
|
|
73
|
+
<label for="node-input-bak2frtcomm" style="min-width: 250px;">
|
|
74
|
+
<span>Backend to Frontend communication?</span>
|
|
75
|
+
</label>
|
|
76
|
+
<input type="checkbox" id="node-input-bak2frtcomm" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="form-row">
|
|
80
|
+
<label for="node-input-createmanifest" style="min-width: 250px;">
|
|
81
|
+
<span>Create manifest?</span>
|
|
82
|
+
</label>
|
|
83
|
+
<input type="checkbox" id="node-input-createmanifest" style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
84
|
+
</div>
|
|
65
85
|
|
|
66
86
|
<div class="form-row">
|
|
67
87
|
<button id="node-input-generate-tmplnodes-but"
|
|
@@ -96,14 +116,19 @@
|
|
|
96
116
|
*/
|
|
97
117
|
data["summary"] = node.editorSummary.getValue();
|
|
98
118
|
data["description"] = node.editorDesc.getValue();
|
|
99
|
-
|
|
100
|
-
data["hasinput"] = $('#node-input-hasinput').is(":checked");
|
|
119
|
+
|
|
101
120
|
data["color"] = $('#node-input-colour').val();
|
|
102
121
|
data["icon"] = $('#red-ui-editor-node-icon').val();
|
|
103
122
|
data["name"] = $('#node-input-nodename').val();
|
|
104
123
|
data["outputcount"] = $('#node-input-outputcount').val();
|
|
105
124
|
data["category"] = $('#node-input-category').val();
|
|
106
125
|
|
|
126
|
+
data["hasbutton"] = $('#node-input-hasbutton').is(":checked");
|
|
127
|
+
data["hasinput"] = $('#node-input-hasinput').is(":checked");
|
|
128
|
+
data["bak2frtcomm"] = $('#node-input-bak2frtcomm').is(":checked");
|
|
129
|
+
data["frt2bakcomm"] = $('#node-input-frt2bakcomm').is(":checked");
|
|
130
|
+
data["createmanifest"] = $('#node-input-createmanifest').is(":checked");
|
|
131
|
+
|
|
107
132
|
data["__task"] = "generate_from_templates";
|
|
108
133
|
|
|
109
134
|
$.ajax({
|
|
@@ -118,7 +143,7 @@
|
|
|
118
143
|
success: function (resp) {
|
|
119
144
|
$('#node-dialog-ok').trigger('click');
|
|
120
145
|
|
|
121
|
-
RED.notify("
|
|
146
|
+
RED.notify("Preparing Node...", {
|
|
122
147
|
type: "warning",
|
|
123
148
|
timeout: 2000
|
|
124
149
|
});
|
|
@@ -161,7 +186,10 @@
|
|
|
161
186
|
category: { value: "" },
|
|
162
187
|
summary: { value: ""},
|
|
163
188
|
description: { value: ""},
|
|
164
|
-
icon: { value: "font-awesome/fa-industry" }
|
|
189
|
+
icon: { value: "font-awesome/fa-industry" },
|
|
190
|
+
frt2bakcomm: { value: false },
|
|
191
|
+
bak2frtcomm: { value: false },
|
|
192
|
+
createmanifest: { value: false }
|
|
165
193
|
},
|
|
166
194
|
inputs: 1,
|
|
167
195
|
outputs: 1,
|
|
@@ -300,6 +328,9 @@
|
|
|
300
328
|
delete this.editorDesc;
|
|
301
329
|
},
|
|
302
330
|
|
|
331
|
+
onpaletteremove: function() {
|
|
332
|
+
},
|
|
333
|
+
|
|
303
334
|
oneditresize: function (size) {
|
|
304
335
|
},
|
|
305
336
|
});
|
package/nodes/10-node-factory.js
CHANGED
|
@@ -9,6 +9,13 @@ module.exports = function (RED) {
|
|
|
9
9
|
var streamx = require('streamx');
|
|
10
10
|
var pakoGzip = require('pako');
|
|
11
11
|
|
|
12
|
+
var spcRepDict = {
|
|
13
|
+
"ocb2": "{{",
|
|
14
|
+
"ocb3": "{{{",
|
|
15
|
+
"ccb3": "}}}",
|
|
16
|
+
"ccb2": "}}"
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
function extractTokens(tokens, set) {
|
|
13
20
|
set = set || new Set();
|
|
14
21
|
tokens.forEach(function (token) {
|
|
@@ -42,6 +49,14 @@ module.exports = function (RED) {
|
|
|
42
49
|
return undefined;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
function parseIntern(key) {
|
|
53
|
+
var match = /^__\.(.+)$/.exec(key);
|
|
54
|
+
if (match) {
|
|
55
|
+
return match[1];
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
/**
|
|
46
61
|
* Custom Mustache Context capable to collect message property and node
|
|
47
62
|
* flow and global context
|
|
@@ -72,6 +87,11 @@ module.exports = function (RED) {
|
|
|
72
87
|
return value;
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
var spcRep = parseIntern(name)
|
|
91
|
+
if ( spcRep ) {
|
|
92
|
+
return spcRepDict[spcRep] || ("UNFOUND - " + spcRep);
|
|
93
|
+
}
|
|
94
|
+
|
|
75
95
|
// try env
|
|
76
96
|
if (parseEnv(name)) {
|
|
77
97
|
return this.cachedContextTokens[name];
|
|
@@ -88,6 +108,7 @@ module.exports = function (RED) {
|
|
|
88
108
|
return this.cachedContextTokens[name];
|
|
89
109
|
}
|
|
90
110
|
}
|
|
111
|
+
|
|
91
112
|
return '';
|
|
92
113
|
}
|
|
93
114
|
catch (err) {
|
|
@@ -160,11 +181,7 @@ module.exports = function (RED) {
|
|
|
160
181
|
output: "str",
|
|
161
182
|
x: 100,
|
|
162
183
|
y: 50,
|
|
163
|
-
wires: [
|
|
164
|
-
[
|
|
165
|
-
secondId
|
|
166
|
-
]
|
|
167
|
-
]
|
|
184
|
+
wires: [[secondId]]
|
|
168
185
|
})
|
|
169
186
|
|
|
170
187
|
allNodes.push({
|
|
@@ -178,15 +195,84 @@ module.exports = function (RED) {
|
|
|
178
195
|
output: "str",
|
|
179
196
|
x: 100,
|
|
180
197
|
y: 100,
|
|
181
|
-
wires: [
|
|
182
|
-
[
|
|
183
|
-
]
|
|
184
|
-
]
|
|
198
|
+
wires: [[]]
|
|
185
199
|
})
|
|
186
200
|
|
|
187
201
|
return allNodes;
|
|
188
202
|
}
|
|
189
203
|
|
|
204
|
+
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
|
+
var packageJsonPath = path.join(__dirname, 'templates', 'tmplpackage.json');
|
|
209
|
+
var readmePath = path.join(__dirname, 'templates', 'tmplreadme.md');
|
|
210
|
+
var licensePath = path.join(__dirname, 'templates', 'tmpllicense');
|
|
211
|
+
|
|
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 + "\""
|
|
217
|
+
|
|
218
|
+
var content = {};
|
|
219
|
+
|
|
220
|
+
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 }),
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
return Promise.all(promises).then( () => {
|
|
227
|
+
var secondId = RED.util.generateId();
|
|
228
|
+
var thirdId = RED.util.generateId();
|
|
229
|
+
|
|
230
|
+
nodeDefinitions.push({
|
|
231
|
+
id: RED.util.generateId(),
|
|
232
|
+
type: "PkgFile",
|
|
233
|
+
name: "LICENSE",
|
|
234
|
+
filename: "LICENSE",
|
|
235
|
+
template: content["lcns"],
|
|
236
|
+
syntax: "mustache",
|
|
237
|
+
format: "text",
|
|
238
|
+
output: "str",
|
|
239
|
+
x: 100,
|
|
240
|
+
y: -100,
|
|
241
|
+
wires: [[ secondId ]]
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
nodeDefinitions.push({
|
|
245
|
+
id: secondId,
|
|
246
|
+
type: "PkgFile",
|
|
247
|
+
name: "README.md",
|
|
248
|
+
filename: "README.md",
|
|
249
|
+
template: content["rdme"],
|
|
250
|
+
syntax: "mustache",
|
|
251
|
+
format: "markdown",
|
|
252
|
+
output: "str",
|
|
253
|
+
x: 100,
|
|
254
|
+
y: -50,
|
|
255
|
+
wires: [[thirdId]]
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
nodeDefinitions.push({
|
|
259
|
+
id: thirdId,
|
|
260
|
+
type: "PkgFile",
|
|
261
|
+
name: "package.json",
|
|
262
|
+
filename: "package.json",
|
|
263
|
+
template: content["pkjs"],
|
|
264
|
+
syntax: "mustache",
|
|
265
|
+
format: "json",
|
|
266
|
+
output: "str",
|
|
267
|
+
x: 100,
|
|
268
|
+
y: 0,
|
|
269
|
+
wires: [[nodeDefinitions[0].id]]
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
return nodeDefinitions;
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
190
276
|
function NodeFactoryFunctionality(config) {
|
|
191
277
|
RED.nodes.createNode(this, config);
|
|
192
278
|
|
|
@@ -208,22 +294,47 @@ module.exports = function (RED) {
|
|
|
208
294
|
|
|
209
295
|
handleTemplate(msg, node, jsonTmpl).then(function (data) {
|
|
210
296
|
var jsData = data;
|
|
297
|
+
|
|
211
298
|
handleTemplate(msg, node, htmlTmpl).then(function (data) {
|
|
212
|
-
var
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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)
|
|
224
321
|
})
|
|
225
|
-
|
|
226
|
-
|
|
322
|
+
} else {
|
|
323
|
+
var nodeImpStr = JSON.stringify(nodeDef);
|
|
324
|
+
|
|
325
|
+
send({ payload: nodeImpStr })
|
|
326
|
+
|
|
327
|
+
RED.comms.publish(
|
|
328
|
+
"nodedev:perform-autoimport-nodes",
|
|
329
|
+
RED.util.encodeObject({
|
|
330
|
+
msg: "autoimport",
|
|
331
|
+
payload: nodeImpStr,
|
|
332
|
+
topic: msg.topic,
|
|
333
|
+
nodeid: node.id,
|
|
334
|
+
_msg: msg
|
|
335
|
+
})
|
|
336
|
+
);
|
|
337
|
+
}
|
|
227
338
|
done()
|
|
228
339
|
}).catch((err) => {
|
|
229
340
|
msg.error = err
|
package/nodes/50-npm-publish.js
CHANGED
|
@@ -43,7 +43,7 @@ module.exports = function (RED) {
|
|
|
43
43
|
var userscope = manifest.name.split("/")[0];
|
|
44
44
|
opts[userscope + ":registry"] = "https://registry.npmjs.org"
|
|
45
45
|
|
|
46
|
-
if (cfg.action == "publish") {
|
|
46
|
+
if (msg.npmpublish || cfg.action == "publish") {
|
|
47
47
|
libpub.publish(
|
|
48
48
|
manifest, tarball, opts
|
|
49
49
|
).then((data) => {
|
|
@@ -54,19 +54,19 @@ module.exports = function (RED) {
|
|
|
54
54
|
msg.error = exp;
|
|
55
55
|
done("publish failed", msg)
|
|
56
56
|
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
57
|
+
} else {
|
|
58
|
+
if (msg.npmunpublish || cfg.action == "unpublish") {
|
|
59
|
+
libpub.unpublish(
|
|
60
|
+
manifest.name, opts
|
|
61
|
+
).then((data) => {
|
|
62
|
+
msg.payload = JSON.stringify(data);
|
|
63
|
+
send(msg)
|
|
64
|
+
done()
|
|
65
|
+
}).catch((exp) => {
|
|
66
|
+
msg.error = exp;
|
|
67
|
+
done("unpublish failed", msg)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
70
|
}
|
|
71
71
|
})
|
|
72
72
|
});
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
2
|
(function(){
|
|
3
|
+
|
|
4
|
+
{{ #node.frt2bakcomm }}
|
|
5
|
+
function sendToBackend(node,data = {}) {
|
|
6
|
+
|
|
7
|
+
$.ajax({
|
|
8
|
+
url: "{{ node.name }}/" + node.id,
|
|
9
|
+
type: "POST",
|
|
10
|
+
contentType: "application/json; charset=utf-8",
|
|
11
|
+
|
|
12
|
+
data: JSON.stringify({
|
|
13
|
+
...data,
|
|
14
|
+
hello: "world",
|
|
15
|
+
}),
|
|
16
|
+
|
|
17
|
+
success: function (resp) {
|
|
18
|
+
RED.notify("Successfully sent something to the backend", {
|
|
19
|
+
type: "warning",
|
|
20
|
+
id: "{{node.name}}",
|
|
21
|
+
timeout: 2000
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
error: function (jqXHR, textStatus, errorThrown) {
|
|
26
|
+
if (jqXHR.status == 404) {
|
|
27
|
+
RED.notify("Node has not yet been deployed, please deploy.", "error");
|
|
28
|
+
} else if (jqXHR.status == 405) {
|
|
29
|
+
RED.notify("Not Allowed.", "error");
|
|
30
|
+
} else if (jqXHR.status == 500) {
|
|
31
|
+
RED.notify(node._("common.notification.error", {
|
|
32
|
+
message: node._("inject.errors.failed")
|
|
33
|
+
}), "error");
|
|
34
|
+
} else if (jqXHR.status == 0) {
|
|
35
|
+
RED.notify(node._("common.notification.error", {
|
|
36
|
+
message: node._("common.notification.errors.no-response")
|
|
37
|
+
}), "error");
|
|
38
|
+
} else {
|
|
39
|
+
RED.notify(node._("common.notification.error", {
|
|
40
|
+
message: node._("common.notification.errors.unexpected", {
|
|
41
|
+
status: jqXHR.status, message: textStatus }) }), "error");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
{{ /node.frt2bakcomm }}
|
|
3
47
|
|
|
4
48
|
function frontendSupportFunction() {
|
|
5
49
|
}
|
|
@@ -36,6 +80,18 @@
|
|
|
36
80
|
},
|
|
37
81
|
|
|
38
82
|
onpaletteadd: function() {
|
|
83
|
+
{{#node.bak2frtcomm}}
|
|
84
|
+
this.messageFromBackendHandler = (topic,dataobj) => {
|
|
85
|
+
console.log( "here goes the code for handling a message from the backend", topic, dataobj);
|
|
86
|
+
|
|
87
|
+
RED.notify("Backend sent something to the frontend - check browser console.", {
|
|
88
|
+
type: "success",
|
|
89
|
+
id: "{{node.name}}",
|
|
90
|
+
timeout: 2000
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
RED.comms.subscribe('{{node.name}}:message-from-backend',this.messageFromBackendHandler);
|
|
94
|
+
{{/node.bak2frtcomm}}
|
|
39
95
|
},
|
|
40
96
|
|
|
41
97
|
oneditprepare: function() {
|
|
@@ -47,6 +103,13 @@
|
|
|
47
103
|
oneditsave: function() {
|
|
48
104
|
},
|
|
49
105
|
|
|
106
|
+
onpaletteremove: function() {
|
|
107
|
+
{{#node.bak2frtcomm}}
|
|
108
|
+
RED.comms.unsubscribe('{{node.name}}:message-from-backend',this.messageFromBackendHandler);
|
|
109
|
+
{{/node.bak2frtcomm}}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
|
|
50
113
|
{{#node.hasbutton}}
|
|
51
114
|
button: {
|
|
52
115
|
enabled: function() {
|
|
@@ -59,6 +122,13 @@
|
|
|
59
122
|
message: RED._("notification.warnings.undeployedChanges")
|
|
60
123
|
}), "warning");
|
|
61
124
|
}
|
|
125
|
+
|
|
126
|
+
{{ #node.frt2bakcomm }}
|
|
127
|
+
var that = this;
|
|
128
|
+
sendToBackend(that, {"payload": "button pressed on frontend"})
|
|
129
|
+
{{ /node.frt2bakcomm }}
|
|
130
|
+
|
|
131
|
+
/* here goes the button code to be executed on click */
|
|
62
132
|
}
|
|
63
133
|
},
|
|
64
134
|
{{/node.hasbutton}}
|
package/nodes/templates/tmpl.js
CHANGED
|
@@ -9,10 +9,51 @@ module.exports = function(RED) {
|
|
|
9
9
|
node.status({});
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
/* msg handler, in this case pass the message on unchanged */
|
|
12
13
|
node.on("input", function(msg, send, done) {
|
|
14
|
+
{{#node.bak2frtcomm}}
|
|
15
|
+
RED.comms.publish("{{node.name}}:message-from-backend",
|
|
16
|
+
RED.util.encodeObject({
|
|
17
|
+
...msg,
|
|
18
|
+
"data": "message from backend",
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
{{/node.bak2frtcomm}}
|
|
13
22
|
send(msg);
|
|
14
23
|
done();
|
|
15
24
|
});
|
|
16
25
|
}
|
|
26
|
+
|
|
17
27
|
RED.nodes.registerType("{{ node.name }}", {{ node.name }}Functionality);
|
|
28
|
+
|
|
29
|
+
{{ #node.frt2bakcomm }}
|
|
30
|
+
RED.httpAdmin.post("/{{ node.name }}/:id",
|
|
31
|
+
RED.auth.needsPermission("{{ node.name }}.write"),
|
|
32
|
+
(req, res) => {
|
|
33
|
+
var node = RED.nodes.getNode(req.params.id);
|
|
34
|
+
if (node != null) {
|
|
35
|
+
try {
|
|
36
|
+
if (req.body && node.type == "{{ node.name }}") {
|
|
37
|
+
/* here goes the code for handling a request from the frontend */
|
|
38
|
+
|
|
39
|
+
/* this sends the request to the input handler above */
|
|
40
|
+
node.receive(req.body);
|
|
41
|
+
|
|
42
|
+
/* this tells the frontend that all went well */
|
|
43
|
+
res.status(200).send({
|
|
44
|
+
"status": "ok"
|
|
45
|
+
})
|
|
46
|
+
} else {
|
|
47
|
+
res.sendStatus(404);
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(err);
|
|
51
|
+
res.status(500).send(err.toString());
|
|
52
|
+
node.error("{{ node.name }}: Submission failed: " + err.toString())
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
res.sendStatus(404);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
{{/node.frt2bakcomm}}
|
|
18
59
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name" : "{{__.ocb3}} pname {{__.ccb3}}",
|
|
3
|
+
"version": "{{__.ocb2}} pversion {{__.ccb2}}",
|
|
4
|
+
"dependencies": {
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"keywords": [
|
|
8
|
+
"node-red"
|
|
9
|
+
],
|
|
10
|
+
|
|
11
|
+
"homepage": "https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}#readme",
|
|
12
|
+
"license": "Don't do evil.",
|
|
13
|
+
"author": "Joe Blog <joe.blog@spreads-the.love>",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=14"
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"node-red" : {
|
|
19
|
+
"version": ">=3.0.0",
|
|
20
|
+
"nodes": {
|
|
21
|
+
{{{__.nodestanza}}}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"description": "Description of what these nodes do.",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "github",
|
|
28
|
+
"url": "git+https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/{{__.ocb2}} githubowner {{__.ccb2}}/{{__.ocb2}} githubrepo {{__.ccb2}}"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name" : "@gregoriusrippenstein/node-red-contrib-nodedev",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
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",
|
|
16
|
+
"homepage": "https://github.com/gorenje/node-red-contrib-nodedev#README.md",
|
|
17
17
|
"license": "Don't do evil.",
|
|
18
18
|
"author": "Gerrit Riessen <does.anyone@spread-the.love> (https://spread-the.love)",
|
|
19
19
|
"engines": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"node-red" : {
|
|
24
24
|
"version": ">=3.0.0",
|
|
25
25
|
"nodes": {
|
|
26
|
+
"nodedevops": "nodes/05-node-dev-ops.js",
|
|
26
27
|
"nodefactory": "nodes/10-node-factory.js",
|
|
27
28
|
"pkgfile": "nodes/20-pkg-file.js",
|
|
28
29
|
"npmtarball": "nodes/30-npm-tarball.js",
|