@travetto/email-compiler 8.0.0-alpha.9 → 8.0.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 CHANGED
@@ -13,14 +13,14 @@ npm install @travetto/email-compiler
13
13
  yarn add @travetto/email-compiler
14
14
  ```
15
15
 
16
- This is primarily a set of command line tools for compiling and developing templates. The inputs are compiled files, generally under the `support/` folder, that represents the necessary input for the email compilation. [Email Inky Templates](https://github.com/travetto/travetto/tree/main/module/email-inky#readme "Email Inky templating module") shows this pattern by leveraging [JSX](https://en.wikipedia.org/wiki/JSX_(JavaScript)) bindings for the [inky](https://github.com/zurb/inky) framework, allowing for compile-time checked templates.
16
+ This is primarily a set of command line tools for compiling and developing templates. The inputs are compiled files, generally under the `support/` folder, that represents the necessary input for the email compilation. [Email Inky Templates](https://github.com/travetto/travetto/tree/main/module/email-inky#readme "Email Inky templating module") shows this pattern by leveraging [JSX](https://en.wikipedia.org/wiki/JSX_(JavaScript)) bindings for the [inky](https://github.com/zurb/inky) framework, allowing for compile-time checked templates.
17
17
 
18
18
  ## Asset Management
19
- The templating process involves loading various assets (html, css, images), and so there is provision for asset management and loading. The templating config allows for specifying asset paths, with the following paths (in order of precedence):
19
+ The templating process involves loading various assets (html, css, images), and so there is provision for asset management and loading. The templating config allows for specifying asset paths, with the following paths (in order of precedence):
20
20
  1. `%ROOT%/resources/email`
21
21
  1. `@travetto/email-{engine}/resources/email`
22
22
 
23
- When looking up a resources, every asset folder is consulted, in order, and the first to resolve an asset wins. This allows for overriding of default templating resources, as needed. The compilation process will convert `.email.html` files into `.compiled.html`, `.compiled.text` and `.compiled.subject` suffixes to generate the outputs respectively.
23
+ When looking up a resources, every asset folder is consulted, in order, and the first to resolve an asset wins. This allows for overriding of default templating resources, as needed. The compilation process will convert `.email.html` files into `.compiled.html`, `.compiled.text` and `.compiled.subject` suffixes to generate the outputs respectively.
24
24
 
25
25
  ## Template Extension
26
26
  The template extension points are defined at:
@@ -40,10 +40,10 @@ When referencing an image from the `resources` folder in a template, e.g.
40
40
 
41
41
  **Code: Sample Image Reference**
42
42
  ```html
43
- <img src="/email/logo.png">
43
+ <img src="/email/logo.png" alt="logo" />
44
44
  ```
45
45
 
46
- The image will be extracted out and embedded in the email as a multi part message. This allows for compression and optimization of images as well as externalizing resources that may not be immediately public. The currently supported set of image types are:
46
+ The image will be extracted out and embedded in the email as a multi part message. This allows for compression and optimization of images as well as externalizing resources that may not be immediately public. The currently supported set of image types are:
47
47
  * jpeg
48
48
  * png
49
49
 
@@ -53,18 +53,27 @@ The module provides [Command Line Interface](https://github.com/travetto/travett
53
53
  ## CLI Compilation
54
54
  The module provides [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") support for email template compilation also. Running
55
55
 
56
- **Terminal: Running template compilation**
56
+ ## CLI - email:compile
57
+
58
+ **Terminal: Help for email:compile**
57
59
  ```bash
58
60
  $ trv email:compile --help
59
61
 
60
62
  Usage: email:compile [options]
61
63
 
64
+ Description:
65
+ Compile all email templates into generated runtime artifacts.
66
+
67
+ The command discovers templated inputs (for example, `.email.html`) and emits
68
+ compiled outputs used at runtime (html/text/subject variants). With watch
69
+ enabled, recompilation runs automatically on matching template changes.
70
+
62
71
  Options:
63
- -w, --watch Compile in watch mode
72
+ -w, --watch Recompile templates whenever source templates change.
64
73
  --help display help for command
