Mail Markdown Templates
ZinTrust ships a lightweight Markdown template registry for mail templates.
It is intentionally simple:
- templates are
.mdfiles on disk - metadata is parsed from top-of-file HTML comments
- variables are interpolated using
placeholders - output HTML is produced by the safe
MarkdownRenderer
Where templates live
The built-in registry reads templates from the project working directory:
src/tools/mail/templates/markdown/**(relative toprocess.cwd())
Template names use slash-separated paths without the .md extension:
auth/welcometransactional/password-reset
Public API
In Node contexts (CLI, server-side rendering), import from the node entrypoint:
import { listTemplates, loadTemplate, renderTemplate } from '@zintrust/core/node';The key functions are:
listTemplates()→string[]- walks
src/tools/mail/templates/markdownand returns normalized names (lowercased, no extension)
- walks
loadTemplate(name)→{ subject?, preheader?, variables?, content }- reads
src/tools/mail/templates/markdown/<name>.mdand parses metadata
- reads
renderTemplate(name, vars?)→{ html, meta }- loads template, validates metadata, then renders
contentto HTML
- loads template, validates metadata, then renders
Example
import { listTemplates, renderTemplate } from '@zintrust/core/node';
const templates = listTemplates();
const { html, meta } = renderTemplate('auth/welcome', {
name: 'Alice',
confirmLink: 'https://example.com/verify',
expiryMinutes: 30,
});
console.log(templates);
console.log(meta.subject);
console.log(html);Template format (metadata + Markdown)
Templates start with optional top-of-file HTML comment lines that look like:
<!-- Subject: Welcome, {{name}}! -->
<!-- Preheader: Thanks for joining -->
<!-- Variables: name, confirmLink, expiryMinutes -->Parsing rules:
- metadata lines must be consecutive at the top of the file
- each metadata line must be an HTML comment with a
Key: Valueshape - parsing stops at the first non-matching line
After metadata, the Markdown body begins.
Variable rules and validation
ZinTrust supports placeholders of the form:
For mail templates, renderTemplate(...) validates metadata using validateTemplateMeta(...):
subjectis required (missing/empty subject throws a validation error)- the
Variables: ...list must match placeholders found in the body- if metadata declares variables that do not appear in the content, rendering fails
- if the content contains placeholders not declared in metadata, rendering fails
This strictness is intentional: it prevents templates from silently drifting.
Rendering behavior
renderTemplate(...) uses MarkdownRenderer.render(...) under the hood.
- interpolated values are HTML-escaped to reduce injection risk
- links are sanitized so only safe protocols are emitted
If you want an email-friendly wrapper document, use the renderer directly:
import { loadTemplate } from '@zintrust/core/node';
import { MarkdownRenderer } from '@zintrust/core';
const tpl = loadTemplate('auth/welcome');
const html = MarkdownRenderer.renderWithLayout(tpl.content, 'email', { name: 'Alice' });CLI support
ZinTrust includes a CLI command for listing and rendering templates:
zin templates list mail
zin templates render mail auth/welcomescope can be mail, notification, or all.
Scaffolding new templates
The CLI can scaffold application-owned templates into src/mail/markdown:
zin make:mail-template welcome --category auth --vars name,confirmLink,expiryMinutesNote: the built-in registry documented above reads from src/tools/mail/templates/markdown. If you want your new template to appear in zin templates list, either:
- place/copy it under
src/tools/mail/templates/markdown/<category>/<name>.md, or - implement your own loader that targets
src/mail/markdown.