glasswork 0.10.0 → 0.11.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 (93) hide show
  1. package/dist/email/compiler/code-generator.d.ts +11 -0
  2. package/dist/email/compiler/code-generator.d.ts.map +1 -0
  3. package/dist/email/compiler/code-generator.js +319 -0
  4. package/dist/email/compiler/code-generator.js.map +1 -0
  5. package/dist/email/compiler/compile-templates.d.ts +68 -0
  6. package/dist/email/compiler/compile-templates.d.ts.map +1 -0
  7. package/dist/email/compiler/compile-templates.js +156 -0
  8. package/dist/email/compiler/compile-templates.js.map +1 -0
  9. package/dist/email/compiler/compiler.d.ts +30 -0
  10. package/dist/email/compiler/compiler.d.ts.map +1 -0
  11. package/dist/email/compiler/compiler.js +510 -0
  12. package/dist/email/compiler/compiler.js.map +1 -0
  13. package/dist/email/compiler/index.d.ts +4 -0
  14. package/dist/email/compiler/index.d.ts.map +1 -0
  15. package/dist/email/compiler/index.js +3 -0
  16. package/dist/email/compiler/index.js.map +1 -0
  17. package/dist/email/compiler/tokenizer.d.ts +7 -0
  18. package/dist/email/compiler/tokenizer.d.ts.map +1 -0
  19. package/dist/email/compiler/tokenizer.js +154 -0
  20. package/dist/email/compiler/tokenizer.js.map +1 -0
  21. package/dist/email/compiler/type-extractor.d.ts +11 -0
  22. package/dist/email/compiler/type-extractor.d.ts.map +1 -0
  23. package/dist/email/compiler/type-extractor.js +296 -0
  24. package/dist/email/compiler/type-extractor.js.map +1 -0
  25. package/dist/email/compiler/types.d.ts +108 -0
  26. package/dist/email/compiler/types.d.ts.map +1 -0
  27. package/dist/email/compiler/types.js +2 -0
  28. package/dist/email/compiler/types.js.map +1 -0
  29. package/dist/email/config.d.ts +89 -0
  30. package/dist/email/config.d.ts.map +1 -0
  31. package/dist/email/config.js +55 -0
  32. package/dist/email/config.js.map +1 -0
  33. package/dist/email/email-service.d.ts +54 -0
  34. package/dist/email/email-service.d.ts.map +1 -0
  35. package/dist/email/email-service.js +94 -0
  36. package/dist/email/email-service.js.map +1 -0
  37. package/dist/email/index.d.ts +12 -0
  38. package/dist/email/index.d.ts.map +1 -0
  39. package/dist/email/index.js +23 -0
  40. package/dist/email/index.js.map +1 -0
  41. package/dist/email/template-registry.d.ts +83 -0
  42. package/dist/email/template-registry.d.ts.map +1 -0
  43. package/dist/email/template-registry.js +85 -0
  44. package/dist/email/template-registry.js.map +1 -0
  45. package/dist/email/templated-email-service.d.ts +59 -0
  46. package/dist/email/templated-email-service.d.ts.map +1 -0
  47. package/dist/email/templated-email-service.js +63 -0
  48. package/dist/email/templated-email-service.js.map +1 -0
  49. package/dist/email/transports/index.d.ts +3 -0
  50. package/dist/email/transports/index.d.ts.map +1 -0
  51. package/dist/email/transports/index.js +3 -0
  52. package/dist/email/transports/index.js.map +1 -0
  53. package/dist/email/transports/mock-transport.d.ts +74 -0
  54. package/dist/email/transports/mock-transport.d.ts.map +1 -0
  55. package/dist/email/transports/mock-transport.js +102 -0
  56. package/dist/email/transports/mock-transport.js.map +1 -0
  57. package/dist/email/transports/ses-transport.d.ts +65 -0
  58. package/dist/email/transports/ses-transport.d.ts.map +1 -0
  59. package/dist/email/transports/ses-transport.js +290 -0
  60. package/dist/email/transports/ses-transport.js.map +1 -0
  61. package/dist/email/types.d.ts +128 -0
  62. package/dist/email/types.d.ts.map +1 -0
  63. package/dist/email/types.js +2 -0
  64. package/dist/email/types.js.map +1 -0
  65. package/dist/email/webhooks/handler-factory.d.ts +41 -0
  66. package/dist/email/webhooks/handler-factory.d.ts.map +1 -0
  67. package/dist/email/webhooks/handler-factory.js +136 -0
  68. package/dist/email/webhooks/handler-factory.js.map +1 -0
  69. package/dist/email/webhooks/index.d.ts +6 -0
  70. package/dist/email/webhooks/index.d.ts.map +1 -0
  71. package/dist/email/webhooks/index.js +9 -0
  72. package/dist/email/webhooks/index.js.map +1 -0
  73. package/dist/email/webhooks/notification-parser.d.ts +45 -0
  74. package/dist/email/webhooks/notification-parser.d.ts.map +1 -0
  75. package/dist/email/webhooks/notification-parser.js +150 -0
  76. package/dist/email/webhooks/notification-parser.js.map +1 -0
  77. package/dist/email/webhooks/signature-verification.d.ts +38 -0
  78. package/dist/email/webhooks/signature-verification.d.ts.map +1 -0
  79. package/dist/email/webhooks/signature-verification.js +179 -0
  80. package/dist/email/webhooks/signature-verification.js.map +1 -0
  81. package/dist/email/webhooks/subscription-handler.d.ts +37 -0
  82. package/dist/email/webhooks/subscription-handler.d.ts.map +1 -0
  83. package/dist/email/webhooks/subscription-handler.js +99 -0
  84. package/dist/email/webhooks/subscription-handler.js.map +1 -0
  85. package/dist/email/webhooks/types.d.ts +187 -0
  86. package/dist/email/webhooks/types.d.ts.map +1 -0
  87. package/dist/email/webhooks/types.js +5 -0
  88. package/dist/email/webhooks/types.js.map +1 -0
  89. package/dist/index.d.ts +9 -0
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +22 -0
  92. package/dist/index.js.map +1 -1
  93. package/package.json +10 -2
