@ackplus/nest-dynamic-templates 0.1.50 → 1.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.
Files changed (148) hide show
  1. package/README.md +6 -284
  2. package/eslint.config.mjs +22 -0
  3. package/jest.config.ts +10 -0
  4. package/package.json +7 -58
  5. package/project.json +38 -0
  6. package/src/{index.d.ts → index.ts} +14 -1
  7. package/src/lib/constant.ts +2 -0
  8. package/src/lib/dto/create-template-layout.dto.ts +65 -0
  9. package/src/lib/dto/create-template.dto.ts +80 -0
  10. package/src/lib/dto/render-content-template-layout.dto.ts +32 -0
  11. package/src/lib/dto/render-content-template.dto.ts +37 -0
  12. package/src/lib/dto/render-template-layout.dto.ts +55 -0
  13. package/src/lib/dto/render-template.dto.ts +74 -0
  14. package/src/lib/dto/template-filter.dto.ts +52 -0
  15. package/src/lib/dto/template-layout-filter.dto.ts +51 -0
  16. package/src/lib/engines/language/html.engine.ts +49 -0
  17. package/src/lib/engines/language/markdown.engine.ts +37 -0
  18. package/src/lib/engines/language/mjml.engine.ts +44 -0
  19. package/src/lib/engines/language/text.engine.ts +15 -0
  20. package/src/lib/engines/{language-engine.d.ts → language-engine.ts} +7 -1
  21. package/src/lib/engines/template/ejs.engine.ts +33 -0
  22. package/src/lib/engines/template/handlebars.engine.ts +35 -0
  23. package/src/lib/engines/template/nunjucks.engine.ts +70 -0
  24. package/src/lib/engines/template/pug.engine.ts +43 -0
  25. package/src/lib/engines/{template-engine.d.ts → template-engine.ts} +6 -1
  26. package/src/lib/entities/template-layout.entity.ts +99 -0
  27. package/src/lib/entities/template.entity.ts +105 -0
  28. package/src/lib/errors/{template.errors.js → template.errors.ts} +20 -23
  29. package/src/lib/interfaces/{module-config.interface.d.ts → module-config.interface.ts} +17 -2
  30. package/src/lib/interfaces/template.types.ts +25 -0
  31. package/src/lib/nest-dynamic-templates.module.ts +143 -0
  32. package/src/lib/services/template-config.service.ts +112 -0
  33. package/src/lib/services/template-engine.registry.ts +109 -0
  34. package/src/lib/services/template-layout.service.ts +407 -0
  35. package/src/lib/services/template.service.ts +476 -0
  36. package/src/test/{helpers.js → helpers.ts} +5 -8
  37. package/src/test/nunjucks.service.spec.ts +157 -0
  38. package/src/test/pug.service.spec-temp +254 -0
  39. package/src/test/template-layout.service.spec.ts +422 -0
  40. package/src/test/template.service.spec.ts +862 -0
  41. package/src/test/test-database.config.ts +24 -0
  42. package/src/test/test-database.d.ts +6 -0
  43. package/src/test/test.setup.ts +34 -0
  44. package/src/types/ioredis.d.ts +6 -0
  45. package/src/types/mjml.d.ts +5 -0
  46. package/tsconfig.json +17 -0
  47. package/tsconfig.lib.json +14 -0
  48. package/tsconfig.spec.json +15 -0
  49. package/src/index.js +0 -23
  50. package/src/index.js.map +0 -1
  51. package/src/lib/constant.d.ts +0 -2
  52. package/src/lib/constant.js +0 -6
  53. package/src/lib/constant.js.map +0 -1
  54. package/src/lib/dto/create-template-layout.dto.d.ts +0 -15
  55. package/src/lib/dto/create-template-layout.dto.js +0 -87
  56. package/src/lib/dto/create-template-layout.dto.js.map +0 -1
  57. package/src/lib/dto/create-template.dto.d.ts +0 -17
  58. package/src/lib/dto/create-template.dto.js +0 -104
  59. package/src/lib/dto/create-template.dto.js.map +0 -1
  60. package/src/lib/dto/render-content-template-layout.dto.d.ts +0 -7
  61. package/src/lib/dto/render-content-template-layout.dto.js +0 -41
  62. package/src/lib/dto/render-content-template-layout.dto.js.map +0 -1
  63. package/src/lib/dto/render-content-template.dto.d.ts +0 -8
  64. package/src/lib/dto/render-content-template.dto.js +0 -47
  65. package/src/lib/dto/render-content-template.dto.js.map +0 -1
  66. package/src/lib/dto/render-template-layout.dto.d.ts +0 -10
  67. package/src/lib/dto/render-template-layout.dto.js +0 -68
  68. package/src/lib/dto/render-template-layout.dto.js.map +0 -1
  69. package/src/lib/dto/render-template.dto.d.ts +0 -14
  70. package/src/lib/dto/render-template.dto.js +0 -91
  71. package/src/lib/dto/render-template.dto.js.map +0 -1
  72. package/src/lib/dto/template-filter.dto.d.ts +0 -8
  73. package/src/lib/dto/template-filter.dto.js +0 -62
  74. package/src/lib/dto/template-filter.dto.js.map +0 -1
  75. package/src/lib/dto/template-layout-filter.dto.d.ts +0 -7
  76. package/src/lib/dto/template-layout-filter.dto.js +0 -61
  77. package/src/lib/dto/template-layout-filter.dto.js.map +0 -1
  78. package/src/lib/engines/language/html.engine.d.ts +0 -9
  79. package/src/lib/engines/language/html.engine.js +0 -79
  80. package/src/lib/engines/language/html.engine.js.map +0 -1
  81. package/src/lib/engines/language/index.js +0 -12
  82. package/src/lib/engines/language/index.js.map +0 -1
  83. package/src/lib/engines/language/markdown.engine.d.ts +0 -9
  84. package/src/lib/engines/language/markdown.engine.js +0 -26
  85. package/src/lib/engines/language/markdown.engine.js.map +0 -1
  86. package/src/lib/engines/language/mjml.engine.d.ts +0 -9
  87. package/src/lib/engines/language/mjml.engine.js +0 -76
  88. package/src/lib/engines/language/mjml.engine.js.map +0 -1
  89. package/src/lib/engines/language/text.engine.d.ts +0 -7
  90. package/src/lib/engines/language/text.engine.js +0 -16
  91. package/src/lib/engines/language/text.engine.js.map +0 -1
  92. package/src/lib/engines/language-engine.js +0 -7
  93. package/src/lib/engines/language-engine.js.map +0 -1
  94. package/src/lib/engines/template/ejs.engine.d.ts +0 -10
  95. package/src/lib/engines/template/ejs.engine.js +0 -66
  96. package/src/lib/engines/template/ejs.engine.js.map +0 -1
  97. package/src/lib/engines/template/handlebars.engine.d.ts +0 -10
  98. package/src/lib/engines/template/handlebars.engine.js +0 -67
  99. package/src/lib/engines/template/handlebars.engine.js.map +0 -1
  100. package/src/lib/engines/template/index.js +0 -12
  101. package/src/lib/engines/template/index.js.map +0 -1
  102. package/src/lib/engines/template/nunjucks.engine.d.ts +0 -16
  103. package/src/lib/engines/template/nunjucks.engine.js +0 -63
  104. package/src/lib/engines/template/nunjucks.engine.js.map +0 -1
  105. package/src/lib/engines/template/pug.engine.d.ts +0 -12
  106. package/src/lib/engines/template/pug.engine.js +0 -75
  107. package/src/lib/engines/template/pug.engine.js.map +0 -1
  108. package/src/lib/engines/template-engine.js +0 -7
  109. package/src/lib/engines/template-engine.js.map +0 -1
  110. package/src/lib/entities/template-layout.entity.d.ts +0 -20
  111. package/src/lib/entities/template-layout.entity.js +0 -121
  112. package/src/lib/entities/template-layout.entity.js.map +0 -1
  113. package/src/lib/entities/template.entity.d.ts +0 -21
  114. package/src/lib/entities/template.entity.js +0 -128
  115. package/src/lib/entities/template.entity.js.map +0 -1
  116. package/src/lib/errors/template.errors.d.ts +0 -19
  117. package/src/lib/errors/template.errors.js.map +0 -1
  118. package/src/lib/interfaces/module-config.interface.js +0 -3
  119. package/src/lib/interfaces/module-config.interface.js.map +0 -1
  120. package/src/lib/interfaces/template.types.d.ts +0 -22
  121. package/src/lib/interfaces/template.types.js +0 -25
  122. package/src/lib/interfaces/template.types.js.map +0 -1
  123. package/src/lib/nest-dynamic-templates.module.d.ts +0 -10
  124. package/src/lib/nest-dynamic-templates.module.js +0 -131
  125. package/src/lib/nest-dynamic-templates.module.js.map +0 -1
  126. package/src/lib/services/template-config.service.d.ts +0 -18
  127. package/src/lib/services/template-config.service.js +0 -66
  128. package/src/lib/services/template-config.service.js.map +0 -1
  129. package/src/lib/services/template-engine.registry.d.ts +0 -21
  130. package/src/lib/services/template-engine.registry.js +0 -94
  131. package/src/lib/services/template-engine.registry.js.map +0 -1
  132. package/src/lib/services/template-layout.service.d.ts +0 -24
  133. package/src/lib/services/template-layout.service.js +0 -301
  134. package/src/lib/services/template-layout.service.js.map +0 -1
  135. package/src/lib/services/template.service.d.ts +0 -26
  136. package/src/lib/services/template.service.js +0 -366
  137. package/src/lib/services/template.service.js.map +0 -1
  138. package/src/test/helpers.d.ts +0 -4
  139. package/src/test/helpers.js.map +0 -1
  140. package/src/test/test-database.config.d.ts +0 -4
  141. package/src/test/test-database.config.js +0 -24
  142. package/src/test/test-database.config.js.map +0 -1
  143. package/src/test/test.setup.d.ts +0 -3
  144. package/src/test/test.setup.js +0 -29
  145. package/src/test/test.setup.js.map +0 -1
  146. package/tsconfig.tsbuildinfo +0 -1
  147. /package/src/lib/engines/language/{index.d.ts → index.ts} +0 -0
  148. /package/src/lib/engines/template/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,105 @@
