@aswin.dev/renderer 0.6.3

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 ADDED
@@ -0,0 +1,91 @@
1
+ # @aswin.dev/renderer
2
+
3
+ > JSON → MJML renderer for Templatical email templates. Runs in the browser or on Node.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@aswin.dev/renderer?label=npm&color=cb3837)](https://www.npmjs.com/package/@aswin.dev/renderer)
6
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/templatical/sdk/blob/main/LICENSE-MIT)
7
+
8
+ Converts Templatical's JSON template format into MJML, which you then compile to HTML using any [MJML library](https://mjml.io). Pure functions, zero runtime dependencies aside from `@templatical/types`.
9
+
10
+ Use this on your backend to render templates server-side before sending email — or in the browser to preview before save.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @aswin.dev/renderer mjml
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Browser or Node — JSON → MJML
21
+
22
+ ```ts
23
+ import { renderToMjml } from '@aswin.dev/renderer';
24
+ import type { TemplateContent } from '@templatical/types';
25
+
26
+ const content: TemplateContent = JSON.parse(stored);
27
+ const mjml = await renderToMjml(content);
28
+ ```
29
+
30
+ `renderToMjml` is async — it returns `Promise<string>`. Custom blocks may need async work to resolve, so the call always returns a promise even when content has none.
31
+
32
+ ### Node — MJML → HTML for sending
33
+
34
+ ```ts
35
+ import { renderToMjml } from '@aswin.dev/renderer';
36
+ import mjml2html from 'mjml';
37
+
38
+ const mjml = await renderToMjml(content);
39
+ const { html } = mjml2html(mjml);
40
+
41
+ // Send via Postmark, Resend, SES, Mailgun, anything
42
+ await mailer.send({ to, subject, html });
43
+ ```
44
+
45
+ ### With custom fonts
46
+
47
+ ```ts
48
+ const mjml = await renderToMjml(content, {
49
+ customFonts: [
50
+ { name: 'Geist', url: 'https://fonts.googleapis.com/...' },
51
+ ],
52
+ defaultFallbackFont: 'Arial, sans-serif',
53
+ allowHtmlBlocks: true,
54
+ });
55
+ ```
56
+
57
+ ### With custom blocks
58
+
59
+ The renderer doesn't know how to render custom blocks on its own — you supply a callback. Editor consumers wire this automatically through `editor.toMjml()`. Headless callers provide their own resolver:
60
+
61
+ ```ts
62
+ const mjml = await renderToMjml(content, {
63
+ async renderCustomBlock(block) {
64
+ // e.g. run the same liquid template the editor uses, against block.fieldValues
65
+ return await myLiquidEngine.render(block.customType, block.fieldValues);
66
+ },
67
+ });
68
+ ```
69
+
70
+ If you don't pass `renderCustomBlock`, the renderer falls back to the block's `renderedHtml` field (if any) and otherwise omits the block from output.
71
+
72
+ ## API
73
+
74
+ - `renderToMjml(content, options?): Promise<string>` — render `TemplateContent` to an MJML string. Pair with the [`mjml`](https://www.npmjs.com/package/mjml) package to compile to final HTML.
75
+
76
+ Options:
77
+ - `customFonts` — `CustomFont[]` declarations rendered into `<mj-attributes>`
78
+ - `defaultFallbackFont` — fallback when a block doesn't specify a font
79
+ - `allowHtmlBlocks` — pass `false` to strip HTML blocks before render (e.g. for untrusted content)
80
+ - `renderCustomBlock` — `(block: CustomBlock) => Promise<string>` — resolves custom blocks to HTML
81
+
82
+ ## Documentation
83
+
84
+ - [How rendering works](https://docs.templatical.com/getting-started/how-rendering-works)
85
+ - [Renderer API](https://docs.templatical.com/api/renderer-typescript)
86
+
87
+ Full reference at **[docs.templatical.com](https://docs.templatical.com)**.
88
+
89
+ ## License
90
+
91
+ MIT
@@ -0,0 +1,137 @@
1
+ import { CustomFont, Block, ColumnLayout, SpacingValue, CustomBlock, TemplateContent } from '@templatical/types';
2
+
3
+ /**
4
+ * Immutable context passed through the block rendering chain.
5
+ */
6
+ declare class RenderContext {
7
+ readonly containerWidth: number;
8
+ readonly customFonts: CustomFont[];
9
+ readonly defaultFallbackFont: string;
10
+ readonly allowHtmlBlocks: boolean;
11
+ /**
12
+ * Map of custom block id → pre-rendered HTML, populated by `renderToMjml`
13
+ * before the synchronous render pass. Set when the consumer provides a
14
+ * `renderCustomBlock` option. Empty by default.
15
+ */
16
+ readonly customBlockHtml: ReadonlyMap<string, string>;
17
+ constructor(containerWidth: number, customFonts: CustomFont[], defaultFallbackFont: string, allowHtmlBlocks: boolean,
18
+ /**
19
+ * Map of custom block id → pre-rendered HTML, populated by `renderToMjml`
20
+ * before the synchronous render pass. Set when the consumer provides a
21
+ * `renderCustomBlock` option. Empty by default.
22
+ */
23
+ customBlockHtml?: ReadonlyMap<string, string>);
24
+ /**
25
+ * Create a new context with a different container width.
26
+ * Used when rendering columns with narrower widths.
27
+ */
28
+ withContainerWidth(width: number): RenderContext;
29
+ /**
30
+ * Resolve a font family name to include custom font fallbacks.
31
+ * If the font matches a custom font, returns `'FontName', fallback`.
32
+ * Otherwise returns the original font family string.
33
+ */
34
+ resolveFontFamily(fontFamily: string): string;
35
+ }
36
+
37
+ /**
38
+ * Escape HTML special characters (& < > " ').
39
+ * Equivalent to PHP htmlspecialchars with ENT_QUOTES | ENT_HTML5.
40
+ */
41
+ declare function escapeHtml(text: string): string;
42
+ /**
43
+ * Escape a string for use in an HTML attribute value.
44
+ * Same implementation as escapeHtml for consistency with PHP.
45
+ */
46
+ declare function escapeAttr(text: string): string;
47
+ /**
48
+ * Replace merge tag span elements with their data attribute values.
49
+ * Converts `<span data-merge-tag="{{name}}">Label</span>` to `{{name}}`.
50
+ * Also handles `data-logic-merge-tag` attributes.
51
+ */
52
+ declare function convertMergeTagsToValues(html: string): string;
53
+
54
+ /**
55
+ * Check if a block is hidden on all viewports.
56
+ */
57
+ declare function isHiddenOnAll(block: Block): boolean;
58
+ /**
59
+ * Get the MJML css-class attribute string for visibility hiding.
60
+ * Returns a string like ` css-class="tpl-hide-desktop tpl-hide-tablet"` or empty string.
61
+ */
62
+ declare function getCssClassAttr(block: Block): string;
63
+ /**
64
+ * Get the CSS classes for visibility hiding.
65
+ */
66
+ declare function getCssClasses(block: Block): string;
67
+
68
+ /**
69
+ * Get width percentages for each column in a layout.
70
+ */
71
+ declare function getWidthPercentages(layout: ColumnLayout): string[];
72
+ /**
73
+ * Get width in pixels for each column in a layout.
74
+ */
75
+ declare function getWidthPixels(layout: ColumnLayout, containerWidth: number): number[];
76
+
77
+ /**
78
+ * Convert a SpacingValue to a CSS padding string like "10px 10px 10px 10px".
79
+ */
80
+ declare function toPaddingString(padding: SpacingValue): string;
81
+
82
+ /**
83
+ * Social icon SVG path data and brand colors for all supported platforms.
84
+ */
85
+ declare const SOCIAL_ICONS: Record<string, {
86
+ path: string;
87
+ color: string;
88
+ }>;
89
+ /**
90
+ * Generate a base64-encoded SVG data URI for a social icon.
91
+ * Ported from SocialIconSvgGenerator.php.
92
+ */
93
+ declare function generateSocialIconDataUri(platform: string, style: string, size: number): string;
94
+
95
+ /**
96
+ * Render a single block to MJML markup.
97
+ * Dispatches to the appropriate block-type renderer.
98
+ */
99
+ declare function renderBlock(block: Block, context: RenderContext): string;
100
+
101
+ /**
102
+ * A function type that renders a single block to MJML markup.
103
+ */
104
+ type BlockRenderer = (block: Block, context: RenderContext) => string;
105
+
106
+ interface RenderOptions {
107
+ customFonts?: CustomFont[];
108
+ defaultFallbackFont?: string;
109
+ allowHtmlBlocks?: boolean;
110
+ /**
111
+ * Resolves custom blocks to their HTML representation. Called once per
112
+ * custom block in the content tree before MJML rendering. The renderer
113
+ * has no built-in knowledge of how to render custom blocks; consumers
114
+ * provide this function.
115
+ *
116
+ * Editor consumers: pass `editor.renderCustomBlock`.
117
+ *
118
+ * Headless consumers (Node.js, server, CLI): provide your own resolver,
119
+ * typically using the same liquid template + field values pipeline as
120
+ * the editor uses. If omitted, custom blocks fall back to
121
+ * `block.renderedHtml` (if present) and otherwise are omitted from the
122
+ * output.
123
+ */
124
+ renderCustomBlock?: (block: CustomBlock) => Promise<string>;
125
+ }
126
+ /**
127
+ * Render template content to an MJML string.
128
+ *
129
+ * The function is async because resolving custom blocks may require
130
+ * asynchronous work (e.g., the editor's liquid renderer dynamically imports
131
+ * `liquidjs`). When the content has no custom blocks or `renderCustomBlock`
132
+ * is omitted, no async work is performed but the function still resolves
133
+ * synchronously — i.e., it always returns a Promise.
134
+ */
135
+ declare function renderToMjml(content: TemplateContent, options?: RenderOptions): Promise<string>;
136
+
137
+ export { type BlockRenderer, RenderContext, type RenderOptions, SOCIAL_ICONS, convertMergeTagsToValues, escapeAttr, escapeHtml, generateSocialIconDataUri, getCssClassAttr, getCssClasses, getWidthPercentages, getWidthPixels, isHiddenOnAll, renderBlock, renderToMjml, toPaddingString };