@@ -0,0 +1,11 @@
1
+ import type { InferredType, Token } from './types.js';
2
+ /**
3
+ * Generates TypeScript code from tokenized template.
4
+ * Converts control flow markers and variables to JS template literals.
5
+ */
6
+ export declare function generateCode(name: string, tokens: Token[], types: Map<string, InferredType>, html: string): string;
7
+ /**
8
+ * Alternative code generator that produces cleaner output for simple templates
9
+ */
10
+ export declare function generateCodeWithContext(name: string, compiledHtml: string, tokens: Token[], types: Map<string, InferredType>): string;
11
+ //# sourceMappingURL=code-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/email/compiler/code-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAChC,IAAI,EAAE,MAAM,GACX,MAAM,CAgDR;AAoID;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAC/B,MAAM,CAgDR"}
@@ -0,0 +1,319 @@
1
+ import { generateInterface } from './type-extractor.js';
2
+ /**
3
+ * Generates TypeScript code from tokenized template.
4
+ * Converts control flow markers and variables to JS template literals.
5
+ */
6
+ export function generateCode(name, tokens, types, html) {
7
+ const interfaceName = toPascalCase(name) + 'Context';
8
+ const interfaceCode = generateInterface(interfaceName, types);
9
+ // Transform the HTML with control flow markers into template literal
10
+ const transformedHtml = transformToTemplateLiteral(tokens);
11
+ const code = `// This file is auto-generated by @glasswork/email. Do not edit manually.
12
+
13
+ ${interfaceCode}
14
+
15
+ export function render(ctx: ${interfaceName}): { html: string; text: string } {
16
+ const html = \`${escapeBackticks(transformedHtml)}\`;
17
+
18
+ // Auto-generated plain text version
19
+ const text = htmlToText(html);
20
+
21
+ return { html, text };
22
+ }
23
+
24
+ function htmlToText(html: string): string {
25
+ return html
26
+ // Remove style and script tags with content
27
+ .replace(/<style[^>]*>[sS]*?</style>/gi, '')
28
+ .replace(/<script[^>]*>[sS]*?</script>/gi, '')
29
+ // Convert line breaks
30
+ .replace(/<br\\s*\\/?>/gi, '\\n')
31
+ // Convert block elements to newlines
32
+ .replace(/<\\/(p|div|tr|h[1-6])>/gi, '\\n')
33
+ // Convert list items
34
+ .replace(/<li[^>]*>/gi, '• ')
35
+ .replace(/<\\/li>/gi, '\\n')
36
+ // Remove remaining HTML tags
37
+ .replace(/<[^>]+>/g, '')
38
+ // Decode common HTML entities
39
+ .replace(/&nbsp;/gi, ' ')
40
+ .replace(/&amp;/gi, '&')
41
+ .replace(/&lt;/gi, '<')
42
+ .replace(/&gt;/gi, '>')
43
+ .replace(/&quot;/gi, '"')
44
+ .replace(/&#39;/gi, "'")
45
+ // Clean up whitespace
46
+ .replace(/\\n\\s*\\n\\s*\\n/g, '\\n\\n')
47
+ .trim();
48
+ }
49
+ `;
50
+ return code;
51
+ }
52
+ /**
53
+ * Transforms tokens back into a string with JS template literal expressions.
54
+ */
55
+ function transformToTemplateLiteral(tokens) {
56
+ const parts = [];
57
+ const blockStack = [];
58
+ for (const token of tokens) {
59
+ switch (token.type) {
60
+ case 'text':
61
+ parts.push(token.content);
62
+ break;
63
+ case 'variable':
64
+ parts.push(transformVariable(token));
65
+ break;
66
+ case 'if':
67
+ blockStack.push('if');
68
+ parts.push(`\${${transformCondition(token.condition)} ? \``);
69
+ break;
70
+ case 'elseif':
71
+ // Close previous if/elseif block and start new conditional
72
+ parts.push(`\` : ${transformCondition(token.condition)} ? \``);
73
+ break;
74
+ case 'else':
75
+ // Close previous if/elseif block and start else
76
+ parts.push('` : `');
77
+ break;
78
+ case 'each':
79
+ blockStack.push('each');
80
+ parts.push(transformEachStart(token));
81
+ break;
82
+ case 'end': {
83
+ const blockType = blockStack.pop();
84
+ if (blockType === 'if') {
85
+ // Close conditional - if no else was provided, use empty string
86
+ parts.push("` : ''}");
87
+ }
88
+ else if (blockType === 'each') {
89
+ parts.push("`).join('')}");
90
+ }
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ return parts.join('');
96
+ }
97
+ function transformVariable(token) {
98
+ const path = token.path.join('.');
99
+ // Handle loop context variables
100
+ if (token.path[0].startsWith('@')) {
101
+ const loopVar = token.path[0].slice(1); // Remove @ prefix
102
+ switch (loopVar) {
103
+ case 'index':
104
+ return '${__index}';
105
+ case 'first':
106
+ return '${__index === 0}';
107
+ case 'last':
108
+ return '${__index === __array.length - 1}';
109
+ case 'length':
110
+ return '${__array.length}';
111
+ default:
112
+ return `\${${loopVar}}`;
113
+ }
114
+ }
115
+ // Handle expressions with operators (like @index + 1)
116
+ if (token.expression.includes('+') || token.expression.includes('-')) {
117
+ const transformed = token.expression
118
+ .replace(/@index/g, '__index')
119
+ .replace(/@first/g, '(__index === 0)')
120
+ .replace(/@last/g, '(__index === __array.length - 1)')
121
+ .replace(/@length/g, '__array.length');
122
+ return `\${${transformed}}`;
123
+ }
124
+ if (token.defaultValue !== undefined) {
125
+ return `\${${path} ?? '${token.defaultValue}'}`;
126
+ }
127
+ return `\${${path}}`;
128
+ }
129
+ function transformCondition(condition) {
130
+ // Transform loop context variables in conditions
131
+ return condition
132
+ .replace(/@index/g, '__index')
133
+ .replace(/@first/g, '(__index === 0)')
134
+ .replace(/@last/g, '(__index === __array.length - 1)')
135
+ .replace(/@length/g, '__array.length');
136
+ }
137
+ function transformEachStart(token) {
138
+ const { arrayPath, itemName, indexName } = token;
139
+ // Build the map function with proper destructuring
140
+ const mapParams = indexName ? `(${itemName}, ${indexName})` : `(${itemName}, __index)`;
141
+ // Store array reference for @last, @length access
142
+ return `\${(((__array) => __array.map(${mapParams} => \``;
143
+ }
144
+ function toPascalCase(str) {
145
+ return str
146
+ .split(/[-_\s]+/)
147
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
148
+ .join('');
149
+ }
150
+ function escapeBackticks(str) {
151
+ // Escape backticks that aren't part of our template expressions
152
+ // We need to be careful not to escape the ones we're inserting
153
+ return str.replace(/`/g, '\\`');
154
+ }
155
+ /**
156
+ * Alternative code generator that produces cleaner output for simple templates
157
+ */
158
+ export function generateCodeWithContext(name, compiledHtml, tokens, types) {
159
+ const interfaceName = toPascalCase(name) + 'Context';
160
+ const interfaceCode = generateInterface(interfaceName, types);
161
+ // Replace control flow markers and variables in the compiled HTML
162
+ const transformedHtml = transformCompiledHtml(compiledHtml, tokens);
163
+ const code = `// This file is auto-generated by @glasswork/email. Do not edit manually.
164
+
165
+ ${interfaceCode}
166
+
167
+ export function render(ctx: ${interfaceName}): { html: string; text: string } {
168
+ const html = \`${escapeBackticks(transformedHtml)}\`;
169
+
170
+ // Auto-generated plain text version
171
+ const text = htmlToText(html);
172
+
173
+ return { html, text };
174
+ }
175
+
176
+ function htmlToText(html: string): string {
177
+ return html
178
+ // Remove style and script tags with content
179
+ .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')
180
+ .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')
181
+ // Convert line breaks
182
+ .replace(/<br\\s*\\/?>/gi, '\\n')
183
+ // Convert block elements to newlines
184
+ .replace(/<\\/(p|div|tr|h[1-6])>/gi, '\\n')
185
+ // Convert list items
186
+ .replace(/<li[^>]*>/gi, '• ')
187
+ .replace(/<\\/li>/gi, '\\n')
188
+ // Remove remaining HTML tags
189
+ .replace(/<[^>]+>/g, '')
190
+ // Decode common HTML entities
191
+ .replace(/&nbsp;/gi, ' ')
192
+ .replace(/&amp;/gi, '&')
193
+ .replace(/&lt;/gi, '<')
194
+ .replace(/&gt;/gi, '>')
195
+ .replace(/&quot;/gi, '"')
196
+ .replace(/&#39;/gi, "'")
197
+ // Clean up whitespace
198
+ .replace(/\\n\\s*\\n\\s*\\n/g, '\\n\\n')
199
+ .trim();
200
+ }
201
+ `;
202
+ return code;
203
+ }
204
+ /**
205
+ * Transforms compiled HTML (from MJML) by replacing markers with template expressions.
206
+ * This is the second pass of the two-pass compilation.
207
+ */
208
+ function transformCompiledHtml(html, tokens) {
209
+ // Build a map of original positions to replacements
210
+ // Since MJML compilation may have moved things around, we use regex to find markers
211
+ let result = html;
212
+ // Transform control flow markers (they're preserved in HTML comments)
213
+ result = result.replace(/<!--\s*@if\s+([^-]*?)\s*-->/g, (_match, condition) => {
214
+ return `\${${transformCondition(condition.trim())} ? \``;
215
+ });
216
+ result = result.replace(/<!--\s*@elseif\s+([^-]*?)\s*-->/g, (_match, condition) => {
217
+ return `\` : ${transformCondition(condition.trim())} ? \``;
218
+ });
219
+ result = result.replace(/<!--\s*@else\s*-->/g, '` : `');
220
+ // Handle @each - need to track which @end belongs to which @each/@if
221
+ const eachPattern = /<!--\s*@each\s+(\S+)\s+as\s+(\w+)(?:\s*,\s*(\w+))?\s*-->/g;
222
+ result = result.replace(eachPattern, (_match, arrayPath, itemName, indexName) => {
223
+ const mapParams = indexName ? `(${itemName}, ${indexName})` : `(${itemName}, __index)`;
224
+ return `\${(((__array) => __array.map(${mapParams} => \``;
225
+ });
226
+ // Handle @end markers - need context to know if it's if or each
227
+ // For simplicity in spike, we'll use a simple approach
228
+ result = transformEndMarkers(result);
229
+ // Transform variables
230
+ result = result.replace(/\{\{([^}]+)\}\}/g, (_match, expression) => {
231
+ const trimmed = expression.trim();
232
+ // Check for default value
233
+ const defaultMatch = trimmed.match(/^(.+?)\s*\?\?\s*['"]([^'"]*)['"]\s*$/);
234
+ if (defaultMatch) {
235
+ return `\${${defaultMatch[1].trim()} ?? '${defaultMatch[2]}'}`;
236
+ }
237
+ // Handle loop context variables
238
+ if (trimmed.startsWith('@')) {
239
+ const loopVar = trimmed.slice(1);
240
+ switch (loopVar) {
241
+ case 'index':
242
+ return '${__index}';
243
+ case 'first':
244
+ return '${__index === 0}';
245
+ case 'last':
246
+ return '${__index === __array.length - 1}';
247
+ case 'length':
248
+ return '${__array.length}';
249
+ }
250
+ }
251
+ // Handle expressions with loop context
252
+ if (trimmed.includes('@')) {
253
+ const transformed = trimmed
254
+ .replace(/@index/g, '__index')
255
+ .replace(/@first/g, '(__index === 0)')
256
+ .replace(/@last/g, '(__index === __array.length - 1)')
257
+ .replace(/@length/g, '__array.length');
258
+ return `\${${transformed}}`;
259
+ }
260
+ return `\${ctx.${trimmed}}`;
261
+ });
262
+ return result;
263
+ }
264
+ /**
265
+ * Transforms @end markers based on context.
266
+ * This is tricky because we need to know if it ends an @if or @each.
267
+ */
268
+ function transformEndMarkers(html) {
269
+ // Track block types as we encounter them
270
+ const blockStack = [];
271
+ const markers = [];
272
+ // Find all markers
273
+ const markerPattern = /<!--\s*@(if|elseif|else|each|end)(?:\s+[^-]*?)?\s*-->/g;
274
+ let match;
275
+ while ((match = markerPattern.exec(html)) !== null) {
276
+ const type = match[1];
277
+ markers.push({ type, index: match.index, length: match[0].length });
278
+ }
279
+ // Process markers in order and build replacement map
280
+ const replacements = [];
281
+ for (const marker of markers) {
282
+ if (marker.type === 'if') {
283
+ blockStack.push('if');
284
+ }
285
+ else if (marker.type === 'each') {
286
+ blockStack.push('each');
287
+ }
288
+ else if (marker.type === 'end') {
289
+ const blockType = blockStack.pop();
290
+ if (blockType === 'if') {
291
+ replacements.push({
292
+ index: marker.index,
293
+ length: marker.length,
294
+ replacement: "` : ''}",
295
+ });
296
+ }
297
+ else if (blockType === 'each') {
298
+ replacements.push({
299
+ index: marker.index,
300
+ length: marker.length,
301
+ replacement: "`).join('')})(ctx." + findArrayPath(markers, replacements.length) + ')}', // This is simplified
302
+ });
303
+ }
304
+ }
305
+ }
306
+ // Apply replacements in reverse order to preserve indices
307
+ let result = html;
308
+ for (let i = replacements.length - 1; i >= 0; i--) {
309
+ const { index, length, replacement } = replacements[i];
310
+ result = result.slice(0, index) + replacement + result.slice(index + length);
311
+ }
312
+ return result;
313
+ }
314
+ // Helper to find the array path for an @each block
315
+ function findArrayPath(markers, endIndex) {
316
+ // This is a simplified version - in a real implementation we'd track this properly
317
+ return 'items';
318
+ }
319
+ //# sourceMappingURL=code-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-generator.js","sourceRoot":"","sources":["../../../src/email/compiler/code-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Options for the template compiler
3
+ */
4
+ export interface CompileOptions {
5
+ /** Source directory containing MJML templates */
6
+ sourceDir: string;
7
+ /** Output directory for compiled TypeScript files */
8
+ outputDir: string;
9
+ /** Base layout file to wrap templates (optional) */
10
+ layoutFile?: string;
11
+ /** Layout content marker to replace with template content */
12
+ layoutMarker?: string;
13
+ /** Whether to generate an index file */
14
+ generateIndex?: boolean;
15
+ /** File extension for templates (default: .mjml) */
16
+ templateExtension?: string;
17
+ /** Subdirectories to exclude (default: ['layouts']) */
18
+ excludeDirs?: string[];
19
+ /** MJML compiler function (must be provided) */
20
+ mjmlCompile: (mjml: string) => {
21
+ html: string;
22
+ errors: Array<{
23
+ message: string;
24
+ }>;
25
+ };
26
+ }
27
+ /**
28
+ * Compiled template info
29
+ */
30
+ export interface CompiledTemplateInfo {
31
+ /** Template name */
32
+ name: string;
33
+ /** Subject line (if found in template) */
34
+ subject?: string;
35
+ }
36
+ /**
37
+ * Result of compiling templates
38
+ */
39
+ export interface CompileResult {
40
+ /** Number of templates compiled */
41
+ count: number;
42
+ /** List of compiled template info */
43
+ templates: CompiledTemplateInfo[];
44
+ /** Any errors encountered */
45
+ errors: Array<{
46
+ template: string;
47
+ error: string;
48
+ }>;
49
+ }
50
+ /**
51
+ * Compiles all MJML templates in a directory
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import mjml2html from 'mjml';
56
+ * import { compileTemplates } from '@glasswork/email/compiler';
57
+ *
58
+ * const result = await compileTemplates({
59
+ * sourceDir: './src/templates',
60
+ * outputDir: './src/templates.compiled',
61
+ * mjmlCompile: (source) => mjml2html(source, { validationLevel: 'soft' }),
62
+ * });
63
+ *
64
+ * console.log(`Compiled ${result.count} templates`);
65
+ * ```
66
+ */
67
+ export declare function compileTemplates(options: CompileOptions): CompileResult;
68
+ //# sourceMappingURL=compile-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-templates.d.ts","sourceRoot":"","sources":["../../../src/email/compiler/compile-templates.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CACrF;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,6BAA6B;IAC7B,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CA0EvE"}
@@ -0,0 +1,156 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { createLogger } from '../../utils/logger.js';
4
+ import { compile } from './compiler.js';
5
+ const logger = createLogger('TemplateCompiler');
6
+ /**
7
+ * Compiles all MJML templates in a directory
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import mjml2html from 'mjml';
12
+ * import { compileTemplates } from '@glasswork/email/compiler';
13
+ *
14
+ * const result = await compileTemplates({
15
+ * sourceDir: './src/templates',
16
+ * outputDir: './src/templates.compiled',
17
+ * mjmlCompile: (source) => mjml2html(source, { validationLevel: 'soft' }),
18
+ * });
19
+ *
20
+ * console.log(`Compiled ${result.count} templates`);
21
+ * ```
22
+ */
23
+ export function compileTemplates(options) {
24
+ const { sourceDir, outputDir, layoutFile, layoutMarker = '<!-- content -->', generateIndex = true, templateExtension = '.mjml', excludeDirs = ['layouts'], mjmlCompile, } = options;
25
+ // Ensure output directory exists
26
+ mkdirSync(outputDir, { recursive: true });
27
+ // Read layout if provided
28
+ let layout = null;
29
+ if (layoutFile && existsSync(layoutFile)) {
30
+ layout = readFileSync(layoutFile, 'utf-8');
31
+ }
32
+ // Find all template files
33
+ const templateFiles = findTemplateFiles(sourceDir, templateExtension, excludeDirs);
34
+ const result = {
35
+ count: 0,
36
+ templates: [],
37
+ errors: [],
38
+ };
39
+ // Compile each template
40
+ for (const templatePath of templateFiles) {
41
+ const templateName = basename(templatePath, templateExtension);
42
+ try {
43
+ // Read template content
44
+ let source = readFileSync(templatePath, 'utf-8');
45
+ // If layout is provided and template doesn't have full MJML structure, wrap it
46
+ if (layout && !source.trim().startsWith('<mjml')) {
47
+ source = layout.replace(layoutMarker, source);
48
+ }
49
+ // Compile the template
50
+ const compiled = compile(source, templateName, mjmlCompile);
51
+ // Write the compiled output
52
+ const outputPath = join(outputDir, `${templateName}.ts`);
53
+ writeFileSync(outputPath, compiled.source);
54
+ result.count++;
55
+ result.templates.push({
56
+ name: templateName,
57
+ subject: compiled.subject,
58
+ });
59
+ logger.info(`✓ Compiled ${templateName}${compiled.subject ? ` (subject: "${compiled.subject}")` : ''}`);
60
+ }
61
+ catch (error) {
62
+ const errorMessage = error instanceof Error ? error.message : String(error);
63
+ result.errors.push({ template: templateName, error: errorMessage });
64
+ logger.error(`✗ Failed to compile ${templateName}: ${errorMessage}`);
65
+ }
66
+ }
67
+ // Generate index file
68
+ if (generateIndex && result.templates.length > 0) {
69
+ const indexContent = generateIndexFile(result.templates);
70
+ writeFileSync(join(outputDir, 'index.ts'), indexContent);
71
+ logger.info('✓ Generated index.ts');
72
+ }
73
+ return result;
74
+ }
75
+ /**
76
+ * Escapes a string for use in a JavaScript string literal
77
+ */
78
+ function escapeString(str) {
79
+ return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
80
+ }
81
+ /**
82
+ * Finds all template files in a directory recursively
83
+ */
84
+ function findTemplateFiles(dir, extension, excludeDirs) {
85
+ const files = [];
86
+ if (!existsSync(dir)) {
87
+ return files;
88
+ }
89
+ const entries = readdirSync(dir, { withFileTypes: true });
90
+ for (const entry of entries) {
91
+ const fullPath = join(dir, entry.name);
92
+ if (entry.isDirectory()) {
93
+ if (!excludeDirs.includes(entry.name)) {
94
+ files.push(...findTemplateFiles(fullPath, extension, excludeDirs));
95
+ }
96
+ }
97
+ else if (entry.isFile() && entry.name.endsWith(extension)) {
98
+ files.push(fullPath);
99
+ }
100
+ }
101
+ return files;
102
+ }
103
+ /**
104
+ * Generates an index file that re-exports all templates
105
+ */
106
+ function generateIndexFile(templates) {
107
+ const imports = [];
108
+ const registrations = [];
109
+ for (const template of templates) {
110
+ const varName = toCamelCase(template.name);
111
+ imports.push(`import * as ${varName} from './${template.name}.js';`);
112
+ // Include subject if available
113
+ if (template.subject) {
114
+ registrations.push(` .register('${template.name}', ${varName}.render, { subject: '${escapeString(template.subject)}' })`);
115
+ }
116
+ else {
117
+ registrations.push(` .register('${template.name}', ${varName}.render)`);
118
+ }
119
+ }
120
+ return `// This file is auto-generated. Do not edit manually.
121
+ import {
122
+ createTemplateRegistry,
123
+ type TemplatedEmailService,
124
+ type TemplateRegistry,
125
+ } from 'glasswork';
126
+
127
+ ${imports.join('\n')}
128
+
129
+ /**
130
+ * Registry of all compiled email templates
131
+ */
132
+ export const templates = createTemplateRegistry()
133
+ ${registrations.join('\n')};
134
+
135
+ // Re-export individual templates for direct access
136
+ ${templates.map((t) => `export * as ${toCamelCase(t.name)} from './${t.name}.js';`).join('\n')}
137
+
138
+ // Type utilities for dependency injection
139
+ type InferTemplates<T> = T extends TemplateRegistry<infer U> ? U : never;
140
+
141
+ /** Template definitions for type-safe email sending */
142
+ export type Templates = InferTemplates<typeof templates>;
143
+
144
+ /** Typed email service for dependency injection */
145
+ export type EmailService = TemplatedEmailService<Templates>;
146
+ `;
147
+ }
148
+ /**
149
+ * Converts a string to camelCase
150
+ */
151
+ function toCamelCase(str) {
152
+ return str
153
+ .replace(/[-_.]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
154
+ .replace(/^[A-Z]/, (char) => char.toLowerCase());
155
+ }
156
+ //# sourceMappingURL=compile-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-templates.js","sourceRoot":"","sources":["../../../src/email/compiler/compile-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,MAAM,MAAM,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;AA8ChD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACtD,MAAM,EACJ,SAAS,EACT,SAAS,EACT,UAAU,EACV,YAAY,GAAG,kBAAkB,EACjC,aAAa,GAAG,IAAI,EACpB,iBAAiB,GAAG,OAAO,EAC3B,WAAW,GAAG,CAAC,SAAS,CAAC,EACzB,WAAW,GACZ,GAAG,OAAO,CAAC;IAEZ,iCAAiC;IACjC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,0BAA0B;IAC1B,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACzC,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,0BAA0B;IAC1B,MAAM,aAAa,GAAG,iBAAiB,CAAC,SAAS,EAAE,iBAAiB,EAAE,WAAW,CAAC,CAAC;IAEnF,MAAM,MAAM,GAAkB;QAC5B,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,EAAE;QACb,MAAM,EAAE,EAAE;KACX,CAAC;IAEF,wBAAwB;IACxB,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAE/D,IAAI,CAAC;YACH,wBAAwB;YACxB,IAAI,MAAM,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YAEjD,+EAA+E;YAC/E,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAChD,CAAC;YAED,uBAAuB;YACvB,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;YAE5D,4BAA4B;YAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC;YACzD,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YAE3C,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,QAAQ,CAAC,OAAO;aAC1B,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CACT,cAAc,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3F,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YACpE,MAAM,CAAC,KAAK,CAAC,uBAAuB,YAAY,KAAK,YAAY,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,sBAAsB;IACtB,IAAI,aAAa,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzD,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,GAAW,EAAE,SAAiB,EAAE,WAAqB;IAC9E,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtC,KAAK,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,SAAiC;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,eAAe,OAAO,YAAY,QAAQ,CAAC,IAAI,OAAO,CAAC,CAAC;QAErE,+BAA+B;QAC/B,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,aAAa,CAAC,IAAI,CAChB,gBAAgB,QAAQ,CAAC,IAAI,MAAM,OAAO,wBAAwB,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CACvG,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,IAAI,CAAC,gBAAgB,QAAQ,CAAC,IAAI,MAAM,OAAO,UAAU,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,OAAO;;;;;;;EAOP,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;EAMlB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;;;EAGxB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;CAU7F,CAAC;AACF,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG;SACP,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACrE,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { CompiledTemplate } from './types.js';
2
+ /**
3
+ * Two-pass MJML template compiler.
4
+ *
5
+ * Pass 1: MJML → HTML (preserving control flow markers in comments)
6
+ * Pass 2: HTML with markers → TypeScript template literal function
7
+ *
8
+ * This approach allows us to:
9
+ * 1. Use the full power of MJML at build time
10
+ * 2. Generate zero-dependency TypeScript render functions
11
+ * 3. Infer TypeScript types from template usage
12
+ */
13
+ /**
14
+ * Compiles an MJML template with control flow syntax to a TypeScript render function.
15
+ *
16
+ * @param source - MJML source with control flow markers
17
+ * @param name - Template name (used for interface naming)
18
+ * @param mjmlCompile - MJML compiler function (injected to avoid bundling MJML)
19
+ * @returns Compiled TypeScript source code
20
+ */
21
+ export declare function compile(source: string, name: string, mjmlCompile: (mjml: string) => {
22
+ html: string;
23
+ errors: Array<{
24
+ message: string;
25
+ }>;
26
+ }): CompiledTemplate;
27
+ export { tokenize } from './tokenizer.js';
28
+ export { extractTypes, generateInterface } from './type-extractor.js';
29
+ export type { CompiledTemplate, CompilerOptions, Token } from './types.js';
30
+ //# sourceMappingURL=compiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../../src/email/compiler/compiler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,YAAY,CAAC;AAEjE;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAClF,gBAAgB,CA2BlB;AAyjBD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}