@travetto/email-compiler 3.1.21 → 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 +4 -4
- package/src/compiler.ts +14 -7
- package/src/util.ts +16 -10
- package/support/bin/config.ts +17 -14
- package/support/bin/editor.ts +12 -8
- package/support/bin/manager.ts +13 -14
- package/support/bin/send.ts +14 -11
- package/support/cli.email_compile.ts +2 -1
- package/support/cli.email_editor.ts +4 -9
- package/support/cli.email_test.ts +7 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/email-compiler",
|
|
3
|
-
"version": "3.1.
|
|
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.
|
|
30
|
+
"@travetto/config": "^3.1.8",
|
|
31
31
|
"@travetto/di": "^3.1.4",
|
|
32
|
-
"@travetto/email": "^3.1.
|
|
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.
|
|
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 {
|
|
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<
|
|
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
|
|
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):
|
|
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:
|
|
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<
|
|
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) {
|
|
@@ -128,6 +134,7 @@ export class EmailCompiler {
|
|
|
128
134
|
try {
|
|
129
135
|
if (EmailCompileUtil.isTemplateFile(file)) {
|
|
130
136
|
await this.compile(file, true);
|
|
137
|
+
console.log(`Successfully compiled ${1} templates`, { changed: [file] });
|
|
131
138
|
yield file;
|
|
132
139
|
} else if (VALID_FILE(file)) {
|
|
133
140
|
const rootFile = file.replace(/\/resources.*/, '/package.json');
|
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 {
|
|
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):
|
|
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:
|
|
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(/'/g, ''') // 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}>`)
|
|
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:
|
|
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:
|
|
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
|
}
|
package/support/bin/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
|
|
3
|
-
import { path } from '@travetto/manifest';
|
|
3
|
+
import { RootIndex, path } from '@travetto/manifest';
|
|
4
4
|
import { YamlUtil } from '@travetto/yaml';
|
|
5
5
|
|
|
6
6
|
interface ConfigType {
|
|
@@ -22,7 +22,7 @@ interface ConfigType {
|
|
|
22
22
|
*/
|
|
23
23
|
export class $EditorConfig {
|
|
24
24
|
|
|
25
|
-
#configFile =
|
|
25
|
+
#configFile: Record<string, string> = {};
|
|
26
26
|
#defaultConfig = {
|
|
27
27
|
to: 'my-email@gmail.com',
|
|
28
28
|
from: 'from-email@gmail.com',
|
|
@@ -42,9 +42,10 @@ export class $EditorConfig {
|
|
|
42
42
|
/**
|
|
43
43
|
*
|
|
44
44
|
*/
|
|
45
|
-
async get(): Promise<ConfigType> {
|
|
45
|
+
async get(file: string): Promise<ConfigType> {
|
|
46
46
|
try {
|
|
47
|
-
const
|
|
47
|
+
const mod = RootIndex.getModuleFromSource(file)!.name;
|
|
48
|
+
const content = await fs.readFile(this.#configFile[mod], 'utf8');
|
|
48
49
|
return YamlUtil.parse<ConfigType>(content);
|
|
49
50
|
} catch {
|
|
50
51
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -52,13 +53,13 @@ export class $EditorConfig {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
async getContext(): Promise<Exclude<ConfigType['context'], undefined>> {
|
|
56
|
-
const conf = await this.get();
|
|
56
|
+
async getContext(file: string): Promise<Exclude<ConfigType['context'], undefined>> {
|
|
57
|
+
const conf = await this.get(file);
|
|
57
58
|
return conf.context ?? {};
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
async getSenderConfig(): Promise<Exclude<ConfigType['sender'], undefined>> {
|
|
61
|
-
const conf = await this.get();
|
|
61
|
+
async getSenderConfig(file: string): Promise<Exclude<ConfigType['sender'], undefined>> {
|
|
62
|
+
const conf = await this.get(file);
|
|
62
63
|
return conf.sender ?? {};
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -66,13 +67,15 @@ export class $EditorConfig {
|
|
|
66
67
|
return YamlUtil.serialize(this.#defaultConfig);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
async ensureConfig(): Promise<string> {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
async ensureConfig(file: string): Promise<string> {
|
|
71
|
+
console.log('Ensuring config', file);
|
|
72
|
+
const mod = RootIndex.getModuleFromSource(file)!;
|
|
73
|
+
const resolved = this.#configFile[mod.name] ??= path.resolve(mod.sourcePath, 'resources/email/dev.yml');
|
|
74
|
+
if (!(await fs.stat(resolved).catch(() => { }))) {
|
|
75
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
76
|
+
await fs.writeFile(resolved, this.getDefaultConfig(), { encoding: 'utf8' });
|
|
74
77
|
}
|
|
75
|
-
return
|
|
78
|
+
return resolved;
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
package/support/bin/editor.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ShutdownManager } from '@travetto/base';
|
|
2
|
+
|
|
1
3
|
import { EmailCompilationManager } from './manager';
|
|
2
4
|
import { EditorSendService } from './send';
|
|
3
5
|
import { EditorConfig } from './config';
|
|
@@ -6,7 +8,7 @@ import { EmailCompiler } from '../../src/compiler';
|
|
|
6
8
|
import { EmailCompileUtil } from '../../src/util';
|
|
7
9
|
|
|
8
10
|
type InboundMessage =
|
|
9
|
-
{ type: 'configure' } |
|
|
11
|
+
{ type: 'configure', file: string } |
|
|
10
12
|
{ type: 'redraw', file: string } |
|
|
11
13
|
{ type: 'send', file: string, from?: string, to?: string };
|
|
12
14
|
|
|
@@ -23,12 +25,10 @@ type OutboundMessage =
|
|
|
23
25
|
export class EditorState {
|
|
24
26
|
|
|
25
27
|
#lastFile = '';
|
|
26
|
-
#sender: EditorSendService;
|
|
27
28
|
#template: EmailCompilationManager;
|
|
28
29
|
|
|
29
30
|
constructor(template: EmailCompilationManager) {
|
|
30
31
|
this.#template = template;
|
|
31
|
-
this.#sender = new EditorSendService();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
async renderFile(file: string): Promise<void> {
|
|
@@ -36,7 +36,7 @@ export class EditorState {
|
|
|
36
36
|
if (file) {
|
|
37
37
|
try {
|
|
38
38
|
const content = await this.#template.resolveCompiledTemplate(
|
|
39
|
-
file, await EditorConfig.getContext()
|
|
39
|
+
file, await EditorConfig.getContext(file)
|
|
40
40
|
);
|
|
41
41
|
this.response({
|
|
42
42
|
type: 'changed',
|
|
@@ -60,7 +60,7 @@ export class EditorState {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
async onConfigure(msg: InboundMessage & { type: 'configure' }): Promise<void> {
|
|
63
|
-
this.response({ type: 'configured', file: await EditorConfig.ensureConfig() });
|
|
63
|
+
this.response({ type: 'configured', file: await EditorConfig.ensureConfig(msg.file) });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async #onRedraw(msg: InboundMessage & { type: 'redraw' }): Promise<void> {
|
|
@@ -77,15 +77,15 @@ export class EditorState {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
async onSend(msg: InboundMessage & { type: 'send' }): Promise<void> {
|
|
80
|
-
const cfg = await EditorConfig.get();
|
|
80
|
+
const cfg = await EditorConfig.get(msg.file);
|
|
81
81
|
const to = msg.to || cfg.to;
|
|
82
82
|
const from = msg.from || cfg.from;
|
|
83
83
|
const content = await this.#template.resolveCompiledTemplate(
|
|
84
|
-
msg.file
|
|
84
|
+
msg.file, await EditorConfig.getContext(msg.file)
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const url = await
|
|
88
|
+
const url = await EditorSendService.sendEmail(msg.file, { from, to, ...content, });
|
|
89
89
|
this.response({ type: 'sent', to, file: msg.file, ...url });
|
|
90
90
|
} catch (err) {
|
|
91
91
|
if (err && err instanceof Error) {
|
|
@@ -107,6 +107,10 @@ export class EditorState {
|
|
|
107
107
|
case 'send': this.onSend(msg); break;
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
|
+
|
|
111
|
+
process.on('disconnect', () => ShutdownManager.execute());
|
|
112
|
+
process.send?.('ready');
|
|
113
|
+
|
|
110
114
|
for await (const f of EmailCompiler.watchCompile()) {
|
|
111
115
|
await this.renderFile(f);
|
|
112
116
|
}
|
package/support/bin/manager.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
|
|
3
|
-
import type
|
|
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 {
|
|
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<
|
|
17
|
+
await DependencyRegistry.getInstance<MailInterpolator>(MailInterpolatorTarget),
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
engine:
|
|
21
|
+
engine: MailInterpolator;
|
|
22
22
|
|
|
23
|
-
constructor(engine:
|
|
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<
|
|
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
|
|
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<
|
|
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
|
-
|
|
55
|
-
|
|
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
|
}
|
package/support/bin/send.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import { MailService,
|
|
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
|
|
|
5
5
|
import { EditorConfig } from './config';
|
|
6
|
+
import { RootIndex } from '@travetto/manifest';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Util for sending emails
|
|
9
10
|
*/
|
|
10
11
|
export class EditorSendService {
|
|
11
12
|
|
|
12
|
-
#svc: MailService;
|
|
13
|
+
static #svc: Record<string, MailService> = {};
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Get mail service
|
|
16
17
|
*/
|
|
17
|
-
async getMailService(): Promise<MailService> {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
static async getMailService(file: string): Promise<MailService> {
|
|
19
|
+
const mod = RootIndex.getModuleFromSource(file)!.name;
|
|
20
|
+
|
|
21
|
+
if (!this.#svc[mod]) {
|
|
22
|
+
const senderConfig = await EditorConfig.getSenderConfig(file);
|
|
20
23
|
|
|
21
24
|
if (senderConfig?.host?.includes('ethereal.email')) {
|
|
22
25
|
const cls = class { };
|
|
@@ -39,30 +42,30 @@ ${EditorConfig.getDefaultConfig()}`.trim();
|
|
|
39
42
|
throw new Error(errorMessage);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
this.#svc = await DependencyRegistry.getInstance(MailService);
|
|
45
|
+
this.#svc[mod] = await DependencyRegistry.getInstance(MailService);
|
|
43
46
|
}
|
|
44
|
-
return this.#svc;
|
|
47
|
+
return this.#svc[mod];
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
/**
|
|
48
51
|
* Resolve template
|
|
49
52
|
*/
|
|
50
|
-
async sendEmail(message:
|
|
53
|
+
static async sendEmail(file: string, message: EmailOptions): Promise<{
|
|
51
54
|
url?: string | false;
|
|
52
55
|
}> {
|
|
53
56
|
const to = message.to!;
|
|
54
57
|
try {
|
|
55
58
|
console.log('Sending email', { to });
|
|
56
59
|
// Let the engine template
|
|
57
|
-
const svc = await this.getMailService();
|
|
60
|
+
const svc = await this.getMailService(file);
|
|
58
61
|
if (!svc) {
|
|
59
62
|
throw new Error('Node mailer support is missing');
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
const info = await svc.send<{ host?: string } &
|
|
65
|
+
const info = await svc.send<{ host?: string } & SentEmail>(message);
|
|
63
66
|
console.log('Sent email', { to });
|
|
64
67
|
|
|
65
|
-
const senderConfig = await EditorConfig.getSenderConfig();
|
|
68
|
+
const senderConfig = await EditorConfig.getSenderConfig(file);
|
|
66
69
|
return senderConfig.host?.includes('ethereal.email') ? {
|
|
67
70
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
|
68
71
|
url: (await import('nodemailer')).getTestMessageUrl(info as any)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { GlobalEnvConfig
|
|
1
|
+
import { GlobalEnvConfig } from '@travetto/base';
|
|
2
2
|
import { CliCommand } from '@travetto/cli';
|
|
3
|
-
import { RootIndex } from '@travetto/manifest';
|
|
4
3
|
import { RootRegistry } from '@travetto/registry';
|
|
5
4
|
|
|
6
5
|
import { EditorState } from './bin/editor';
|
|
@@ -13,17 +12,13 @@ export class EmailEditorCommand {
|
|
|
13
12
|
envInit(): GlobalEnvConfig {
|
|
14
13
|
return {
|
|
15
14
|
envName: 'dev',
|
|
16
|
-
|
|
15
|
+
dynamic: true,
|
|
16
|
+
profiles: ['email-dev']
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async main(): Promise<void> {
|
|
21
21
|
await RootRegistry.init();
|
|
22
|
-
|
|
23
|
-
await editor.init();
|
|
24
|
-
if (process.send) {
|
|
25
|
-
process.on('disconnect', () => ShutdownManager.execute());
|
|
26
|
-
process.send('ready');
|
|
27
|
-
}
|
|
22
|
+
await new EditorState(await EmailCompilationManager.createInstance()).init();
|
|
28
23
|
}
|
|
29
24
|
}
|
|
@@ -16,7 +16,10 @@ import { EmailCompiler } from '../src/compiler';
|
|
|
16
16
|
export class EmailTestCommand implements CliCommandShape {
|
|
17
17
|
|
|
18
18
|
envInit(): GlobalEnvConfig {
|
|
19
|
-
return {
|
|
19
|
+
return {
|
|
20
|
+
envName: 'dev',
|
|
21
|
+
profiles: ['email-dev']
|
|
22
|
+
};
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
async main(file: string, to: string): Promise<void> {
|
|
@@ -25,10 +28,9 @@ export class EmailTestCommand implements CliCommandShape {
|
|
|
25
28
|
await EmailCompiler.compile(file, true);
|
|
26
29
|
|
|
27
30
|
const mgr = await EmailCompilationManager.createInstance();
|
|
28
|
-
const
|
|
29
|
-
const
|
|
31
|
+
const cfg = await EditorConfig.get(file);
|
|
32
|
+
const content = await mgr.resolveCompiledTemplate(file, await EditorConfig.getContext(file));
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
await send.sendEmail({ from: cfg.from, to, ...content, });
|
|
34
|
+
await EditorSendService.sendEmail(file, { from: cfg.from, to, ...content, });
|
|
33
35
|
}
|
|
34
36
|
}
|