@cosmicdrift/kumiko-renderer-mail-html 0.377.0 → 0.378.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.
package/dist/render.js CHANGED
@@ -22,6 +22,8 @@ import { buildMailBandLinksHtml, buildMailFooterHtml, layoutLinksToPlaceholder,
22
22
  import { mailChromeText } from "./mail-strings.js";
23
23
  import { MAIL_IMAGE_MAX_WIDTH_PX, renderStructuredLayoutMjml } from "./structured-layout.js";
24
24
  const LINE_BREAKS = /[\r\n]+/g;
25
+ const PLAIN_REFERENCE = /\{\{\s*([\w.]+)\s*\}\}/g;
26
+ const FORMATTED_VARIABLE_TYPES = new Set(["money", "date", "percent"]);
25
27
  function describe(e) {
26
28
  return e instanceof Error ? e.message : String(e);
27
29
  }
@@ -211,24 +213,35 @@ async function renderMailBody(tplApi, ctx, template, hbs, context, buttonInlineS
211
213
  throw new RendererError(`renderer-mail-html: rendering the body of "${template.slug}" failed — ${describe(e)}`, "other");
212
214
  }
213
215
  }
214
- function renderSubjectLine(hbs, source, context) {
215
- return compileDocumentSource(hbs, source, { noEscape: true })(context).replace(LINE_BREAKS, "");
216
+ // Meta lines only hold plain {{name}} references (checked on save); typed values
217
+ // need the same format helpers the body compiler adds, or money renders as "[object Object]".
218
+ function withFormatHelpers(source, variables) {
219
+ return source.replace(PLAIN_REFERENCE, (reference, name) => {
220
+ const type = variables[name]?.type;
221
+ return type !== undefined && FORMATTED_VARIABLE_TYPES.has(type)
222
+ ? `{{${type} ${name}}}`
223
+ : reference;
224
+ });
225
+ }
226
+ function renderSubjectLine(hbs, source, context, variables) {
227
+ return compileDocumentSource(hbs, withFormatHelpers(source, variables), { noEscape: true })(context).replace(LINE_BREAKS, "");
216
228
  }
217
229
  function renderFileNamePattern(hbs, pattern, slug, context) {
218
230
  try {
219
- return renderSubjectLine(hbs, pattern, context);
231
+ // File names keep raw values: a formatted date or amount would put spaces into the name.
232
+ return renderSubjectLine(hbs, pattern, context, {});
220
233
  }
221
234
  catch (e) {
222
235
  throw new RendererError(`renderer-mail-html: rendering the attachment file name "${pattern}" of "${slug}" failed — ${describe(e)}`, "other");
223
236
  }
224
237
  }
225
- function renderSubjectLines(hbs, meta, slug, context) {
238
+ function renderSubjectLines(hbs, meta, slug, context, variables) {
226
239
  try {
227
240
  return {
228
- subject: renderSubjectLine(hbs, meta.subject, context),
241
+ subject: renderSubjectLine(hbs, meta.subject, context, variables),
229
242
  previewText: meta.previewText === undefined
230
243
  ? undefined
231
- : renderSubjectLine(hbs, meta.previewText, context),
244
+ : renderSubjectLine(hbs, meta.previewText, context, variables),
232
245
  };
233
246
  }
234
247
  catch (e) {
@@ -236,8 +249,8 @@ function renderSubjectLines(hbs, meta, slug, context) {
236
249
  }
237
250
  }
238
251
  // Chip and tone are author-chosen per mail; a chip without a known tone takes the accent tone.
239
- function renderMailChrome(hbs, meta, slug, context, brandColor) {
240
- const render = (source) => source === undefined ? "" : renderSubjectLine(hbs, source, context).trim();
252
+ function renderMailChrome(hbs, meta, slug, context, variables, brandColor) {
253
+ const render = (source) => source === undefined ? "" : renderSubjectLine(hbs, source, context, variables).trim();
241
254
  try {
242
255
  const headerChip = render(meta.headerChip);
243
256
  const tone = readMailTone(render(meta.tone));
@@ -483,8 +496,9 @@ export async function mailHtmlRender(req, ctx, options = {}) {
483
496
  unsubscribeUrl: unsubscribeUrl ?? "",
484
497
  };
485
498
  const context = { ...variables, ...systemPlaceholders };
499
+ const variableDefs = resolveTemplateVariables(ctx.registry, template.slug, template.variableSchema);
486
500
  const { appearance, accentColor, brandColor } = await resolveMailBranding(ctx, layout);
487
- const chrome = renderMailChrome(hbs, meta, slug, context, brandColor);
501
+ const chrome = renderMailChrome(hbs, meta, slug, context, variableDefs, brandColor);
488
502
  const body = await renderMailBody(tplApi, ctx, template, hbs, context, appearance?.buttonStyle, chrome.tone?.bar ?? brandColor ?? DEFAULT_ACCENT_COLOR, brandingLogoUrl);
489
503
  const { contentHtml } = body;
490
504
  const layoutFileRefs = await resolveLayoutFileRefs(tplApi, ctx, layout);
@@ -498,7 +512,7 @@ export async function mailHtmlRender(req, ctx, options = {}) {
498
512
  locale,
499
513
  renderFileName: (pattern) => renderFileNamePattern(hbs, pattern, slug, context),
500
514
  });
501
- const { subject, previewText } = renderSubjectLines(hbs, meta, slug, context);
515
+ const { subject, previewText } = renderSubjectLines(hbs, meta, slug, context, variableDefs);
502
516
  const mjmlSource = compileLayoutMjml({
503
517
  layout,
504
518
  hbs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer-mail-html",
3
- "version": "0.377.0",
3
+ "version": "0.378.0",
4
4
  "description": "Renderer-Foundation plugin for branded HTML emails: mail-kind templates (document HTML + subject meta) rendered with isolated Handlebars into an MJML layout, plus plain text via html-to-text. Templates resolved from template-resolver.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -19,17 +19,17 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@cosmicdrift/kumiko-bundled-features": "0.377.0",
23
- "@cosmicdrift/kumiko-framework": "0.377.0",
24
- "@cosmicdrift/kumiko-renderer": "0.377.0",
25
- "@cosmicdrift/kumiko-types": "0.377.0",
22
+ "@cosmicdrift/kumiko-bundled-features": "0.378.0",
23
+ "@cosmicdrift/kumiko-framework": "0.378.0",
24
+ "@cosmicdrift/kumiko-renderer": "0.378.0",
25
+ "@cosmicdrift/kumiko-types": "0.378.0",
26
26
  "handlebars": "^4.7.8",
27
27
  "html-to-text": "^9.0.5",
28
28
  "mjml": "^5.2.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@cosmicdrift/kumiko-locale-de": "0.377.0",
32
- "@cosmicdrift/kumiko-testing": "0.377.0",
31
+ "@cosmicdrift/kumiko-locale-de": "0.378.0",
32
+ "@cosmicdrift/kumiko-testing": "0.378.0",
33
33
  "@types/html-to-text": "^9.0.4",
34
34
  "@types/mjml": "^5.0.0"
35
35
  },
package/src/changes.json CHANGED
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.378.0",
4
+ "type": "fix",
5
+ "title": "Typed placeholders in mail subject lines are formatted",
6
+ "detail": "A money placeholder in the subject or preview text rendered as \"[object Object]\" and a date as its raw ISO string, because only the body compiler added the format helpers. Subject, preview text, header chip, tone and footer note now use the same money, date and percent formatting as the body. Attachment file names keep the raw values."
7
+ },
2
8
  {
3
9
  "version": "0.377.0",
4
10
  "type": "improvement",