@theotherwillembotha/node-red-plugincore 0.1.0 → 0.3.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.
Files changed (167) hide show
  1. package/README.md +616 -220
  2. package/build/GenerateNodes.js +9 -33
  3. package/build/Nodes.html +2 -1505
  4. package/build/Nodes.js +5 -19
  5. package/build/Plugins.html +11 -0
  6. package/build/Plugins.js +17 -12
  7. package/build/core/NodeConstructor.d.ts +12 -16
  8. package/build/core/NodeConstructor.d.ts.map +1 -1
  9. package/build/core/NodeConstructor.js +125 -185
  10. package/build/core/NodeGenerator.d.ts +3 -1
  11. package/build/core/NodeGenerator.d.ts.map +1 -1
  12. package/build/core/NodeGenerator.js +139 -43
  13. package/build/core/NodeManagerRuntime.d.ts +44 -0
  14. package/build/core/NodeManagerRuntime.d.ts.map +1 -0
  15. package/build/core/NodeManagerRuntime.js +140 -0
  16. package/build/core/Utils.d.ts.map +1 -1
  17. package/build/core/logger/service/LoggerService.d.ts +8 -6
  18. package/build/core/logger/service/LoggerService.d.ts.map +1 -1
  19. package/build/core/logger/service/LoggerService.js +101 -23
  20. package/build/core/logger/template/LoggerTemplate.d.ts +1 -2
  21. package/build/core/logger/template/LoggerTemplate.d.ts.map +1 -1
  22. package/build/core/logger/template/LoggerTemplate.js +18 -5
  23. package/build/core/metrics/MetricsDecorator.d.ts +1 -2
  24. package/build/core/metrics/MetricsDecorator.d.ts.map +1 -1
  25. package/build/core/metrics/MetricsDecorator.js +89 -116
  26. package/build/core/metrics/node/CounterMetricConfigNode.d.ts.map +1 -1
  27. package/build/core/metrics/node/CounterMetricConfigNode.js +4 -5
  28. package/build/core/metrics/node/GaugeMetricConfigNode.d.ts.map +1 -1
  29. package/build/core/metrics/node/GaugeMetricConfigNode.js +5 -6
  30. package/build/core/metrics/node/MetricsConfigNode.d.ts +19 -9
  31. package/build/core/metrics/node/MetricsConfigNode.d.ts.map +1 -1
  32. package/build/core/metrics/node/MetricsConfigNode.js +14 -45
  33. package/build/core/metrics/node/TimerMetricConfigNode.d.ts.map +1 -1
  34. package/build/core/metrics/node/TimerMetricConfigNode.js +11 -17
  35. package/build/core/metrics/service/MetricsService.d.ts +110 -114
  36. package/build/core/metrics/service/MetricsService.d.ts.map +1 -1
  37. package/build/core/metrics/service/MetricsService.js +122 -339
  38. package/build/core/metrics/template/CounterMetricTemplate.d.ts +1 -2
  39. package/build/core/metrics/template/CounterMetricTemplate.d.ts.map +1 -1
  40. package/build/core/metrics/template/CounterMetricTemplate.js +16 -5
  41. package/build/core/metrics/template/GaugeMetricTemplate.d.ts +1 -2
  42. package/build/core/metrics/template/GaugeMetricTemplate.d.ts.map +1 -1
  43. package/build/core/metrics/template/GaugeMetricTemplate.js +16 -5
  44. package/build/core/metrics/template/MetricsTemplate.d.ts +1 -2
  45. package/build/core/metrics/template/MetricsTemplate.d.ts.map +1 -1
  46. package/build/core/metrics/template/MetricsTemplate.js +18 -6
  47. package/build/core/metrics/template/TimerMetricTemplate.d.ts +1 -2
  48. package/build/core/metrics/template/TimerMetricTemplate.d.ts.map +1 -1
  49. package/build/core/metrics/template/TimerMetricTemplate.js +16 -5
  50. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts +1 -1
  51. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts.map +1 -1
  52. package/build/core/other/node/DelegatedConfigReferenceNode.js +1 -1
  53. package/build/core/other/service/InputService.d.ts.map +1 -1
  54. package/build/core/other/service/SettingsService.d.ts +1 -2
  55. package/build/core/other/service/SettingsService.d.ts.map +1 -1
  56. package/build/core/other/service/SettingsService.js +19 -5
  57. package/build/core/other/template/BasicTemplate.d.ts +1 -2
  58. package/build/core/other/template/BasicTemplate.d.ts.map +1 -1
  59. package/build/core/other/template/BasicTemplate.js +15 -5
  60. package/build/core/other/template/SettingsTemplate.d.ts +1 -2
  61. package/build/core/other/template/SettingsTemplate.d.ts.map +1 -1
  62. package/build/core/other/template/SettingsTemplate.js +17 -5
  63. package/build/core/state/node/InternalStateConfigNode.d.ts +16 -0
  64. package/build/core/state/node/InternalStateConfigNode.d.ts.map +1 -0
  65. package/build/core/state/node/InternalStateConfigNode.js +89 -0
  66. package/build/core/state/node/StateConfigNode.d.ts +24 -0
  67. package/build/core/state/node/StateConfigNode.d.ts.map +1 -0
  68. package/build/core/state/node/StateConfigNode.js +22 -0
  69. package/build/core/state/service/StateService.d.ts +34 -0
  70. package/build/core/state/service/StateService.d.ts.map +1 -0
  71. package/build/core/state/service/StateService.js +201 -0
  72. package/build/core/state/template/StateTemplate.d.ts +7 -0
  73. package/build/core/state/template/StateTemplate.d.ts.map +1 -0
  74. package/build/core/state/template/StateTemplate.js +25 -0
  75. package/build/core/tagging/ServiceDescriptionDecorator.d.ts +10 -0
  76. package/build/core/tagging/ServiceDescriptionDecorator.d.ts.map +1 -0
  77. package/build/core/tagging/ServiceDescriptionDecorator.js +10 -0
  78. package/build/core/tagging/TemplateDescriptionDecorator.d.ts +8 -0
  79. package/build/core/tagging/TemplateDescriptionDecorator.d.ts.map +1 -0
  80. package/build/core/tagging/TemplateDescriptionDecorator.js +10 -0
  81. package/build/core/tagging/service/NodeTypeService.d.ts +1 -2
  82. package/build/core/tagging/service/NodeTypeService.d.ts.map +1 -1
  83. package/build/core/tagging/service/NodeTypeService.js +19 -5
  84. package/build/core/ui/template/ScriptEditorTemplate.d.ts +1 -2
  85. package/build/core/ui/template/ScriptEditorTemplate.d.ts.map +1 -1
  86. package/build/core/ui/template/ScriptEditorTemplate.js +15 -5
  87. package/build/core/ui/template/UIHelperTemplate.d.ts +1 -2
  88. package/build/core/ui/template/UIHelperTemplate.d.ts.map +1 -1
  89. package/build/core/ui/template/UIHelperTemplate.js +15 -5
  90. package/build/core/webhook/node/WebhookServerConfigNode.d.ts.map +1 -1
  91. package/build/core/webhook/service/ReverseProxyTypeService.d.ts.map +1 -1
  92. package/build/core/webhook/service/WebhookServerService.d.ts +3 -4
  93. package/build/core/webhook/service/WebhookServerService.d.ts.map +1 -1
  94. package/build/core/webhook/service/WebhookServerService.js +48 -18
  95. package/build/core/webhook/template/WebhookTemplate.d.ts +1 -2
  96. package/build/core/webhook/template/WebhookTemplate.d.ts.map +1 -1
  97. package/build/core/webhook/template/WebhookTemplate.js +19 -6
  98. package/build/index.d.ts +6 -2
  99. package/build/index.d.ts.map +1 -1
  100. package/build/index.js +7 -2
  101. package/build/runtime/NodeManagerRuntime.js +140 -0
  102. package/documentation/CounterMetricConfigNode.png +0 -0
  103. package/documentation/GaugeMetricConfigNode.png +0 -0
  104. package/documentation/LoggerTemplate.png +0 -0
  105. package/documentation/MetricsTemplate.png +0 -0
  106. package/documentation/StateTemplate.png +0 -0
  107. package/documentation/TimerMetricConfigNode_histogram.png +0 -0
  108. package/documentation/TimerMetricConfigNode_summary.png +0 -0
  109. package/documentation/WebhookServerConfigNode.png +0 -0
  110. package/documentation/WebhookTemplate.png +0 -0
  111. package/package.json +4 -11
  112. package/src/GenerateNodes.ts +10 -40
  113. package/src/core/NodeConstructor.ts +150 -228
  114. package/src/core/NodeGenerator.ts +156 -47
  115. package/src/core/NodeManagerRuntime.ts +179 -0
  116. package/src/core/logger/service/LoggerService.ts +60 -32
  117. package/src/core/logger/template/LoggerTemplate.html +11 -6
  118. package/src/core/logger/template/LoggerTemplate.ts +9 -11
  119. package/src/core/metrics/MetricsDecorator.ts +140 -168
  120. package/src/core/metrics/node/CounterMetricConfigNode.ts +20 -20
  121. package/src/core/metrics/node/GaugeMetricConfigNode.ts +24 -25
  122. package/src/core/metrics/node/MetricsConfigNode.ts +29 -48
  123. package/src/core/metrics/node/TimerMetricConfigNode.ts +50 -59
  124. package/src/core/metrics/service/MetricsService.ts +214 -489
  125. package/src/core/metrics/template/CounterMetricTemplate.html +0 -1
  126. package/src/core/metrics/template/CounterMetricTemplate.ts +8 -11
  127. package/src/core/metrics/template/GaugeMetricTemplate.html +1 -1
  128. package/src/core/metrics/template/GaugeMetricTemplate.ts +8 -11
  129. package/src/core/metrics/template/MetricsTemplate.html +132 -13
  130. package/src/core/metrics/template/MetricsTemplate.ts +9 -10
  131. package/src/core/metrics/template/TimerMetricTemplate.ts +8 -10
  132. package/src/core/other/node/DelegatedConfigReferenceNode.ts +1 -1
  133. package/src/core/other/service/SettingsService.ts +6 -10
  134. package/src/core/other/template/BasicTemplate.html +1 -1
  135. package/src/core/other/template/BasicTemplate.ts +6 -11
  136. package/src/core/other/template/SettingsTemplate.ts +8 -13
  137. package/src/core/state/node/InternalStateConfigNode.html +77 -0
  138. package/src/core/state/node/InternalStateConfigNode.ts +102 -0
  139. package/src/core/state/node/StateConfigNode.ts +29 -0
  140. package/src/core/state/service/StateService.ts +180 -0
  141. package/src/core/state/template/StateTemplate.html +244 -0
  142. package/src/core/state/template/StateTemplate.ts +18 -0
  143. package/src/core/tagging/ServiceDescriptionDecorator.ts +23 -0
  144. package/src/core/tagging/TemplateDescriptionDecorator.ts +19 -0
  145. package/src/core/tagging/service/NodeTypeService.ts +6 -11
  146. package/src/core/ui/template/ScriptEditorTemplate.ts +6 -10
  147. package/src/core/ui/template/UIHelperTemplate.html +41 -26
  148. package/src/core/ui/template/UIHelperTemplate.ts +6 -10
  149. package/src/core/webhook/service/WebhookServerService.ts +39 -23
  150. package/src/core/webhook/template/WebhookTemplate.html +28 -35
  151. package/src/core/webhook/template/WebhookTemplate.ts +10 -12
  152. package/src/index.ts +8 -2
  153. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts +0 -20
  154. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts.map +0 -1
  155. package/build/core/logger/node/ConsoleLoggerConfigNode.js +0 -62
  156. package/build/core/logger/node/RestLoggerConfigNode.d.ts +0 -40
  157. package/build/core/logger/node/RestLoggerConfigNode.d.ts.map +0 -1
  158. package/build/core/logger/node/RestLoggerConfigNode.js +0 -128
  159. package/documentation/ConsoleLoggerConfigNode.png +0 -0
  160. package/documentation/MetricsConfigNode.png +0 -0
  161. package/documentation/RestLoggerConfigNode.png +0 -0
  162. package/documentation/TimerMetricConfigNode.png +0 -0
  163. package/documentation/WebhookNode.png +0 -0
  164. package/src/core/logger/node/ConsoleLoggerConfigNode.html +0 -90
  165. package/src/core/logger/node/ConsoleLoggerConfigNode.ts +0 -71
  166. package/src/core/logger/node/RestLoggerConfigNode.html +0 -183
  167. package/src/core/logger/node/RestLoggerConfigNode.ts +0 -170
