@travetto/email-compiler 3.1.22 → 3.1.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/email-compiler",
3
- "version": "3.1.22",
3
+ "version": "3.1.23",
4
4
  "description": "Email compiling module",
5
5
  "keywords": [
6
6
  "email",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@travetto/base": "^3.1.3",
30
- "@travetto/config": "^3.1.7",
30
+ "@travetto/config": "^3.1.8",
31
31
  "@travetto/di": "^3.1.4",
32
- "@travetto/email": "^3.1.13",
32
+ "@travetto/email": "^3.1.14",
33
33
  "@travetto/image": "^3.1.4",
34
34
  "@types/inline-css": "^3.0.1",
35
35
  "html-entities": "^2.3.3",
@@ -38,7 +38,7 @@
38
38
  "purgecss": "^5.0.0"
39
39
  },
40
40
  "peerDependencies": {
41
- "@travetto/cli": "^3.1.8"
41
+ "@travetto/cli": "^3.1.9"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "@travetto/cli": {
package/src/compiler.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
3
  import { FileQueryProvider, TypedObject } from '@travetto/base';
4
- import { MessageCompilationSource, MessageCompiled } from '@travetto/email';
4
+ import { EmailCompileSource, EmailCompiled, EmailCompileContext, MailUtil } from '@travetto/email';
5
5
  import { RootIndex, path } from '@travetto/manifest';
6
6
  import { DynamicFileLoader } from '@travetto/base/src/internal/file-loader';
7
7
 
@@ -15,13 +15,19 @@ const VALID_FILE = (file: string): boolean => /[.](scss|css|png|jpe?g|gif|ya?ml)
15
15
  export class EmailCompiler {
16
16
 
17
17
  /** Load Template */
18
- static async loadTemplate(file: string): Promise<MessageCompilationSource> {
18
+ static async loadTemplate(file: string): Promise<EmailCompileContext> {
19
19
  const entry = RootIndex.getEntry(file);
20
20
  if (!entry) {
21
21
  throw new Error(`Unable to find template for ${file}`);
22
22
  }
23
23
  const root = (await import(entry.outputFile)).default;
24
- const res: MessageCompilationSource = { ...await root.wrap(), file: entry.sourceFile };
24
+ const og: EmailCompileSource = await root.wrap();
25
+ const res = {
26
+ file: entry.sourceFile,
27
+ module: entry.module,
28
+ ...og
29
+ };
30
+
25
31
  const mod = RootIndex.getModule(entry.module)!;
26
32
 
27
33
  const resourcePaths = [
@@ -51,7 +57,7 @@ export class EmailCompiler {
51
57
  /**
52
58
  * Get output files
53
59
  */
54
- static getOutputFiles(file: string): MessageCompiled {
60
+ static getOutputFiles(file: string): EmailCompiled {
55
61
  const entry = RootIndex.getEntry(file)!;
56
62
  const mod = RootIndex.getModule(entry.module)!;
57
63
  return EmailCompileUtil.getOutputs(file, path.join(mod.sourcePath, 'resources'));
@@ -67,12 +73,12 @@ export class EmailCompiler {
67
73
  /**
68
74
  * Write template to file
69
75
  */
70
- static async writeTemplate(file: string, msg: MessageCompiled): Promise<void> {
76
+ static async writeTemplate(file: string, msg: EmailCompiled): Promise<void> {
71
77
  const outs = this.getOutputFiles(file);
72
78
  await Promise.all(TypedObject.keys(outs).map(async k => {
73
79
  if (msg[k]) {
74
80
  await fs.mkdir(path.dirname(outs[k]), { recursive: true });
75
- await fs.writeFile(outs[k], msg[k], { encoding: 'utf8' });
81
+ await fs.writeFile(outs[k], MailUtil.buildBrand(file, msg[k], 'trv email:compile'), { encoding: 'utf8' });
76
82
  } else {
77
83
  await fs.unlink(outs[k]).catch(() => { }); // Remove file if data not provided
78
84
  }
@@ -82,7 +88,7 @@ export class EmailCompiler {
82
88
  /**
83
89
  * Compile a file given a resource provider
84
90
  */
85
- static async compile(file: string, persist: boolean = false): Promise<MessageCompiled> {
91
+ static async compile(file: string, persist: boolean = false): Promise<EmailCompiled> {
86
92
  const src = await this.loadTemplate(file);
87
93
  const compiled = await EmailCompileUtil.compile(src);
88
94
  if (persist) {
package/src/util.ts CHANGED
@@ -2,11 +2,13 @@ import util from 'util';
2
2
  import { Readable } from 'stream';
3
3
 
4
4
  import { FileResourceProvider, StreamUtil } from '@travetto/base';
5
- import { MessageCompilationImages, MessageCompilationSource, MessageCompilationStyles, MessageCompiled } from '@travetto/email';
5
+ import {
6
+ EmailTemplateImageConfig, EmailTemplateStyleConfig,
7
+ EmailCompiled, EmailCompileContext
8
+ } from '@travetto/email';
6
9
  import { ImageConverter } from '@travetto/image';
7
10
  import { path } from '@travetto/manifest';
8
11
 
9
-
10
12
  type Tokenized = {
11
13
  text: string;
12
14
  tokens: Map<string, string>;
@@ -42,7 +44,7 @@ export class EmailCompileUtil {
42
44
  /**
43
45
  * Get the different parts from the file name
44
46
  */
45
- static getOutputs(file: string, prefix?: string): MessageCompiled {
47
+ static getOutputs(file: string, prefix?: string): EmailCompiled {
46
48
  return {
47
49
  html: this.buildOutputPath(file, '.compiled.html', prefix),
48
50
  subject: this.buildOutputPath(file, '.compiled.subject', prefix),
@@ -132,7 +134,7 @@ export class EmailCompileUtil {
132
134
  /**
133
135
  * Inline image sources
134
136
  */
135
- static async inlineImages(html: string, opts: MessageCompilationImages): Promise<string> {
137
+ static async inlineImages(html: string, opts: EmailTemplateImageConfig): Promise<string> {
136
138
  const { tokens, finalize } = await this.tokenizeResources(html, this.#HTML_CSS_IMAGE_URLS);
137
139
  const pendingImages: [token: string, ext: string, stream: Readable | Promise<Readable>][] = [];
138
140
  const resources = new FileResourceProvider(opts.search ?? []);
@@ -157,6 +159,7 @@ export class EmailCompileUtil {
157
159
  */
158
160
  static handleHtmlEdgeCases(html: string): string {
159
161
  return html
162
+ .replace(/\n{3,100}/msg, '\n\n')
160
163
  .replace(/<(meta|img|link|hr|br)[^>]*>/g, a => a.replace('>', '/>')) // Fix self closing
161
164
  .replace(/&apos;/g, '&#39;') // Fix apostrophes, as outlook hates them
162
165
  .replace(/(background(?:-color)?:\s*)([#0-9a-f]{6,8})([^>.#,]+)>/ig,
@@ -164,14 +167,16 @@ export class EmailCompileUtil {
164
167
  .replace(/<([^>]+vertical-align:\s*(top|bottom|middle)[^>]+)>/g,
165
168
  (a, tag, valign) => tag.indexOf('valign') ? `<${tag}>` : `<${tag} valign="${valign}">`) // Vertically align if it has the style
166
169
  .replace(/<(table[^>]+expand[^>]+width:\s*)(100%\s+!important)([^>]+)>/g,
167
- (a, left, size, right) => `<${left}100%${right}>`); // Drop important as a fix for outlook
170
+ (a, left, size, right) => `<${left}100%${right}>`) // Drop important as a fix for outlook
171
+ .trim()
172
+ .concat('\n');
168
173
  }
169
174
 
170
175
 
171
176
  /**
172
177
  * Apply styles into a given html document
173
178
  */
174
- static async applyStyles(html: string, opts: MessageCompilationStyles): Promise<string> {
179
+ static async applyStyles(html: string, opts: EmailTemplateStyleConfig): Promise<string> {
175
180
  const styles: string[] = [];
176
181
 
177
182
  if (opts.global) {
@@ -200,11 +205,11 @@ export class EmailCompileUtil {
200
205
  return html;
201
206
  }
202
207
 
203
- static async compile(src: MessageCompilationSource): Promise<MessageCompiled> {
204
- const subject = await this.simplifiedText(await src.subject());
205
- const text = await this.simplifiedText(await src.text());
208
+ static async compile(src: EmailCompileContext): Promise<EmailCompiled> {
209
+ const subject = await this.simplifiedText(await src.subject(src));
210
+ const text = await this.simplifiedText(await src.text(src));
206
211
 
207
- let html = await src.html();
212
+ let html = await src.html(src);
208
213
 
209
214
  if (src.styles?.inline !== false) {
210
215
  html = await this.applyStyles(html, src.styles!);
@@ -216,6 +221,7 @@ export class EmailCompileUtil {
216
221
  if (src.images?.inline !== false) {
217
222
  html = await this.inlineImages(html, src.images!);
218
223
  }
224
+
219
225
  return { html, subject, text };
220
226
  }
221
227
  }
@@ -1,9 +1,9 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
- import type { MailTemplateEngine, MessageCompiled } from '@travetto/email';
3
+ import { type MailInterpolator, type EmailCompiled } from '@travetto/email';
4
4
  import { DependencyRegistry } from '@travetto/di';
5
5
  import { TypedObject } from '@travetto/base';
6
- import { MailTemplateEngineTarget } from '@travetto/email/src/internal/types';
6
+ import { MailInterpolatorTarget } from '@travetto/email/src/internal/types';
7
7
 
8
8
  import { EmailCompiler } from '../../src/compiler';
9
9
 
@@ -14,20 +14,20 @@ export class EmailCompilationManager {
14
14
 
15
15
  static async createInstance(): Promise<EmailCompilationManager> {
16
16
  return new EmailCompilationManager(
17
- await DependencyRegistry.getInstance<MailTemplateEngine>(MailTemplateEngineTarget),
17
+ await DependencyRegistry.getInstance<MailInterpolator>(MailInterpolatorTarget),
18
18
  );
19
19
  }
20
20
 
21
- engine: MailTemplateEngine;
21
+ engine: MailInterpolator;
22
22
 
23
- constructor(engine: MailTemplateEngine) {
23
+ constructor(engine: MailInterpolator) {
24
24
  this.engine = engine;
25
25
  }
26
26
 
27
27
  /**
28
28
  * Resolve template
29
29
  */
30
- async resolveTemplateParts(file: string): Promise<MessageCompiled> {
30
+ async resolveTemplateParts(file: string): Promise<EmailCompiled> {
31
31
  const files = EmailCompiler.getOutputFiles(file);
32
32
  const missing = await Promise.all(Object.values(files).map(x => fs.stat(file).catch(() => { })));
33
33
 
@@ -41,21 +41,20 @@ export class EmailCompilationManager {
41
41
  .then(content => [key, content] as const)
42
42
  )
43
43
  );
44
- return TypedObject.fromEntries<keyof MessageCompiled, string>(parts);
44
+ return TypedObject.fromEntries<keyof EmailCompiled, string>(parts);
45
45
  }
46
46
 
47
47
  /**
48
48
  * Render
49
49
  * @param rel
50
50
  */
51
- async resolveCompiledTemplate(rel: string, context: Record<string, unknown>): Promise<MessageCompiled> {
51
+ async resolveCompiledTemplate(rel: string, context: Record<string, unknown>): Promise<EmailCompiled> {
52
+ const { MailUtil } = await import('@travetto/email');
52
53
  const { html, text, subject } = await this.resolveTemplateParts(rel);
53
54
 
54
- return {
55
- html: await this.engine.template(html, context),
56
- text: await this.engine.template(text, context),
57
- subject: await this.engine.template(subject, context),
58
- };
59
- }
55
+ const get = (input: string): Promise<string> =>
56
+ Promise.resolve(this.engine.render(input, context)).then(MailUtil.purgeBrand);
60
57
 
58
+ return { html: await get(html), text: await get(text), subject: await get(subject) };
59
+ }
61
60
  }
@@ -1,4 +1,4 @@
1
- import { MailService, MessageOptions, SentMessage } from '@travetto/email';
1
+ import { MailService, EmailOptions, SentEmail } from '@travetto/email';
2
2
  import { MailTransportTarget } from '@travetto/email/src/internal/types';
3
3
  import { DependencyRegistry } from '@travetto/di';
4
4
 
@@ -50,7 +50,7 @@ ${EditorConfig.getDefaultConfig()}`.trim();
50
50
  /**
51
51
  * Resolve template
52
52
  */
53
- static async sendEmail(file: string, message: MessageOptions): Promise<{
53
+ static async sendEmail(file: string, message: EmailOptions): Promise<{
54
54
  url?: string | false;
55
55
  }> {
56
56
  const to = message.to!;
@@ -62,7 +62,7 @@ ${EditorConfig.getDefaultConfig()}`.trim();
62
62
  throw new Error('Node mailer support is missing');
63
63
  }
64
64
 
65
- const info = await svc.send<{ host?: string } & SentMessage>(message);
65
+ const info = await svc.send<{ host?: string } & SentEmail>(message);
66
66
  console.log('Sent email', { to });
67
67
 
68
68
  const senderConfig = await EditorConfig.getSenderConfig(file);
@@ -16,7 +16,8 @@ export class EmailCompileCommand implements CliCommandShape {
16
16
  envInit(): GlobalEnvConfig {
17
17
  return {
18
18
  debug: false,
19
- dynamic: this.watch
19
+ dynamic: this.watch,
20
+ profiles: ['email-dev']
20
21
  };
21
22
  }
22
23
 
@@ -10,7 +10,11 @@ import { EmailCompilationManager } from './bin/manager';
10
10
  export class EmailEditorCommand {
11
11
 
12
12
  envInit(): GlobalEnvConfig {
13
- return { envName: 'dev', dynamic: true };
13
+ return {
14
+ envName: 'dev',
15
+ dynamic: true,
16
+ profiles: ['email-dev']
17
+ };
14
18
  }
15
19
 
16
20
  async main(): Promise<void> {
@@ -16,7 +16,10 @@ import { EmailCompiler } from '../src/compiler';
16
16
  export class EmailTestCommand implements CliCommandShape {
17
17
 
18
18
  envInit(): GlobalEnvConfig {
19
- return { envName: 'dev' };
19
+ return {
20
+ envName: 'dev',
21
+ profiles: ['email-dev']
22
+ };
20
23
  }
21
24
 
22
25
  async main(file: string, to: string): Promise<void> {