1
+ import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, Index, JoinColumn, ManyToOne, RelationId, BaseEntity } from 'typeorm';
2
+ import { TemplateTypeEnum, TemplateEngineEnum, TemplateLanguageEnum } from '../interfaces/template.types';
3
+ import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator';
4
+ import { ApiProperty } from '@nestjs/swagger';
5
+
6
+
7
+ @Entity('nest_dynamic_templates')
8
+ @Index(['name', 'scope', 'scopeId', 'locale'], { unique: true })
9
+ export class NestDynamicTemplate extends BaseEntity {
10
+
11
+ @ApiProperty({ type: String, format: 'uuid', readOnly: true })
12
+ @PrimaryGeneratedColumn('uuid')
13
+ id: string;
14
+
15
+ @ApiProperty({ type: String })
16
+ @IsString()
17
+ @IsNotEmpty()
18
+ @Matches(/^[a-z0-9\-_]+$/, { message: 'Invalid template name format' })
19
+ @Column()
20
+ name: string;
21
+
22
+ @ApiProperty({ type: String, nullable: true })
23
+ @IsString()
24
+ @IsOptional()
25
+ @Column({ nullable: true })
26
+ displayName?: string;
27
+
28
+ @ApiProperty({ type: String, nullable: true })
29
+ @IsString()
30
+ @IsOptional()
31
+ @Column({ type: 'text', nullable: true })
32
+ description?: string;
33
+
34
+ @ApiProperty({ type: String, nullable: true })
35
+ @IsString()
36
+ @IsOptional()
37
+ @Column({ type: 'text', nullable: true })
38
+ type?: string;
39
+
40
+ @ApiProperty({ enum: TemplateEngineEnum, type: String })
41
+ @IsEnum(TemplateEngineEnum)
42
+ @Column({
43
+ type: 'text',
44
+ enum: TemplateEngineEnum,
45
+ default: TemplateEngineEnum.NUNJUCKS
46
+ })
47
+ engine: TemplateEngineEnum;
48
+
49
+ @ApiProperty({ enum: TemplateLanguageEnum, type: String, nullable: true })
50
+ @IsEnum(TemplateLanguageEnum)
51
+ @IsOptional()
52
+ @Column({ type: 'text' })
53
+ language?: TemplateLanguageEnum;
54
+
55
+ @ApiProperty({ type: String, nullable: true })
56
+ @IsString()
57
+ @IsOptional()
58
+ @Column({ nullable: true })
59
+ subject?: string;
60
+
61
+ @ApiProperty({ type: String })
62
+ @IsString()
63
+ @IsOptional()
64
+ @Column('text')
65
+ content: string;
66
+
67
+ @ApiProperty({ type: String, nullable: true })
68
+ @IsString()
69
+ @IsOptional()
70
+ @Column({ nullable: true })
71
+ templateLayoutName?: string;
72
+
73
+ @ApiProperty({ type: String, nullable: true })
74
+ @IsString()
75
+ @Column({ nullable: true, default: 'system' })
76
+ scope?: string;
77
+
78
+ @ApiProperty({ type: String, nullable: true })
79
+ @IsString()
80
+ @Column({ nullable: true })
81
+ scopeId?: string;
82
+
83
+ @ApiProperty({ type: String, nullable: true })
84
+ @IsString()
85
+ @Column({ nullable: true, default: 'en' })
86
+ locale?: string;
87
+
88
+ @ApiProperty({ type: Object, nullable: true })
89
+ @IsOptional()
90
+ @Column('simple-json', { nullable: true })
91
+ previewContext?: Record<string, any>;
92
+
93
+ @ApiProperty({ type: Boolean })
94
+ @IsBoolean()
95
+ @Column({ default: true })
96
+ isActive: boolean;
97
+
98
+ @ApiProperty({ type: Date, readOnly: true })
99
+ @CreateDateColumn()
100
+ createdAt: Date;
101
+
102
+ @ApiProperty({ type: Date, readOnly: true })
103
+ @UpdateDateColumn()
104
+ updatedAt: Date;
105
+ }
@@ -1,12 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TemplateValidationError = exports.TemplateContentError = exports.TemplateLayoutError = exports.TemplateLanguageError = exports.TemplateEngineError = exports.TemplateRenderError = void 0;
4
- const common_1 = require("@nestjs/common");
5
- class TemplateRenderError extends common_1.InternalServerErrorException {
6
- constructor(operation, originalError, templateName) {
1
+ import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
2
+
3
+ export class TemplateRenderError extends InternalServerErrorException {
4
+ constructor(operation: string, originalError: Error, templateName?: string) {
7
5
  const message = templateName
8
6
  ? `Failed to render template '${templateName}' during ${operation}: ${originalError.message}`
9
7
  : `Failed to render content during ${operation}: ${originalError.message}`;
8
+
10
9
  super({
11
10
  message,
12
11
  error: 'TEMPLATE_RENDER_ERROR',
@@ -16,9 +15,9 @@ class TemplateRenderError extends common_1.InternalServerErrorException {
16
15
  });
17
16
  }
18
17
  }
19
- exports.TemplateRenderError = TemplateRenderError;
20
- class TemplateEngineError extends common_1.InternalServerErrorException {
21
- constructor(engine, originalError) {
18
+
19
+ export class TemplateEngineError extends InternalServerErrorException {
20
+ constructor(engine: string, originalError: Error) {
22
21
  super({
23
22
  message: `Template engine '${engine}' failed to render content: ${originalError.message}`,
24
23
  error: 'TEMPLATE_ENGINE_ERROR',
@@ -27,9 +26,9 @@ class TemplateEngineError extends common_1.InternalServerErrorException {
27
26
  });
28
27
  }
29
28
  }
30
- exports.TemplateEngineError = TemplateEngineError;
31
- class TemplateLanguageError extends common_1.InternalServerErrorException {
32
- constructor(language, originalError) {
29
+
30
+ export class TemplateLanguageError extends InternalServerErrorException {
31
+ constructor(language: string, originalError: Error) {
33
32
  super({
34
33
  message: `Language processor '${language}' failed to render content: ${originalError.message}`,
35
34
  error: 'TEMPLATE_LANGUAGE_ERROR',
@@ -38,9 +37,9 @@ class TemplateLanguageError extends common_1.InternalServerErrorException {
38
37
  });
39
38
  }
40
39
  }
41
- exports.TemplateLanguageError = TemplateLanguageError;
42
- class TemplateLayoutError extends common_1.InternalServerErrorException {
43
- constructor(layoutName, originalError) {
40
+
41
+ export class TemplateLayoutError extends InternalServerErrorException {
42
+ constructor(layoutName: string, originalError: Error) {
44
43
  super({
45
44
  message: `Template layout '${layoutName}' failed to render: ${originalError.message}`,
46
45
  error: 'TEMPLATE_LAYOUT_ERROR',
@@ -49,9 +48,9 @@ class TemplateLayoutError extends common_1.InternalServerErrorException {
49
48
  });
50
49
  }
51
50
  }
52
- exports.TemplateLayoutError = TemplateLayoutError;
53
- class TemplateContentError extends common_1.InternalServerErrorException {
54
- constructor(contentType, originalError) {
51
+
52
+ export class TemplateContentError extends InternalServerErrorException {
53
+ constructor(contentType: string, originalError: Error) {
55
54
  super({
56
55
  message: `Failed to process ${contentType} content: ${originalError.message}`,
57
56
  error: 'TEMPLATE_CONTENT_ERROR',
@@ -60,9 +59,9 @@ class TemplateContentError extends common_1.InternalServerErrorException {
60
59
  });
61
60
  }
62
61
  }
63
- exports.TemplateContentError = TemplateContentError;
64
- class TemplateValidationError extends common_1.BadRequestException {
65
- constructor(field, value, reason) {
62
+
63
+ export class TemplateValidationError extends BadRequestException {
64
+ constructor(field: string, value: any, reason: string) {
66
65
  super({
67
66
  message: `Template validation failed for field '${field}': ${reason}`,
68
67
  error: 'TEMPLATE_VALIDATION_ERROR',
@@ -72,5 +71,3 @@ class TemplateValidationError extends common_1.BadRequestException {
72
71
  });
73
72
  }
74
73
  }
75
- exports.TemplateValidationError = TemplateValidationError;
76
- //# sourceMappingURL=template.errors.js.map
@@ -1,18 +1,25 @@
1
1
  import { TemplateEngineEnum, TemplateLanguageEnum } from './template.types';
2
2
  import { ModuleMetadata, Type } from '@nestjs/common';
3
+
3
4
  export interface EngineConfig {
4
5
  enabled: boolean;
5
6
  options?: Record<string, any>;
6
7
  }
8
+
7
9
  export interface FilterOptions {
8
10
  name: string;
9
11
  filter: (...args: any[]) => any;
10
12
  }
11
- export type EngineOptions<T> = T & {};
13
+
14
+ export type EngineOptions<T> = T & {
15
+
16
+ };
17
+
12
18
  export type CustomEngineOptions = {
13
19
  filters?: Record<string, FilterOptions['filter']>;
14
- globalValues?: Record<string, (...args: any[]) => any>;
20
+ globalValues?: Record<string, (...args: any) => any>;
15
21
  };
22
+
16
23
  export interface NestDynamicTemplatesModuleConfig {
17
24
  isGlobal?: boolean;
18
25
  enginesOptions?: {
@@ -26,9 +33,17 @@ export interface NestDynamicTemplatesModuleConfig {
26
33
  language?: TemplateLanguageEnum[];
27
34
  };
28
35
  }
36
+
37
+ /**
38
+ * Interface for async module configuration factory
39
+ */
29
40
  export interface NestDynamicTemplatesModuleOptionsFactory {
30
41
  createNestDynamicTemplatesModuleOptions(): Promise<NestDynamicTemplatesModuleConfig> | NestDynamicTemplatesModuleConfig;
31
42
  }
43
+
44
+ /**
45
+ * Interface for async module configuration
46
+ */
32
47
  export interface NestDynamicTemplatesModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
33
48
  isGlobal?: boolean;
34
49
  useExisting?: Type<NestDynamicTemplatesModuleOptionsFactory>;
@@ -0,0 +1,25 @@
1
+ export enum TemplateTypeEnum {
2
+ EMAIL = 'email',
3
+ SMS = 'sms',
4
+ PUSH = 'push',
5
+ PDF = 'pdf',
6
+ }
7
+
8
+ export enum TemplateEngineEnum {
9
+ NUNJUCKS = 'njk',
10
+ HANDLEBARS = 'hbs',
11
+ EJS = 'ejs',
12
+ PUG = 'pug',
13
+ }
14
+
15
+ export enum TemplateLanguageEnum {
16
+ MJML = 'mjml',
17
+ HTML = 'html',
18
+ MARKDOWN = 'md',
19
+ TEXT = 'txt',
20
+ }
21
+
22
+ export interface Scope {
23
+ scope: string;
24
+ id: string;
25
+ }
@@ -0,0 +1,143 @@
1
+ import { Module, DynamicModule, Provider } from '@nestjs/common';
2
+ import { TypeOrmModule } from '@nestjs/typeorm';
3
+ import { NestDynamicTemplate } from './entities/template.entity';
4
+ import { NestDynamicTemplateLayout } from './entities/template-layout.entity';
5
+ import {
6
+ NestDynamicTemplatesModuleConfig,
7
+ NestDynamicTemplatesModuleAsyncOptions,
8
+ NestDynamicTemplatesModuleOptionsFactory
9
+ } from './interfaces/module-config.interface';
10
+ import deepmerge from 'deepmerge';
11
+ import { TemplateEngineRegistryService } from './services/template-engine.registry';
12
+ import { TemplateLayoutService } from './services/template-layout.service';
13
+ import { TemplateService } from './services/template.service';
14
+ import { TemplateConfigService } from './services/template-config.service';
15
+ import { NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER } from './constant';
16
+ import { TemplateEngineEnum } from './interfaces/template.types';
17
+ import { TemplateLanguageEnum } from './interfaces/template.types';
18
+
19
+
20
+ const defaultConfig: NestDynamicTemplatesModuleConfig = {
21
+ engines: {
22
+ template: [TemplateEngineEnum.NUNJUCKS],
23
+ language: [TemplateLanguageEnum.HTML, TemplateLanguageEnum.MJML, TemplateLanguageEnum.TEXT]
24
+ }
25
+ }
26
+
27
+ @Module({
28
+ imports: [TypeOrmModule.forFeature([NestDynamicTemplate, NestDynamicTemplateLayout])],
29
+ providers: [
30
+ TemplateConfigService,
31
+ TemplateEngineRegistryService,
32
+ TemplateLayoutService,
33
+ TemplateService,
34
+ ],
35
+ exports: [
36
+ TemplateConfigService,
37
+ TemplateService,
38
+ TemplateLayoutService,
39
+ ],
40
+ })
41
+ export class NestDynamicTemplatesModule {
42
+ static moduleDefaultOptions: Partial<NestDynamicTemplatesModuleConfig> = defaultConfig;
43
+
44
+ static forRoot(config: NestDynamicTemplatesModuleConfig = {}): DynamicModule {
45
+ const mergedConfig = this.getOptions(config);
46
+
47
+ // Set options in static service
48
+ TemplateConfigService.setOptions(mergedConfig);
49
+
50
+ return {
51
+ module: NestDynamicTemplatesModule,
52
+ global: mergedConfig.isGlobal,
53
+ imports: [
54
+ TypeOrmModule.forFeature([NestDynamicTemplate, NestDynamicTemplateLayout]),
55
+ ],
56
+ providers: [
57
+ TemplateConfigService,
58
+ TemplateEngineRegistryService,
59
+ TemplateLayoutService,
60
+ TemplateService,
61
+ ],
62
+ exports: [
63
+ TemplateConfigService,
64
+ TemplateService,
65
+ TemplateLayoutService,
66
+ ],
67
+ };
68
+ }
69
+
70
+ static forRootAsync(options: NestDynamicTemplatesModuleAsyncOptions): DynamicModule {
71
+ const asyncProviders = this.createAsyncProviders(options);
72
+
73
+ return {
74
+ module: NestDynamicTemplatesModule,
75
+ global: options.isGlobal,
76
+ imports: [
77
+ TypeOrmModule.forFeature([NestDynamicTemplate, NestDynamicTemplateLayout]),
78
+ ...(options.imports || []),
79
+ ],
80
+ providers: [
81
+ ...asyncProviders,
82
+ TemplateConfigService,
83
+ TemplateEngineRegistryService,
84
+ TemplateLayoutService,
85
+ TemplateService,
86
+ ],
87
+ exports: [
88
+ TemplateConfigService,
89
+ TemplateService,
90
+ TemplateLayoutService,
91
+ ],
92
+ };
93
+ }
94
+
95
+ private static createAsyncProviders(options: NestDynamicTemplatesModuleAsyncOptions): Provider[] {
96
+ if (options.useExisting || options.useFactory) {
97
+ return [this.createAsyncOptionsProvider(options)];
98
+ }
99
+
100
+ if (options.useClass) {
101
+ return [
102
+ this.createAsyncOptionsProvider(options),
103
+ {
104
+ provide: options.useClass,
105
+ useClass: options.useClass,
106
+ },
107
+ ];
108
+ }
109
+
110
+ return [];
111
+ }
112
+
113
+ private static createAsyncOptionsProvider(options: NestDynamicTemplatesModuleAsyncOptions): Provider {
114
+ if (options.useFactory) {
115
+ return {
116
+ provide: NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER,
117
+ useFactory: async (...args: any[]) => {
118
+ const userOptions = options.useFactory ? await options.useFactory(...args) : {};
119
+ const mergedOptions = this.getOptions(userOptions);
120
+ TemplateConfigService.setOptions(mergedOptions);
121
+ return mergedOptions;
122
+ },
123
+ inject: options.inject || [],
124
+ };
125
+ }
126
+
127
+ return {
128
+ provide: NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER,
129
+ useFactory: async (optionsFactory: NestDynamicTemplatesModuleOptionsFactory) => {
130
+ const userOptions = await optionsFactory.createNestDynamicTemplatesModuleOptions();
131
+ const mergedOptions = this.getOptions(userOptions);
132
+ TemplateConfigService.setOptions(mergedOptions);
133
+ return mergedOptions;
134
+ },
135
+ inject: [options.useExisting || options.useClass!],
136
+ };
137
+ }
138
+
139
+ private static getOptions(options: NestDynamicTemplatesModuleConfig): NestDynamicTemplatesModuleConfig {
140
+ return deepmerge(this.moduleDefaultOptions, options);
141
+ }
142
+
143
+ }
@@ -0,0 +1,112 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { NestDynamicTemplatesModuleConfig } from '../interfaces/module-config.interface';
3
+ import { TemplateEngineEnum, TemplateLanguageEnum } from '../interfaces/template.types';
4
+
5
+ @Injectable()
6
+ export class TemplateConfigService {
7
+ private static config: NestDynamicTemplatesModuleConfig;
8
+
9
+ private static readonly defaultConfig: NestDynamicTemplatesModuleConfig = {
10
+ engines: {
11
+ template: [TemplateEngineEnum.NUNJUCKS],
12
+ language: [TemplateLanguageEnum.HTML, TemplateLanguageEnum.MJML, TemplateLanguageEnum.TEXT]
13
+ }
14
+ };
15
+
16
+ /**
17
+ * Set the configuration options
18
+ */
19
+ static setOptions(config: NestDynamicTemplatesModuleConfig): void {
20
+ this.config = { ...this.defaultConfig, ...config };
21
+ }
22
+
23
+ /**
24
+ * Get the current configuration options
25
+ */
26
+ static getOptions(): NestDynamicTemplatesModuleConfig {
27
+ if (!this.config) {
28
+ return this.defaultConfig;
29
+ }
30
+ return this.config;
31
+ }
32
+
33
+ /**
34
+ * Check if configuration has been set
35
+ */
36
+ static hasConfig(): boolean {
37
+ return !!this.config;
38
+ }
39
+
40
+ /**
41
+ * Reset configuration to default
42
+ */
43
+ static reset(): void {
44
+ this.config = { ...this.defaultConfig };
45
+ }
46
+
47
+ /**
48
+ * Get a specific configuration value
49
+ */
50
+ static get<K extends keyof NestDynamicTemplatesModuleConfig>(
51
+ key: K
52
+ ): NestDynamicTemplatesModuleConfig[K] {
53
+ const config = this.getOptions();
54
+ return config[key];
55
+ }
56
+
57
+ /**
58
+ * Check if a template engine is enabled
59
+ */
60
+ static isTemplateEngineEnabled(engine: TemplateEngineEnum): boolean {
61
+ const config = this.getOptions();
62
+ return config.engines?.template?.includes(engine) ?? false;
63
+ }
64
+
65
+ /**
66
+ * Check if a language engine is enabled
67
+ */
68
+ static isLanguageEngineEnabled(language: TemplateLanguageEnum): boolean {
69
+ const config = this.getOptions();
70
+ return config.engines?.language?.includes(language) ?? false;
71
+ }
72
+
73
+ /**
74
+ * Get enabled template engines
75
+ */
76
+ static getEnabledTemplateEngines(): TemplateEngineEnum[] {
77
+ const config = this.getOptions();
78
+ return config.engines?.template ?? this.defaultConfig.engines!.template!;
79
+ }
80
+
81
+ /**
82
+ * Get enabled language engines
83
+ */
84
+ static getEnabledLanguageEngines(): TemplateLanguageEnum[] {
85
+ const config = this.getOptions();
86
+ return config.engines?.language ?? this.defaultConfig.engines!.language!;
87
+ }
88
+
89
+ /**
90
+ * Get engine options for a specific template engine
91
+ */
92
+ static getTemplateEngineOptions(engine: TemplateEngineEnum): any {
93
+ const config = this.getOptions();
94
+ return config.enginesOptions?.template?.[engine];
95
+ }
96
+
97
+ /**
98
+ * Get engine options for a specific language engine
99
+ */
100
+ static getLanguageEngineOptions(language: TemplateLanguageEnum): any {
101
+ const config = this.getOptions();
102
+ return config.enginesOptions?.language?.[language];
103
+ }
104
+
105
+ /**
106
+ * Get custom filters
107
+ */
108
+ static getCustomFilters(): Record<string, Function> {
109
+ const config = this.getOptions();
110
+ return config.enginesOptions?.filters ?? {};
111
+ }
112
+ }
@@ -0,0 +1,109 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { TemplateEngineEnum, TemplateLanguageEnum } from '../interfaces/template.types';
3
+ import { NunjucksEngine } from '../engines/template/nunjucks.engine';
4
+ import { MjmlEngine } from '../engines/language/mjml.engine';
5
+ import { HtmlEngine } from '../engines/language/html.engine';
6
+ import { MarkdownEngine, TextEngine } from '../engines/language';
7
+ import { EjsEngine } from '../engines/template/ejs.engine';
8
+ import { HandlebarsEngine } from '../engines/template/handlebars.engine';
9
+ import { PugEngine } from '../engines/template/pug.engine';
10
+ import { TemplateConfigService } from './template-config.service';
11
+ import { TemplateEngine } from '../engines/template-engine';
12
+ import { LanguageEngine } from '../engines/language-engine';
13
+ import { NestDynamicTemplatesModuleConfig } from '../interfaces/module-config.interface';
14
+
15
+ type ClassType<T> = new (...args: any[]) => T;
16
+
17
+ @Injectable()
18
+ export class TemplateEngineRegistryService {
19
+ private templateEngines: Map<TemplateEngineEnum, TemplateEngine> = new Map();
20
+ private languageEngines: Map<TemplateLanguageEnum, LanguageEngine> = new Map();
21
+ private config: NestDynamicTemplatesModuleConfig;
22
+
23
+ constructor() {
24
+ this.config = TemplateConfigService.getOptions();
25
+
26
+ this.registerTemplateEngines([
27
+ NunjucksEngine,
28
+ HandlebarsEngine,
29
+ EjsEngine,
30
+ PugEngine
31
+ ]);
32
+
33
+
34
+ this.registerLanguageEngines([
35
+ MjmlEngine,
36
+ HtmlEngine,
37
+ TextEngine,
38
+ MarkdownEngine
39
+ ]);
40
+ }
41
+
42
+ registerTemplateEngines(engineClasses: ClassType<TemplateEngine>[]): void {
43
+ engineClasses.forEach(EngineClass => {
44
+ const engineName = (EngineClass as any).engineName;
45
+ if (!engineName) {
46
+ throw new Error(`Engine class ${EngineClass.name} must define static engineName property`);
47
+ }
48
+ const options = (this.config.enginesOptions?.template as any)?.[engineName];
49
+
50
+ const customOptions = {
51
+ filters: this.config.enginesOptions?.filters,
52
+ globalValues: this.config.enginesOptions?.globalValues
53
+ }
54
+
55
+ this.registerTemplateEngine(engineName, new EngineClass(options, customOptions));
56
+ });
57
+ }
58
+
59
+ registerTemplateEngine(format: TemplateEngineEnum, engine: TemplateEngine): void {
60
+ this.templateEngines.set(format, engine);
61
+ }
62
+
63
+ registerLanguageEngine(format: TemplateLanguageEnum, engine: LanguageEngine): void {
64
+ this.languageEngines.set(format, engine);
65
+ }
66
+
67
+ getTemplateEngine(format: TemplateEngineEnum): TemplateEngine {
68
+ const engine = this.templateEngines.get(format);
69
+ if (!engine) {
70
+ throw new Error(`Template engine not found for format: ${format}`);
71
+ }
72
+ return engine;
73
+ }
74
+
75
+ getLanguageEngine(format: TemplateLanguageEnum): LanguageEngine {
76
+ const engine = this.languageEngines.get(format);
77
+ if (!engine) {
78
+ throw new Error(`Language engine not found for format: ${format}`);
79
+ }
80
+ return engine;
81
+ }
82
+
83
+ registerLanguageEngines(engineClasses: ClassType<LanguageEngine>[]): void {
84
+ engineClasses.forEach(EngineClass => {
85
+ const engineName = (EngineClass as any).engineName;
86
+ if (!engineName) {
87
+ throw new Error(`Engine class ${EngineClass.name} must define static engineName property`);
88
+ }
89
+ const options = (this.config.enginesOptions?.language as any)?.[engineName];
90
+ this.registerLanguageEngine(engineName, new EngineClass(options));
91
+ });
92
+ }
93
+
94
+ hasTemplateEngine(format: TemplateEngineEnum): boolean {
95
+ return this.templateEngines.has(format);
96
+ }
97
+
98
+ hasLanguageEngine(format: TemplateLanguageEnum): boolean {
99
+ return this.languageEngines.has(format);
100
+ }
101
+
102
+ getSupportedTemplateFormats(): TemplateEngineEnum[] {
103
+ return Array.from(this.templateEngines.keys());
104
+ }
105
+
106
+ getSupportedLanguageFormats(): TemplateLanguageEnum[] {
107
+ return Array.from(this.languageEngines.keys());
108
+ }
109
+ }