@node-red/editor-api 4.1.5 → 4.1.7

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.
@@ -53,10 +53,10 @@ module.exports = {
53
53
  var opts = {
54
54
  user: req.user
55
55
  }
56
- runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
56
+ runtimeAPI.settings.getRuntimeSettings(opts).then(async function(result) {
57
57
  if (!settings.disableEditor) {
58
58
  result.editorTheme = result.editorTheme||{};
59
- var themeSettings = theme.settings();
59
+ const themeSettings = await theme.settings();
60
60
  if (themeSettings) {
61
61
  // result.editorTheme may already exist with the palette
62
62
  // disabled. Need to merge that into the receive settings
@@ -42,7 +42,13 @@ var defaultContext = {
42
42
  var settings;
43
43
 
44
44
  var theme = null;
45
+ /**
46
+ * themeContext is an object passed to the mustache template to generate the editor index.html.
47
+ */
45
48
  var themeContext = clone(defaultContext);
49
+ /**
50
+ * themeSettings is an object passed to the editor client as the "editorTheme" property of the settings object
51
+ */
46
52
  var themeSettings = null;
47
53
 
48
54
  var activeTheme = null;
@@ -91,6 +97,123 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
91
97
  return result
92
98
  }
93
99
 
100
+ /**
101
+ * Check if a theme is enabled and load its settings.
102
+ * This is done lazily as it has to happen after the plugins have been loaded, but before the editor is served.
103
+ */
104
+ async function loadThemePlugin () {
105
+ if (activeTheme && !activeThemeInitialised) {
106
+ const themePlugin = await runtimeAPI.plugins.getPlugin({
107
+ id:activeTheme
108
+ });
109
+ if (themePlugin) {
110
+ if (themePlugin.css) {
111
+ const cssFiles = serveFilesFromTheme(
112
+ themePlugin.css,
113
+ themeApp,
114
+ "/css/",
115
+ themePlugin.path
116
+ );
117
+ themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
118
+ // Mutating `theme` is not ideal, but currently necessary as debug (packages/node_modules/@node-red/nodes/core/common/21-debug.js)
119
+ // accesses RED.settings.editorTheme.page._.css directly to apply theme to the debug pop-out window.
120
+ theme.page = theme.page || {_:{}}
121
+ theme.page._.css = cssFiles.concat(theme.page._.css || [])
122
+ }
123
+ if (themePlugin.scripts) {
124
+ const scriptFiles = serveFilesFromTheme(
125
+ themePlugin.scripts,
126
+ themeApp,
127
+ "/scripts/",
128
+ themePlugin.path
129
+ )
130
+ themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
131
+ theme.page = theme.page || {_:{}}
132
+ theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || [])
133
+ }
134
+ // check and load page settings from theme
135
+ if (themePlugin.page) {
136
+ if (themePlugin.page.favicon && !theme.page.favicon) {
137
+ const result = serveFilesFromTheme(
138
+ [themePlugin.page.favicon],
139
+ themeApp,
140
+ "/",
141
+ themePlugin.path
142
+ )
143
+ if(result && result.length > 0) {
144
+ // update themeContext page favicon
145
+ themeContext.page.favicon = result[0]
146
+ }
147
+ }
148
+ if (themePlugin.page.tabicon && themePlugin.page.tabicon.icon && !theme.page.tabicon) {
149
+ const result = serveFilesFromTheme(
150
+ [themePlugin.page.tabicon.icon],
151
+ themeApp,
152
+ "/page/",
153
+ themePlugin.path
154
+ )
155
+ if(result && result.length > 0) {
156
+ // update themeContext page tabicon
157
+ themeContext.page.tabicon.icon = result[0]
158
+ themeContext.page.tabicon.colour = themeContext.page.tabicon.colour || themeContext.page.tabicon.colour
159
+ }
160
+ }
161
+ // if the plugin has a title AND the users settings.js does NOT
162
+ if (themePlugin.page.title && !theme.page.title) {
163
+ themeContext.page.title = themePlugin.page.title || themeContext.page.title
164
+ }
165
+ }
166
+ // check and load header settings from theme
167
+ if (themePlugin.header) {
168
+ if (themePlugin.header.image && !theme.header.image) {
169
+ const result = serveFilesFromTheme(
170
+ [themePlugin.header.image],
171
+ themeApp,
172
+ "/header/",
173
+ themePlugin.path
174
+ )
175
+ if(result && result.length > 0) {
176
+ // update themeContext header image
177
+ themeContext.header.image = result[0]
178
+ }
179
+ }
180
+ // if the plugin has a title AND the users settings.js does NOT have a title
181
+ if (themePlugin.header.title && !theme.header.title) {
182
+ themeContext.header.title = themePlugin.header.title || themeContext.header.title
183
+ }
184
+ // if the plugin has a header url AND the users settings.js does NOT
185
+ if (themePlugin.header.url && !theme.header.url) {
186
+ themeContext.header.url = themePlugin.header.url || themeContext.header.url
187
+ }
188
+ }
189
+
190
+ if (Array.isArray(themePlugin.palette?.theme)) {
191
+ themeSettings.palette = themeSettings.palette || {};
192
+ themeSettings.palette.theme = themePlugin.palette.theme;
193
+ // The theme is providing its own palette theme. It *might* include icons that need namespacing
194
+ // to the theme plugin module.
195
+ themePlugin.palette.theme.forEach(themeRule => {
196
+ if (themeRule.icon && themeRule.icon.indexOf("/") === -1) {
197
+ themeRule.icon = `${themePlugin.module}/${themeRule.icon}`;
198
+ }
199
+ })
200
+ }
201
+ if (Array.isArray(themePlugin.palette?.categories)) {
202
+ themeSettings.palette = themeSettings.palette || {};
203
+ themeSettings.palette.categories = themePlugin.palette.categories;
204
+ }
205
+
206
+ // These settings are not exposed under `editorTheme`, so we don't have a merge strategy for them
207
+ // If they're defined in the theme plugin, they replace any settings.js values.
208
+ // But, this direct manipulation of `theme` is not ideal and relies on mutating a passed-in object
209
+ theme.codeEditor = theme.codeEditor || {}
210
+ theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
211
+ theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
212
+ }
213
+ activeThemeInitialised = true;
214
+ }
215
+ }
216
+
94
217
  module.exports = {
95
218
  init: function(_settings, _runtimeAPI) {
96
219
  settings = _settings;
@@ -232,6 +355,7 @@ module.exports = {
232
355
  res.json(themeContext);
233
356
  })
234
357
 
358
+ // Copy the settings that need passing to the editor into themeSettings.
235
359
  if (theme.hasOwnProperty("menu")) {
236
360
  themeSettings.menu = theme.menu;
237
361
  }
@@ -263,104 +387,11 @@ module.exports = {
263
387
  return themeApp;
264
388
  },
265
389
  context: async function() {
266
- if (activeTheme && !activeThemeInitialised) {
267
- const themePlugin = await runtimeAPI.plugins.getPlugin({
268
- id:activeTheme
269
- });
270
- if (themePlugin) {
271
- if (themePlugin.css) {
272
- const cssFiles = serveFilesFromTheme(
273
- themePlugin.css,
274
- themeApp,
275
- "/css/",
276
- themePlugin.path
277
- );
278
- themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
279
- theme.page = theme.page || {_:{}}
280
- theme.page._.css = cssFiles.concat(theme.page._.css || [])
281
- }
282
- if (themePlugin.scripts) {
283
- const scriptFiles = serveFilesFromTheme(
284
- themePlugin.scripts,
285
- themeApp,
286
- "/scripts/",
287
- themePlugin.path
288
- )
289
- themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
290
- theme.page = theme.page || {_:{}}
291
- theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || [])
292
- }
293
- // check and load page settings from theme
294
- if (themePlugin.page) {
295
- if (themePlugin.page.favicon && !theme.page.favicon) {
296
- const result = serveFilesFromTheme(
297
- [themePlugin.page.favicon],
298
- themeApp,
299
- "/",
300
- themePlugin.path
301
- )
302
- if(result && result.length > 0) {
303
- // update themeContext page favicon
304
- themeContext.page.favicon = result[0]
305
- theme.page = theme.page || {_:{}}
306
- theme.page._.favicon = result[0]
307
- }
308
- }
309
- if (themePlugin.page.tabicon && themePlugin.page.tabicon.icon && !theme.page.tabicon) {
310
- const result = serveFilesFromTheme(
311
- [themePlugin.page.tabicon.icon],
312
- themeApp,
313
- "/page/",
314
- themePlugin.path
315
- )
316
- if(result && result.length > 0) {
317
- // update themeContext page tabicon
318
- themeContext.page.tabicon.icon = result[0]
319
- themeContext.page.tabicon.colour = themeContext.page.tabicon.colour || themeContext.page.tabicon.colour
320
- theme.page = theme.page || {_:{}}
321
- theme.page._.tabicon = theme.page._.tabicon || {}
322
- theme.page._.tabicon.icon = themeContext.page.tabicon.icon
323
- theme.page._.tabicon.colour = themeContext.page.tabicon.colour
324
- }
325
- }
326
- // if the plugin has a title AND the users settings.js does NOT
327
- if (themePlugin.page.title && !theme.page.title) {
328
- themeContext.page.title = themePlugin.page.title || themeContext.page.title
329
- }
330
- }
331
- // check and load header settings from theme
332
- if (themePlugin.header) {
333
- if (themePlugin.header.image && !theme.header.image) {
334
- const result = serveFilesFromTheme(
335
- [themePlugin.header.image],
336
- themeApp,
337
- "/header/",
338
- themePlugin.path
339
- )
340
- if(result && result.length > 0) {
341
- // update themeContext header image
342
- themeContext.header.image = result[0]
343
- }
344
- }
345
- // if the plugin has a title AND the users settings.js does NOT have a title
346
- if (themePlugin.header.title && !theme.header.title) {
347
- themeContext.header.title = themePlugin.header.title || themeContext.header.title
348
- }
349
- // if the plugin has a header url AND the users settings.js does NOT
350
- if (themePlugin.header.url && !theme.header.url) {
351
- themeContext.header.url = themePlugin.header.url || themeContext.header.url
352
- }
353
- }
354
- theme.codeEditor = theme.codeEditor || {}
355
- theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
356
-
357
- theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
358
- }
359
- activeThemeInitialised = true;
360
- }
390
+ await loadThemePlugin();
361
391
  return themeContext;
362
392
  },
363
- settings: function() {
393
+ settings: async function() {
394
+ await loadThemePlugin();
364
395
  return themeSettings;
365
396
  },
366
397
  serveFile: function(baseUrl,file) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-red/editor-api",
3
- "version": "4.1.5",
3
+ "version": "4.1.7",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./lib/index.js",
6
6
  "repository": {
@@ -16,9 +16,9 @@
16
16
  }
17
17
  ],
18
18
  "dependencies": {
19
- "@node-red/util": "4.1.5",
20
- "@node-red/editor-client": "4.1.5",
21
- "bcryptjs": "3.0.2",
19
+ "@node-red/util": "4.1.7",
20
+ "@node-red/editor-client": "4.1.7",
21
+ "bcryptjs": "3.0.3",
22
22
  "body-parser": "1.20.4",
23
23
  "clone": "2.1.2",
24
24
  "cors": "2.8.5",
@@ -26,7 +26,7 @@
26
26
  "express": "4.22.1",
27
27
  "memorystore": "1.6.7",
28
28
  "mime": "3.0.0",
29
- "multer": "2.0.2",
29
+ "multer": "2.1.1",
30
30
  "mustache": "4.2.0",
31
31
  "oauth2orize": "1.12.0",
32
32
  "passport-http-bearer": "1.0.1",