@@ -1,22 +1,25 @@
1
- import { PluginDefinition } from "@node-red/registry";
2
- import { Node, NodeAPI, NodeDef, NodeAPISettingsWithData} from "node-red";
1
+ import { Node, NodeAPI, NodeAPISettingsWithData} from "node-red";
3
2
  import { INamedType, INodeClass, ITemplateClass } from "./NodeGenerator";
4
3
 
5
4
  import "reflect-metadata";
6
- import jsdom from "jsdom";
7
5
  import fs from 'fs';
8
- import beautify from 'js-beautify';
9
- import markdownit from 'markdown-it'
10
- import Handlebars, { template } from "handlebars";
11
- import { NodeTypeService } from "./tagging/service/NodeTypeService";
12
6
 
7
+ import {
8
+ NODEMANAGER_API_VERSION,
9
+ POST_CONSTRUCT_KEY,
10
+ BaseNodeConfig,
11
+ runPostConstructInitializers,
12
+ NodeManager
13
+ } from "./NodeManagerRuntime";
13
14
 
14
-
15
- export interface BaseNodeConfig {
16
- id:string;
17
- name:string;
18
- type:string;
19
- }
15
+ // Re-export so existing downstream code keeps working unchanged.
16
+ export {
17
+ NODEMANAGER_API_VERSION,
18
+ POST_CONSTRUCT_KEY,
19
+ BaseNodeConfig,
20
+ runPostConstructInitializers,
21
+ NodeManager
22
+ };
20
23
 
