@aswin.dev/import-beefree 0.7.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/LICENSE ADDED
@@ -0,0 +1,56 @@
1
+ Functional Source License, Version 1.1, MIT Future License
2
+
3
+ Copyright (c) 2026-present Templatical
4
+
5
+ ## Terms and Conditions
6
+
7
+ ### Licensor ("We")
8
+
9
+ Templatical
10
+
11
+ ### The Software
12
+
13
+ Templatical Email Editor — the visual drag-and-drop email template editor
14
+ (@aswin.dev/core, @aswin.dev/editor, and @aswin.dev/media-library
15
+ packages).
16
+
17
+ ### Grant of Rights
18
+
19
+ Subject to the terms and conditions of this License, We hereby grant You a
20
+ non-exclusive, worldwide, non-transferable license to use, copy, modify,
21
+ create derivative works, and redistribute the Software, subject to the
22
+ following conditions:
23
+
24
+ ### Permitted Uses
25
+
26
+ You may use the Software for any purpose, including commercial purposes,
27
+ **provided that** you do not offer the Software, or a substantially similar
28
+ product built using the Software, as a hosted or managed service that
29
+ competes with Templatical's commercial offerings.
30
+
31
+ ### Change Date
32
+
33
+ Two (2) years from the date of each release of the Software.
34
+
35
+ ### Change License
36
+
37
+ MIT License
38
+
39
+ On the Change Date, the above copyright notice and this permission notice
40
+ shall be replaced with the MIT License, and the Software will be available
41
+ under the MIT License for all purposes without restriction.
42
+
43
+ ### Notices
44
+
45
+ You must retain this license notice in all copies or substantial portions
46
+ of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
51
+
52
+ ---
53
+
54
+ Note: The @aswin.dev/types, @aswin.dev/renderer, and
55
+ @aswin.dev/import-beefree packages are licensed separately under the MIT
56
+ License. See LICENSE-MIT for those packages' terms.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # @aswin.dev/import-beefree
2
+
3
+ > Convert BeeFree email templates to Templatical's JSON format.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@aswin.dev/import-beefree?label=npm&color=cb3837)](https://www.npmjs.com/package/@aswin.dev/import-beefree)
6
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/templatical/sdk/blob/main/LICENSE-MIT)
7
+
8
+ Migrate from [BeeFree](https://beefree.io) to [Templatical](https://github.com/templatical/sdk) without rebuilding your templates by hand. Maps BeeFree modules to Templatical block types and reports any modules that need manual conversion.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @aswin.dev/import-beefree
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```ts
19
+ import { convertBeeFreeTemplate } from '@aswin.dev/import-beefree';
20
+
21
+ const beefreeJson = await fetch('/path/to/beefree-template.json').then(r => r.json());
22
+
23
+ const result = convertBeeFreeTemplate(beefreeJson);
24
+
25
+ console.log(result.content); // → Templatical TemplateContent
26
+ console.log(result.report); // → conversion report (per-module status, warnings, summary)
27
+
28
+ // Inspect modules that didn't convert cleanly
29
+ for (const entry of result.report.entries) {
30
+ if (entry.status !== 'converted') {
31
+ console.warn(
32
+ `${entry.beeFreeModuleType} → ${entry.templaticalBlockType ?? 'n/a'} ` +
33
+ `(${entry.status})${entry.note ? `: ${entry.note}` : ''}`,
34
+ );
35
+ }
36
+ }
37
+
38
+ console.log(result.report.summary);
39
+ // { total, converted, approximated, htmlFallback, skipped }
40
+ ```
41
+
42
+ ## What's converted
43
+
44
+ | BeeFree module | Templatical block |
45
+ |---|---|
46
+ | heading / title | `TitleBlock` |
47
+ | paragraph / text | `ParagraphBlock` |
48
+ | image | `ImageBlock` |
49
+ | button | `ButtonBlock` |
50
+ | divider | `DividerBlock` |
51
+ | spacer | `SpacerBlock` |
52
+ | social | `SocialIconsBlock` |
53
+ | menu | `MenuBlock` |
54
+ | html | `HtmlBlock` |
55
+ | video | `VideoBlock` |
56
+ | Unknown modules | Fallback to `HtmlBlock` |
57
+
58
+ Unknown or partially-supported modules are flagged in `result.report` so you can review them.
59
+
60
+ ## API
61
+
62
+ - `convertBeeFreeTemplate(template)` — converts a BeeFree JSON template, returns `{ content, report }`
63
+
64
+ Types:
65
+ - `BeeFreeTemplate` — input shape (see source for full structure)
66
+ - `ImportResult` — `{ content: TemplateContent, report: ImportReport }`
67
+ - `ImportReport` — `{ entries: ImportReportEntry[], warnings: string[], summary: { total, converted, approximated, htmlFallback, skipped } }`
68
+ - `ImportReportEntry` — `{ beeFreeModuleType, templaticalBlockType, status, note? }`
69
+ - `ConversionStatus` — `'converted' | 'approximated' | 'html-fallback' | 'skipped'`
70
+
71
+ ## Documentation
72
+
73
+ - [Migrating from BeeFree](https://docs.templatical.com/guide/migration-from-beefree)
74
+
75
+ Full reference at **[docs.templatical.com](https://docs.templatical.com)**.
76
+
77
+ ## License
78
+
79
+ MIT
@@ -0,0 +1,215 @@
1
+ import * as _aswin_dev_types from '@aswin.dev/types';
2
+
3
+ /**
4
+ * BeeFree JSON type definitions.
5
+ * Based on the BeeFree export format used by the BEE Plugin / BEE Pro editor.
6
+ */
7
+ interface BeeFreeTemplate {
8
+ page: BeeFreeePage;
9
+ comments?: Record<string, unknown>;
10
+ }
11
+ interface BeeFreeePage {
12
+ title?: string;
13
+ description?: string;
14
+ rows: BeeFreeeRow[];
15
+ body?: BeeFreeeBody;
16
+ }
17
+ interface BeeFreeeBody {
18
+ type?: string;
19
+ webFonts?: BeeFreeeWebFont[];
20
+ content?: {
21
+ style?: Record<string, string>;
22
+ computedStyle?: Record<string, string>;
23
+ };
24
+ container?: {
25
+ style?: Record<string, string>;
26
+ };
27
+ }
28
+ interface BeeFreeeWebFont {
29
+ name: string;
30
+ fontFamily?: string;
31
+ url?: string;
32
+ }
33
+ interface BeeFreeeRow {
34
+ uuid?: string;
35
+ type?: string;
36
+ locked?: boolean;
37
+ synced?: boolean;
38
+ empty?: boolean;
39
+ columns: BeeFreeeColumn[];
40
+ container?: {
41
+ style?: Record<string, string>;
42
+ };
43
+ content?: {
44
+ style?: Record<string, string>;
45
+ computedStyle?: Record<string, string | boolean>;
46
+ };
47
+ }
48
+ interface BeeFreeeColumn {
49
+ uuid?: string;
50
+ "grid-columns"?: number;
51
+ style?: Record<string, string>;
52
+ modules: BeeFreeeModule[];
53
+ }
54
+ interface BeeFreeeModule {
55
+ type: string;
56
+ locked?: boolean;
57
+ uuid?: string;
58
+ descriptor: BeeFreeeModuleDescriptor;
59
+ }
60
+ interface BeeFreeeModuleDescriptor {
61
+ style?: Record<string, string>;
62
+ computedStyle?: Record<string, string | boolean>;
63
+ text?: BeeFreeeTextContent;
64
+ paragraph?: BeeFreeeTextContent;
65
+ heading?: BeeFreeeHeadingContent;
66
+ list?: BeeFreeeTextContent;
67
+ image?: BeeFreeeImageContent;
68
+ button?: BeeFreeeButtonContent;
69
+ divider?: BeeFreeeeDividerContent;
70
+ spacer?: BeeFreeeSpacerContent;
71
+ html?: BeeFreeeHtmlContent;
72
+ iconsList?: BeeFreeeSocialContent;
73
+ video?: BeeFreeeVideoContent;
74
+ menu?: BeeFreeeMenuContent;
75
+ table?: BeeFreeeTableContent;
76
+ }
77
+ interface BeeFreeeTextContent {
78
+ style?: Record<string, string>;
79
+ computedStyle?: Record<string, string>;
80
+ html: string;
81
+ }
82
+ interface BeeFreeeHeadingContent {
83
+ title?: string;
84
+ text?: string;
85
+ style?: Record<string, string>;
86
+ }
87
+ interface BeeFreeeImageContent {
88
+ src: string;
89
+ href?: string;
90
+ alt?: string;
91
+ width?: string;
92
+ height?: string;
93
+ prefix?: string;
94
+ style?: Record<string, string>;
95
+ }
96
+ interface BeeFreeeButtonContent {
97
+ label: string;
98
+ href?: string;
99
+ style?: Record<string, string>;
100
+ }
101
+ interface BeeFreeeeDividerContent {
102
+ style?: Record<string, string>;
103
+ }
104
+ interface BeeFreeeSpacerContent {
105
+ style?: Record<string, string>;
106
+ }
107
+ interface BeeFreeeHtmlContent {
108
+ html: string;
109
+ }
110
+ interface BeeFreeeSocialContent {
111
+ icons: BeeFreeeSocialIcon[];
112
+ }
113
+ interface BeeFreeeSocialIcon {
114
+ image?: {
115
+ title?: string;
116
+ src?: string;
117
+ href?: string;
118
+ alt?: string;
119
+ };
120
+ name?: string;
121
+ text?: string;
122
+ type?: string;
123
+ id?: string;
124
+ }
125
+ interface BeeFreeeVideoContent {
126
+ src: string;
127
+ thumbnail?: string;
128
+ alt?: string;
129
+ style?: Record<string, string>;
130
+ }
131
+ interface BeeFreeeMenuContent {
132
+ items: BeeFreeeMenuItem[];
133
+ separator?: string;
134
+ separatorColor?: string;
135
+ style?: Record<string, string>;
136
+ }
137
+ interface BeeFreeeMenuItem {
138
+ text: string;
139
+ link?: string;
140
+ href?: string;
141
+ target?: string;
142
+ }
143
+ interface BeeFreeeTableContent {
144
+ rows: BeeFreeeTableRow[];
145
+ hasHeaderRow?: boolean;
146
+ headerBackgroundColor?: string;
147
+ cellPadding?: number | string;
148
+ style?: Record<string, string>;
149
+ }
150
+ interface BeeFreeeTableRow {
151
+ cells: BeeFreeeTableCell[];
152
+ }
153
+ interface BeeFreeeTableCell {
154
+ content?: string;
155
+ html?: string;
156
+ }
157
+ /**
158
+ * Conversion status for each module in the import report.
159
+ */
160
+ type ConversionStatus = "converted" | "approximated" | "html-fallback" | "skipped";
161
+ /**
162
+ * A single entry in the import report.
163
+ */
164
+ interface ImportReportEntry {
165
+ beeFreeModuleType: string;
166
+ templaticalBlockType: string | null;
167
+ status: ConversionStatus;
168
+ note?: string;
169
+ }
170
+ /**
171
+ * The full import report returned alongside the converted template.
172
+ */
173
+ interface ImportReport {
174
+ entries: ImportReportEntry[];
175
+ warnings: string[];
176
+ summary: {
177
+ total: number;
178
+ converted: number;
179
+ approximated: number;
180
+ htmlFallback: number;
181
+ skipped: number;
182
+ };
183
+ }
184
+ /**
185
+ * The result of a BeeFree import operation.
186
+ */
187
+ interface ImportResult {
188
+ content: _aswin_dev_types.TemplateContent;
189
+ report: ImportReport;
190
+ }
191
+
192
+ /**
193
+ * Converts a BeeFree template JSON to Templatical TemplateContent.
194
+ *
195
+ * @param template - The parsed BeeFree JSON object
196
+ * @returns An ImportResult with the converted content and a detailed report
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * import { convertBeeFreeTemplate } from '@aswin.dev/import-beefree';
201
+ *
202
+ * const beeFreeJson = JSON.parse(fileContent);
203
+ * const { content, report } = convertBeeFreeTemplate(beeFreeJson);
204
+ *
205
+ * // Use the content with the editor
206
+ * const editor = init({ container: '#editor', content });
207
+ *
208
+ * // Check the report for any issues
209
+ * console.log(report.summary);
210
+ * console.log(report.warnings);
211
+ * ```
212
+ */
213
+ declare function convertBeeFreeTemplate(template: BeeFreeTemplate): ImportResult;
214
+
215
+ export { type BeeFreeTemplate, type ConversionStatus, type ImportReport, type ImportReportEntry, type ImportResult, convertBeeFreeTemplate };
package/dist/index.js ADDED
@@ -0,0 +1,583 @@
1
+ // src/converter.ts
2
+ import {
3
+ createSectionBlock,
4
+ createDefaultTemplateContent
5
+ } from "@aswin.dev/types";
6
+
7
+ // src/block-mapper.ts
8
+ import {
9
+ createTitleBlock,
10
+ createParagraphBlock,
11
+ createImageBlock,
12
+ createButtonBlock,
13
+ createDividerBlock,
14
+ createSpacerBlock,
15
+ createHtmlBlock,
16
+ createSocialIconsBlock,
17
+ createMenuBlock,
18
+ createTableBlock,
19
+ createVideoBlock,
20
+ generateId
21
+ } from "@aswin.dev/types";
22
+
23
+ // src/style-parser.ts
24
+ function parsePxValue(value) {
25
+ if (!value) return 0;
26
+ const match = value.match(/^(-?\d+(?:\.\d+)?)\s*px/);
27
+ return match ? Math.round(parseFloat(match[1])) : 0;
28
+ }
29
+ function parseColor(value) {
30
+ if (!value || value === "transparent") return "";
31
+ const trimmed = value.trim();
32
+ if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) return trimmed.toLowerCase();
33
+ if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {
34
+ const r = trimmed[1];
35
+ const g = trimmed[2];
36
+ const b = trimmed[3];
37
+ return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
38
+ }
39
+ return trimmed;
40
+ }
41
+ function parseBorderTop(value) {
42
+ if (!value) return { width: 0, style: "solid", color: "#000000" };
43
+ const parts = value.trim().split(/\s+/);
44
+ return {
45
+ width: parsePxValue(parts[0]),
46
+ style: parts[1] || "solid",
47
+ color: parseColor(parts[2]) || "#000000"
48
+ };
49
+ }
50
+ function extractPadding(style) {
51
+ if (!style) return { top: 0, right: 0, bottom: 0, left: 0 };
52
+ if (style.padding) {
53
+ return parseShorthandPadding(style.padding);
54
+ }
55
+ return {
56
+ top: parsePxValue(style["padding-top"]),
57
+ right: parsePxValue(style["padding-right"]),
58
+ bottom: parsePxValue(style["padding-bottom"]),
59
+ left: parsePxValue(style["padding-left"])
60
+ };
61
+ }
62
+ function parseShorthandPadding(value) {
63
+ const parts = value.trim().split(/\s+/);
64
+ const values = parts.map((p) => parsePxValue(p));
65
+ switch (values.length) {
66
+ case 1:
67
+ return {
68
+ top: values[0],
69
+ right: values[0],
70
+ bottom: values[0],
71
+ left: values[0]
72
+ };
73
+ case 2:
74
+ return {
75
+ top: values[0],
76
+ right: values[1],
77
+ bottom: values[0],
78
+ left: values[1]
79
+ };
80
+ case 3:
81
+ return {
82
+ top: values[0],
83
+ right: values[1],
84
+ bottom: values[2],
85
+ left: values[1]
86
+ };
87
+ default:
88
+ return {
89
+ top: values[0],
90
+ right: values[1],
91
+ bottom: values[2],
92
+ left: values[3]
93
+ };
94
+ }
95
+ }
96
+ function parseWidthPercent(value) {
97
+ if (!value) return 100;
98
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*%/);
99
+ if (match) return Math.round(parseFloat(match[1]));
100
+ return 100;
101
+ }
102
+ function parseFontFamily(value) {
103
+ if (!value) return "";
104
+ return value.split(",")[0].trim().replace(/['"]/g, "");
105
+ }
106
+
107
+ // src/block-mapper.ts
108
+ var MODULE_TYPE_MAP = {
109
+ "mailup-bee-newsletter-modules-text": "paragraph",
110
+ "mailup-bee-newsletter-modules-paragraph": "paragraph",
111
+ "mailup-bee-newsletter-modules-heading": "title",
112
+ "mailup-bee-newsletter-modules-list": "list",
113
+ "mailup-bee-newsletter-modules-image": "image",
114
+ "mailup-bee-newsletter-modules-button": "button",
115
+ "mailup-bee-newsletter-modules-divider": "divider",
116
+ "mailup-bee-newsletter-modules-spacer": "spacer",
117
+ "mailup-bee-newsletter-modules-html": "html",
118
+ "mailup-bee-newsletter-modules-social": "social",
119
+ "mailup-bee-newsletter-modules-video": "video",
120
+ "mailup-bee-newsletter-modules-menu": "menu",
121
+ "mailup-bee-newsletter-modules-table": "table"
122
+ };
123
+ var SOCIAL_PLATFORM_MAP = {
124
+ facebook: "facebook",
125
+ twitter: "twitter",
126
+ x: "twitter",
127
+ instagram: "instagram",
128
+ linkedin: "linkedin",
129
+ youtube: "youtube",
130
+ tiktok: "tiktok",
131
+ pinterest: "pinterest",
132
+ email: "email",
133
+ whatsapp: "whatsapp",
134
+ telegram: "telegram",
135
+ discord: "discord",
136
+ snapchat: "snapchat",
137
+ reddit: "reddit",
138
+ github: "github",
139
+ dribbble: "dribbble",
140
+ behance: "behance"
141
+ };
142
+ function toAlign(value, fallback = "left") {
143
+ if (value === "left" || value === "center" || value === "right") return value;
144
+ return fallback;
145
+ }
146
+ function toLineStyle(value, fallback = "solid") {
147
+ if (value === "solid" || value === "dashed" || value === "dotted")
148
+ return value;
149
+ return fallback;
150
+ }
151
+ function defaultMargin() {
152
+ return { top: 0, right: 0, bottom: 0, left: 0 };
153
+ }
154
+ function makeStyles(descriptor) {
155
+ const padding = extractPadding(descriptor.style);
156
+ const bg = parseColor(descriptor.style?.["background-color"]);
157
+ return {
158
+ padding,
159
+ margin: defaultMargin(),
160
+ ...bg ? { backgroundColor: bg } : {}
161
+ };
162
+ }
163
+ function inlineStylesToHtml(html, style) {
164
+ const spanParts = [];
165
+ const fontSize = parsePxValue(style["font-size"]);
166
+ if (fontSize && fontSize !== 16) spanParts.push(`font-size: ${fontSize}px`);
167
+ const color = parseColor(style.color);
168
+ if (color && color !== "#1a1a1a") spanParts.push(`color: ${color}`);
169
+ const fontWeight = style["font-weight"];
170
+ if (fontWeight && fontWeight !== "normal")
171
+ spanParts.push(`font-weight: ${fontWeight}`);
172
+ const fontFamily = parseFontFamily(style["font-family"]);
173
+ if (fontFamily) spanParts.push(`font-family: ${fontFamily}`);
174
+ const textAlign = style["text-align"];
175
+ const pStyle = textAlign && textAlign !== "left" ? `text-align: ${textAlign}` : "";
176
+ if (!pStyle && spanParts.length === 0) return html;
177
+ const spanStyle = spanParts.join("; ");
178
+ let result = html;
179
+ if (pStyle) {
180
+ result = result.replace(/<p style="([^"]*)">/g, `<p style="$1; ${pStyle}">`).replaceAll("<p>", `<p style="${pStyle}">`);
181
+ }
182
+ if (spanStyle) {
183
+ result = result.replace(
184
+ /<p([^>]*)>([\s\S]*?)<\/p>/g,
185
+ `<p$1><span style="${spanStyle}">$2</span></p>`
186
+ );
187
+ }
188
+ return result;
189
+ }
190
+ function convertText(descriptor) {
191
+ const textContent = descriptor.text ?? descriptor.paragraph ?? descriptor.list;
192
+ const html = textContent?.html ?? "";
193
+ const style = textContent?.style ?? {};
194
+ return createParagraphBlock({
195
+ content: inlineStylesToHtml(html, style),
196
+ styles: makeStyles(descriptor)
197
+ });
198
+ }
199
+ function parseHeadingLevel(tag) {
200
+ const match = tag.match(/^h(\d)$/i);
201
+ if (match) {
202
+ const num = Number(match[1]);
203
+ if (num >= 1 && num <= 4) return num;
204
+ }
205
+ return 2;
206
+ }
207
+ function convertHeading(descriptor) {
208
+ const heading = descriptor.heading;
209
+ if (!heading) return convertText(descriptor);
210
+ const style = heading.style ?? {};
211
+ const tag = heading.title ?? "h2";
212
+ const text = heading.text ?? "";
213
+ const content = text.startsWith("<") ? text.replace(/^<h\d[^>]*>|<\/h\d>$/gi, "") : text;
214
+ return createTitleBlock({
215
+ content: content ? `<p>${content}</p>` : "<p></p>",
216
+ level: parseHeadingLevel(tag),
217
+ color: parseColor(style.color) || "#1a1a1a",
218
+ textAlign: toAlign(style["text-align"]),
219
+ fontFamily: parseFontFamily(style["font-family"]) || void 0,
220
+ styles: makeStyles(descriptor)
221
+ });
222
+ }
223
+ function convertImage(descriptor) {
224
+ const image = descriptor.image;
225
+ if (!image) {
226
+ return createImageBlock({ styles: makeStyles(descriptor) });
227
+ }
228
+ return createImageBlock({
229
+ src: image.src || "",
230
+ alt: image.alt || "",
231
+ width: parsePxValue(image.width) || 600,
232
+ align: toAlign(image.style?.["text-align"], "center"),
233
+ linkUrl: image.href || void 0,
234
+ styles: makeStyles(descriptor)
235
+ });
236
+ }
237
+ function convertButton(descriptor) {
238
+ const button = descriptor.button;
239
+ if (!button) {
240
+ return createButtonBlock({ styles: makeStyles(descriptor) });
241
+ }
242
+ const style = button.style ?? {};
243
+ const label = button.label?.replace(/<[^>]*>/g, "") ?? "Button";
244
+ return createButtonBlock({
245
+ text: label,
246
+ url: button.href || "#",
247
+ backgroundColor: parseColor(style["background-color"]) || "#4f46e5",
248
+ textColor: parseColor(style.color) || "#ffffff",
249
+ borderRadius: parsePxValue(style["border-radius"]),
250
+ fontSize: parsePxValue(style["font-size"]) || 16,
251
+ fontFamily: parseFontFamily(style["font-family"]) || void 0,
252
+ buttonPadding: {
253
+ top: parsePxValue(style["padding-top"]) || 12,
254
+ right: parsePxValue(style["padding-right"]) || 24,
255
+ bottom: parsePxValue(style["padding-bottom"]) || 12,
256
+ left: parsePxValue(style["padding-left"]) || 24
257
+ },
258
+ styles: makeStyles(descriptor)
259
+ });
260
+ }
261
+ function convertDivider(descriptor) {
262
+ const divider = descriptor.divider;
263
+ const style = divider?.style ?? {};
264
+ const border = parseBorderTop(style["border-top"]);
265
+ return createDividerBlock({
266
+ lineStyle: toLineStyle(border.style),
267
+ color: border.color,
268
+ thickness: border.width || 1,
269
+ width: parseWidthPercent(style.width),
270
+ styles: makeStyles(descriptor)
271
+ });
272
+ }
273
+ function convertSpacer(descriptor) {
274
+ const spacer = descriptor.spacer;
275
+ const height = parsePxValue(spacer?.style?.height) || 24;
276
+ return createSpacerBlock({
277
+ height,
278
+ styles: makeStyles(descriptor)
279
+ });
280
+ }
281
+ function convertHtml(descriptor) {
282
+ const html = descriptor.html?.html ?? "";
283
+ return createHtmlBlock({
284
+ content: html,
285
+ styles: makeStyles(descriptor)
286
+ });
287
+ }
288
+ function convertSocial(descriptor, warnings) {
289
+ const iconsList = descriptor.iconsList;
290
+ if (!iconsList?.icons) {
291
+ return createSocialIconsBlock({ styles: makeStyles(descriptor) });
292
+ }
293
+ const icons = [];
294
+ for (const beeIcon of iconsList.icons) {
295
+ const id = (beeIcon.id ?? beeIcon.name ?? "").toLowerCase();
296
+ const platform = SOCIAL_PLATFORM_MAP[id];
297
+ if (!platform) {
298
+ warnings.push(
299
+ `Unrecognized social icon "${beeIcon.name || id}" was skipped.`
300
+ );
301
+ continue;
302
+ }
303
+ icons.push({
304
+ id: generateId(),
305
+ platform,
306
+ url: beeIcon.image?.href || "#"
307
+ });
308
+ }
309
+ return createSocialIconsBlock({
310
+ icons,
311
+ styles: makeStyles(descriptor)
312
+ });
313
+ }
314
+ function convertVideo(descriptor) {
315
+ const video = descriptor.video;
316
+ if (!video) {
317
+ return createVideoBlock({ styles: makeStyles(descriptor) });
318
+ }
319
+ return createVideoBlock({
320
+ url: video.src || "",
321
+ thumbnailUrl: video.thumbnail || "",
322
+ alt: video.alt || "",
323
+ width: parsePxValue(video.style?.width) || 600,
324
+ align: toAlign(video.style?.["text-align"], "center"),
325
+ styles: makeStyles(descriptor)
326
+ });
327
+ }
328
+ function convertMenu(descriptor) {
329
+ const menu = descriptor.menu;
330
+ if (!menu) {
331
+ return createMenuBlock({ styles: makeStyles(descriptor) });
332
+ }
333
+ const style = menu.style ?? {};
334
+ const items = (menu.items ?? []).map((item) => ({
335
+ id: generateId(),
336
+ text: item.text || "",
337
+ url: item.link || item.href || "#",
338
+ openInNewTab: item.target === "_blank",
339
+ bold: false,
340
+ underline: false
341
+ }));
342
+ return createMenuBlock({
343
+ items,
344
+ separator: menu.separator || "|",
345
+ separatorColor: parseColor(menu.separatorColor) || "#999999",
346
+ fontSize: parsePxValue(style["font-size"]) || 14,
347
+ color: parseColor(style.color) || "#1a1a1a",
348
+ fontFamily: parseFontFamily(style["font-family"]) || void 0,
349
+ textAlign: toAlign(style["text-align"], "center"),
350
+ styles: makeStyles(descriptor)
351
+ });
352
+ }
353
+ function convertTable(descriptor) {
354
+ const table = descriptor.table;
355
+ if (!table) {
356
+ return createTableBlock({ styles: makeStyles(descriptor) });
357
+ }
358
+ const style = table.style ?? {};
359
+ const rows = (table.rows ?? []).map((row) => ({
360
+ id: generateId(),
361
+ cells: (row.cells ?? []).map(
362
+ (cell) => ({
363
+ id: generateId(),
364
+ content: cell.content ?? cell.html ?? ""
365
+ })
366
+ )
367
+ }));
368
+ return createTableBlock({
369
+ rows,
370
+ hasHeaderRow: table.hasHeaderRow ?? false,
371
+ headerBackgroundColor: parseColor(table.headerBackgroundColor) || void 0,
372
+ borderColor: parseColor(style["border-color"]) || "#dddddd",
373
+ borderWidth: parsePxValue(style["border-width"]) || 1,
374
+ cellPadding: typeof table.cellPadding === "number" ? table.cellPadding : parsePxValue(table.cellPadding) || 8,
375
+ fontSize: parsePxValue(style["font-size"]) || 14,
376
+ color: parseColor(style.color) || "#1a1a1a",
377
+ textAlign: toAlign(style["text-align"]),
378
+ styles: makeStyles(descriptor)
379
+ });
380
+ }
381
+ function convertHtmlFallback(module) {
382
+ const descriptor = module.descriptor;
383
+ let html = "";
384
+ if (descriptor.text?.html) html = descriptor.text.html;
385
+ else if (descriptor.html?.html) html = descriptor.html.html;
386
+ else if (descriptor.heading?.text) html = descriptor.heading.text;
387
+ else html = `<!-- Unsupported BeeFree module: ${module.type} -->`;
388
+ return createHtmlBlock({
389
+ content: html,
390
+ styles: makeStyles(descriptor)
391
+ });
392
+ }
393
+ function convertModule(module, warnings) {
394
+ const mappedType = MODULE_TYPE_MAP[module.type];
395
+ const descriptor = module.descriptor;
396
+ if (!mappedType) {
397
+ return {
398
+ block: convertHtmlFallback(module),
399
+ entry: {
400
+ beeFreeModuleType: module.type,
401
+ templaticalBlockType: "html",
402
+ status: "html-fallback",
403
+ note: `Unknown module type "${module.type}" converted to HTML block.`
404
+ }
405
+ };
406
+ }
407
+ let block;
408
+ let isApproximation = false;
409
+ switch (mappedType) {
410
+ case "paragraph":
411
+ case "list":
412
+ block = convertText(descriptor);
413
+ break;
414
+ case "title":
415
+ block = convertHeading(descriptor);
416
+ break;
417
+ case "image":
418
+ block = convertImage(descriptor);
419
+ break;
420
+ case "button":
421
+ block = convertButton(descriptor);
422
+ break;
423
+ case "divider":
424
+ block = convertDivider(descriptor);
425
+ break;
426
+ case "spacer":
427
+ block = convertSpacer(descriptor);
428
+ break;
429
+ case "html":
430
+ block = convertHtml(descriptor);
431
+ break;
432
+ case "social":
433
+ block = convertSocial(descriptor, warnings);
434
+ break;
435
+ case "video":
436
+ block = convertVideo(descriptor);
437
+ break;
438
+ case "menu":
439
+ block = convertMenu(descriptor);
440
+ isApproximation = true;
441
+ break;
442
+ case "table":
443
+ block = convertTable(descriptor);
444
+ break;
445
+ default:
446
+ block = convertHtmlFallback(module);
447
+ return {
448
+ block,
449
+ entry: {
450
+ beeFreeModuleType: module.type,
451
+ templaticalBlockType: "html",
452
+ status: "html-fallback"
453
+ }
454
+ };
455
+ }
456
+ return {
457
+ block,
458
+ entry: {
459
+ beeFreeModuleType: module.type,
460
+ templaticalBlockType: block.type,
461
+ status: isApproximation ? "approximated" : "converted"
462
+ }
463
+ };
464
+ }
465
+
466
+ // src/converter.ts
467
+ function resolveColumnLayout(columns, warnings) {
468
+ if (columns.length === 1) return null;
469
+ if (columns.length === 3) return "3";
470
+ if (columns.length === 2) {
471
+ const left = columns[0]["grid-columns"] ?? 6;
472
+ const right = columns[1]["grid-columns"] ?? 6;
473
+ const total = left + right;
474
+ const ratio = left / total;
475
+ if (ratio > 0.58) return "2-1";
476
+ if (ratio < 0.42) return "1-2";
477
+ return "2";
478
+ }
479
+ if (columns.length >= 4) {
480
+ warnings.push(
481
+ `Row with ${columns.length} columns was flattened to a single column. BeeFree supports arbitrary columns, but Templatical supports up to 3.`
482
+ );
483
+ return null;
484
+ }
485
+ return "2";
486
+ }
487
+ function convertColumnModules(column, entries, warnings) {
488
+ const blocks = [];
489
+ for (const module of column.modules) {
490
+ const { block, entry } = convertModule(module, warnings);
491
+ blocks.push(block);
492
+ entries.push(entry);
493
+ }
494
+ return blocks;
495
+ }
496
+ function processRow(row, entries, warnings) {
497
+ const columns = row.columns;
498
+ if (!columns || columns.length === 0) return [];
499
+ if (row.locked) {
500
+ warnings.push(
501
+ "A locked row was imported. Lock metadata was dropped; content is preserved."
502
+ );
503
+ }
504
+ if (row.synced) {
505
+ warnings.push(
506
+ "A synced row was imported. Sync metadata was dropped; content is preserved."
507
+ );
508
+ }
509
+ const layout = resolveColumnLayout(columns, warnings);
510
+ if (!layout) {
511
+ const blocks = [];
512
+ for (const column of columns) {
513
+ blocks.push(...convertColumnModules(column, entries, warnings));
514
+ }
515
+ return blocks;
516
+ }
517
+ const children = columns.map(
518
+ (col) => convertColumnModules(col, entries, warnings)
519
+ );
520
+ const rowBg = parseColor(row.content?.style?.["background-color"]);
521
+ const section = createSectionBlock({
522
+ columns: layout,
523
+ children,
524
+ styles: {
525
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
526
+ margin: { top: 0, right: 0, bottom: 0, left: 0 },
527
+ ...rowBg ? { backgroundColor: rowBg } : {}
528
+ }
529
+ });
530
+ return [section];
531
+ }
532
+ function extractSettings(template) {
533
+ const body = template.page.body;
534
+ const contentStyle = body?.content?.style ?? {};
535
+ const containerStyle = body?.container?.style ?? {};
536
+ const width = parsePxValue(contentStyle["width"] ?? contentStyle.width);
537
+ const bgColor = parseColor(contentStyle["background-color"]) || parseColor(containerStyle["background-color"]) || "#ffffff";
538
+ const fontFamily = parseFontFamily(contentStyle["font-family"]) || "Arial";
539
+ return {
540
+ width: width || 600,
541
+ backgroundColor: bgColor,
542
+ fontFamily,
543
+ locale: "en"
544
+ };
545
+ }
546
+ function convertBeeFreeTemplate(template) {
547
+ if (!template?.page?.rows) {
548
+ throw new Error(
549
+ "Invalid BeeFree template: missing page.rows. Ensure you are passing a valid BeeFree JSON export."
550
+ );
551
+ }
552
+ const entries = [];
553
+ const warnings = [];
554
+ const blocks = [];
555
+ const webFonts = template.page.body?.webFonts;
556
+ if (webFonts && webFonts.length > 1) {
557
+ warnings.push(
558
+ `Template uses ${webFonts.length} web fonts. Only the primary font is preserved; additional fonts may need to be configured via the fonts option.`
559
+ );
560
+ }
561
+ for (const row of template.page.rows) {
562
+ if (row.empty) continue;
563
+ blocks.push(...processRow(row, entries, warnings));
564
+ }
565
+ const content = {
566
+ ...createDefaultTemplateContent(),
567
+ blocks,
568
+ settings: extractSettings(template)
569
+ };
570
+ const summary = {
571
+ total: entries.length,
572
+ converted: entries.filter((e) => e.status === "converted").length,
573
+ approximated: entries.filter((e) => e.status === "approximated").length,
574
+ htmlFallback: entries.filter((e) => e.status === "html-fallback").length,
575
+ skipped: entries.filter((e) => e.status === "skipped").length
576
+ };
577
+ const report = { entries, warnings, summary };
578
+ return { content, report };
579
+ }
580
+ export {
581
+ convertBeeFreeTemplate
582
+ };
583
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/converter.ts","../src/block-mapper.ts","../src/style-parser.ts"],"sourcesContent":["import {\n createSectionBlock,\n createDefaultTemplateContent,\n} from \"@aswin.dev/types\";\nimport type { Block, ColumnLayout, TemplateContent } from \"@aswin.dev/types\";\nimport type {\n BeeFreeTemplate,\n BeeFreeeRow,\n BeeFreeeColumn,\n ImportResult,\n ImportReport,\n ImportReportEntry,\n} from \"./types\";\nimport { convertModule } from \"./block-mapper\";\nimport { parsePxValue, parseColor, parseFontFamily } from \"./style-parser\";\n\n/**\n * Determines the Templatical ColumnLayout from BeeFree column grid values.\n */\nfunction resolveColumnLayout(\n columns: BeeFreeeColumn[],\n warnings: string[],\n): ColumnLayout | null {\n if (columns.length === 1) return null; // Single column — no section wrapper needed\n if (columns.length === 3) return \"3\";\n\n if (columns.length === 2) {\n const left = columns[0][\"grid-columns\"] ?? 6;\n const right = columns[1][\"grid-columns\"] ?? 6;\n const total = left + right;\n const ratio = left / total;\n\n if (ratio > 0.58) return \"2-1\";\n if (ratio < 0.42) return \"1-2\";\n return \"2\";\n }\n\n if (columns.length >= 4) {\n warnings.push(\n `Row with ${columns.length} columns was flattened to a single column. BeeFree supports arbitrary columns, but Templatical supports up to 3.`,\n );\n return null; // Flatten to single column\n }\n\n return \"2\";\n}\n\n/**\n * Converts all modules in a column to Templatical blocks.\n */\nfunction convertColumnModules(\n column: BeeFreeeColumn,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const blocks: Block[] = [];\n\n for (const module of column.modules) {\n const { block, entry } = convertModule(module, warnings);\n blocks.push(block);\n entries.push(entry);\n }\n\n return blocks;\n}\n\n/**\n * Processes a single BeeFree row into one or more Templatical blocks.\n */\nfunction processRow(\n row: BeeFreeeRow,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const columns = row.columns;\n if (!columns || columns.length === 0) return [];\n\n // Track locked/synced metadata\n if (row.locked) {\n warnings.push(\n \"A locked row was imported. Lock metadata was dropped; content is preserved.\",\n );\n }\n if (row.synced) {\n warnings.push(\n \"A synced row was imported. Sync metadata was dropped; content is preserved.\",\n );\n }\n\n const layout = resolveColumnLayout(columns, warnings);\n\n if (!layout) {\n // Single column (or flattened 4+ columns) — return modules directly\n const blocks: Block[] = [];\n for (const column of columns) {\n blocks.push(...convertColumnModules(column, entries, warnings));\n }\n return blocks;\n }\n\n // Multi-column — wrap in a SectionBlock\n const children: Block[][] = columns.map((col) =>\n convertColumnModules(col, entries, warnings),\n );\n\n // Extract section-level background from row content styles\n const rowBg = parseColor(row.content?.style?.[\"background-color\"]);\n\n const section = createSectionBlock({\n columns: layout,\n children,\n styles: {\n padding: { top: 0, right: 0, bottom: 0, left: 0 },\n margin: { top: 0, right: 0, bottom: 0, left: 0 },\n ...(rowBg ? { backgroundColor: rowBg } : {}),\n },\n });\n\n return [section];\n}\n\n/**\n * Extracts template-level settings from the BeeFree body.\n */\nfunction extractSettings(\n template: BeeFreeTemplate,\n): TemplateContent[\"settings\"] {\n const body = template.page.body;\n const contentStyle = body?.content?.style ?? {};\n const containerStyle = body?.container?.style ?? {};\n\n const width = parsePxValue(contentStyle[\"width\"] ?? contentStyle.width);\n const bgColor =\n parseColor(contentStyle[\"background-color\"]) ||\n parseColor(containerStyle[\"background-color\"]) ||\n \"#ffffff\";\n const fontFamily = parseFontFamily(contentStyle[\"font-family\"]) || \"Arial\";\n\n return {\n width: width || 600,\n backgroundColor: bgColor,\n fontFamily,\n locale: \"en\",\n };\n}\n\n/**\n * Converts a BeeFree template JSON to Templatical TemplateContent.\n *\n * @param template - The parsed BeeFree JSON object\n * @returns An ImportResult with the converted content and a detailed report\n *\n * @example\n * ```ts\n * import { convertBeeFreeTemplate } from '@aswin.dev/import-beefree';\n *\n * const beeFreeJson = JSON.parse(fileContent);\n * const { content, report } = convertBeeFreeTemplate(beeFreeJson);\n *\n * // Use the content with the editor\n * const editor = init({ container: '#editor', content });\n *\n * // Check the report for any issues\n * console.log(report.summary);\n * console.log(report.warnings);\n * ```\n */\nexport function convertBeeFreeTemplate(\n template: BeeFreeTemplate,\n): ImportResult {\n // Validate structure\n if (!template?.page?.rows) {\n throw new Error(\n \"Invalid BeeFree template: missing page.rows. Ensure you are passing a valid BeeFree JSON export.\",\n );\n }\n\n const entries: ImportReportEntry[] = [];\n const warnings: string[] = [];\n const blocks: Block[] = [];\n\n // Extract web font warnings\n const webFonts = template.page.body?.webFonts;\n if (webFonts && webFonts.length > 1) {\n warnings.push(\n `Template uses ${webFonts.length} web fonts. Only the primary font is preserved; additional fonts may need to be configured via the fonts option.`,\n );\n }\n\n // Process rows\n for (const row of template.page.rows) {\n if (row.empty) continue;\n blocks.push(...processRow(row, entries, warnings));\n }\n\n // Build template content\n const content: TemplateContent = {\n ...createDefaultTemplateContent(),\n blocks,\n settings: extractSettings(template),\n };\n\n // Build report summary\n const summary = {\n total: entries.length,\n converted: entries.filter((e) => e.status === \"converted\").length,\n approximated: entries.filter((e) => e.status === \"approximated\").length,\n htmlFallback: entries.filter((e) => e.status === \"html-fallback\").length,\n skipped: entries.filter((e) => e.status === \"skipped\").length,\n };\n\n const report: ImportReport = { entries, warnings, summary };\n\n return { content, report };\n}\n","import {\n createTitleBlock,\n createParagraphBlock,\n createImageBlock,\n createButtonBlock,\n createDividerBlock,\n createSpacerBlock,\n createHtmlBlock,\n createSocialIconsBlock,\n createMenuBlock,\n createTableBlock,\n createVideoBlock,\n generateId,\n} from \"@aswin.dev/types\";\nimport type {\n Block,\n HeadingLevel,\n SocialPlatform,\n SocialIcon,\n MenuItemData,\n TableRowData,\n TableCellData,\n SpacingValue,\n} from \"@aswin.dev/types\";\nimport type {\n BeeFreeeModule,\n BeeFreeeModuleDescriptor,\n ImportReportEntry,\n} from \"./types\";\nimport {\n parsePxValue,\n parseColor,\n parseBorderTop,\n extractPadding,\n parseWidthPercent,\n parseFontFamily,\n} from \"./style-parser\";\n\n/**\n * Maps BeeFree module type strings to short keys.\n */\nconst MODULE_TYPE_MAP: Record<string, string> = {\n \"mailup-bee-newsletter-modules-text\": \"paragraph\",\n \"mailup-bee-newsletter-modules-paragraph\": \"paragraph\",\n \"mailup-bee-newsletter-modules-heading\": \"title\",\n \"mailup-bee-newsletter-modules-list\": \"list\",\n \"mailup-bee-newsletter-modules-image\": \"image\",\n \"mailup-bee-newsletter-modules-button\": \"button\",\n \"mailup-bee-newsletter-modules-divider\": \"divider\",\n \"mailup-bee-newsletter-modules-spacer\": \"spacer\",\n \"mailup-bee-newsletter-modules-html\": \"html\",\n \"mailup-bee-newsletter-modules-social\": \"social\",\n \"mailup-bee-newsletter-modules-video\": \"video\",\n \"mailup-bee-newsletter-modules-menu\": \"menu\",\n \"mailup-bee-newsletter-modules-table\": \"table\",\n};\n\nconst SOCIAL_PLATFORM_MAP: Record<string, SocialPlatform> = {\n facebook: \"facebook\",\n twitter: \"twitter\",\n x: \"twitter\",\n instagram: \"instagram\",\n linkedin: \"linkedin\",\n youtube: \"youtube\",\n tiktok: \"tiktok\",\n pinterest: \"pinterest\",\n email: \"email\",\n whatsapp: \"whatsapp\",\n telegram: \"telegram\",\n discord: \"discord\",\n snapchat: \"snapchat\",\n reddit: \"reddit\",\n github: \"github\",\n dribbble: \"dribbble\",\n behance: \"behance\",\n};\n\ntype Align = \"left\" | \"center\" | \"right\";\ntype LineStyle = \"solid\" | \"dashed\" | \"dotted\";\n\nfunction toAlign(value: string | undefined, fallback: Align = \"left\"): Align {\n if (value === \"left\" || value === \"center\" || value === \"right\") return value;\n return fallback;\n}\n\nfunction toLineStyle(\n value: string | undefined,\n fallback: LineStyle = \"solid\",\n): LineStyle {\n if (value === \"solid\" || value === \"dashed\" || value === \"dotted\")\n return value;\n return fallback;\n}\n\nfunction defaultMargin(): SpacingValue {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction makeStyles(descriptor: BeeFreeeModuleDescriptor): Block[\"styles\"] {\n const padding = extractPadding(descriptor.style);\n const bg = parseColor(descriptor.style?.[\"background-color\"]);\n return {\n padding,\n margin: defaultMargin(),\n ...(bg ? { backgroundColor: bg } : {}),\n };\n}\n\n/**\n * Apply BeeFree text styles as TipTap-compatible inline markup.\n * - text-align → added to each <p> tag's style attribute\n * - color, font-size, font-weight, font-family → wrapped in <span style=\"...\"> inside each <p>\n */\nfunction inlineStylesToHtml(\n html: string,\n style: Record<string, string | undefined>,\n): string {\n const spanParts: string[] = [];\n const fontSize = parsePxValue(style[\"font-size\"]);\n if (fontSize && fontSize !== 16) spanParts.push(`font-size: ${fontSize}px`);\n const color = parseColor(style.color);\n if (color && color !== \"#1a1a1a\") spanParts.push(`color: ${color}`);\n const fontWeight = style[\"font-weight\"];\n if (fontWeight && fontWeight !== \"normal\")\n spanParts.push(`font-weight: ${fontWeight}`);\n const fontFamily = parseFontFamily(style[\"font-family\"]);\n if (fontFamily) spanParts.push(`font-family: ${fontFamily}`);\n\n const textAlign = style[\"text-align\"];\n const pStyle =\n textAlign && textAlign !== \"left\" ? `text-align: ${textAlign}` : \"\";\n\n if (!pStyle && spanParts.length === 0) return html;\n\n const spanStyle = spanParts.join(\"; \");\n\n // Apply styles to each <p> tag in the HTML\n let result = html;\n\n if (pStyle) {\n // Add text-align to existing <p style=\"...\"> or add style to plain <p>\n result = result\n .replace(/<p style=\"([^\"]*)\">/g, `<p style=\"$1; ${pStyle}\">`)\n .replaceAll(\"<p>\", `<p style=\"${pStyle}\">`);\n }\n\n if (spanStyle) {\n // Wrap inner content of each <p> in a styled span\n result = result.replace(\n /<p([^>]*)>([\\s\\S]*?)<\\/p>/g,\n `<p$1><span style=\"${spanStyle}\">$2</span></p>`,\n );\n }\n\n return result;\n}\n\nfunction convertText(descriptor: BeeFreeeModuleDescriptor): Block {\n const textContent =\n descriptor.text ?? descriptor.paragraph ?? descriptor.list;\n const html = textContent?.html ?? \"\";\n const style = textContent?.style ?? {};\n\n return createParagraphBlock({\n content: inlineStylesToHtml(html, style),\n styles: makeStyles(descriptor),\n });\n}\n\nfunction parseHeadingLevel(tag: string): HeadingLevel {\n const match = tag.match(/^h(\\d)$/i);\n if (match) {\n const num = Number(match[1]);\n if (num >= 1 && num <= 4) return num as HeadingLevel;\n }\n return 2;\n}\n\nfunction convertHeading(descriptor: BeeFreeeModuleDescriptor): Block {\n const heading = descriptor.heading;\n if (!heading) return convertText(descriptor);\n\n const style = heading.style ?? {};\n const tag = heading.title ?? \"h2\";\n const text = heading.text ?? \"\";\n // Strip heading tags — content goes inside TipTap, heading tag is rendered by the block\n const content = text.startsWith(\"<\")\n ? text.replace(/^<h\\d[^>]*>|<\\/h\\d>$/gi, \"\")\n : text;\n\n return createTitleBlock({\n content: content ? `<p>${content}</p>` : \"<p></p>\",\n level: parseHeadingLevel(tag),\n color: parseColor(style.color) || \"#1a1a1a\",\n textAlign: toAlign(style[\"text-align\"]),\n fontFamily: parseFontFamily(style[\"font-family\"]) || undefined,\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertImage(descriptor: BeeFreeeModuleDescriptor): Block {\n const image = descriptor.image;\n if (!image) {\n return createImageBlock({ styles: makeStyles(descriptor) });\n }\n\n return createImageBlock({\n src: image.src || \"\",\n alt: image.alt || \"\",\n width: parsePxValue(image.width) || 600,\n align: toAlign(image.style?.[\"text-align\"], \"center\"),\n linkUrl: image.href || undefined,\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertButton(descriptor: BeeFreeeModuleDescriptor): Block {\n const button = descriptor.button;\n if (!button) {\n return createButtonBlock({ styles: makeStyles(descriptor) });\n }\n\n const style = button.style ?? {};\n const label = button.label?.replace(/<[^>]*>/g, \"\") ?? \"Button\";\n\n return createButtonBlock({\n text: label,\n url: button.href || \"#\",\n backgroundColor: parseColor(style[\"background-color\"]) || \"#4f46e5\",\n textColor: parseColor(style.color) || \"#ffffff\",\n borderRadius: parsePxValue(style[\"border-radius\"]),\n fontSize: parsePxValue(style[\"font-size\"]) || 16,\n fontFamily: parseFontFamily(style[\"font-family\"]) || undefined,\n buttonPadding: {\n top: parsePxValue(style[\"padding-top\"]) || 12,\n right: parsePxValue(style[\"padding-right\"]) || 24,\n bottom: parsePxValue(style[\"padding-bottom\"]) || 12,\n left: parsePxValue(style[\"padding-left\"]) || 24,\n },\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertDivider(descriptor: BeeFreeeModuleDescriptor): Block {\n const divider = descriptor.divider;\n const style = divider?.style ?? {};\n const border = parseBorderTop(style[\"border-top\"]);\n\n return createDividerBlock({\n lineStyle: toLineStyle(border.style),\n color: border.color,\n thickness: border.width || 1,\n width: parseWidthPercent(style.width),\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertSpacer(descriptor: BeeFreeeModuleDescriptor): Block {\n const spacer = descriptor.spacer;\n const height = parsePxValue(spacer?.style?.height) || 24;\n\n return createSpacerBlock({\n height,\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertHtml(descriptor: BeeFreeeModuleDescriptor): Block {\n const html = descriptor.html?.html ?? \"\";\n\n return createHtmlBlock({\n content: html,\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertSocial(\n descriptor: BeeFreeeModuleDescriptor,\n warnings: string[],\n): Block {\n const iconsList = descriptor.iconsList;\n if (!iconsList?.icons) {\n return createSocialIconsBlock({ styles: makeStyles(descriptor) });\n }\n\n const icons: SocialIcon[] = [];\n\n for (const beeIcon of iconsList.icons) {\n const id = (beeIcon.id ?? beeIcon.name ?? \"\").toLowerCase();\n const platform = SOCIAL_PLATFORM_MAP[id];\n\n if (!platform) {\n warnings.push(\n `Unrecognized social icon \"${beeIcon.name || id}\" was skipped.`,\n );\n continue;\n }\n\n icons.push({\n id: generateId(),\n platform,\n url: beeIcon.image?.href || \"#\",\n });\n }\n\n return createSocialIconsBlock({\n icons,\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertVideo(descriptor: BeeFreeeModuleDescriptor): Block {\n const video = descriptor.video;\n if (!video) {\n return createVideoBlock({ styles: makeStyles(descriptor) });\n }\n\n return createVideoBlock({\n url: video.src || \"\",\n thumbnailUrl: video.thumbnail || \"\",\n alt: video.alt || \"\",\n width: parsePxValue(video.style?.width) || 600,\n align: toAlign(video.style?.[\"text-align\"], \"center\"),\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertMenu(descriptor: BeeFreeeModuleDescriptor): Block {\n const menu = descriptor.menu;\n if (!menu) {\n return createMenuBlock({ styles: makeStyles(descriptor) });\n }\n\n const style = menu.style ?? {};\n\n const items: MenuItemData[] = (menu.items ?? []).map((item) => ({\n id: generateId(),\n text: item.text || \"\",\n url: item.link || item.href || \"#\",\n openInNewTab: item.target === \"_blank\",\n bold: false,\n underline: false,\n }));\n\n return createMenuBlock({\n items,\n separator: menu.separator || \"|\",\n separatorColor: parseColor(menu.separatorColor) || \"#999999\",\n fontSize: parsePxValue(style[\"font-size\"]) || 14,\n color: parseColor(style.color) || \"#1a1a1a\",\n fontFamily: parseFontFamily(style[\"font-family\"]) || undefined,\n textAlign: toAlign(style[\"text-align\"], \"center\"),\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertTable(descriptor: BeeFreeeModuleDescriptor): Block {\n const table = descriptor.table;\n if (!table) {\n return createTableBlock({ styles: makeStyles(descriptor) });\n }\n\n const style = table.style ?? {};\n\n const rows: TableRowData[] = (table.rows ?? []).map((row) => ({\n id: generateId(),\n cells: (row.cells ?? []).map(\n (cell): TableCellData => ({\n id: generateId(),\n content: cell.content ?? cell.html ?? \"\",\n }),\n ),\n }));\n\n return createTableBlock({\n rows,\n hasHeaderRow: table.hasHeaderRow ?? false,\n headerBackgroundColor: parseColor(table.headerBackgroundColor) || undefined,\n borderColor: parseColor(style[\"border-color\"]) || \"#dddddd\",\n borderWidth: parsePxValue(style[\"border-width\"]) || 1,\n cellPadding:\n typeof table.cellPadding === \"number\"\n ? table.cellPadding\n : parsePxValue(table.cellPadding as string) || 8,\n fontSize: parsePxValue(style[\"font-size\"]) || 14,\n color: parseColor(style.color) || \"#1a1a1a\",\n textAlign: toAlign(style[\"text-align\"]),\n styles: makeStyles(descriptor),\n });\n}\n\nfunction convertHtmlFallback(module: BeeFreeeModule): Block {\n // Attempt to extract any HTML content from the descriptor\n const descriptor = module.descriptor;\n let html = \"\";\n\n // Try common content fields\n if (descriptor.text?.html) html = descriptor.text.html;\n else if (descriptor.html?.html) html = descriptor.html.html;\n else if (descriptor.heading?.text) html = descriptor.heading.text;\n else html = `<!-- Unsupported BeeFree module: ${module.type} -->`;\n\n return createHtmlBlock({\n content: html,\n styles: makeStyles(descriptor),\n });\n}\n\n/**\n * Converts a single BeeFree module to a Templatical block.\n * Returns the block and a report entry.\n */\nexport function convertModule(\n module: BeeFreeeModule,\n warnings: string[],\n): { block: Block; entry: ImportReportEntry } {\n const mappedType = MODULE_TYPE_MAP[module.type];\n const descriptor = module.descriptor;\n\n if (!mappedType) {\n return {\n block: convertHtmlFallback(module),\n entry: {\n beeFreeModuleType: module.type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: `Unknown module type \"${module.type}\" converted to HTML block.`,\n },\n };\n }\n\n let block: Block;\n let isApproximation = false;\n\n switch (mappedType) {\n case \"paragraph\":\n case \"list\":\n block = convertText(descriptor);\n break;\n case \"title\":\n block = convertHeading(descriptor);\n break;\n case \"image\":\n block = convertImage(descriptor);\n break;\n case \"button\":\n block = convertButton(descriptor);\n break;\n case \"divider\":\n block = convertDivider(descriptor);\n break;\n case \"spacer\":\n block = convertSpacer(descriptor);\n break;\n case \"html\":\n block = convertHtml(descriptor);\n break;\n case \"social\":\n block = convertSocial(descriptor, warnings);\n break;\n case \"video\":\n block = convertVideo(descriptor);\n break;\n case \"menu\":\n block = convertMenu(descriptor);\n isApproximation = true; // menu styles are approximate\n break;\n case \"table\":\n block = convertTable(descriptor);\n break;\n default:\n block = convertHtmlFallback(module);\n return {\n block,\n entry: {\n beeFreeModuleType: module.type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n },\n };\n }\n\n return {\n block,\n entry: {\n beeFreeModuleType: module.type,\n templaticalBlockType: block.type,\n status: isApproximation ? \"approximated\" : \"converted\",\n },\n };\n}\n","import type { SpacingValue } from \"@aswin.dev/types\";\n\n/**\n * Parses CSS-like style values from BeeFree descriptors.\n */\n\nexport function parsePxValue(value: string | undefined): number {\n if (!value) return 0;\n const match = value.match(/^(-?\\d+(?:\\.\\d+)?)\\s*px/);\n return match ? Math.round(parseFloat(match[1])) : 0;\n}\n\nexport function parseColor(value: string | undefined): string {\n if (!value || value === \"transparent\") return \"\";\n\n const trimmed = value.trim();\n\n // Already a valid hex color\n if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) return trimmed.toLowerCase();\n\n // 3-digit hex → 6-digit\n if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {\n const r = trimmed[1];\n const g = trimmed[2];\n const b = trimmed[3];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n\n // Return as-is for rgb(), named colors, etc.\n return trimmed;\n}\n\nexport function parseBorderTop(value: string | undefined): {\n width: number;\n style: string;\n color: string;\n} {\n if (!value) return { width: 0, style: \"solid\", color: \"#000000\" };\n\n // \"2px solid #cccccc\"\n const parts = value.trim().split(/\\s+/);\n return {\n width: parsePxValue(parts[0]),\n style: parts[1] || \"solid\",\n color: parseColor(parts[2]) || \"#000000\",\n };\n}\n\nexport function extractPadding(\n style: Record<string, string> | undefined,\n): SpacingValue {\n if (!style) return { top: 0, right: 0, bottom: 0, left: 0 };\n\n // Check for shorthand `padding` first\n if (style.padding) {\n return parseShorthandPadding(style.padding);\n }\n\n return {\n top: parsePxValue(style[\"padding-top\"]),\n right: parsePxValue(style[\"padding-right\"]),\n bottom: parsePxValue(style[\"padding-bottom\"]),\n left: parsePxValue(style[\"padding-left\"]),\n };\n}\n\nfunction parseShorthandPadding(value: string): SpacingValue {\n const parts = value.trim().split(/\\s+/);\n const values = parts.map((p) => parsePxValue(p));\n\n switch (values.length) {\n case 1:\n return {\n top: values[0],\n right: values[0],\n bottom: values[0],\n left: values[0],\n };\n case 2:\n return {\n top: values[0],\n right: values[1],\n bottom: values[0],\n left: values[1],\n };\n case 3:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[1],\n };\n default:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[3],\n };\n }\n}\n\nexport function parseWidthPercent(value: string | undefined): number {\n if (!value) return 100;\n const match = value.match(/^(\\d+(?:\\.\\d+)?)\\s*%/);\n if (match) return Math.round(parseFloat(match[1]));\n // Might be px — return 100 as default\n return 100;\n}\n\nexport function parseFontFamily(value: string | undefined): string {\n if (!value) return \"\";\n // Take the first font in the stack\n return value.split(\",\")[0].trim().replace(/['\"]/g, \"\");\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@aswin.dev/import-beefree",
3
+ "description": "Convert BeeFree email templates to Templatical format",
4
+ "version": "0.7.0",
5
+ "bugs": "https://github.com/templatical/sdk/issues",
6
+ "dependencies": {
7
+ "@aswin.dev/types": "0.7.0"
8
+ },
9
+ "devDependencies": {
10
+ "tsup": "^8.5.1",
11
+ "typescript": "^6.0.3",
12
+ "vitest": "^4.1.5"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "homepage": "https://templatical.com",
24
+ "keywords": [
25
+ "beefree",
26
+ "email",
27
+ "email-template",
28
+ "importer",
29
+ "migration",
30
+ "templatical"
31
+ ],
32
+ "license": "MIT",
33
+ "module": "./dist/index.js",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/templatical/sdk.git",
40
+ "directory": "packages/import-beefree"
41
+ },
42
+ "type": "module",
43
+ "types": "./dist/index.d.ts",
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "test": "vitest run --config vitest.config.ts",
47
+ "typecheck": "tsc --noEmit"
48
+ }
49
+ }