-
-
Notifications
You must be signed in to change notification settings - Fork 934
/
handlebars-mjml-generator.ts
73 lines (67 loc) · 3.27 KB
/
handlebars-mjml-generator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import dateFormat from 'dateformat';
import Handlebars from 'handlebars';
import mjml2html from 'mjml';
import { InitializedEmailPluginOptions } from '../types';
import { EmailGenerator } from './email-generator';
/**
* @description
* Uses Handlebars (https://handlebarsjs.com/) to output MJML (https://mjml.io) which is then
* compiled down to responsive email HTML.
*
* @docsCategory core plugins/EmailPlugin
* @docsPage EmailGenerator
*/
export class HandlebarsMjmlGenerator implements EmailGenerator {
async onInit(options: InitializedEmailPluginOptions) {
if (options.templateLoader.loadPartials) {
const partials = await options.templateLoader.loadPartials();
partials.forEach(({ name, content }) => Handlebars.registerPartial(name, content));
}
this.registerHelpers();
}
generate(from: string, subject: string, template: string, templateVars: any) {
const compiledFrom = Handlebars.compile(from, { noEscape: true });
const compiledSubject = Handlebars.compile(subject);
const compiledTemplate = Handlebars.compile(template);
// We enable prototype properties here, aware of the security implications
// described here: https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access
// This is needed because some Vendure entities use getters on the entity
// prototype (e.g. Order.total) which may need to be interpolated.
const templateOptions: RuntimeOptions = { allowProtoPropertiesByDefault: true };
const fromResult = compiledFrom(templateVars, { allowProtoPropertiesByDefault: true });
const subjectResult = compiledSubject(templateVars, { allowProtoPropertiesByDefault: true });
const mjml = compiledTemplate(templateVars, { allowProtoPropertiesByDefault: true });
const body = mjml2html(mjml).html;
return { from: fromResult, subject: subjectResult, body };
}
private registerHelpers() {
Handlebars.registerHelper('formatDate', (date: Date | undefined, format: string | object) => {
if (!date) {
return date;
}
if (typeof format !== 'string') {
format = 'default';
}
return dateFormat(date, format);
});
Handlebars.registerHelper(
'formatMoney',
(amount?: number, currencyCode?: string, locale?: string) => {
if (amount == null) {
return amount;
}
// Last parameter is a generic "options" object which is not used here.
// If it's supplied, it means the helper function did not receive the additional, optional parameters.
// See https://handlebarsjs.com/api-reference/helpers.html#the-options-parameter
if (!currencyCode || typeof currencyCode === 'object') {
return (amount / 100).toFixed(2);
}
// Same reasoning for `locale` as for `currencyCode` here.
return new Intl.NumberFormat(typeof locale === 'object' ? undefined : locale, {
style: 'currency',
currency: currencyCode,
}).format(amount / 100);
},
);
}
}