21
24
  export abstract class BaseNode<Config extends BaseNodeConfig> {
22
25
 
@@ -198,8 +201,6 @@ export abstract class Template{
198
201
 
199
202
  type VoidMethod = (...args: any[]) => void | Promise<void>;
200
203
 
201
- const POST_CONSTRUCT_KEY = Symbol('postConstructInitializers');
202
-
203
204
  // Builder class for post-construct decorators
204
205
  class PostConstructDecoratorBuilder<TMetadata = any> {
205
206
  private decoratorName: string;
@@ -341,143 +342,6 @@ export function createPostConstructDecorator<TMetadata = any>(decoratorName: str
341
342
  return new PostConstructDecoratorBuilder<TMetadata>(decoratorName);
342
343
  }
343
344
 
344
- // Single function to run all post-construct initializers
345
- let runPostConstructInitializers = function(instance: any) {
346
- const initializers = instance[POST_CONSTRUCT_KEY];
347
- if (initializers && initializers.length > 0) {
348
- const className = instance.constructor.name;
349
- //console.log(`Running ${initializers.length} post-construct initializers for ${className}`);
350
-
351
- initializers.forEach((init: Function, index: number) => {
352
- try {
353
- init(instance);
354
- } catch (error) {
355
- console.error(
356
- `Post-construct initialization failed:\n` +
357
- ` Class: ${className}\n` +
358
- ` Initializer: ${index + 1}/${initializers.length}\n` +
359
- ` Error: ${error instanceof Error ? error.message : String(error)}`
360
- );
361
- throw error;
362
- }
363
- });
364
-
365
- // Clear the initializers after running
366
- delete instance[POST_CONSTRUCT_KEY];
367
- }
368
- }
369
-
370
- // ************************************************
371
-
372
- export class NodeManager{
373
- private static PLUGINID = "node-red-contrib-theotherwillembotha/services-plugin";
374
- private static _RED: NodeAPI<NodeAPISettingsWithData>;
375
-
376
- private nodeTypeService:NodeTypeService;
377
- private typeBacklog:INodeClass[] = []
378
-
379
- public constructor(RED : NodeAPI<NodeAPISettingsWithData>){
380
- NodeManager._RED = RED;
381
-
382
- let nodeTypeServiceListener = async (pluginID:String) => {
383
- if(pluginID === "@theotherwillembotha/nodetypeservice"){
384
- // disable the listener- the plugin has been installed.
385
- RED.events.off('plugin.instantiated', nodeTypeServiceListener)
386
-
387
- // get a handle on the NodeTypeService
388
- let plugin = RED.plugins.get("@theotherwillembotha/nodetypeservice") as any;
389
- this.nodeTypeService = (RED.plugins.get("@theotherwillembotha/nodetypeservice") as any).instance as NodeTypeService;
390
-
391
- // process the backlog.
392
- for(let nodeType of this.typeBacklog){
393
- for(let tag of nodeType.getNodeDescriptor().tags()){
394
- this.nodeTypeService.registerNodeType(tag, nodeType);
395
- }
396
- }
397
- }
398
- }
399
-
400
- // check if the reverserproxyttypesewrvice is installed yet.
401
- this.nodeTypeService = (RED.plugins.get("@theotherwillembotha/nodetypeservice") as any).instance as NodeTypeService;
402
- if(!this.nodeTypeService) {
403
- RED.events.on('plugin.instantiated', nodeTypeServiceListener);
404
- }
405
- }
406
-
407
- public static get RED() {
408
- return NodeManager._RED;
409
- }
410
-
411
- public registerService(type:IServiceClass):NodeManager{
412
- let servicePlugin:any = NodeManager.RED.plugins.get(NodeManager.PLUGINID) as PluginDefinition<any>;
413
- if(servicePlugin){
414
- servicePlugin.instance.install(NodeManager.RED, type);
415
- }
416
- else{
417
- let serviceName = `${type.getServiceDescriptor().sourceFile()}.${type.getServiceDescriptor().name()}`;
418
- console.log(`ERROR: plugin ${NodeManager.PLUGINID} does not seem to be loaded. Cannot start service: ${serviceName}`);
419
- }
420
- return this;
421
- }
422
-
423
- public registerNodeType(typeName: string, type: INodeClass):NodeManager{
424
- if(!type){
425
- console.error(`Type "${typeName}" has no associated type. Make sure that it was exported and included in the index.js file!`)
426
- }
427
-
428
- let nodeConstructor = function(this: any, config: BaseNodeConfig | NodeDef){
429
- //console.log("constructing node", typeName);
430
-
431
- try{
432
- // instantiate the node.
433
- NodeManager.RED.nodes.createNode(this, config as any as NodeDef);
434
- // assign an instance of it to the config.
435
- //(config as any).__node = this;
436
-
437
- // instatiate the actual node.
438
- let node = new (type as any)(this, config);
439
- //this.__node = node;
440
-
441
- // run the constructor initializers.
442
- runPostConstructInitializers(node);
443
-
444
-
445
- // after the node has been instantiated, attempt to invole the onInit method.
446
- if(node.onInit){
447
- node.onInit();
448
- }
449
- }
450
- catch(error){
451
- console.log(error);
452
- console.log("typeName:", typeName)
453
- console.log("type:", type)
454
- console.log("config:", config)
455
- }
456
- }
457
-
458
- try{
459
- NodeManager.RED.nodes.registerType(typeName, nodeConstructor, { settings: {}});
460
-
461
- let nodeDescription = type.getNodeDescriptor();
462
-
463
- // registger the node tags.
464
- if(this.nodeTypeService){
465
- for(let tag of nodeDescription.tags()){
466
- this.nodeTypeService.registerNodeType(tag, type);
467
- }
468
- }
469
- else{
470
- this.typeBacklog.push(type);
471
- }
472
-
473
- }
474
- catch(error){
475
- console.error("registering node: " + typeName + " failed", error);
476
- }
477
- return this;
478
- }
479
- }
480
-
481
345
  export type FlowDeployment = {
482
346
  rev:string,
483
347
  flows:[FlowElement]
@@ -513,25 +377,27 @@ export abstract class BaseService {
513
377
  }
514
378
  }
515
379
 
516
- const { JSDOM } = jsdom;
517
- const md = markdownit({
518
- html: true,
519
- linkify: true,
520
- typographer: true
521
- })
522
-
523
- // Custom renderer for headings
524
- md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
525
- const token = tokens[idx];
526
- const level = token.tag.substring(1); // 'h1', 'h2', etc. -> '1', '2'
527
- return `<h${level} class="custom-heading">`;
528
- };
529
-
530
- md.renderer.rules.heading_close = function (tokens, idx, options, env, self) {
531
- const token = tokens[idx];
532
- const level = token.tag.substring(1);
533
- return `</h${level}>`;
534
- };
380
+ // Build-time deps - lazy loaded so they are never required at runtime in production bundles.
381
+ // These are only invoked during `npm run build` (GenerateNodes), never in the deployed container.
382
+ function getJSDOM(): any { return require('jsdom').JSDOM; }
383
+ function getBeautify(): any { return require('js-beautify'); }
384
+ function getHandlebars(): any { return require('handlebars'); }
385
+
386
+ let _md: any = null;
387
+ function getMd(): any {
388
+ if (!_md) {
389
+ _md = require('markdown-it')({ html: true, linkify: true, typographer: true });
390
+ _md.renderer.rules.heading_open = (tokens: any[], idx: number) => {
391
+ const level = tokens[idx].tag.substring(1);
392
+ return `<h${level} class="custom-heading">`;
393
+ };
394
+ _md.renderer.rules.heading_close = (tokens: any[], idx: number) => {
395
+ const level = tokens[idx].tag.substring(1);
396
+ return `</h${level}>`;
397
+ };
398
+ }
399
+ return _md;
400
+ }
535
401
 
536
402
  let beautifyOptions = {
537
403
  "indent_size": 4,
@@ -591,7 +457,7 @@ export class NodeBuilder {
591
457
  }
592
458
 
593
459
  public addDefault(name:string, template:DefaultTemplateType):NodeBuilder{
594
- // if list:true, this is an array of config references generate shadow defaults for each slot.
460
+ // if list:true, this is an array of config references - generate shadow defaults for each slot.
595
461
  if(template.list === true) {
596
462
  template.maxInstances = template.maxInstances ? template.maxInstances : 10;
597
463
  this._defaults[name] = {value:"", required:(template.required) ? template.required : false, maxInstances:template.maxInstances};
@@ -641,9 +507,10 @@ export class NodeBuilder {
641
507
 
642
508
  public addTemplate(template: { template: ITemplateClass; config: TemplateConfig; }): NodeBuilder {
643
509
  let templateDescriptor = template.template.getTemplateDescriptor();
510
+ let templateConfig = template.config;
644
511
 
645
512
  // load the html source file
646
- let uiDom = new JSDOM(fs.readFileSync(templateDescriptor.templateFile()!));
513
+ let uiDom = new (getJSDOM())(fs.readFileSync(templateDescriptor.templateFile()!));
647
514
 
648
515
  // apply the onCompose section if available.
649
516
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
@@ -655,7 +522,16 @@ export class NodeBuilder {
655
522
  let sections:string[] = ['IncludeOnce', 'IncludeEditPrepare', 'IncludeEditSave', 'IncludeEditCancel', 'IncludeEditDelete', 'IncludeEditForm', 'IncludeDocumentation'];
656
523
  Object.values(sections).forEach(section => {
657
524
  let sectionData = uiDom.window.document.querySelector(`[template-section='on${section}']`)?.innerHTML;
525
+
658
526
  if(sectionData){
527
+ if(section !== "IncludeOnce" && section !== "IncludeEditForm" && section !== "IncludeDocumentation") {
528
+ // add the packageing around the sectionData
529
+ sectionData = `{
530
+ let node = this;
531
+ let templateConfig = ${JSON.stringify(templateConfig)};
532
+ ${sectionData}
533
+ }`;
534
+ }
659
535
  (this as any)[`add${section}`](templateDescriptor.name(), sectionData);
660
536
  }
661
537
  });
@@ -698,6 +574,85 @@ export class NodeBuilder {
698
574
  return this;
699
575
  }
700
576
 
577
+ public buildOnceHtml(): {source: string, html: string}[] {
578
+ return this._onIncludeOnce.map(entry => ({
579
+ source: entry.source,
580
+ html: this.serializeHTML(entry.source, entry.script)
581
+ }));
582
+ }
583
+
584
+ private applyShaddowDefaults(): void {
585
+ let shaddowDefaults = Object.entries(this._defaults).filter(([name, template]) => template.type && template.type.endsWith("[]"));
586
+ if (shaddowDefaults.length > 0) {
587
+ this.addIncludeEditSave(this._name + "_shaddowDefaults",
588
+ `{ let node = this; ` +
589
+ shaddowDefaults
590
+ .map(([name, template]) => `
591
+ for(let i = 0; i < ${template.maxInstances}; i++){
592
+ node["_${name}_" + i] = (node.${name}[i] || {}).proxy || "";
593
+ }`)
594
+ .join("\n\n") +
595
+ `}`
596
+ );
597
+ }
598
+ }
599
+
600
+ private buildRegisterTypeBody(): string {
601
+ return `{
602
+ category: '${this._category}',
603
+ ${(this._category !== "config" && this._icon) ? `icon: '${this._icon}',` : ""}
604
+ ${(this._category !== "config" && this._color) ? `color: '${this._color}',` : ""}
605
+ ${(this._category !== "config" && this._labelStyle) ? `labelStyle: '${this._labelStyle}',` : ""}
606
+ ${(this._label) ? `label: ${this._label},` : ""}
607
+ ${this._paletteLabel ? `paletteLabel: '${this._paletteLabel}',` : ""}
608
+ ${(this._category !== "config") ? `inputs:${this._inputs ? "1" : "0"},` : ""}
609
+ ${(this._category !== "config") ? `inputLabels:(i) => '${this._inputs}',` : ""}
610
+ ${(this._category !== "config") ? `outputs:${this._outputs.length},` : ""}
611
+ ${(this._category !== "config") ? `outputLabels:(i) => ${this.serializeProperty(this._outputs)}[i],` : ""}
612
+ defaults: {
613
+ ${Object.entries(this._defaults).map(([key, value]) => this.serializeDefault(key, value)).join("\n")}
614
+ },
615
+ oneditprepare: function() {
616
+ ${this._onIncludeEditPrepare.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
617
+ },
618
+ oneditsave: function() {
619
+ ${this._onIncludeEditSave.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
620
+ },
621
+ oneditcancel: function() {
622
+ ${this._onIncludeEditCancel.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
623
+ },
624
+ oneditdelete: function() {
625
+ ${this._onIncludeEditDelete.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
626
+ },
627
+ }`;
628
+ }
629
+
630
+ public buildDeferredType(): string {
631
+ this.applyShaddowDefaults();
632
+ const registerTypeBody = this.buildRegisterTypeBody();
633
+
634
+ return getBeautify().html_beautify(`
635
+ <script type="text/javascript">
636
+ (function () {
637
+ RED.events.on('registry:node-set-added', function (ns) {
638
+ if (ns.types && ns.types.indexOf('${this._name}') !== -1) {
639
+ RED.nodes.registerType('${this._name}', ${registerTypeBody});
640
+ }
641
+ });
642
+ }());
643
+ </script>`, beautifyOptions);
644
+ }
645
+
646
+ public buildStandardType(): string {
647
+ this.applyShaddowDefaults();
648
+ const registerTypeBody = this.buildRegisterTypeBody();
649
+
650
+ return getBeautify().html_beautify(`
651
+ <script type="text/javascript">
652
+ RED.nodes.registerType('${this._name}', ${registerTypeBody});
653
+ </script>`, beautifyOptions);
654
+ }
655
+
701
656
  private serializeProperty(value:any):string{
702
657
  switch(typeof value){
703
658
  case "boolean": {
@@ -730,18 +685,18 @@ export class NodeBuilder {
730
685
  }
731
686
 
732
687
  return `<!-- ${source} -->
733
- ${Handlebars.compile(script)(context)}
688
+ ${getHandlebars().compile(script)(context)}
734
689
  `
735
690
  }
736
691
 
737
- private serializeJavaScript(source:string, script:string):string{
692
+ private serializeJavaScript(source:string, script:string):string{
738
693
  let context = {
739
- defaults: (this._category === "config")
694
+ defaults: (this._category === "config")
740
695
  ? Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-config-input-${k}`]))
741
696
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
742
697
  }
743
698
 
744
- let serialized = Handlebars.compile(script)(context).trim();
699
+ let serialized = getHandlebars().compile(script)(context).trim();
745
700
  if(!serialized.startsWith("{") && !serialized.endsWith("}")){
746
701
  serialized = "{\n" + serialized + "\n}";
747
702
  }
@@ -751,14 +706,14 @@ export class NodeBuilder {
751
706
  `
752
707
  }
753
708
 
754
- private serializeFormEntry(source:string, script:string):string{
709
+ private serializeFormEntry(source:string, script:string):string{
755
710
  let context = {
756
- defaults: (this._category === "config")
711
+ defaults: (this._category === "config")
757
712
  ? Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-config-input-${k}`]))
758
713
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
759
714
  }
760
715
  return `
761
- <div id='section_${source}'>${Handlebars.compile(script)(context)}
716
+ <div id='section_${source}'>${getHandlebars().compile(script)(context)}
762
717
  </div>
763
718
  `
764
719
  }
@@ -771,57 +726,19 @@ export class NodeBuilder {
771
726
  return script;
772
727
  }
773
728
 
774
- public buildType():string{
775
- // compose the shaddow defaults if need be.
776
- let shaddowDefaults = Object.entries(this._defaults).filter(([name, template]) => template.type && template.type.endsWith("[]"));
777
- if(shaddowDefaults.length > 0){
778
- this.addIncludeEditSave(this._name + "_shaddowDefaults",
779
- `{ let node = this; ` +
780
- shaddowDefaults
781
- .map(([name, template]) => `
782
- for(let i = 0; i < ${template.maxInstances}; i++){
783
- node["_${name}_" + i] = (node.${name}[i] || {}).proxy || "";
784
- }`)
785
- .join("\n\n") +
786
- `}`
787
- );
788
- }
729
+ public buildType():string{
730
+ this.applyShaddowDefaults();
731
+ const registerTypeBody = this.buildRegisterTypeBody();
789
732
 
790
- return beautify.html_beautify(`
733
+ return getBeautify().html_beautify(`
791
734
  ${this._onIncludeOnce.map(entry => this.serializeHTML(entry.source, entry.script)).join("")}
792
735
  <script type="text/javascript">
793
- RED.nodes.registerType('${this._name}',{
794
- category: '${this._category}',
795
- ${(this._category !== "config" && this._icon) ? `icon: '${this._icon}',` : ""}
796
- ${(this._category !== "config" && this._color) ? `color: '${this._color}',` : ""}
797
- ${(this._category !== "config" && this._labelStyle) ? `labelStyle: '${this._labelStyle}',` : ""}
798
- ${(this._label) ? `label: ${this._label},` : ""}
799
- ${this._paletteLabel ? `paletteLabel: '${this._paletteLabel}',` : ""}
800
- ${(this._category !== "config") ? `inputs:${this._inputs ? "1" : "0"},` : ""}
801
- ${(this._category !== "config") ? `inputLabels:(i) => '${this._inputs}',` : ""}
802
- ${(this._category !== "config") ? `outputs:${this._outputs.length},` : ""}
803
- ${(this._category !== "config") ? `outputLabels:(i) => ${this.serializeProperty(this._outputs)}[i],` : ""}
804
- defaults: {
805
- ${Object.entries(this._defaults).map(([key, value]) => this.serializeDefault(key, value)).join("\n")}
806
- },
807
- oneditprepare: function() {
808
- ${this._onIncludeEditPrepare.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
809
- },
810
- oneditsave: function() {
811
- ${this._onIncludeEditSave.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
812
- },
813
- oneditcancel: function() {
814
- ${this._onIncludeEditCancel.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
815
- },
816
- oneditdelete: function() {
817
- ${this._onIncludeEditDelete.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
818
- },
819
- });
736
+ RED.nodes.registerType('${this._name}', ${registerTypeBody});
820
737
  </script>`, beautifyOptions);
821
738
  };
822
739
 
823
740
  public buildHtml():string{
824
- return beautify.html_beautify(`
741
+ return getBeautify().html_beautify(`
825
742
  <script type="text/html" data-template-name='${this._name}'>
826
743
  ${this._onIncludeEditForm.map(entry => this.serializeFormEntry(entry.source, entry.script)).join("")}
827
744
  </script>
@@ -854,13 +771,13 @@ let evalInContext = (context: any, js: string) => {
854
771
  }
855
772
  };
856
773
 
857
- let buildNode = function(node:INodeClass){
774
+ let buildNodeBuilder = function(node: INodeClass): NodeBuilder {
858
775
  // get the node descriptor and create the builder.
859
776
  let nodeDescriptor = node.getNodeDescriptor();
860
777
  let nodeBuilder = new NodeBuilder(nodeDescriptor.id(), nodeDescriptor.group());
861
778
 
862
779
  // load the html source file
863
- let uiDom = new JSDOM(fs.readFileSync(nodeDescriptor.sourceFile()));
780
+ let uiDom = new (getJSDOM())(fs.readFileSync(nodeDescriptor.sourceFile()));
864
781
 
865
782
  // apply the onCompose section if available.
866
783
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
@@ -880,10 +797,15 @@ let buildNode = function(node:INodeClass){
880
797
  // add some of the dependencies here.
881
798
  nodeDescriptor.templates().forEach(template => nodeBuilder.addTemplate(template));
882
799
 
883
- return [nodeBuilder.buildType(), nodeBuilder.buildHtml(), nodeBuilder.buildDocumentation()].join("\n\n");
800
+ return nodeBuilder;
801
+ }
884
802
 
803
+ let buildNode = function(node:INodeClass){
804
+ let nodeBuilder = buildNodeBuilder(node);
805
+ return [nodeBuilder.buildType(), nodeBuilder.buildHtml(), nodeBuilder.buildDocumentation()].join("\n\n");
885
806
  }
886
807
 
887
808
  export {
888
- buildNode
809
+ buildNode,
810
+ buildNodeBuilder
889
811
  }