@ackplus/nest-dynamic-templates 0.1.51 → 1.1.1
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 +6 -284
- package/eslint.config.mjs +22 -0
- package/jest.config.ts +10 -0
- package/package.json +7 -58
- package/project.json +38 -0
- package/src/{index.d.ts → index.ts} +14 -1
- package/src/lib/constant.ts +2 -0
- package/src/lib/dto/create-template-layout.dto.ts +65 -0
- package/src/lib/dto/create-template.dto.ts +80 -0
- package/src/lib/dto/render-content-template-layout.dto.ts +32 -0
- package/src/lib/dto/render-content-template.dto.ts +37 -0
- package/src/lib/dto/render-template-layout.dto.ts +55 -0
- package/src/lib/dto/render-template.dto.ts +74 -0
- package/src/lib/dto/template-filter.dto.ts +52 -0
- package/src/lib/dto/template-layout-filter.dto.ts +51 -0
- package/src/lib/engines/language/html.engine.ts +49 -0
- package/src/lib/engines/language/markdown.engine.ts +37 -0
- package/src/lib/engines/language/mjml.engine.ts +44 -0
- package/src/lib/engines/language/text.engine.ts +15 -0
- package/src/lib/engines/{language-engine.d.ts → language-engine.ts} +7 -1
- package/src/lib/engines/template/ejs.engine.ts +33 -0
- package/src/lib/engines/template/handlebars.engine.ts +35 -0
- package/src/lib/engines/template/nunjucks.engine.ts +70 -0
- package/src/lib/engines/template/pug.engine.ts +43 -0
- package/src/lib/engines/{template-engine.d.ts → template-engine.ts} +6 -1
- package/src/lib/entities/template-layout.entity.ts +99 -0
- package/src/lib/entities/template.entity.ts +105 -0
- package/src/lib/errors/{template.errors.js → template.errors.ts} +20 -23
- package/src/lib/interfaces/{module-config.interface.d.ts → module-config.interface.ts} +16 -1
- package/src/lib/interfaces/template.types.ts +25 -0
- package/src/lib/nest-dynamic-templates.module.ts +143 -0
- package/src/lib/services/template-config.service.ts +112 -0
- package/src/lib/services/template-engine.registry.ts +109 -0
- package/src/lib/services/template-layout.service.ts +407 -0
- package/src/lib/services/template.service.ts +476 -0
- package/src/test/{helpers.js → helpers.ts} +5 -8
- package/src/test/nunjucks.service.spec.ts +157 -0
- package/src/test/pug.service.spec-temp +254 -0
- package/src/test/template-layout.service.spec.ts +422 -0
- package/src/test/template.service.spec.ts +862 -0
- package/src/test/test-database.config.ts +24 -0
- package/src/test/test-database.d.ts +6 -0
- package/src/test/test.setup.ts +34 -0
- package/src/types/ioredis.d.ts +6 -0
- package/src/types/mjml.d.ts +5 -0
- package/tsconfig.json +17 -0
- package/tsconfig.lib.json +14 -0
- package/tsconfig.spec.json +15 -0
- package/src/index.js +0 -23
- package/src/index.js.map +0 -1
- package/src/lib/constant.d.ts +0 -2
- package/src/lib/constant.js +0 -6
- package/src/lib/constant.js.map +0 -1
- package/src/lib/dto/create-template-layout.dto.d.ts +0 -15
- package/src/lib/dto/create-template-layout.dto.js +0 -87
- package/src/lib/dto/create-template-layout.dto.js.map +0 -1
- package/src/lib/dto/create-template.dto.d.ts +0 -17
- package/src/lib/dto/create-template.dto.js +0 -104
- package/src/lib/dto/create-template.dto.js.map +0 -1
- package/src/lib/dto/render-content-template-layout.dto.d.ts +0 -7
- package/src/lib/dto/render-content-template-layout.dto.js +0 -41
- package/src/lib/dto/render-content-template-layout.dto.js.map +0 -1
- package/src/lib/dto/render-content-template.dto.d.ts +0 -8
- package/src/lib/dto/render-content-template.dto.js +0 -47
- package/src/lib/dto/render-content-template.dto.js.map +0 -1
- package/src/lib/dto/render-template-layout.dto.d.ts +0 -10
- package/src/lib/dto/render-template-layout.dto.js +0 -68
- package/src/lib/dto/render-template-layout.dto.js.map +0 -1
- package/src/lib/dto/render-template.dto.d.ts +0 -14
- package/src/lib/dto/render-template.dto.js +0 -91
- package/src/lib/dto/render-template.dto.js.map +0 -1
- package/src/lib/dto/template-filter.dto.d.ts +0 -8
- package/src/lib/dto/template-filter.dto.js +0 -62
- package/src/lib/dto/template-filter.dto.js.map +0 -1
- package/src/lib/dto/template-layout-filter.dto.d.ts +0 -7
- package/src/lib/dto/template-layout-filter.dto.js +0 -61
- package/src/lib/dto/template-layout-filter.dto.js.map +0 -1
- package/src/lib/engines/language/html.engine.d.ts +0 -9
- package/src/lib/engines/language/html.engine.js +0 -79
- package/src/lib/engines/language/html.engine.js.map +0 -1
- package/src/lib/engines/language/index.js +0 -12
- package/src/lib/engines/language/index.js.map +0 -1
- package/src/lib/engines/language/markdown.engine.d.ts +0 -9
- package/src/lib/engines/language/markdown.engine.js +0 -26
- package/src/lib/engines/language/markdown.engine.js.map +0 -1
- package/src/lib/engines/language/mjml.engine.d.ts +0 -9
- package/src/lib/engines/language/mjml.engine.js +0 -76
- package/src/lib/engines/language/mjml.engine.js.map +0 -1
- package/src/lib/engines/language/text.engine.d.ts +0 -7
- package/src/lib/engines/language/text.engine.js +0 -16
- package/src/lib/engines/language/text.engine.js.map +0 -1
- package/src/lib/engines/language-engine.js +0 -7
- package/src/lib/engines/language-engine.js.map +0 -1
- package/src/lib/engines/template/ejs.engine.d.ts +0 -10
- package/src/lib/engines/template/ejs.engine.js +0 -66
- package/src/lib/engines/template/ejs.engine.js.map +0 -1
- package/src/lib/engines/template/handlebars.engine.d.ts +0 -10
- package/src/lib/engines/template/handlebars.engine.js +0 -67
- package/src/lib/engines/template/handlebars.engine.js.map +0 -1
- package/src/lib/engines/template/index.js +0 -12
- package/src/lib/engines/template/index.js.map +0 -1
- package/src/lib/engines/template/nunjucks.engine.d.ts +0 -16
- package/src/lib/engines/template/nunjucks.engine.js +0 -63
- package/src/lib/engines/template/nunjucks.engine.js.map +0 -1
- package/src/lib/engines/template/pug.engine.d.ts +0 -12
- package/src/lib/engines/template/pug.engine.js +0 -75
- package/src/lib/engines/template/pug.engine.js.map +0 -1
- package/src/lib/engines/template-engine.js +0 -7
- package/src/lib/engines/template-engine.js.map +0 -1
- package/src/lib/entities/template-layout.entity.d.ts +0 -20
- package/src/lib/entities/template-layout.entity.js +0 -121
- package/src/lib/entities/template-layout.entity.js.map +0 -1
- package/src/lib/entities/template.entity.d.ts +0 -21
- package/src/lib/entities/template.entity.js +0 -128
- package/src/lib/entities/template.entity.js.map +0 -1
- package/src/lib/errors/template.errors.d.ts +0 -19
- package/src/lib/errors/template.errors.js.map +0 -1
- package/src/lib/interfaces/module-config.interface.js +0 -3
- package/src/lib/interfaces/module-config.interface.js.map +0 -1
- package/src/lib/interfaces/template.types.d.ts +0 -22
- package/src/lib/interfaces/template.types.js +0 -25
- package/src/lib/interfaces/template.types.js.map +0 -1
- package/src/lib/nest-dynamic-templates.module.d.ts +0 -10
- package/src/lib/nest-dynamic-templates.module.js +0 -131
- package/src/lib/nest-dynamic-templates.module.js.map +0 -1
- package/src/lib/services/template-config.service.d.ts +0 -18
- package/src/lib/services/template-config.service.js +0 -66
- package/src/lib/services/template-config.service.js.map +0 -1
- package/src/lib/services/template-engine.registry.d.ts +0 -21
- package/src/lib/services/template-engine.registry.js +0 -94
- package/src/lib/services/template-engine.registry.js.map +0 -1
- package/src/lib/services/template-layout.service.d.ts +0 -24
- package/src/lib/services/template-layout.service.js +0 -301
- package/src/lib/services/template-layout.service.js.map +0 -1
- package/src/lib/services/template.service.d.ts +0 -26
- package/src/lib/services/template.service.js +0 -366
- package/src/lib/services/template.service.js.map +0 -1
- package/src/test/helpers.d.ts +0 -4
- package/src/test/helpers.js.map +0 -1
- package/src/test/test-database.config.d.ts +0 -4
- package/src/test/test-database.config.js +0 -24
- package/src/test/test-database.config.js.map +0 -1
- package/src/test/test.setup.d.ts +0 -3
- package/src/test/test.setup.js +0 -29
- package/src/test/test.setup.js.map +0 -1
- package/tsconfig.tsbuildinfo +0 -1
- /package/src/lib/engines/language/{index.d.ts → index.ts} +0 -0
- /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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
20
|
-
class TemplateEngineError extends
|
|
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
|
-
|
|
31
|
-
class TemplateLanguageError extends
|
|
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
|
-
|
|
42
|
-
class TemplateLayoutError extends
|
|
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
|
-
|
|
53
|
-
class TemplateContentError extends
|
|
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
|
-
|
|
64
|
-
class TemplateValidationError extends
|
|
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
|
-
|
|
13
|
+
|
|
14
|
+
export type EngineOptions<T> = T & {
|
|
15
|
+
|
|
16
|
+
};
|
|
17
|
+
|
|
12
18
|
export type CustomEngineOptions = {
|
|
13
19
|
filters?: Record<string, FilterOptions['filter']>;
|
|
14
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
|
+
}
|