@brilab-mailer/template-handlebars 0.2.0 → 0.2.2

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/cjs/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./lib/handlebars-template.engine.js"), exports);
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ var __importDefault = (this && this.__importDefault) || function (mod) {
48
+ return (mod && mod.__esModule) ? mod : { "default": mod };
49
+ };
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.HandlebarsTemplateEngine = void 0;
52
+ const common_1 = require("@nestjs/common");
53
+ const contracts_1 = require("@brilab-mailer/contracts");
54
+ const fs = __importStar(require("fs"));
55
+ const path = __importStar(require("path"));
56
+ const handlebars_1 = __importDefault(require("handlebars"));
57
+ const juice_1 = __importDefault(require("juice"));
58
+ let HandlebarsTemplateEngine = class HandlebarsTemplateEngine {
59
+ options;
60
+ hbs = handlebars_1.default.create();
61
+ _cached;
62
+ templateCache = new Map();
63
+ layoutCache = new Map();
64
+ templatesDir;
65
+ layoutsDir;
66
+ partialsDir;
67
+ constructor(options) {
68
+ this.options = options;
69
+ this._cached = this.options.cached ?? true;
70
+ const rootDir = this.options?.rootDir ?? process.cwd();
71
+ const templateDir = this.options?.templateDir ?? '';
72
+ const baseDir = path.join(rootDir, templateDir);
73
+ this.templatesDir = baseDir;
74
+ const layoutsFolder = options?.folders?.layouts || 'layouts';
75
+ this.layoutsDir = path.join(baseDir, layoutsFolder);
76
+ const partialsFolder = options?.folders?.partials || 'partials';
77
+ this.partialsDir = path.join(baseDir, partialsFolder);
78
+ this.registerI18nHelpers();
79
+ this.registerPartialsHelpers();
80
+ }
81
+ registerI18nHelpers() {
82
+ if (!this.hbs)
83
+ return;
84
+ const translator = this.options?.translator;
85
+ if (!translator)
86
+ return;
87
+ this.hbs.registerHelper('t', (key, opts) => {
88
+ const root = opts?.data?.root ?? {};
89
+ const hash = opts?.hash ?? {};
90
+ const lang = hash.lang ?? root.lang ?? translator.getLang?.() ?? 'en';
91
+ const { defaultValue, lang: _lang, ...args } = hash;
92
+ const value = translator.t(key, args, lang, defaultValue);
93
+ if (value === key)
94
+ return defaultValue ?? key;
95
+ return value;
96
+ });
97
+ this.hbs.registerHelper('eq', (a, b) => a === b);
98
+ this.hbs.registerHelper('ne', (a, b) => a !== b);
99
+ this.hbs.registerHelper('isNotFalse', (value) => value !== false);
100
+ }
101
+ registerPartialsHelpers() {
102
+ if (!this.hbs)
103
+ return;
104
+ const partialsPath = this.partialsDir;
105
+ if (!fs.existsSync(partialsPath))
106
+ return;
107
+ const registered = new Set();
108
+ const registerRecursive = (dir, namespace = '') => {
109
+ const files = fs.readdirSync(dir, { withFileTypes: true });
110
+ for (const file of files) {
111
+ const fullPath = path.join(dir, file.name);
112
+ if (file.isDirectory()) {
113
+ registerRecursive(fullPath, `${namespace}${file.name}/`);
114
+ continue;
115
+ }
116
+ if (!file.name.toLowerCase().endsWith('.hbs'))
117
+ continue;
118
+ const name = `${namespace}${path.basename(file.name, '.hbs')}`;
119
+ if (this._cached && registered.has(name))
120
+ continue;
121
+ const content = fs.readFileSync(fullPath, 'utf8');
122
+ registered.add(name);
123
+ this.hbs.registerPartial(name, content);
124
+ }
125
+ };
126
+ registerRecursive(partialsPath);
127
+ }
128
+ /**
129
+ * Resolves `<baseDir>/<key>.hbs` and guarantees the result stays inside
130
+ * `baseDir`, rejecting path-traversal keys like `../../etc/passwd`.
131
+ */
132
+ resolveWithin(baseDir, key) {
133
+ const root = path.resolve(baseDir);
134
+ const filePath = path.resolve(root, `${key}.hbs`);
135
+ if (filePath !== root && !filePath.startsWith(root + path.sep)) {
136
+ throw new Error(`Invalid template key (path traversal): ${key}`);
137
+ }
138
+ return filePath;
139
+ }
140
+ loadTemplate(key) {
141
+ if (this._cached && this.templateCache.has(key))
142
+ return this.templateCache.get(key);
143
+ const filePath = this.resolveWithin(this.templatesDir, key);
144
+ if (!fs.existsSync(filePath)) {
145
+ throw new Error(`Template not found: ${filePath}`);
146
+ }
147
+ const source = fs.readFileSync(filePath, 'utf8');
148
+ const compiled = this.hbs.compile(source);
149
+ this.templateCache.set(key, compiled);
150
+ return compiled;
151
+ }
152
+ loadLayout(key) {
153
+ if (this._cached && this.layoutCache.has(key))
154
+ return this.layoutCache.get(key);
155
+ const filePath = this.resolveWithin(this.layoutsDir, key);
156
+ if (!fs.existsSync(filePath))
157
+ return null;
158
+ const source = fs.readFileSync(filePath, 'utf8');
159
+ const compiled = this.hbs.compile(source);
160
+ this.layoutCache.set(key, compiled);
161
+ return compiled;
162
+ }
163
+ async render(templateKey, context = {}, options = { layoutKey: 'default' }) {
164
+ const template = this.loadTemplate(templateKey);
165
+ const bodyHtml = template(context);
166
+ const layout = this.loadLayout(options?.layoutKey || 'default');
167
+ const fullHtml = layout
168
+ ? layout({ ...context, body: bodyHtml })
169
+ : bodyHtml;
170
+ return (0, juice_1.default)(fullHtml);
171
+ }
172
+ };
173
+ exports.HandlebarsTemplateEngine = HandlebarsTemplateEngine;
174
+ exports.HandlebarsTemplateEngine = HandlebarsTemplateEngine = __decorate([
175
+ (0, common_1.Injectable)(),
176
+ __param(0, (0, common_1.Inject)(contracts_1.MAILER_TEMPLATE_ENGINE_OPTIONS)),
177
+ __metadata("design:paramtypes", [Object])
178
+ ], HandlebarsTemplateEngine);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@brilab-mailer/template-handlebars",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "author": "Bohdan Radchenko <radchenkobs@gmail.com>",
5
5
  "type": "module",
6
- "main": "./index.js",
6
+ "main": "./cjs/index.js",
7
7
  "module": "./index.js",
8
8
  "types": "./index.d.ts",
9
9
  "exports": {
@@ -11,7 +11,7 @@
11
11
  ".": {
12
12
  "types": "./index.d.ts",
13
13
  "import": "./index.js",
14
- "default": "./index.js"
14
+ "require": "./cjs/index.js"
15
15
  }
16
16
  },
17
17
  "files": [
@@ -26,8 +26,8 @@
26
26
  "peerDependencies": {
27
27
  "@nestjs/common": "^10.0.0",
28
28
  "@nestjs/config": "^3.0.0",
29
- "@brilab-mailer/contracts": "^0.2.0",
30
- "@brilab-mailer/core": "^0.2.0"
29
+ "@brilab-mailer/contracts": "^0.2.2",
30
+ "@brilab-mailer/core": "^0.2.2"
31
31
  },
32
32
  "peerDependenciesMeta": {
33
33
  "@brilab-mailer/contracts": {