65
74
  ```
66
75
 
67
- Will convert all `.email.html` files into the appropriate `.compiled.html`, `.compiled.text` and `.compiled.subject` files. These will be used during the running of the application. By default these files are added to the `.gitignore` as they are generally not intended to be saved but to be generated during the build process.
76
+ Will convert all `.email.html` files into the appropriate `.compiled.html`, `.compiled.text` and `.compiled.subject` files. These will be used during the running of the application. By default these files are added to the `.gitignore` as they are generally not intended to be saved but to be generated during the build process.
68
77
 
69
78
  ## VSCode Plugin
70
79
  In addition to command line tools, the [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=arcsine.travetto-plugin) also supports:
package/__index__.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export * from './src/compiler.ts';
2
- export * from './src/util.ts';
2
+ export * from './src/util.ts';
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/email-compiler",
3
- "version": "8.0.0-alpha.9",
4
- "type": "module",
3
+ "version": "8.0.1",
5
4
  "description": "Email compiling module",
6
5
  "keywords": [
7
6
  "email",
@@ -12,8 +11,12 @@
12
11
  "homepage": "https://travetto.io",
13
12
  "license": "MIT",
14
13
  "author": {
15
- "email": "travetto.framework@gmail.com",
16
- "name": "Travetto Framework"
14
+ "name": "Travetto Framework",
15
+ "email": "travetto.framework@gmail.com"
16
+ },
17
+ "repository": {
18
+ "url": "git+https://github.com/travetto/travetto.git",
19
+ "directory": "module/email-compiler"
17
20
  },
18
21
  "files": [
19
22
  "__index__.ts",
@@ -21,26 +24,25 @@
21
24
  "!resources",
22
25
  "support"
23
26
  ],
27
+ "type": "module",
24
28
  "main": "__index__.ts",
25
- "repository": {
26
- "url": "git+https://github.com/travetto/travetto.git",
27
- "directory": "module/email-compiler"
29
+ "publishConfig": {
30
+ "access": "public"
28
31
  },
29
32
  "dependencies": {
30
- "@travetto/config": "^8.0.0-alpha.8",
31
- "@travetto/di": "^8.0.0-alpha.8",
32
- "@travetto/email": "^8.0.0-alpha.8",
33
- "@travetto/image": "^8.0.0-alpha.8",
34
- "@travetto/runtime": "^8.0.0-alpha.8",
35
- "@travetto/worker": "^8.0.0-alpha.8",
36
- "@types/inline-css": "^3.0.4",
33
+ "@css-inline/css-inline": "^0.21.2",
34
+ "@travetto/config": "^8.0.1",
35
+ "@travetto/di": "^8.0.1",
36
+ "@travetto/email": "^8.0.1",
37
+ "@travetto/image": "^8.0.1",
38
+ "@travetto/runtime": "^8.0.1",
39
+ "@travetto/worker": "^8.0.1",
37
40
  "html-entities": "^2.6.0",
38
- "inline-css": "^4.0.3",
39
41
  "purgecss": "^8.0.0",
40
- "sass": "^1.98.0"
42
+ "sass": "^1.104.0"
41
43
  },
42
44
  "peerDependencies": {
43
- "@travetto/cli": "^8.0.0-alpha.13"
45
+ "@travetto/cli": "^8.0.1"
44
46
  },
45
47
  "peerDependenciesMeta": {
46
48
  "@travetto/cli": {
@@ -52,8 +54,5 @@
52
54
  "roles": [
53
55
  "build"
54
56
  ]
55
- },
56
- "publishConfig": {
57
- "access": "public"
58
57
  }
59
58
  }
package/src/compiler.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
- import { TypedObject, RuntimeIndex, Runtime, ExecUtil } from '@travetto/runtime';
5
- import { type EmailCompiled, MailUtil, type EmailTemplateImport, type EmailTemplateModule } from '@travetto/email';
4
+ import { type EmailCompiled, type EmailTemplateImport, type EmailTemplateModule, MailUtil } from '@travetto/email';
6
5
  import { ManifestFileUtil } from '@travetto/manifest';
6
+ import { ExecUtil, Runtime, RuntimeIndex, TypedObject } from '@travetto/runtime';
7
7
 
8
8
  import { EmailCompileUtil } from './util.ts';
9
9
 
@@ -11,7 +11,6 @@ import { EmailCompileUtil } from './util.ts';
11
11
  * Email compilation support
12
12
  */
13
13
  export class EmailCompiler {
14
-
15
14
  /**
16
15
  * Load Template
17
16
  */
@@ -25,13 +24,11 @@ export class EmailCompiler {
25
24
  * Grab list of all available templates
26
25
  */
27
26
  static findAllTemplates(moduleName?: string): string[] {
28
- return RuntimeIndex
29
- .find({
30
- module: module => (!moduleName ? module.roles.includes('std') : moduleName === module.name) && module.production,
31
- folder: folder => folder === 'support',
32
- file: file => EmailCompileUtil.isTemplateFile(file.sourceFile)
33
- })
34
- .map(file => file.sourceFile);
27
+ return RuntimeIndex.find({
28
+ module: module => (!moduleName ? module.roles.includes('std') : moduleName === module.name) && module.production,
29
+ folder: folder => folder === 'support',
30
+ file: file => EmailCompileUtil.isTemplateFile(file.sourceFile)
31
+ }).map(file => file.sourceFile);
35
32
  }
36
33
 
37
34
  /**
@@ -55,14 +52,16 @@ export class EmailCompiler {
55
52
  */
56
53
  static async writeTemplate(file: string, message: EmailCompiled): Promise<void> {
57
54
  const outs = this.getOutputFiles(file);
58
- await Promise.all(TypedObject.keys(outs).map(async key => {
59
- if (message[key]) {
60
- const content = MailUtil.buildBrand(file, message[key], 'trv email:compile');
61
- await ManifestFileUtil.bufferedFileWrite(outs[key], content);
62
- } else {
63
- await fs.rm(outs[key], { force: true }); // Remove file if data not provided
64
- }
65
- }));
55
+ await Promise.all(
56
+ TypedObject.keys(outs).map(async key => {
57
+ if (message[key]) {
58
+ const content = MailUtil.buildBrand(file, message[key], 'trv email:compile');
59
+ await ManifestFileUtil.bufferedFileWrite(outs[key], content);
60
+ } else {
61
+ await fs.rm(outs[key], { force: true }); // Remove file if data not provided
62
+ }
63
+ })
64
+ );
66
65
  }
67
66
 
68
67
  /**
@@ -90,7 +89,7 @@ export class EmailCompiler {
90
89
  static async spawnCompile(file: string): Promise<boolean> {
91
90
  const child = ExecUtil.spawnPackageCommand('trv', ['email:compile', file], {
92
91
  cwd: Runtime.mainSourcePath,
93
- env: { ...process.env },
92
+ env: { ...process.env }
94
93
  });
95
94
 
96
95
  const result = await ExecUtil.getResult(child, { catch: true });
@@ -102,4 +101,4 @@ export class EmailCompiler {
102
101
  }
103
102
  return result.valid;
104
103
  }
105
- }
104
+ }
package/src/util.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import path from 'node:path';
2
+
2
3
  import type { CompileResult, Options } from 'sass';
3
4
 
4
5
  import type { EmailCompiled, EmailTemplateModule, EmailTemplateResource } from '@travetto/email';
5
6
  import { ImageUtil } from '@travetto/image';
6
- import { BinaryUtil, CodecUtil, RuntimeIndex, type BinaryArray } from '@travetto/runtime';
7
+ import { type BinaryArray, BinaryUtil, CodecUtil, RuntimeIndex } from '@travetto/runtime';
7
8
 
8
9
  type Tokenized = {
9
10
  text: string;
@@ -24,7 +25,6 @@ const EXT = /[.]email[.]tsx$/;
24
25
  * Email compile tools
25
26
  */
26
27
  export class EmailCompileUtil {
27
-
28
28
  /**
29
29
  * Is file a template?
30
30
  */
@@ -47,7 +47,7 @@ export class EmailCompileUtil {
47
47
  return {
48
48
  html: this.buildOutputPath(file, '.compiled.html', prefix),
49
49
  subject: this.buildOutputPath(file, '.compiled.subject', prefix),
50
- text: this.buildOutputPath(file, '.compiled.text', prefix),
50
+ text: this.buildOutputPath(file, '.compiled.text', prefix)
51
51
  };
52
52
  }
53
53
 
@@ -62,11 +62,12 @@ export class EmailCompileUtil {
62
62
  let id = 0;
63
63
  const tokens = new Map();
64
64
  for (const pattern of patterns) {
65
- for (const { [0]: all, groups: { prefix, source } = { prefix: '', source: '' } } of text.matchAll(pattern)) {
66
- if (source.includes('://')) { // No urls
65
+ for (const { 0: all, groups: { prefix, source } = { prefix: '', source: '' } } of text.matchAll(pattern)) {
66
+ if (source.includes('://')) {
67
+ // No urls
67
68
  continue;
68
69
  }
69
- const token = `@@${id += 1}@@`;
70
+ const token = `@@${(id += 1)}@@`;
70
71
  tokens.set(token, source);
71
72
  text = text.replace(all, `${prefix}${token}`);
72
73
  }
@@ -85,7 +86,7 @@ export class EmailCompileUtil {
85
86
  const compilerOptions: Options<'async'> = {
86
87
  sourceMap: false,
87
88
  quietDeps: true,
88
- loadPaths: options.loader.searchPaths.slice(0),
89
+ loadPaths: options.loader.searchPaths.slice(0)
89
90
  };
90
91
 
91
92
  let result: CompileResult;
@@ -105,7 +106,7 @@ export class EmailCompileUtil {
105
106
  const purge = new PurgeCSS();
106
107
  const [result] = await purge.purge({
107
108
  content: [{ raw: html, extension: 'html' }],
108
- css: [{ raw: css }],
109
+ css: [{ raw: css }]
109
110
  });
110
111
  return result.css;
111
112
  }
@@ -115,17 +116,14 @@ export class EmailCompileUtil {
115
116
  */
116
117
  static async inlineCss(html: string, css: string): Promise<string> {
117
118
  // Inline css
118
- const { default: inlineCss } = await import('inline-css');
119
- return inlineCss(
120
- // Style needs to be in head to preserve media queries
121
- html.replace('</head>', `<style>${css}</style></head>`),
122
- {
123
- url: 'https://app.dev',
124
- preserveMediaQueries: true,
125
- removeStyleTags: true,
126
- removeLinkTags: true,
127
- applyStyleTags: true,
128
- });
119
+ const { inline } = await import('@css-inline/css-inline');
120
+ return inline(html, {
121
+ extraCss: css,
122
+ keepAtRules: true,
123
+ keepStyleTags: false,
124
+ keepLinkTags: false,
125
+ loadRemoteStylesheets: false
126
+ });
129
127
  }
130
128
 
131
129
  /**
@@ -166,15 +164,17 @@ export class EmailCompileUtil {
166
164
  */
167
165
  static handleHtmlEdgeCases(html: string): string {
168
166
  return html
169
- .replace(/\n{3,100}/msg, '\n\n')
167
+ .replace(/\n{3,100}/gms, '\n\n')
170
168
  .replace(/<(meta|img|link|hr|br)[^>]{0,200}>/g, a => a.replace(/>/g, '/>')) // Fix self closing
171
169
  .replace(/&apos;/g, '&#39;') // Fix apostrophes, as outlook hates them
172
- .replace(/(background(?:-color)?:\s*)([#0-9a-f]{6,8})([^>.#,]+)>/ig,
173
- (all, property, color, rest) => `${property}${color}${rest} bgcolor="${color}">`) // Inline bg-color
174
- .replace(/<([^>]+vertical-align:\s*(top|bottom|middle)[^>]+)>/g,
175
- (a, tag, valign) => tag.indexOf('valign') ? `<${tag}>` : `<${tag} valign="${valign}">`) // Vertically align if it has the style
176
- .replace(/<(table[^>]+expand[^>]+width:\s*)(100%\s+!important)([^>]+)>/g,
177
- (a, left, size, right) => `<${left}100%${right}>`) // Drop important as a fix for outlook
170
+ .replace(
171
+ /(background(?:-color)?:\s*)([#0-9a-f]{6,8})([^>.#,]+)>/gi,
172
+ (all, property, color, rest) => `${property}${color}${rest} bgcolor="${color}">`
173
+ ) // Inline bg-color
174
+ .replace(/<([^>]+vertical-align:\s*(top|bottom|middle)[^>]+)>/g, (a, tag, valign) =>
175
+ tag.indexOf('valign') ? `<${tag}>` : `<${tag} valign="${valign}">`
176
+ ) // Vertically align if it has the style
177
+ .replace(/<(table[^>]+expand[^>]+width:\s*)(100%\s+!important)([^>]+)>/g, (a, left, size, right) => `<${left}100%${right}>`) // Drop important as a fix for outlook
178
178
  .trim()
179
179
  .concat('\n');
180
180
  }
@@ -183,10 +183,7 @@ export class EmailCompileUtil {
183
183
  * Apply styles into a given html document
184
184
  */
185
185
  static async applyStyles(html: string, options: EmailTemplateResource): Promise<string> {
186
- const styles = [
187
- options.globalStyles ?? '',
188
- await options.loader.readUTF8('/email/main.scss').catch(() => '')
189
- ]
186
+ const styles = [options.globalStyles ?? '', await options.loader.readUTF8('/email/main.scss').catch(() => '')]
190
187
  .filter(line => !!line)
191
188
  .join('\n');
192
189
 
@@ -222,4 +219,4 @@ export class EmailCompileUtil {
222
219
 
223
220
  return { html, subject, text };
224
221
  }
225
- }
222
+ }
@@ -1,8 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
+
2
3
  import { parse, stringify } from 'yaml';
3
4
 
4
- import { Runtime } from '@travetto/runtime';
5
5
  import { ManifestFileUtil } from '@travetto/manifest';
6
+ import { Runtime } from '@travetto/runtime';
6
7
 
7
8
  import type { EditorConfigType } from './types.ts';
8
9
 
@@ -12,7 +13,6 @@ export const CONFIG_FILE = 'resources/email/local.yml';
12
13
  * Configuration utils
13
14
  */
14
15
  export class EditorConfig {
15
-
16
16
  static DEFAULT_CONFIG = {
17
17
  to: 'my-email@gmail.com',
18
18
  from: 'from-email@gmail.com',
@@ -25,8 +25,8 @@ export class EditorConfig {
25
25
  auth: {
26
26
  user: 'email@blah.com',
27
27
  pass: 'password'
28
- },
29
- },
28
+ }
29
+ }
30
30
  };
31
31
 
32
32
  /**
@@ -56,4 +56,4 @@ export class EditorConfig {
56
56
  }
57
57
  return resolved;
58
58
  }
59
- }
59
+ }
@@ -1,20 +1,18 @@
1
1
  import { Inject, Injectable } from '@travetto/di';
2
- import { MailUtil, type EmailCompiled, type MailInterpolator } from '@travetto/email';
2
+ import { type EmailCompiled, type MailInterpolator, MailUtil } from '@travetto/email';
3
3
  import { RuntimeError, TypedObject, WatchUtil } from '@travetto/runtime';
4
4
 
5
- import type { EditorSendService } from './send.ts';
6
- import { EditorConfig } from './config.ts';
7
- import type { EditorRequest, EditorResponse } from './types.ts';
8
-
9
5
  import { EmailCompiler } from '../../src/compiler.ts';
10
6
  import { EmailCompileUtil } from '../../src/util.ts';
7
+ import { EditorConfig } from './config.ts';
8
+ import type { EditorSendService } from './send.ts';
9
+ import type { EditorRequest, EditorResponse } from './types.ts';
11
10
 
12
11
  /**
13
12
  * Utils for interacting with editors
14
13
  */
15
14
  @Injectable()
16
15
  export class EditorService {
17
-
18
16
  @Inject()
19
17
  sender: EditorSendService;
20
18
 
@@ -28,12 +26,11 @@ export class EditorService {
28
26
  async #renderTemplate(templateFile: string, context: Record<string, unknown>): Promise<EmailCompiled> {
29
27
  const email = await EmailCompiler.compile(templateFile);
30
28
  return TypedObject.fromEntries(
31
- await Promise.all(TypedObject.entries(email).map(([key, value]) =>
32
- this.#interpolate(value, context).then((result) => [key, result])))
29
+ await Promise.all(TypedObject.entries(email).map(([key, value]) => this.#interpolate(value, context).then(result => [key, result])))
33
30
  );
34
31
  }
35
32
 
36
- async #renderFile(file: string): Promise<{ content: EmailCompiled, file: string }> {
33
+ async #renderFile(file: string): Promise<{ content: EmailCompiled; file: string }> {
37
34
  const content = await this.#renderTemplate(file, await EditorConfig.get('context'));
38
35
  return { content, file };
39
36
  }
@@ -41,7 +38,9 @@ export class EditorService {
41
38
  async #response<T>(operation: Promise<T>, success: (value: T) => EditorResponse, fail?: (error: Error) => EditorResponse): Promise<void> {
42
39
  try {
43
40
  const response = await operation;
44
- if (process.connected) { process.send?.(success(response)); }
41
+ if (process.connected) {
42
+ process.send?.(success(response));
43
+ }
45
44
  } catch (error) {
46
45
  if (fail && process.connected && error && error instanceof Error) {
47
46
  process.send?.(fail(error));
@@ -51,11 +50,11 @@ export class EditorService {
51
50
  }
52
51
  }
53
52
 
54
- async sendFile(file: string, to?: string): Promise<{ to: string, file: string, url?: string | false | undefined }> {
53
+ async sendFile(file: string, to?: string): Promise<{ to: string; file: string; url?: string | false | undefined }> {
55
54
  const config = await EditorConfig.get();
56
55
  to ||= config.to;
57
56
  const content = await this.#renderTemplate(file, config.context ?? {});
58
- return { to, file, ...await this.sender.send({ from: config.from, to, ...content, }) };
57
+ return { to, file, ...(await this.sender.send({ from: config.from, to, ...content })) };
59
58
  }
60
59
 
61
60
  /**
@@ -71,7 +70,8 @@ export class EditorService {
71
70
  return await this.#response(EditorConfig.ensureConfig(), file => ({ type: 'configured', file }));
72
71
  }
73
72
  case 'compile': {
74
- return await this.#response(this.#renderFile(request.file),
73
+ return await this.#response(
74
+ this.#renderFile(request.file),
75
75
  result => ({ type: 'compiled', ...result }),
76
76
  error => ({ type: 'compiled-failed', message: error.message, stack: error.stack, file: request.file })
77
77
  );
@@ -88,13 +88,19 @@ export class EditorService {
88
88
 
89
89
  process.send({ type: 'init' });
90
90
 
91
- await WatchUtil.watchCompilerEvents('change',
92
- ({ file }) => EmailCompiler.spawnCompile(file).then(success =>
93
- success ? this.#response(this.#renderFile(file),
94
- result => ({ type: 'compiled', ...result }),
95
- error => ({ type: 'compiled-failed', message: error.message, stack: error.stack, file })
96
- ) : undefined
97
- ),
98
- ({ file }) => EmailCompileUtil.isTemplateFile(file));
91
+ await WatchUtil.watchCompilerEvents(
92
+ 'change',
93
+ ({ file }) =>
94
+ EmailCompiler.spawnCompile(file).then(success =>
95
+ success
96
+ ? this.#response(
97
+ this.#renderFile(file),
98
+ result => ({ type: 'compiled', ...result }),
99
+ error => ({ type: 'compiled-failed', message: error.message, stack: error.stack, file })
100
+ )
101
+ : undefined
102
+ ),
103
+ ({ file }) => EmailCompileUtil.isTemplateFile(file)
104
+ );
99
105
  }
100
- }
106
+ }
@@ -1,7 +1,7 @@
1
- import { MailService, type EmailOptions, type MailTransport } from '@travetto/email';
2
1
  import { DependencyRegistryIndex, Injectable } from '@travetto/di';
3
- import { toConcrete } from '@travetto/runtime';
2
+ import { type EmailOptions, MailService, type MailTransport } from '@travetto/email';
4
3
  import { Registry } from '@travetto/registry';
4
+ import { toConcrete } from '@travetto/runtime';
5
5
 
6
6
  import { EditorConfig } from './config.ts';
7
7
 
@@ -10,7 +10,6 @@ import { EditorConfig } from './config.ts';
10
10
  */
11
11
  @Injectable()
12
12
  export class EditorSendService {
13
-
14
13
  ethereal = false;
15
14
 
16
15
  async service(): Promise<MailService> {
@@ -22,14 +21,14 @@ export class EditorSendService {
22
21
  try {
23
22
  const { NodemailerTransport } = await import('@travetto/email-nodemailer');
24
23
  const senderConfig = await EditorConfig.get('sender');
25
- const cls = class { };
24
+ const cls = class {};
26
25
  DependencyRegistryIndex.getForRegister(cls).register({
27
26
  candidates: {
28
27
  factory: {
29
28
  candidateType: MailTransportTarget,
30
29
  factory: () => new NodemailerTransport(senderConfig),
31
30
  class: cls,
32
- method: 'factory',
31
+ method: 'factory'
33
32
  }
34
33
  }
35
34
  });
@@ -37,8 +36,12 @@ export class EditorSendService {
37
36
 
38
37
  this.ethereal = !!senderConfig.host?.includes('ethereal.email');
39
38
  } catch {
40
- console.error('A mail transport is currently needed to support sending emails. Please install @travetto/email-nodemailer or any other compatible transport');
41
- throw new Error('A mail transport is currently needed to support sending emails. Please install @travetto/email-nodemailer or any other compatible transport');
39
+ console.error(
40
+ 'A mail transport is currently needed to support sending emails. Please install @travetto/email-nodemailer or any other compatible transport'
41
+ );
42
+ throw new Error(
43
+ 'A mail transport is currently needed to support sending emails. Please install @travetto/email-nodemailer or any other compatible transport'
44
+ );
42
45
  }
43
46
  }
44
47
  const service = await DependencyRegistryIndex.getInstance(MailService);
@@ -72,4 +75,4 @@ export class EditorSendService {
72
75
  throw error;
73
76
  }
74
77
  }
75
- }
78
+ }
@@ -1,15 +1,15 @@
1
1
  export type EditorRequest =
2
- { type: 'configure', file: string } |
3
- { type: 'compile', file: string } |
4
- { type: 'send', file: string, from?: string, to?: string };
2
+ | { type: 'configure'; file: string }
3
+ | { type: 'compile'; file: string }
4
+ | { type: 'send'; file: string; from?: string; to?: string };
5
5
 
6
6
  export type EditorResponse =
7
- { type: 'configured', file: string } |
8
- { type: 'sent', to: string, file: string, url?: string | false } |
9
- { type: 'compiled', file: string, content: Record<'html' | 'subject' | 'text', string> } |
10
- { type: 'sent-failed', message: string, stack: Error['stack'], to: string, file: string } |
11
- { type: 'compiled-failed', message: string, stack: Error['stack'], file: string } |
12
- { type: 'init' };
7
+ | { type: 'configured'; file: string }
8
+ | { type: 'sent'; to: string; file: string; url?: string | false }
9
+ | { type: 'compiled'; file: string; content: Record<'html' | 'subject' | 'text', string> }
10
+ | { type: 'sent-failed'; message: string; stack: Error['stack']; to: string; file: string }
11
+ | { type: 'compiled-failed'; message: string; stack: Error['stack']; file: string }
12
+ | { type: 'init' };
13
13
 
14
14
  export type EditorSender = {
15
15
  port?: number;
@@ -1,17 +1,20 @@
1
+ import { CliCommand, type CliCommandShape, cliTpl } from '@travetto/cli';
1
2
  import { Registry } from '@travetto/registry';
2
- import { type CliCommandShape, CliCommand, cliTpl } from '@travetto/cli';
3
3
  import { Env, Runtime, WatchUtil } from '@travetto/runtime';
4
4
 
5
5
  import { EmailCompiler } from '../src/compiler.ts';
6
6
  import { EmailCompileUtil } from '../src/util.ts';
7
7
 
8
8
  /**
9
- * CLI Entry point for running the email server
9
+ * Compile all email templates into generated runtime artifacts.
10
+ *
11
+ * The command discovers templated inputs (for example, `.email.html`) and emits
12
+ * compiled outputs used at runtime (html/text/subject variants). With watch
13
+ * enabled, recompilation runs automatically on matching template changes.
10
14
  */
11
15
  @CliCommand()
12
16
  export class EmailCompileCommand implements CliCommandShape {
13
-
14
- /** Compile in watch mode */
17
+ /** Recompile templates whenever source templates change. */
15
18
  watch?: boolean;
16
19
 
17
20
  finalize(): void {
@@ -30,9 +33,11 @@ export class EmailCompileCommand implements CliCommandShape {
30
33
  }
31
34
 
32
35
  if (this.watch) {
33
- await WatchUtil.watchCompilerEvents('change',
36
+ await WatchUtil.watchCompilerEvents(
37
+ 'change',
34
38
  ({ file }) => EmailCompiler.spawnCompile(file),
35
- ({ file }) => EmailCompileUtil.isTemplateFile(file));
39
+ ({ file }) => EmailCompileUtil.isTemplateFile(file)
40
+ );
36
41
  }
37
42
  }
38
- }
43
+ }
@@ -1,14 +1,18 @@
1
- import { Env } from '@travetto/runtime';
2
1
  import { CliCommand, CliProfilesFlag } from '@travetto/cli';
3
- import { Registry } from '@travetto/registry';
4
2
  import { DependencyRegistryIndex } from '@travetto/di';
3
+ import { Registry } from '@travetto/registry';
4
+ import { Env } from '@travetto/runtime';
5
5
 
6
6
  import { EditorService } from './bin/editor.ts';
7
7
 
8
- /** The email editor compilation service and output serving */
8
+ /**
9
+ * Start the email template editor service for interactive preview and testing.
10
+ *
11
+ * The editor compiles templates on demand and hosts the preview/test workflow
12
+ * used during local template authoring.
13
+ */
9
14
  @CliCommand()
10
15
  export class EmailEditorCommand {
11
-
12
16
  @CliProfilesFlag()
13
17
  profile: string[];
14
18
 
@@ -21,4 +25,4 @@ export class EmailEditorCommand {
21
25
  const service = await DependencyRegistryIndex.getInstance(EditorService);
22
26
  await service.listen();
23
27
  }
24
- }
28
+ }
@@ -1,18 +1,20 @@
1
1
  import path from 'node:path';
2
2
 
3
- import { Registry } from '@travetto/registry';
4
- import { type CliCommandShape, CliCommand, CliProfilesFlag } from '@travetto/cli';
3
+ import { CliCommand, type CliCommandShape, CliProfilesFlag } from '@travetto/cli';
5
4
  import { DependencyRegistryIndex } from '@travetto/di';
5
+ import { Registry } from '@travetto/registry';
6
6
  import { Env } from '@travetto/runtime';
7
7
 
8
8
  import { EditorService } from './bin/editor.ts';
9
9
 
10
10
  /**
11
- * CLI Entry point for running the email server
11
+ * Render and send a template file to a target recipient for quick validation.
12
+ *
13
+ * This command is useful during template development to verify real delivery and
14
+ * formatting without running the full editor workflow.
12
15
  */
13
16
  @CliCommand()
14
17
  export class EmailTestCommand implements CliCommandShape {
15
-
16
18
  @CliProfilesFlag()
17
19
  profile: string[];
18
20
 
@@ -26,4 +28,4 @@ export class EmailTestCommand implements CliCommandShape {
26
28
  const editor = await DependencyRegistryIndex.getInstance(EditorService);
27
29
  await editor.sendFile(file, to);
28
30
  }
29
- }
31
+ }