@templatical/import-easy-email-pro 0.42.0 → 0.43.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/index.js CHANGED
@@ -4,6 +4,7 @@ const VAR_PREFIX = "$var(";
4
4
  /** Spec §3.3 category map — unknown types skip the category layer. */
5
5
  const CATEGORY_BY_TYPE = {
6
6
  "standard-paragraph": "TEXT",
7
+ "standard-text": "TEXT",
7
8
  "standard-h1": "TEXT",
8
9
  "standard-h2": "TEXT",
9
10
  "standard-h3": "TEXT",
@@ -259,6 +260,58 @@ function serialiseTextNode(node) {
259
260
  if (node.link) html = `<a href="${escapeHtml(node.link.href ?? "")}"${node.link.blank === true ? " target=\"_blank\"" : ""}>${html}</a>`;
260
261
  return html;
261
262
  }
263
+ /** Easy Email Pro's `HTML_NODE_VOID_TAGS`: rendered `<tag />`, children ignored. */
264
+ const VOID_TAGS = /* @__PURE__ */ new Set([
265
+ "img",
266
+ "br",
267
+ "hr",
268
+ "area",
269
+ "base",
270
+ "col",
271
+ "embed",
272
+ "input",
273
+ "link",
274
+ "meta",
275
+ "source"
276
+ ]);
277
+ const TAG_NAME = /^[a-z][a-z0-9-]*$/;
278
+ const ATTRIBUTE_NAME = /^[a-z_:][a-z0-9_:.-]*$/i;
279
+ /**
280
+ * An inline `html-node` renders as `<tagName attrs>children</tagName>`. Only
281
+ * non-empty string attributes are written, as Easy Email Pro does. A tag or
282
+ * attribute name that could break out of the markup is dropped, the tag
283
+ * falling back to `span`.
284
+ */
285
+ function serialiseHtmlNode(node) {
286
+ const rawTag = node.data?.["tagName"];
287
+ const lowered = typeof rawTag === "string" ? rawTag.toLowerCase() : "";
288
+ const tagName = TAG_NAME.test(lowered) ? lowered : "span";
289
+ let attrs = "";
290
+ for (const [name, value] of Object.entries(node.attributes ?? {})) {
291
+ if (typeof value !== "string" || !ATTRIBUTE_NAME.test(name)) continue;
292
+ const trimmed = value.trim();
293
+ if (trimmed === "") continue;
294
+ attrs += ` ${name}="${escapeHtml(trimmed)}"`;
295
+ }
296
+ if (VOID_TAGS.has(tagName)) return `<${tagName}${attrs} />`;
297
+ return `<${tagName}${attrs}>${serialiseChildren(node.children)}</${tagName}>`;
298
+ }
299
+ /**
300
+ * Easy Email Pro renders a `mergetag` as `{{ name }}`, the name being its
301
+ * first text child and the marks on that child applying to the token.
302
+ * `data.default` is not part of that output. A blank name has no variable to
303
+ * keep.
304
+ */
305
+ function serialiseMergetag(node) {
306
+ const first = node.children?.[0];
307
+ if (!first || !isTextNode(first)) return "";
308
+ const name = first.text.trim();
309
+ if (name === "") return "";
310
+ return serialiseTextNode({
311
+ ...first,
312
+ text: `{{ ${name} }}`
313
+ });
314
+ }
262
315
  function serialiseNode(node) {
263
316
  if (node.type === "line-break") return "<br>";
264
317
  if (node.type === "html-block-node") {
@@ -267,6 +320,8 @@ function serialiseNode(node) {
267
320
  const tagName = typeof rawTag === "string" && rawTag !== "" ? rawTag : "div";
268
321
  return `<${tagName}>${serialiseChildren(element.children)}</${tagName}>`;
269
322
  }
323
+ if (node.type === "html-node") return serialiseHtmlNode(node);
324
+ if (node.type === "mergetag") return serialiseMergetag(node);
270
325
  if (typeof node.type === "string" && node.type !== "") return "";
271
326
  if (isTextNode(node)) return serialiseTextNode(node);
272
327
  return "";
@@ -282,6 +337,7 @@ function serialiseChildren(children) {
282
337
  //#region src/block-mapper.ts
283
338
  const LEAF_TYPES = /* @__PURE__ */ new Set([
284
339
  "standard-paragraph",
340
+ "standard-text",
285
341
  "standard-h1",
286
342
  "standard-h2",
287
343
  "standard-h3",
@@ -295,7 +351,8 @@ const LEAF_TYPES = /* @__PURE__ */ new Set([
295
351
  "standard-table2",
296
352
  "marketing-countdown",
297
353
  "placeholder",
298
- "common-video"
354
+ "common-video",
355
+ "raw"
299
356
  ]);
300
357
  const SOCIAL_PLATFORMS = [
301
358
  "facebook",
@@ -318,6 +375,15 @@ const ENTITIES = {
318
375
  "&apos;": "'",
319
376
  "&nbsp;": " "
320
377
  };
378
+ /**
379
+ * Easy Email Pro writes `standard-table2-tr` / `standard-table2-td`; the plain
380
+ * `tr` / `td` / `th` shape is read too.
381
+ */
382
+ const TABLE_CELL_TYPES = /* @__PURE__ */ new Set([
383
+ "standard-table2-td",
384
+ "td",
385
+ "th"
386
+ ]);
321
387
  const COUNTDOWN_NOTE = "marketing-countdown GIF is the timer; Templatical countdown is Cloud-only";
322
388
  function isLeafType(type) {
323
389
  if (typeof type !== "string") return false;
@@ -327,7 +393,7 @@ function isLeafType(type) {
327
393
  function convertLeaf(node, map) {
328
394
  const type = node.type;
329
395
  if (type === "placeholder") return empty$1();
330
- if (type === "standard-paragraph") return convertParagraph(node, map);
396
+ if (type === "standard-paragraph" || type === "standard-text") return convertParagraph(node, map);
331
397
  const heading = typeof type === "string" ? headingLevelFromType(type) : null;
332
398
  if (heading) return convertHeading(node, map, heading);
333
399
  switch (type) {
@@ -340,14 +406,19 @@ function convertLeaf(node, map) {
340
406
  case "standard-table2": return convertTable(node, map);
341
407
  case "marketing-countdown": return convertCountdown(node, map);
342
408
  case "common-video": return convertVideo(node, map);
409
+ case "raw": return convertRaw(node, map);
343
410
  default: return convertUnknown(node, map);
344
411
  }
345
412
  }
413
+ /**
414
+ * `standard-text` (the default text block) and `standard-paragraph` share one
415
+ * shape and one MJML renderer, so both land here.
416
+ */
346
417
  function convertParagraph(node, map) {
347
418
  return finish(createParagraphBlock({
348
419
  content: paragraphHtml(node, map),
349
420
  ...leafStyles(node, map)
350
- }), node, report("standard-paragraph", "paragraph", "converted"));
421
+ }), node, report(node.type ?? "standard-paragraph", "paragraph", "converted"));
351
422
  }
352
423
  /**
353
424
  * ParagraphBlock has no color / fontSize / textAlign. Bake the cascaded
@@ -429,13 +500,36 @@ function convertDivider(node, map) {
429
500
  const color = parseColor(readAttr(node, "border-color", map.resolve));
430
501
  const thickness = parsePx(readAttr(node, "border-width", map.resolve));
431
502
  const lineStyle = asLineStyle(readAttr(node, "border-style", map.resolve));
503
+ const styles = leafStyles(node, map);
504
+ const { left, right } = styles.styles.padding;
505
+ const room = map.contentWidth === void 0 ? void 0 : map.contentWidth - left - right;
432
506
  return finish(createDividerBlock({
433
507
  ...color ? { color } : {},
434
508
  ...thickness !== void 0 ? { thickness } : {},
435
509
  ...lineStyle ? { lineStyle } : {},
436
- ...leafStyles(node, map)
510
+ width: dividerWidth(readAttr(node, "width", map.resolve), room),
511
+ ...styles
437
512
  }), node, report("standard-divider", "divider", "converted"));
438
513
  }
514
+ /**
515
+ * A missing or `100%` width is `"full"`. Any other percentage stays a share of
516
+ * the column, clamped to 0-100 and rounded to two decimals so it stays a
517
+ * `DividerPercentWidth`. A px width stays px until it fills `room`, the space
518
+ * the column leaves the line once the divider's own padding is off, and then
519
+ * it is `"full"`. An unreadable width falls back to `"full"`, MJML's default.
520
+ */
521
+ function dividerWidth(value, room) {
522
+ if (isUnset(value)) return "full";
523
+ if (typeof value === "string" && value.trim().endsWith("%")) {
524
+ const percent = parsePercent(value);
525
+ if (percent === null) return "full";
526
+ const rounded = Math.round(Math.min(100, Math.max(0, percent)) * 100) / 100;
527
+ return rounded >= 100 ? "full" : `${rounded}%`;
528
+ }
529
+ const px = parsePx(value);
530
+ if (px === void 0) return "full";
531
+ return room !== void 0 && px >= room ? "full" : px;
532
+ }
439
533
  function convertSpacer(node, map) {
440
534
  const height = parsePx(readAttr(node, "height", map.resolve));
441
535
  return finish(createSpacerBlock({
@@ -500,8 +594,7 @@ function convertTable(node, map) {
500
594
  const cells = [];
501
595
  let rowHasTh = false;
502
596
  for (const cell of row.children ?? []) {
503
- if (!isElement$2(cell)) continue;
504
- if (cell.type !== "td" && cell.type !== "th") continue;
597
+ if (!isElement$2(cell) || !TABLE_CELL_TYPES.has(cell.type ?? "")) continue;
505
598
  if (cell.type === "th") rowHasTh = true;
506
599
  cells.push({
507
600
  id: generateId(),
@@ -574,6 +667,15 @@ function convertVideo(node, map) {
574
667
  ...leafStyles(node, map)
575
668
  }), node, report("common-video", "video", "converted"));
576
669
  }
670
+ /** `raw` is `<mj-raw>{data.content}</mj-raw>`: the markup moves across as is. */
671
+ function convertRaw(node, map) {
672
+ const content = node.data?.["content"];
673
+ if (typeof content !== "string") return convertUnknown(node, map);
674
+ return finish(createHtmlBlock({
675
+ content,
676
+ ...leafStyles(node, map)
677
+ }), node, report("raw", "html", "converted"));
678
+ }
577
679
  function convertUnknown(node, map) {
578
680
  const sourceTag = node.type ?? "unknown";
579
681
  return finish(createHtmlBlock({
@@ -586,7 +688,7 @@ function remapOverlay(child) {
586
688
  ...child,
587
689
  type: "standard-paragraph"
588
690
  };
589
- if (child.type === "standard-paragraph") return child;
691
+ if (child.type === "standard-paragraph" || child.type === "standard-text") return child;
590
692
  if (typeof child.type === "string" && headingLevelFromType(child.type)) return child;
591
693
  return null;
592
694
  }
@@ -594,14 +696,17 @@ function collectRows(node) {
594
696
  const rows = [];
595
697
  for (const child of node.children ?? []) {
596
698
  if (!isElement$2(child)) continue;
597
- if (child.type === "tr") {
699
+ if (isTableRow(child)) {
598
700
  rows.push(child);
599
701
  continue;
600
702
  }
601
- for (const inner of child.children ?? []) if (isElement$2(inner) && inner.type === "tr") rows.push(inner);
703
+ for (const inner of child.children ?? []) if (isElement$2(inner) && isTableRow(inner)) rows.push(inner);
602
704
  }
603
705
  return rows;
604
706
  }
707
+ function isTableRow(node) {
708
+ return node.type === "standard-table2-tr" || node.type === "tr";
709
+ }
605
710
  function firstUrl(node, map) {
606
711
  for (const key of [
607
712
  "src",
@@ -790,6 +895,19 @@ const COLUMN_COUNT = {
790
895
  "2-1": 2,
791
896
  "1-2": 2
792
897
  };
898
+ function columnPixels(layout, container) {
899
+ switch (layout) {
900
+ case "2": return [container / 2, container / 2];
901
+ case "3": return [
902
+ container / 3,
903
+ container / 3,
904
+ container / 3
905
+ ];
906
+ case "1-2": return [container / 3, container * 2 / 3];
907
+ case "2-1": return [container * 2 / 3, container / 3];
908
+ default: return [container];
909
+ }
910
+ }
793
911
  /**
794
912
  * Resolve column widths to one of the five layouts `ColumnLayout` allows.
795
913
  *
@@ -857,16 +975,18 @@ function buildTopLevel(node, map) {
857
975
  }
858
976
  function buildSection(node, map) {
859
977
  const plan = collectSlots(node);
860
- const leftover = convertFlow(plan.leftovers, map);
861
978
  const rawWidths = plan.columns.map((column) => readAttr(column, "width", map.resolve));
862
979
  const { layout, exact } = matchColumnLayout(rawWidths.map((width) => parsePercent(width)));
863
980
  const slots = COLUMN_COUNT[layout];
981
+ const sectionWidth = insideWidth(map.contentWidth, node, map);
982
+ const slotWidths = sectionWidth === void 0 ? void 0 : columnPixels(layout, sectionWidth);
983
+ const leftover = convertFlow(plan.leftovers, withContentWidth(map, slotWidths?.[0]));
864
984
  const children = Array.from({ length: slots }, () => []);
865
985
  if (leftover.blocks.length > 0) children[0].push(...leftover.blocks);
866
986
  const entries = leftover.entries;
867
987
  plan.columns.forEach((column, index) => {
868
988
  const slot = Math.min(index, slots - 1);
869
- const inner = convertFlow(column.children ?? [], map);
989
+ const inner = convertFlow(column.children ?? [], withContentWidth(map, insideWidth(slotWidths?.[slot], column, map)));
870
990
  children[slot].push(...inner.blocks);
871
991
  for (const entry of inner.entries) entries.push(entry);
872
992
  });
@@ -902,7 +1022,7 @@ function buildHero(node, map) {
902
1022
  note: "hero background-url stacked as a leading image (overlay is the loss)"
903
1023
  });
904
1024
  }
905
- const inner = convertFlow(node.children ?? [], map);
1025
+ const inner = convertFlow(node.children ?? [], withContentWidth(map, insideWidth(map.contentWidth, node, map)));
906
1026
  slot.push(...inner.blocks);
907
1027
  for (const entry of inner.entries) entries.push(entry);
908
1028
  const section = createSectionBlock();
@@ -923,14 +1043,14 @@ function buildHero(node, map) {
923
1043
  function buildWrapper(node, map) {
924
1044
  const blocks = [];
925
1045
  const entries = [];
1046
+ const childMap = withContentWidth(map, insideWidth(map.contentWidth, node, map));
926
1047
  for (const child of elements(node)) {
927
1048
  if (child.type === "placeholder") continue;
928
- const inner = buildTopLevel(child, map);
1049
+ const inner = buildTopLevel(child, childMap);
929
1050
  for (const block of inner.blocks) blocks.push(block);
930
1051
  for (const entry of inner.entries) entries.push(entry);
931
1052
  }
932
- const sections = blocks.filter((block) => block.type === "section");
933
- if (sections.length === 0) return {
1053
+ if (!blocks.some(isSection)) return {
934
1054
  blocks: [],
935
1055
  entries: [{
936
1056
  sourceTag: "standard-wrapper",
@@ -939,10 +1059,24 @@ function buildWrapper(node, map) {
939
1059
  note: "An empty standard-wrapper produces nothing."
940
1060
  }]
941
1061
  };
1062
+ applyWrapper(node, blocks, entries, map);
1063
+ return {
1064
+ blocks,
1065
+ entries
1066
+ };
1067
+ }
1068
+ /**
1069
+ * Paint `node`'s `mj-wrapper` band onto every section in `blocks`. A section
1070
+ * holds one band, so a band around several sections is copied onto each and
1071
+ * their `converted` entries become `approximated`.
1072
+ */
1073
+ function applyWrapper(node, blocks, entries, map) {
1074
+ const sections = blocks.filter(isSection);
1075
+ if (sections.length === 0) return;
942
1076
  const wrapper = readWrapper(node, map);
943
1077
  for (const section of sections) section.wrapper = { ...wrapper };
944
1078
  if (sections.length > 1) {
945
- const note = `standard-wrapper holding ${sections.length} sections was applied to each of them — Templatical has no multi-section band.`;
1079
+ const note = `${node.type ?? "standard-wrapper"} holding ${sections.length} sections was applied to each of them — Templatical has no multi-section band.`;
946
1080
  for (const entry of entries) {
947
1081
  if (entry.templaticalBlockType !== "section") continue;
948
1082
  if (entry.status !== "converted") continue;
@@ -950,11 +1084,22 @@ function buildWrapper(node, map) {
950
1084
  entry.note = note;
951
1085
  }
952
1086
  }
1087
+ }
1088
+ /** `width` less `node`'s own left and right padding; unknown stays unknown. */
1089
+ function insideWidth(width, node, map) {
1090
+ if (width === void 0) return void 0;
1091
+ const padding = readPadding((key) => readAttr(node, key, map.resolve));
1092
+ return Math.max(0, width - padding.left - padding.right);
1093
+ }
1094
+ function withContentWidth(map, contentWidth) {
953
1095
  return {
954
- blocks,
955
- entries
1096
+ ...map,
1097
+ contentWidth
956
1098
  };
957
1099
  }
1100
+ function isSection(block) {
1101
+ return block.type === "section";
1102
+ }
958
1103
  /**
959
1104
  * Direct columns become slots. A sole `standard-group` of columns becomes
960
1105
  * those slots with `stackOnMobile: false`. Mixed group + columns flatten
@@ -1124,6 +1269,12 @@ const STRUCTURE_TYPES = /* @__PURE__ */ new Set([
1124
1269
  ]);
1125
1270
  const WIDGET_TYPES = /* @__PURE__ */ new Set(["section_widget", "wrapper_widget"]);
1126
1271
  /**
1272
+ * Header and footer bands. Easy Email Pro renders each as an `mj-wrapper`
1273
+ * carrying the node's own attributes around `data.content`, whose items are
1274
+ * page children.
1275
+ */
1276
+ const PAGE_BAND_TYPES = /* @__PURE__ */ new Set(["page-header", "page-footer"]);
1277
+ /**
1127
1278
  * `DEFAULT_TEMPLATE_DEFAULTS` is typed `Partial<TemplateSettings>` so a
1128
1279
  * consumer can override any subset, but its own literal always sets the six
1129
1280
  * required fields. Narrowing once here is what lets each fallback come out as
@@ -1147,11 +1298,13 @@ function convertEasyEmailProTemplate(doc) {
1147
1298
  const entries = [];
1148
1299
  const blocks = [];
1149
1300
  const resolve = contextFromPage(page);
1301
+ const data = isPlainObject(page.data) ? page.data : {};
1302
+ const settings = readSettings(page, data, resolve);
1150
1303
  const map = {
1151
1304
  resolve,
1152
- warnings
1305
+ warnings,
1306
+ contentWidth: settings.width
1153
1307
  };
1154
- const data = isPlainObject(page.data) ? page.data : {};
1155
1308
  if (typeof subject === "string" ? subject.trim() : "") warnings.push(`Document subject "${subject}" has no TemplateSettings field and was dropped.`);
1156
1309
  if (Array.isArray(data.fonts) && data.fonts.length > 0) warnings.push("Dropped fonts[] — Templatical templates have no document-level font table.");
1157
1310
  if (isNonEmptyHeadStyles(data.headStyles)) warnings.push("Dropped headStyles — Templatical templates have no document-level head-style table.");
@@ -1160,7 +1313,6 @@ function convertEasyEmailProTemplate(doc) {
1160
1313
  if (!isElement(child)) continue;
1161
1314
  walkNode(child, map, entries, blocks);
1162
1315
  }
1163
- const settings = readSettings(page, data, resolve);
1164
1316
  if (hasMobileAttributes(page)) warnings.push(MOBILE_WARNING);
1165
1317
  if (resolve.stats.resolved > 0 || resolve.stats.unresolved > 0) warnings.push(varWarning(resolve.stats.resolved, resolve.stats.unresolved));
1166
1318
  if (blocks.length === 0) warnings.push(EMPTY_WARNING);
@@ -1226,8 +1378,8 @@ function walkNode(node, map, entries, blocks) {
1226
1378
  const data = isPlainObject(node.data) ? node.data : {};
1227
1379
  const input = isPlainObject(data.input) ? data.input : void 0;
1228
1380
  const inner = {
1229
- resolve: withWidgetInput(map.resolve, input),
1230
- warnings: map.warnings
1381
+ ...map,
1382
+ resolve: withWidgetInput(map.resolve, input)
1231
1383
  };
1232
1384
  for (const child of node.children ?? []) {
1233
1385
  if (!isElement(child)) continue;
@@ -1242,7 +1394,8 @@ function walkNode(node, map, entries, blocks) {
1242
1394
  const converted = buildTopLevel(node, map);
1243
1395
  for (const block of converted.blocks) blocks.push(block);
1244
1396
  for (const entry of converted.entries) entries.push(entry);
1245
- } else {
1397
+ } else if (isPageBand(node)) walkBand(node, map, entries, blocks);
1398
+ else {
1246
1399
  const converted = convertLeaf(node, map);
1247
1400
  for (const entry of converted.entries) entries.push(entry);
1248
1401
  if (converted.blocks.length > 0) {
@@ -1252,6 +1405,37 @@ function walkNode(node, map, entries, blocks) {
1252
1405
  }
1253
1406
  if (logicOn) markApproximated(entries, from, `logic ${logicExpression(logic)}`);
1254
1407
  }
1408
+ /**
1409
+ * A band's content walks as page children, so sections, wrappers, widgets and
1410
+ * bare leaves all convert as they would at the top. The band's own paint then
1411
+ * lands on the resulting sections, the way a `standard-wrapper`'s does.
1412
+ */
1413
+ function walkBand(node, map, entries, blocks) {
1414
+ const inner = withContentWidth(map, insideWidth(map.contentWidth, node, map));
1415
+ const bandBlocks = [];
1416
+ const bandEntries = [];
1417
+ for (const child of bandContent(node)) walkNode(child, inner, bandEntries, bandBlocks);
1418
+ if (bandBlocks.length === 0 && bandEntries.length === 0) {
1419
+ entries.push({
1420
+ sourceTag: node.type,
1421
+ templaticalBlockType: null,
1422
+ status: "skipped",
1423
+ note: `An empty ${node.type} produces nothing.`
1424
+ });
1425
+ return;
1426
+ }
1427
+ applyWrapper(node, bandBlocks, bandEntries, map);
1428
+ for (const block of bandBlocks) blocks.push(block);
1429
+ for (const entry of bandEntries) entries.push(entry);
1430
+ }
1431
+ function isPageBand(node) {
1432
+ return typeof node.type === "string" && PAGE_BAND_TYPES.has(node.type);
1433
+ }
1434
+ function bandContent(node) {
1435
+ const content = isPlainObject(node.data) ? node.data.content : void 0;
1436
+ if (!Array.isArray(content)) return [];
1437
+ return content.filter((child) => isPlainObject(child) && isElement(child));
1438
+ }
1255
1439
  function wrapInSection(blocks) {
1256
1440
  const section = createSectionBlock();
1257
1441
  section.columns = "1";
@@ -1282,7 +1466,8 @@ function logicExpression(logic) {
1282
1466
  return parts.join(" ");
1283
1467
  }
1284
1468
  function hasTypedContent(node) {
1285
- for (const child of node.children ?? []) {
1469
+ const kids = isPageBand(node) ? bandContent(node) : node.children ?? [];
1470
+ for (const child of kids) {
1286
1471
  if (!isElement(child)) continue;
1287
1472
  if (child.type === "placeholder") continue;
1288
1473
  return true;
@@ -1293,8 +1478,9 @@ function hasMobileAttributes(node) {
1293
1478
  if (!isPlainObject(node)) return false;
1294
1479
  const mobile = node.mobileAttributes;
1295
1480
  if (isPlainObject(mobile) && Object.keys(mobile).length > 0) return true;
1296
- if (!Array.isArray(node.children)) return false;
1297
- for (const child of node.children) if (hasMobileAttributes(child)) return true;
1481
+ const band = isPageBand(node) ? bandContent(node) : [];
1482
+ const children = Array.isArray(node.children) ? node.children : [];
1483
+ for (const child of [...children, ...band]) if (hasMobileAttributes(child)) return true;
1298
1484
  return false;
1299
1485
  }
1300
1486
  function assertNotOss(page) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["isPlainObject","empty","isElement","readVisibility","isElement"],"sources":["../src/normalize.ts","../src/attribute-parser.ts","../src/rich-text.ts","../src/block-mapper.ts","../src/column-layout.ts","../src/section-builder.ts","../src/converter.ts"],"sourcesContent":["import type {\n EasyEmailProDocument,\n EasyEmailProNode,\n EasyEmailProVariable,\n} from \"./types\";\n\nconst VAR_PREFIX = \"$var(\";\n\n/** Spec §3.3 category map — unknown types skip the category layer. */\nexport const CATEGORY_BY_TYPE: Record<string, string> = {\n \"standard-paragraph\": \"TEXT\",\n \"standard-h1\": \"TEXT\",\n \"standard-h2\": \"TEXT\",\n \"standard-h3\": \"TEXT\",\n \"standard-h4\": \"TEXT\",\n \"standard-button\": \"BUTTON\",\n \"standard-image\": \"IMAGE\",\n \"standard-navbar\": \"NAVBAR\",\n \"standard-navbar-link\": \"NAVBAR\",\n \"standard-social\": \"SOCIAL\",\n \"standard-social-element\": \"SOCIAL\",\n \"standard-spacer\": \"SPACER\",\n \"standard-divider\": \"DIVIDER\",\n \"standard-hero\": \"HERO\",\n \"standard-wrapper\": \"WRAPPER\",\n \"standard-section\": \"SECTION\",\n \"standard-column\": \"COLUMN\",\n \"standard-group\": \"GROUP\",\n};\n\nexport interface VarStats {\n resolved: number;\n unresolved: number;\n}\n\nexport interface ResolveContext {\n variables: Record<string, string>;\n blockAttributes: Record<string, Record<string, unknown>>;\n categoryAttributes: Record<string, Record<string, unknown>>;\n globalAttributes: Record<string, unknown>;\n /** Page node — section fill falls back to `content-background-color`. */\n page: EasyEmailProNode;\n stats: VarStats;\n /**\n * Per-node attribute cache so a repeated `readAttr` (button fill vs\n * outlined check, section fill fallback) does not double-count `$var`.\n */\n attrCache: WeakMap<EasyEmailProNode, Map<string, unknown>>;\n}\n\n/**\n * `null`, `\"\"`, `\"url()\"`, `\"transparent\"`, and leftover `$var(` after\n * substitution are unset. Presence-guards that treat these as set write\n * illegal empties.\n */\nexport function isUnset(value: unknown): boolean {\n if (value === null || value === undefined) return true;\n if (typeof value !== \"string\") return false;\n const trimmed = value.trim();\n return (\n trimmed === \"\" ||\n trimmed === \"url()\" ||\n trimmed.toLowerCase() === \"transparent\" ||\n trimmed.includes(VAR_PREFIX)\n );\n}\n\n/** Index `data.variables[]` by name for `$var` lookup. */\nexport function buildVariableTable(\n variables: EasyEmailProVariable[] | undefined,\n): Record<string, string> {\n const out: Record<string, string> = {};\n if (!variables) return out;\n for (const entry of variables) {\n if (typeof entry.name !== \"string\" || entry.name === \"\") continue;\n if (typeof entry.value !== \"string\") continue;\n out[entry.name] = entry.value;\n }\n return out;\n}\n\n/**\n * Replace `$var(name)` tokens via a linear scan (no regex — CodeQL\n * `js/polynomial-redos`). Unknown names are left in place and flagged.\n */\nexport function substituteVars(\n value: string,\n variables: Record<string, string>,\n): { value: string; resolved: number; unresolved: number } {\n let out = \"\";\n let i = 0;\n let resolved = 0;\n let unresolved = 0;\n\n while (i < value.length) {\n const start = value.indexOf(VAR_PREFIX, i);\n if (start === -1) {\n out += value.slice(i);\n break;\n }\n out += value.slice(i, start);\n const nameStart = start + VAR_PREFIX.length;\n const nameEnd = findMatchingParen(value, nameStart);\n if (nameEnd === -1) {\n // Unclosed `$var(` — keep the rest verbatim and mark unresolved.\n out += value.slice(start);\n unresolved += 1;\n break;\n }\n const name = value.slice(nameStart, nameEnd);\n if (Object.prototype.hasOwnProperty.call(variables, name)) {\n out += variables[name];\n resolved += 1;\n } else {\n out += value.slice(start, nameEnd + 1);\n unresolved += 1;\n }\n i = nameEnd + 1;\n }\n\n return { value: out, resolved, unresolved };\n}\n\n/** Walk from `openIdx` (char after `(`) to the matching `)`. */\nfunction findMatchingParen(value: string, openIdx: number): number {\n let depth = 1;\n for (let i = openIdx; i < value.length; i++) {\n const ch = value[i];\n if (ch === \"(\") depth += 1;\n else if (ch === \")\") {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\nexport function contextFromPage(page: EasyEmailProNode): ResolveContext {\n const data = isPlainObject(page.data) ? page.data : {};\n return {\n variables: buildVariableTable(\n Array.isArray(data.variables)\n ? (data.variables as EasyEmailProVariable[])\n : undefined,\n ),\n blockAttributes: asNestedAttrMap(data.blockAttributes),\n categoryAttributes: asNestedAttrMap(data.categoryAttributes),\n globalAttributes: asAttrMap(data.globalAttributes),\n page,\n stats: { resolved: 0, unresolved: 0 },\n attrCache: new WeakMap(),\n };\n}\n\n/** Widget `data.input` overrides page variables for `$var` resolution. */\nexport function withWidgetInput(\n ctx: ResolveContext,\n input: Record<string, unknown> | undefined,\n): ResolveContext {\n if (!input) return ctx;\n const variables = { ...ctx.variables };\n for (const [key, value] of Object.entries(input)) {\n if (typeof value === \"string\") variables[key] = value;\n }\n // New cache: widget `data.input` can resolve the same node keys differently.\n return { ...ctx, variables, attrCache: new WeakMap() };\n}\n\n/**\n * Cascade: element → blockAttributes[type] → categoryAttributes[cat] →\n * globalAttributes. Missing keys fall through. String hits run through\n * `substituteVars`. Keys stay kebab-case MJML.\n */\nexport function readAttr(\n node: EasyEmailProNode,\n key: string,\n ctx: ResolveContext,\n): unknown {\n let cached = ctx.attrCache.get(node);\n if (cached?.has(key)) return cached.get(key);\n\n const raw = lookupAttr(node, key, ctx);\n let value: unknown = raw;\n if (typeof raw === \"string\") {\n const result = substituteVars(raw, ctx.variables);\n ctx.stats.resolved += result.resolved;\n ctx.stats.unresolved += result.unresolved;\n value = result.value;\n }\n\n if (!cached) {\n cached = new Map();\n ctx.attrCache.set(node, cached);\n }\n cached.set(key, value);\n return value;\n}\n\nfunction lookupAttr(\n node: EasyEmailProNode,\n key: string,\n ctx: ResolveContext,\n): unknown {\n const fromElement = node.attributes?.[key];\n if (fromElement !== undefined) return fromElement;\n\n if (typeof node.type === \"string\") {\n const fromBlock = ctx.blockAttributes[node.type]?.[key];\n if (fromBlock !== undefined) return fromBlock;\n\n const category = CATEGORY_BY_TYPE[node.type];\n if (category) {\n const fromCategory = ctx.categoryAttributes[category]?.[key];\n if (fromCategory !== undefined) return fromCategory;\n }\n }\n\n return ctx.globalAttributes[key];\n}\n\n/**\n * Envelope unwrap. Never throws — the converter owns invalid-shape errors.\n * Returns `{ page, subject }` when a page is visible; caller checks\n * `page.type === \"page\"`.\n */\nexport function unwrapDocument(root: unknown): {\n page: EasyEmailProNode;\n subject?: string;\n} {\n if (!isPlainObject(root)) {\n return { page: {} };\n }\n\n if (root.type === \"page\") {\n return { page: root as EasyEmailProNode };\n }\n\n const doc = root as EasyEmailProDocument;\n const page = isPlainObject(doc.content)\n ? (doc.content as EasyEmailProNode)\n : (root as EasyEmailProNode);\n const subject = typeof doc.subject === \"string\" ? doc.subject : undefined;\n return subject === undefined ? { page } : { page, subject };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asAttrMap(value: unknown): Record<string, unknown> {\n return isPlainObject(value) ? value : {};\n}\n\nfunction asNestedAttrMap(\n value: unknown,\n): Record<string, Record<string, unknown>> {\n if (!isPlainObject(value)) return {};\n const out: Record<string, Record<string, unknown>> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (isPlainObject(entry)) out[key] = entry;\n }\n return out;\n}\n","import { isUnset } from \"./normalize\";\n\n/** Strip a trailing `px` (case-insensitive) and coerce to number. Unset/NaN → undefined. */\nexport function parsePx(value: unknown): number | undefined {\n if (isUnset(value)) return undefined;\n let raw = String(value).trim();\n if (raw.toLowerCase().endsWith(\"px\")) raw = raw.slice(0, -2).trimEnd();\n const n = Number(raw);\n return Number.isNaN(n) ? undefined : n;\n}\n\n/**\n * Strip a trailing `%` and coerce to number. Unset/NaN → `null` so\n * `matchColumnLayout` can tell \"missing width\" from `0%`.\n */\nexport function parsePercent(value: unknown): number | null {\n if (isUnset(value)) return null;\n let raw = String(value).trim();\n if (raw.endsWith(\"%\")) raw = raw.slice(0, -1).trimEnd();\n const n = Number(raw);\n return Number.isNaN(n) ? null : n;\n}\n\n/** Return the colour string as-is when set. No named-colour expansion. */\nexport function parseColor(value: unknown): string | undefined {\n if (isUnset(value)) return undefined;\n if (typeof value !== \"string\") return undefined;\n return value;\n}\n\n/** Read the four padding sides via a callback (typically wrapping `readAttr`). Missing → 0. */\nexport function readPadding(read: (key: string) => unknown): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const side = (key: string) => parsePx(read(key)) ?? 0;\n return {\n top: side(\"padding-top\"),\n right: side(\"padding-right\"),\n bottom: side(\"padding-bottom\"),\n left: side(\"padding-left\"),\n };\n}\n","import type { EasyEmailProNode, EasyEmailProTextNode } from \"./types\";\n\n/** Escape `& < > \"` for text content and attribute values. */\nfunction escapeHtml(value: string): string {\n let out = \"\";\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === \"&\") out += \"&amp;\";\n else if (ch === \"<\") out += \"&lt;\";\n else if (ch === \">\") out += \"&gt;\";\n else if (ch === '\"') out += \"&quot;\";\n else out += ch;\n }\n return out;\n}\n\nfunction isTextNode(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProTextNode {\n return typeof (node as EasyEmailProTextNode).text === \"string\";\n}\n\n/**\n * Nesting (outer → inner): link → color/bgColor span → underline → strike →\n * em → strong. Matches the Task 4 brief / design §8.1 mark order.\n */\nfunction serialiseTextNode(node: EasyEmailProTextNode): string {\n let html = escapeHtml(node.text);\n\n if (node.bold) html = `<strong>${html}</strong>`;\n if (node.italic) html = `<em>${html}</em>`;\n if (node.strikethrough) html = `<s>${html}</s>`;\n if (node.underline) html = `<u>${html}</u>`;\n\n const styles: string[] = [];\n if (typeof node.color === \"string\" && node.color !== \"\") {\n styles.push(`color: ${escapeHtml(node.color)}`);\n }\n if (typeof node.bgColor === \"string\" && node.bgColor !== \"\") {\n styles.push(`background-color: ${escapeHtml(node.bgColor)}`);\n }\n if (styles.length > 0) {\n html = `<span style=\"${styles.join(\"; \")}\">${html}</span>`;\n }\n\n if (node.link) {\n const href = escapeHtml(node.link.href ?? \"\");\n const blank = node.link.blank === true ? ' target=\"_blank\"' : \"\";\n html = `<a href=\"${href}\"${blank}>${html}</a>`;\n }\n\n return html;\n}\n\nfunction serialiseNode(node: EasyEmailProNode | EasyEmailProTextNode): string {\n if (node.type === \"line-break\") return \"<br>\";\n\n if (node.type === \"html-block-node\") {\n const element = node as EasyEmailProNode;\n const rawTag = element.data?.[\"tagName\"];\n const tagName =\n typeof rawTag === \"string\" && rawTag !== \"\" ? rawTag : \"div\";\n const inner = serialiseChildren(element.children);\n return `<${tagName}>${inner}</${tagName}>`;\n }\n\n // Typed elements other than line-break / html-block-node are ignored —\n // the parent leaf mapper html-fallbacks whole unknown leaves.\n if (typeof node.type === \"string\" && node.type !== \"\") return \"\";\n\n if (isTextNode(node)) return serialiseTextNode(node);\n\n return \"\";\n}\n\n/** Slate children → HTML for TitleBlock / ParagraphBlock content. */\nexport function serialiseChildren(\n children: Array<EasyEmailProNode | EasyEmailProTextNode> | undefined,\n): string {\n if (!children || children.length === 0) return \"\";\n let out = \"\";\n for (const child of children) {\n out += serialiseNode(child);\n }\n return out;\n}\n","import {\n createButtonBlock,\n createDividerBlock,\n createHtmlBlock,\n createImageBlock,\n createMenuBlock,\n createParagraphBlock,\n createSocialIconsBlock,\n createSpacerBlock,\n createTableBlock,\n createTitleBlock,\n createVideoBlock,\n findOpenTagEnd,\n generateId,\n} from \"@templatical/types\";\nimport type {\n Block,\n BlockStyles,\n BlockVisibility,\n ButtonBlock,\n HeadingLevel,\n MenuItemData,\n SocialIcon,\n SocialPlatform,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { parseColor, parsePx, readPadding } from \"./attribute-parser\";\nimport { isUnset, readAttr, type ResolveContext } from \"./normalize\";\nimport { serialiseChildren } from \"./rich-text\";\nimport type {\n ConversionStatus,\n EasyEmailProNode,\n EasyEmailProTextNode,\n ImportReportEntry,\n} from \"./types\";\n\nexport interface MapContext {\n resolve: ResolveContext;\n warnings: string[];\n}\n\nexport interface Converted {\n blocks: Block[];\n entries: ImportReportEntry[];\n}\n\nconst LEAF_TYPES = new Set([\n \"standard-paragraph\",\n \"standard-h1\",\n \"standard-h2\",\n \"standard-h3\",\n \"standard-h4\",\n \"standard-button\",\n \"standard-image\",\n \"standard-divider\",\n \"standard-spacer\",\n \"standard-navbar\",\n \"standard-social\",\n \"standard-table2\",\n \"marketing-countdown\",\n \"placeholder\",\n \"common-video\",\n]);\n\nconst SOCIAL_PLATFORMS = [\n \"facebook\",\n \"instagram\",\n \"twitter\",\n \"linkedin\",\n \"youtube\",\n \"tiktok\",\n \"pinterest\",\n \"whatsapp\",\n \"telegram\",\n \"github\",\n] as const satisfies ReadonlyArray<SocialPlatform>;\n\nconst ENTITIES: Record<string, string> = {\n \"&amp;\": \"&\",\n \"&lt;\": \"<\",\n \"&gt;\": \">\",\n \"&quot;\": '\"',\n \"&#39;\": \"'\",\n \"&apos;\": \"'\",\n \"&nbsp;\": \" \",\n};\n\nconst COUNTDOWN_NOTE =\n \"marketing-countdown GIF is the timer; Templatical countdown is Cloud-only\";\n\nexport function isLeafType(type: string | undefined): boolean {\n if (typeof type !== \"string\") return false;\n if (LEAF_TYPES.has(type)) return true;\n return headingLevelFromType(type) !== null;\n}\n\nexport function convertLeaf(\n node: EasyEmailProNode,\n map: MapContext,\n): Converted {\n const type = node.type;\n if (type === \"placeholder\") return empty();\n if (type === \"standard-paragraph\") return convertParagraph(node, map);\n\n const heading = typeof type === \"string\" ? headingLevelFromType(type) : null;\n if (heading) return convertHeading(node, map, heading);\n\n switch (type) {\n case \"standard-button\":\n return convertButton(node, map);\n case \"standard-image\":\n return convertImage(node, map);\n case \"standard-divider\":\n return convertDivider(node, map);\n case \"standard-spacer\":\n return convertSpacer(node, map);\n case \"standard-navbar\":\n return convertNavbar(node, map);\n case \"standard-social\":\n return convertSocial(node, map);\n case \"standard-table2\":\n return convertTable(node, map);\n case \"marketing-countdown\":\n return convertCountdown(node, map);\n case \"common-video\":\n return convertVideo(node, map);\n default:\n return convertUnknown(node, map);\n }\n}\n\nfunction convertParagraph(node: EasyEmailProNode, map: MapContext): Converted {\n return finish(\n createParagraphBlock({\n content: paragraphHtml(node, map),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-paragraph\", \"paragraph\", \"converted\"),\n );\n}\n\n/**\n * ParagraphBlock has no color / fontSize / textAlign. Bake the cascaded\n * values into a wrapping `<p style>` (single wrapper, Unlayer/BeeFree class).\n */\nfunction paragraphHtml(node: EasyEmailProNode, map: MapContext): string {\n const html = serialiseChildren(node.children);\n const parts: string[] = [];\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n if (color) parts.push(`color: ${escapeAttr(color)}`);\n const fontSize = setString(readAttr(node, \"font-size\", map.resolve));\n if (fontSize) parts.push(`font-size: ${escapeAttr(fontSize)}`);\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n if (align) parts.push(`text-align: ${align}`);\n if (parts.length === 0) return html;\n return `<p style=\"${parts.join(\"; \")}\">${html}</p>`;\n}\n\nfunction escapeAttr(value: string): string {\n let out = \"\";\n for (let i = 0; i < value.length; i++) {\n const ch = value[i];\n if (ch === \"&\") out += \"&amp;\";\n else if (ch === \"<\") out += \"&lt;\";\n else if (ch === \">\") out += \"&gt;\";\n else if (ch === '\"') out += \"&quot;\";\n else out += ch;\n }\n return out;\n}\n\nfunction convertHeading(\n node: EasyEmailProNode,\n map: MapContext,\n heading: { level: HeadingLevel; source: number },\n): Converted {\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const clamped = heading.source > 4;\n return finish(\n createTitleBlock({\n content: serialiseChildren(node.children),\n level: heading.level,\n ...(color ? { color } : {}),\n ...(align ? { textAlign: align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n clamped\n ? report(\n node.type ?? \"standard-h4\",\n \"title\",\n \"approximated\",\n `Heading level h${heading.source} clamped to 4 — Templatical titles support h1-h4.`,\n )\n : report(node.type ?? \"standard-h1\", \"title\", \"converted\"),\n );\n}\n\nfunction convertButton(node: EasyEmailProNode, map: MapContext): Converted {\n const fill = parseColor(readAttr(node, \"background-color\", map.resolve));\n const textColor = parseColor(readAttr(node, \"color\", map.resolve));\n const href = readAttr(node, \"href\", map.resolve);\n const width = buttonWidth(readAttr(node, \"width\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const outlined =\n isBorderEnabled(readAttr(node, \"border-enabled\", map.resolve)) &&\n isUnset(readAttr(node, \"background-color\", map.resolve));\n\n const block: ButtonBlock = createButtonBlock({\n text: stripTags(serialiseChildren(node.children)),\n url: setString(href) ?? \"\",\n ...(fill ? { backgroundColor: fill } : {}),\n ...(textColor ? { textColor } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(align ? { align } : {}),\n ...leafStyles(node, map),\n });\n // createButtonBlock defaults fill to #333333; an unset Pro fill with\n // border-enabled is an outlined button, which has no Templatical equivalent.\n if (outlined) block.backgroundColor = \"#ffffff\";\n\n return finish(\n block,\n node,\n outlined\n ? report(\n \"standard-button\",\n \"button\",\n \"approximated\",\n \"outlined button has no Templatical equivalent\",\n )\n : report(\"standard-button\", \"button\", \"converted\"),\n );\n}\n\nfunction convertImage(node: EasyEmailProNode, map: MapContext): Converted {\n const src = setString(readAttr(node, \"src\", map.resolve)) ?? \"\";\n const alt = setString(readAttr(node, \"alt\", map.resolve)) ?? \"\";\n const href = readAttr(node, \"href\", map.resolve);\n const linkUrl = setString(href);\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const target = setString(readAttr(node, \"target\", map.resolve));\n\n return finish(\n createImageBlock({\n src,\n alt,\n ...(align ? { align } : {}),\n ...(radius !== undefined && radius > 0 ? { borderRadius: radius } : {}),\n ...(linkUrl ? { linkUrl } : {}),\n ...(linkUrl && target === \"_blank\" ? { linkOpenInNewTab: true } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-image\", \"image\", \"converted\"),\n );\n}\n\nfunction convertDivider(node: EasyEmailProNode, map: MapContext): Converted {\n const color = parseColor(readAttr(node, \"border-color\", map.resolve));\n const thickness = parsePx(readAttr(node, \"border-width\", map.resolve));\n const lineStyle = asLineStyle(readAttr(node, \"border-style\", map.resolve));\n return finish(\n createDividerBlock({\n ...(color ? { color } : {}),\n ...(thickness !== undefined ? { thickness } : {}),\n ...(lineStyle ? { lineStyle } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-divider\", \"divider\", \"converted\"),\n );\n}\n\nfunction convertSpacer(node: EasyEmailProNode, map: MapContext): Converted {\n const height = parsePx(readAttr(node, \"height\", map.resolve));\n return finish(\n createSpacerBlock({\n ...(height !== undefined ? { height } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-spacer\", \"spacer\", \"converted\"),\n );\n}\n\nfunction convertNavbar(node: EasyEmailProNode, map: MapContext): Converted {\n const items: MenuItemData[] = [];\n for (const child of node.children ?? []) {\n if (!isElement(child) || child.type !== \"standard-navbar-link\") continue;\n const href = readAttr(child, \"href\", map.resolve);\n const target = setString(readAttr(child, \"target\", map.resolve));\n const color = parseColor(readAttr(child, \"color\", map.resolve));\n const weight = setString(readAttr(child, \"font-weight\", map.resolve));\n const deco = setString(readAttr(child, \"text-decoration\", map.resolve));\n items.push({\n id: generateId(),\n text: stripTags(serialiseChildren(child.children)),\n url: setString(href) ?? \"\",\n openInNewTab: target === \"_blank\",\n bold: weight === \"bold\" || weight === \"700\",\n underline: deco !== undefined && deco.includes(\"underline\"),\n ...(color ? { color } : {}),\n });\n }\n if (items.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n return finish(\n createMenuBlock({\n items,\n ...(align ? { textAlign: align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-navbar\", \"menu\", \"converted\"),\n );\n}\n\nfunction convertSocial(node: EasyEmailProNode, map: MapContext): Converted {\n const icons: SocialIcon[] = [];\n let customSrc = false;\n for (const child of node.children ?? []) {\n if (!isElement(child) || child.type !== \"standard-social-element\") continue;\n const href = readAttr(child, \"href\", map.resolve);\n const src = readAttr(child, \"src\", map.resolve);\n if (typeof src === \"string\" && !isUnset(src)) customSrc = true;\n icons.push({\n id: generateId(),\n platform: inferPlatform(href, src),\n url: setString(href) ?? \"\",\n });\n }\n if (icons.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n return finish(\n createSocialIconsBlock({\n icons,\n ...(align ? { align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n customSrc\n ? report(\n \"standard-social\",\n \"social\",\n \"approximated\",\n \"custom src was present; SocialIconsBlock has no custom PNG\",\n )\n : report(\"standard-social\", \"social\", \"converted\"),\n );\n}\n\nfunction convertTable(node: EasyEmailProNode, map: MapContext): Converted {\n const rows: TableRowData[] = [];\n let hasHeaderRow = false;\n let first = true;\n for (const row of collectRows(node)) {\n const cells: TableCellData[] = [];\n let rowHasTh = false;\n for (const cell of row.children ?? []) {\n if (!isElement(cell)) continue;\n if (cell.type !== \"td\" && cell.type !== \"th\") continue;\n if (cell.type === \"th\") rowHasTh = true;\n cells.push({\n id: generateId(),\n content: serialiseChildren(cell.children),\n });\n }\n if (cells.length === 0) continue;\n if (first) {\n hasHeaderRow = rowHasTh;\n first = false;\n }\n rows.push({ id: generateId(), cells });\n }\n if (rows.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n return finish(\n createTableBlock({\n rows,\n hasHeaderRow,\n ...(align ? { textAlign: align } : {}),\n ...(color ? { color } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-table2\", \"table\", \"converted\"),\n );\n}\n\nfunction convertCountdown(node: EasyEmailProNode, map: MapContext): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n const remapped = remapOverlay(child);\n if (!remapped) continue;\n const inner = convertLeaf(remapped, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) {\n entries.push({\n sourceTag: \"marketing-countdown\",\n templaticalBlockType: entry.templaticalBlockType,\n status: \"approximated\",\n note: entry.note ? `${entry.note} marketing-countdown` : COUNTDOWN_NOTE,\n });\n }\n }\n\n const src = readAttr(node, \"src\", map.resolve);\n if (typeof src === \"string\" && !isUnset(src)) {\n const image = createImageBlock({\n src,\n ...leafStyles(node, map),\n });\n const visibility = readVisibility(node);\n if (visibility) image.visibility = visibility;\n blocks.push(image);\n entries.push({\n sourceTag: \"marketing-countdown\",\n templaticalBlockType: \"image\",\n status: \"approximated\",\n note: COUNTDOWN_NOTE,\n });\n }\n\n return { blocks, entries };\n}\n\nfunction convertVideo(node: EasyEmailProNode, map: MapContext): Converted {\n const url = firstUrl(node, map);\n if (!url) return convertUnknown(node, map);\n return finish(\n createVideoBlock({\n url,\n ...leafStyles(node, map),\n }),\n node,\n report(\"common-video\", \"video\", \"converted\"),\n );\n}\n\nfunction convertUnknown(node: EasyEmailProNode, map: MapContext): Converted {\n const sourceTag = node.type ?? \"unknown\";\n return finish(\n createHtmlBlock({\n content: JSON.stringify(node),\n ...leafStyles(node, map),\n }),\n node,\n report(\n sourceTag,\n \"html\",\n \"html-fallback\",\n `Unknown Easy Email Pro type \"${sourceTag}\"; preserved as HTML.`,\n ),\n );\n}\n\nfunction remapOverlay(child: EasyEmailProNode): EasyEmailProNode | null {\n if (child.type === \"text\") {\n return { ...child, type: \"standard-paragraph\" };\n }\n if (child.type === \"standard-paragraph\") return child;\n if (typeof child.type === \"string\" && headingLevelFromType(child.type)) {\n return child;\n }\n return null;\n}\n\nfunction collectRows(node: EasyEmailProNode): EasyEmailProNode[] {\n const rows: EasyEmailProNode[] = [];\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n if (child.type === \"tr\") {\n rows.push(child);\n continue;\n }\n for (const inner of child.children ?? []) {\n if (isElement(inner) && inner.type === \"tr\") rows.push(inner);\n }\n }\n return rows;\n}\n\nfunction firstUrl(node: EasyEmailProNode, map: MapContext): string | undefined {\n for (const key of [\"src\", \"href\", \"url\"] as const) {\n const value = readAttr(node, key, map.resolve);\n if (typeof value === \"string\" && !isUnset(value)) return value;\n }\n return undefined;\n}\n\nfunction inferPlatform(href: unknown, src: unknown): SocialPlatform {\n const host = hostname(typeof href === \"string\" ? href : \"\");\n for (const platform of SOCIAL_PLATFORMS) {\n if (host.includes(platform)) return platform;\n }\n const path = pathOf(typeof src === \"string\" ? src : \"\");\n for (const platform of SOCIAL_PLATFORMS) {\n if (path.includes(platform)) return platform;\n }\n return \"website\";\n}\n\nfunction hostname(href: string): string {\n const lower = href.toLowerCase();\n const scheme = lower.indexOf(\"://\");\n const rest = scheme === -1 ? lower : lower.slice(scheme + 3);\n const slash = rest.indexOf(\"/\");\n const hostport = slash === -1 ? rest : rest.slice(0, slash);\n const colon = hostport.indexOf(\":\");\n return colon === -1 ? hostport : hostport.slice(0, colon);\n}\n\nfunction pathOf(src: string): string {\n const lower = src.toLowerCase();\n const scheme = lower.indexOf(\"://\");\n const rest = scheme === -1 ? lower : lower.slice(scheme + 3);\n const slash = rest.indexOf(\"/\");\n return slash === -1 ? rest : rest.slice(slash);\n}\n\nfunction headingLevelFromType(\n type: string,\n): { level: HeadingLevel; source: number } | null {\n if (!type.startsWith(\"standard-h\")) return null;\n const raw = type.slice(\"standard-h\".length);\n if (raw.length === 0) return null;\n for (let i = 0; i < raw.length; i++) {\n const code = raw.charCodeAt(i);\n if (code < 48 || code > 57) return null;\n }\n const source = Number(raw);\n if (!Number.isFinite(source) || source < 1) return null;\n const level = (source > 4 ? 4 : source) as HeadingLevel;\n return { level, source };\n}\n\nfunction leafStyles(\n node: EasyEmailProNode,\n map: MapContext,\n): { styles: BlockStyles } {\n return {\n styles: {\n padding: readPadding((key) => readAttr(node, key, map.resolve)),\n },\n };\n}\n\nfunction finish(\n block: Block,\n node: EasyEmailProNode,\n entry: ImportReportEntry,\n): Converted {\n const visibility = readVisibility(node);\n if (visibility) block.visibility = visibility;\n return { blocks: [block], entries: [entry] };\n}\n\nfunction readVisibility(node: EasyEmailProNode): BlockVisibility | undefined {\n if (node.visible === \"desktop\") return { desktop: true, mobile: false };\n if (node.visible === \"mobile\") return { desktop: false, mobile: true };\n return undefined;\n}\n\nfunction report(\n sourceTag: string,\n templaticalBlockType: string,\n status: ConversionStatus,\n note?: string,\n): ImportReportEntry {\n return {\n sourceTag,\n templaticalBlockType,\n status,\n ...(note ? { note } : {}),\n };\n}\n\nfunction empty(): Converted {\n return { blocks: [], entries: [] };\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction isBorderEnabled(value: unknown): boolean {\n return value === true || value === \"true\";\n}\n\nfunction buttonWidth(value: unknown): number | \"full\" | undefined {\n if (isUnset(value)) return undefined;\n if (typeof value === \"string\" && value.trim() === \"100%\") return \"full\";\n return parsePx(value);\n}\n\nfunction parseAlign(value: unknown): \"left\" | \"center\" | \"right\" | undefined {\n if (typeof value !== \"string\") return undefined;\n const align = value.trim().toLowerCase();\n if (align === \"left\" || align === \"center\" || align === \"right\") return align;\n return undefined;\n}\n\nfunction asLineStyle(\n value: unknown,\n): \"solid\" | \"dashed\" | \"dotted\" | undefined {\n if (typeof value !== \"string\") return undefined;\n const style = value.trim().toLowerCase();\n if (style === \"solid\" || style === \"dashed\" || style === \"dotted\") {\n return style;\n }\n return undefined;\n}\n\nfunction setString(value: unknown): string | undefined {\n if (typeof value !== \"string\" || isUnset(value)) return undefined;\n return value;\n}\n\n/** Button labels are plain text — strip tags the serialiser may have emitted. */\nfunction stripTags(html: string): string {\n if (html === \"\") return \"\";\n return decodeEntities(removeTags(html)).trim();\n}\n\nfunction removeTags(html: string): string {\n let out = \"\";\n let i = 0;\n while (i < html.length) {\n const open = html.indexOf(\"<\", i);\n if (open === -1) {\n out += html.slice(i);\n break;\n }\n out += html.slice(i, open);\n const tagEnd = findOpenTagEnd(html, open + 1);\n if (tagEnd === -1) {\n out += html.slice(open);\n break;\n }\n i = tagEnd + 1;\n }\n return out;\n}\n\nfunction decodeEntities(text: string): string {\n let out = \"\";\n let i = 0;\n while (i < text.length) {\n if (text[i] === \"&\") {\n const semi = text.indexOf(\";\", i + 1);\n if (semi !== -1 && semi - i <= 6) {\n const mapped = ENTITIES[text.slice(i, semi + 1)];\n if (mapped !== undefined) {\n out += mapped;\n i = semi + 1;\n continue;\n }\n }\n }\n out += text[i];\n i++;\n }\n return out;\n}\n","import type { ColumnLayout } from \"@templatical/types\";\n\nconst LAYOUT_SHAPES: Array<{ layout: ColumnLayout; percents: number[] }> = [\n { layout: \"1\", percents: [100] },\n { layout: \"2\", percents: [50, 50] },\n { layout: \"1-2\", percents: [33.33, 66.67] },\n { layout: \"2-1\", percents: [66.67, 33.33] },\n { layout: \"3\", percents: [33.33, 33.33, 33.34] },\n];\n\nconst WIDTH_TOLERANCE = 2;\n\nexport const COLUMN_COUNT: Record<ColumnLayout, number> = {\n \"1\": 1,\n \"2\": 2,\n \"3\": 3,\n \"2-1\": 2,\n \"1-2\": 2,\n};\n\nexport function columnPixels(\n layout: ColumnLayout,\n container: number,\n): number[] {\n switch (layout) {\n case \"2\":\n return [container / 2, container / 2];\n case \"3\":\n return [container / 3, container / 3, container / 3];\n case \"1-2\":\n return [container / 3, (container * 2) / 3];\n case \"2-1\":\n return [(container * 2) / 3, container / 3];\n default:\n return [container];\n }\n}\n\n/**\n * Resolve column widths to one of the five layouts `ColumnLayout` allows.\n *\n * `exact: false` means the caller reports `approximated` — Chamaileon permits\n * any column count at any width and this union permits five shapes, so this is\n * the importer's main irreducible loss.\n */\nexport function matchColumnLayout(percents: Array<number | null>): {\n layout: ColumnLayout;\n exact: boolean;\n} {\n const count = percents.length;\n if (count === 0) return { layout: \"1\", exact: true };\n\n // All-unset is \"distribute equally\", which is exactly what the n-column\n // layout means — not a missing value to approximate around.\n if (percents.every((p) => p === null)) {\n if (count === 1) return { layout: \"1\", exact: true };\n if (count === 2) return { layout: \"2\", exact: true };\n if (count === 3) return { layout: \"3\", exact: true };\n return { layout: \"3\", exact: false };\n }\n\n const known = percents.filter((p): p is number => p !== null);\n const unsetCount = count - known.length;\n const remainder =\n unsetCount === 0\n ? 0\n : Math.max(0, 100 - known.reduce((a, b) => a + b, 0)) / unsetCount;\n const resolved = percents.map((p) => p ?? remainder);\n\n const sameCount = LAYOUT_SHAPES.filter((s) => s.percents.length === count);\n\n for (const shape of sameCount) {\n if (\n shape.percents.every(\n (want, i) => Math.abs(want - resolved[i]) <= WIDTH_TOLERANCE,\n )\n ) {\n return { layout: shape.layout, exact: true };\n }\n }\n\n const candidates =\n sameCount.length > 0\n ? sameCount\n : LAYOUT_SHAPES.filter((s) => s.layout === \"3\");\n\n let best = candidates[0];\n let bestError = Infinity;\n for (const shape of candidates) {\n const error = shape.percents.reduce(\n (sum, want, i) => sum + Math.abs(want - (resolved[i] ?? 0)),\n 0,\n );\n if (error < bestError) {\n bestError = error;\n best = shape;\n }\n }\n\n return { layout: best.layout, exact: false };\n}\n\n/** Convert absolute px column widths to percents of `bodyWidth`. */\nexport function widthsToPercents(\n widthsPx: Array<number | undefined>,\n bodyWidth: number,\n): Array<number | null> {\n return widthsPx.map((w) => (w === undefined ? null : (w / bodyWidth) * 100));\n}\n","import { createImageBlock, createSectionBlock } from \"@templatical/types\";\nimport type {\n Block,\n BlockVisibility,\n SectionBlock,\n SectionWrapper,\n} from \"@templatical/types\";\nimport {\n parseColor,\n parsePercent,\n parsePx,\n readPadding,\n} from \"./attribute-parser\";\nimport { convertLeaf, type Converted, type MapContext } from \"./block-mapper\";\nimport { COLUMN_COUNT, matchColumnLayout } from \"./column-layout\";\nimport { isUnset, readAttr } from \"./normalize\";\nimport type {\n ConversionStatus,\n EasyEmailProNode,\n EasyEmailProTextNode,\n ImportReportEntry,\n} from \"./types\";\n\nexport function buildTopLevel(\n node: EasyEmailProNode,\n map: MapContext,\n): Converted {\n switch (node.type) {\n case \"standard-wrapper\":\n return buildWrapper(node, map);\n case \"standard-hero\":\n return buildHero(node, map);\n case \"standard-section\":\n return buildSection(node, map);\n default:\n return empty();\n }\n}\n\nfunction buildSection(node: EasyEmailProNode, map: MapContext): Converted {\n const plan = collectSlots(node);\n const leftover = convertFlow(plan.leftovers, map);\n const rawWidths = plan.columns.map((column) =>\n readAttr(column, \"width\", map.resolve),\n );\n const { layout, exact } = matchColumnLayout(\n rawWidths.map((width) => parsePercent(width)),\n );\n const slots = COLUMN_COUNT[layout];\n const children: Block[][] = Array.from({ length: slots }, () => []);\n if (leftover.blocks.length > 0) {\n children[0].push(...leftover.blocks);\n }\n\n const entries = leftover.entries;\n plan.columns.forEach((column, index) => {\n const slot = Math.min(index, slots - 1);\n const inner = convertFlow(column.children ?? [], map);\n children[slot].push(...inner.blocks);\n for (const entry of inner.entries) entries.push(entry);\n });\n\n const reasons: string[] = [];\n if (plan.mixed) {\n reasons.push(\n \"Section mixed columns and groups; flattened into the column flow.\",\n );\n }\n if (!exact) {\n if (plan.columns.length > slots) {\n reasons.push(\n `${plan.columns.length}-column layout has no exact Templatical equivalent; folded onto \"${layout}\".`,\n );\n } else {\n const shown = rawWidths\n .map((width) =>\n typeof width === \"string\" && !isUnset(width) ? width : \"auto\",\n )\n .join(\", \");\n reasons.push(\n `Column widths ${shown} have no exact Templatical layout; resolved to \"${layout}\".`,\n );\n }\n }\n\n const section = createSectionBlock();\n section.columns = layout;\n section.children = children;\n paintSection(section, node, map, {\n stackOnMobile: plan.grouped ? false : undefined,\n });\n entries.unshift(\n sectionEntry(\n node,\n reasons.length > 0 ? \"approximated\" : \"converted\",\n reasons.length > 0 ? reasons.join(\" \") : undefined,\n ),\n );\n return { blocks: [section], entries };\n}\n\nfunction buildHero(node: EasyEmailProNode, map: MapContext): Converted {\n const slot: Block[] = [];\n const entries: ImportReportEntry[] = [];\n const url = readAttr(node, \"background-url\", map.resolve);\n if (typeof url === \"string\" && !isUnset(url)) {\n slot.push(createImageBlock({ src: url }));\n entries.push({\n sourceTag: \"standard-hero\",\n templaticalBlockType: \"image\",\n status: \"approximated\",\n note: \"hero background-url stacked as a leading image (overlay is the loss)\",\n });\n }\n const inner = convertFlow(node.children ?? [], map);\n slot.push(...inner.blocks);\n for (const entry of inner.entries) entries.push(entry);\n\n const section = createSectionBlock();\n section.columns = \"1\";\n section.children = [slot];\n paintSection(section, node, map);\n entries.unshift({\n sourceTag: \"standard-hero\",\n templaticalBlockType: \"section\",\n status: \"approximated\",\n note: \"standard-hero overlay is stacked as a 1-column section; background-url becomes a leading image.\",\n });\n return { blocks: [section], entries };\n}\n\nfunction buildWrapper(node: EasyEmailProNode, map: MapContext): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n for (const child of elements(node)) {\n if (child.type === \"placeholder\") continue;\n const inner = buildTopLevel(child, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n }\n\n const sections = blocks.filter(\n (block): block is SectionBlock => block.type === \"section\",\n );\n if (sections.length === 0) {\n return {\n blocks: [],\n entries: [\n {\n sourceTag: \"standard-wrapper\",\n templaticalBlockType: null,\n status: \"skipped\",\n note: \"An empty standard-wrapper produces nothing.\",\n },\n ],\n };\n }\n\n const wrapper = readWrapper(node, map);\n for (const section of sections) {\n section.wrapper = { ...wrapper };\n }\n if (sections.length > 1) {\n const note = `standard-wrapper holding ${sections.length} sections was applied to each of them — Templatical has no multi-section band.`;\n for (const entry of entries) {\n if (entry.templaticalBlockType !== \"section\") continue;\n if (entry.status !== \"converted\") continue;\n entry.status = \"approximated\";\n entry.note = note;\n }\n }\n return { blocks, entries };\n}\n\n/**\n * Direct columns become slots. A sole `standard-group` of columns becomes\n * those slots with `stackOnMobile: false`. Mixed group + columns flatten\n * into the slot list and are approximated.\n */\nfunction collectSlots(section: EasyEmailProNode): {\n columns: EasyEmailProNode[];\n leftovers: EasyEmailProNode[];\n grouped: boolean;\n mixed: boolean;\n} {\n const kids = structuralChildren(section);\n\n if (kids.every((kid) => kid.type === \"standard-column\")) {\n return { columns: kids, leftovers: [], grouped: false, mixed: false };\n }\n\n if (kids.length === 1 && kids[0].type === \"standard-group\") {\n const groupKids = structuralChildren(kids[0]);\n if (groupKids.every((kid) => kid.type === \"standard-column\")) {\n return {\n columns: groupKids,\n leftovers: [],\n grouped: true,\n mixed: false,\n };\n }\n }\n\n const columns: EasyEmailProNode[] = [];\n const leftovers: EasyEmailProNode[] = [];\n let grouped = false;\n for (const kid of kids) {\n if (kid.type === \"standard-column\") {\n columns.push(kid);\n continue;\n }\n if (kid.type === \"standard-group\") {\n grouped = true;\n for (const inner of structuralChildren(kid)) {\n if (inner.type === \"standard-column\") columns.push(inner);\n else leftovers.push(inner);\n }\n continue;\n }\n leftovers.push(kid);\n }\n return { columns, leftovers, grouped, mixed: true };\n}\n\nfunction convertFlow(\n nodes: Array<EasyEmailProNode | EasyEmailProTextNode> | undefined,\n map: MapContext,\n): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n for (const child of nodes ?? []) {\n if (!isElement(child)) continue;\n if (child.type === \"placeholder\") continue;\n if (child.type === \"standard-group\") {\n const innerColumns = structuralChildren(child).filter(\n (node) => node.type === \"standard-column\",\n );\n entries.push({\n sourceTag: \"standard-group\",\n templaticalBlockType: null,\n status: \"approximated\",\n note: `nested group flattened (${innerColumns.length} columns)`,\n });\n for (const column of innerColumns) {\n const inner = convertFlow(column.children, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n }\n continue;\n }\n if (child.type === \"standard-column\") {\n const inner = convertFlow(child.children, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n continue;\n }\n const converted = convertLeaf(child, map);\n for (const block of converted.blocks) blocks.push(block);\n for (const entry of converted.entries) entries.push(entry);\n }\n return { blocks, entries };\n}\n\n/** `createSectionBlock()` first, then mutate fill — never a partial `styles`. */\nfunction paintSection(\n section: SectionBlock,\n node: EasyEmailProNode,\n map: MapContext,\n extras?: { stackOnMobile?: boolean },\n): void {\n section.styles.padding = readPadding((key) =>\n readAttr(node, key, map.resolve),\n );\n const fill = sectionFill(node, map);\n if (fill) section.styles.backgroundColor = fill;\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n if (radius !== undefined && radius > 0) section.borderRadius = radius;\n if (extras?.stackOnMobile === false) section.stackOnMobile = false;\n const visibility = readVisibility(node);\n if (visibility) section.visibility = visibility;\n}\n\nfunction sectionFill(\n node: EasyEmailProNode,\n map: MapContext,\n): string | undefined {\n const own = parseColor(readAttr(node, \"background-color\", map.resolve));\n if (own) return own;\n return parseColor(\n readAttr(map.resolve.page, \"content-background-color\", map.resolve),\n );\n}\n\nfunction readWrapper(node: EasyEmailProNode, map: MapContext): SectionWrapper {\n const backgroundColor = parseColor(\n readAttr(node, \"background-color\", map.resolve),\n );\n const padding = readPadding((key) => readAttr(node, key, map.resolve));\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n return {\n ...(backgroundColor ? { backgroundColor } : {}),\n padding,\n ...(radius !== undefined && radius > 0 ? { borderRadius: radius } : {}),\n };\n}\n\nfunction sectionEntry(\n node: EasyEmailProNode,\n status: ConversionStatus,\n note?: string,\n): ImportReportEntry {\n return {\n sourceTag: node.type ?? \"standard-section\",\n templaticalBlockType: \"section\",\n status,\n ...(note ? { note } : {}),\n };\n}\n\nfunction readVisibility(node: EasyEmailProNode): BlockVisibility | undefined {\n if (node.visible === \"desktop\") return { desktop: true, mobile: false };\n if (node.visible === \"mobile\") return { desktop: false, mobile: true };\n return undefined;\n}\n\nfunction structuralChildren(node: EasyEmailProNode): EasyEmailProNode[] {\n return elements(node).filter((child) => child.type !== \"placeholder\");\n}\n\nfunction elements(node: EasyEmailProNode): EasyEmailProNode[] {\n const out: EasyEmailProNode[] = [];\n for (const child of node.children ?? []) {\n if (isElement(child)) out.push(child);\n }\n return out;\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction empty(): Converted {\n return { blocks: [], entries: [] };\n}\n","import {\n createDefaultTemplateContent,\n createSectionBlock,\n DEFAULT_TEMPLATE_DEFAULTS,\n} from \"@templatical/types\";\nimport type {\n Block,\n TemplateContent,\n TemplateSettings,\n} from \"@templatical/types\";\nimport { parseColor, parsePx } from \"./attribute-parser\";\nimport { convertLeaf, isLeafType, type MapContext } from \"./block-mapper\";\nimport {\n contextFromPage,\n isUnset,\n readAttr,\n unwrapDocument,\n withWidgetInput,\n} from \"./normalize\";\nimport { buildTopLevel } from \"./section-builder\";\nimport type {\n EasyEmailProDocument,\n EasyEmailProNode,\n EasyEmailProPage,\n EasyEmailProTextNode,\n ImportReportEntry,\n ImportResult,\n} from \"./types\";\n\nconst INVALID_MESSAGE =\n \"Invalid Easy Email Pro template: expected a page JSON object (EmailTemplate { subject, content } or the page element).\";\nconst OSS_MESSAGE =\n \"Invalid Easy Email Pro template: this looks like open-source Easy Email JSON, not Easy Email Pro (expected standard-* children).\";\nconst EMPTY_WARNING =\n \"No convertible content was found in the Easy Email Pro page. Check that page.children holds at least one standard-section.\";\nconst MOBILE_WARNING =\n \"mobileAttributes were dropped; Templatical has no per-viewport padding.\";\n\nfunction varWarning(resolved: number, unresolved: number): string {\n return `Resolved ${resolved} $var() values, ${unresolved} left unresolved.`;\n}\n\nconst OSS_CHILD_TYPES = new Set([\n \"section\",\n \"column\",\n \"text\",\n \"button\",\n \"image\",\n \"wrapper\",\n \"hero\",\n \"navbar\",\n \"social\",\n]);\n\nconst STRUCTURE_TYPES = new Set([\n \"standard-section\",\n \"standard-wrapper\",\n \"standard-hero\",\n]);\n\nconst WIDGET_TYPES = new Set([\"section_widget\", \"wrapper_widget\"]);\n\n/**\n * `DEFAULT_TEMPLATE_DEFAULTS` is typed `Partial<TemplateSettings>` so a\n * consumer can override any subset, but its own literal always sets the six\n * required fields. Narrowing once here is what lets each fallback come out as\n * `number`/`string`/`boolean` rather than `| undefined`.\n */\nconst REQUIRED_DEFAULTS = DEFAULT_TEMPLATE_DEFAULTS as Required<\n Pick<\n TemplateSettings,\n | \"width\"\n | \"backgroundColor\"\n | \"textColor\"\n | \"linkUnderline\"\n | \"fontFamily\"\n | \"locale\"\n >\n>;\n\n/**\n * Convert an Easy Email Pro page (or EmailTemplate envelope) into a\n * Templatical template. Accepts a bare `page` element, an\n * `EmailTemplate { subject, content }` envelope, or a JSON string of either.\n */\nexport function convertEasyEmailProTemplate(\n doc: EasyEmailProDocument | EasyEmailProPage | string,\n): ImportResult {\n const root: unknown = typeof doc === \"string\" ? safeParse(doc) : doc;\n\n if (typeof root !== \"object\" || root === null || Array.isArray(root)) {\n throw new Error(INVALID_MESSAGE);\n }\n\n const unwrapped = unwrapDocument(root);\n if (unwrapped.page.type !== \"page\") {\n throw new Error(INVALID_MESSAGE);\n }\n const page = unwrapped.page as EasyEmailProPage;\n const subject = unwrapped.subject;\n assertNotOss(page);\n\n const warnings: string[] = [];\n const entries: ImportReportEntry[] = [];\n const blocks: Block[] = [];\n const resolve = contextFromPage(page);\n const map: MapContext = { resolve, warnings };\n const data = isPlainObject(page.data) ? page.data : {};\n\n const subjectRaw = typeof subject === \"string\" ? subject.trim() : \"\";\n if (subjectRaw) {\n warnings.push(\n `Document subject \"${subject}\" has no TemplateSettings field and was dropped.`,\n );\n }\n if (Array.isArray(data.fonts) && data.fonts.length > 0) {\n warnings.push(\n \"Dropped fonts[] — Templatical templates have no document-level font table.\",\n );\n }\n if (isNonEmptyHeadStyles(data.headStyles)) {\n warnings.push(\n \"Dropped headStyles — Templatical templates have no document-level head-style table.\",\n );\n }\n if (\n isPlainObject(data.classAttributes) &&\n Object.keys(data.classAttributes).length > 0\n ) {\n warnings.push(\n \"Dropped classAttributes — class-based MJML attribute cascade is not applied.\",\n );\n }\n\n for (const child of page.children ?? []) {\n if (!isElement(child)) continue;\n walkNode(child, map, entries, blocks);\n }\n\n const settings = readSettings(page, data, resolve);\n\n if (hasMobileAttributes(page)) {\n warnings.push(MOBILE_WARNING);\n }\n\n if (resolve.stats.resolved > 0 || resolve.stats.unresolved > 0) {\n warnings.push(varWarning(resolve.stats.resolved, resolve.stats.unresolved));\n }\n\n if (blocks.length === 0) {\n warnings.push(EMPTY_WARNING);\n }\n\n const defaults = createDefaultTemplateContent();\n const content: TemplateContent = {\n ...defaults,\n blocks,\n settings: {\n ...defaults.settings,\n ...settings,\n },\n };\n\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 return { content, report: { entries, warnings, summary } };\n}\n\nfunction readSettings(\n page: EasyEmailProPage,\n data: Record<string, unknown>,\n resolve: MapContext[\"resolve\"],\n): TemplateSettings {\n const width =\n parsePx(readAttr(page, \"width\", resolve)) ?? REQUIRED_DEFAULTS.width;\n // Page `background-color` is the viewport band. `content-background-color`\n // is the section fill fallback (section-builder), never a settings field.\n const backgroundColor =\n parseColor(readAttr(page, \"background-color\", resolve)) ??\n REQUIRED_DEFAULTS.backgroundColor;\n const fontFamilyRaw = readAttr(page, \"font-family\", resolve);\n const fontFamily =\n typeof fontFamilyRaw === \"string\" && !isUnset(fontFamilyRaw)\n ? fontFamilyRaw\n : REQUIRED_DEFAULTS.fontFamily;\n const textColor =\n parseColor(readAttr(page, \"color\", resolve)) ?? REQUIRED_DEFAULTS.textColor;\n const linkColor = parseColor(readAttr(page, \"link-color\", resolve));\n const deco = readAttr(page, \"link-text-decoration\", resolve);\n const linkUnderline =\n deco === \"underline\"\n ? true\n : deco === \"none\"\n ? false\n : REQUIRED_DEFAULTS.linkUnderline;\n const preheaderRaw =\n typeof data.preheader === \"string\" ? data.preheader.trim() : \"\";\n\n return {\n width,\n backgroundColor,\n textColor,\n fontFamily,\n linkUnderline,\n locale: REQUIRED_DEFAULTS.locale,\n ...(linkColor ? { linkColor } : {}),\n ...(preheaderRaw ? { preheaderText: preheaderRaw } : {}),\n };\n}\n\nfunction walkNode(\n node: EasyEmailProNode,\n map: MapContext,\n entries: ImportReportEntry[],\n blocks: Block[],\n): void {\n if (node.type === \"placeholder\") return;\n\n const logic = node.logic;\n const logicOn = hasLogic(logic);\n if (logicOn && !hasTypedContent(node)) {\n entries.push({\n sourceTag: node.type ?? \"logic\",\n templaticalBlockType: null,\n status: \"skipped\",\n note: `Skipped empty logic (${logicExpression(logic)})`,\n });\n return;\n }\n\n if (typeof node.type === \"string\" && WIDGET_TYPES.has(node.type)) {\n const from = entries.length;\n const data = isPlainObject(node.data) ? node.data : {};\n const input = isPlainObject(data.input) ? data.input : undefined;\n const inner: MapContext = {\n resolve: withWidgetInput(map.resolve, input),\n warnings: map.warnings,\n };\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n walkNode(child, inner, entries, blocks);\n }\n markApproximated(\n entries,\n from,\n `${node.type} flattened; $var resolved from data.input`,\n {\n sourceTag: node.type,\n },\n );\n if (logicOn) {\n markApproximated(entries, from, `logic ${logicExpression(logic)}`);\n }\n return;\n }\n\n const from = entries.length;\n if (typeof node.type === \"string\" && STRUCTURE_TYPES.has(node.type)) {\n const converted = buildTopLevel(node, map);\n for (const block of converted.blocks) blocks.push(block);\n for (const entry of converted.entries) entries.push(entry);\n } else {\n const converted = convertLeaf(node, map);\n for (const entry of converted.entries) entries.push(entry);\n if (converted.blocks.length > 0) {\n if (isLeafType(node.type)) {\n blocks.push(wrapInSection(converted.blocks));\n } else {\n blocks.push(...converted.blocks);\n }\n }\n }\n\n if (logicOn) {\n markApproximated(entries, from, `logic ${logicExpression(logic)}`);\n }\n}\n\nfunction wrapInSection(blocks: Block[]): Block {\n const section = createSectionBlock();\n section.columns = \"1\";\n section.children = [blocks];\n return section;\n}\n\nfunction markApproximated(\n entries: ImportReportEntry[],\n from: number,\n extra: string,\n rewrite?: { sourceTag: string },\n): void {\n for (let i = from; i < entries.length; i++) {\n const entry = entries[i];\n if (rewrite) entry.sourceTag = rewrite.sourceTag;\n if (entry.status === \"converted\") entry.status = \"approximated\";\n entry.note = entry.note ? `${entry.note} ${extra}` : extra;\n }\n}\n\nfunction hasLogic(\n logic: EasyEmailProNode[\"logic\"],\n): logic is NonNullable<EasyEmailProNode[\"logic\"]> {\n if (logic == null || typeof logic !== \"object\") return false;\n return logic.condition != null || logic.iteration != null;\n}\n\nfunction logicExpression(\n logic: NonNullable<EasyEmailProNode[\"logic\"]>,\n): string {\n const parts: string[] = [];\n if (logic.condition != null) {\n parts.push(\n typeof logic.condition === \"string\"\n ? logic.condition\n : JSON.stringify(logic.condition),\n );\n }\n if (logic.iteration != null) {\n const iteration = logic.iteration;\n if (typeof iteration === \"string\") {\n parts.push(iteration);\n } else if (\n isPlainObject(iteration) &&\n typeof iteration.dataSource === \"string\"\n ) {\n parts.push(iteration.dataSource);\n } else {\n parts.push(JSON.stringify(iteration));\n }\n }\n return parts.join(\" \");\n}\n\nfunction hasTypedContent(node: EasyEmailProNode): boolean {\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n if (child.type === \"placeholder\") continue;\n return true;\n }\n return false;\n}\n\nfunction hasMobileAttributes(node: unknown): boolean {\n if (!isPlainObject(node)) return false;\n const mobile = node.mobileAttributes;\n if (isPlainObject(mobile) && Object.keys(mobile).length > 0) return true;\n if (!Array.isArray(node.children)) return false;\n for (const child of node.children) {\n if (hasMobileAttributes(child)) return true;\n }\n return false;\n}\n\nfunction assertNotOss(page: EasyEmailProPage): void {\n const children = page.children ?? [];\n for (const child of children) {\n if (!child || typeof child !== \"object\") continue;\n const type = (child as EasyEmailProNode).type;\n if (typeof type !== \"string\") continue;\n if (OSS_CHILD_TYPES.has(type)) {\n throw new Error(OSS_MESSAGE);\n }\n break;\n }\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction isNonEmptyHeadStyles(value: unknown): boolean {\n if (Array.isArray(value)) return value.length > 0;\n return isPlainObject(value) && Object.keys(value).length > 0;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction safeParse(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new Error(INVALID_MESSAGE);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["isPlainObject","empty","isElement","readVisibility","isElement"],"sources":["../src/normalize.ts","../src/attribute-parser.ts","../src/rich-text.ts","../src/block-mapper.ts","../src/column-layout.ts","../src/section-builder.ts","../src/converter.ts"],"sourcesContent":["import type {\n EasyEmailProDocument,\n EasyEmailProNode,\n EasyEmailProVariable,\n} from \"./types\";\n\nconst VAR_PREFIX = \"$var(\";\n\n/** Spec §3.3 category map — unknown types skip the category layer. */\nexport const CATEGORY_BY_TYPE: Record<string, string> = {\n \"standard-paragraph\": \"TEXT\",\n \"standard-text\": \"TEXT\",\n \"standard-h1\": \"TEXT\",\n \"standard-h2\": \"TEXT\",\n \"standard-h3\": \"TEXT\",\n \"standard-h4\": \"TEXT\",\n \"standard-button\": \"BUTTON\",\n \"standard-image\": \"IMAGE\",\n \"standard-navbar\": \"NAVBAR\",\n \"standard-navbar-link\": \"NAVBAR\",\n \"standard-social\": \"SOCIAL\",\n \"standard-social-element\": \"SOCIAL\",\n \"standard-spacer\": \"SPACER\",\n \"standard-divider\": \"DIVIDER\",\n \"standard-hero\": \"HERO\",\n \"standard-wrapper\": \"WRAPPER\",\n \"standard-section\": \"SECTION\",\n \"standard-column\": \"COLUMN\",\n \"standard-group\": \"GROUP\",\n};\n\nexport interface VarStats {\n resolved: number;\n unresolved: number;\n}\n\nexport interface ResolveContext {\n variables: Record<string, string>;\n blockAttributes: Record<string, Record<string, unknown>>;\n categoryAttributes: Record<string, Record<string, unknown>>;\n globalAttributes: Record<string, unknown>;\n /** Page node — section fill falls back to `content-background-color`. */\n page: EasyEmailProNode;\n stats: VarStats;\n /**\n * Per-node attribute cache so a repeated `readAttr` (button fill vs\n * outlined check, section fill fallback) does not double-count `$var`.\n */\n attrCache: WeakMap<EasyEmailProNode, Map<string, unknown>>;\n}\n\n/**\n * `null`, `\"\"`, `\"url()\"`, `\"transparent\"`, and leftover `$var(` after\n * substitution are unset. Presence-guards that treat these as set write\n * illegal empties.\n */\nexport function isUnset(value: unknown): boolean {\n if (value === null || value === undefined) return true;\n if (typeof value !== \"string\") return false;\n const trimmed = value.trim();\n return (\n trimmed === \"\" ||\n trimmed === \"url()\" ||\n trimmed.toLowerCase() === \"transparent\" ||\n trimmed.includes(VAR_PREFIX)\n );\n}\n\n/** Index `data.variables[]` by name for `$var` lookup. */\nexport function buildVariableTable(\n variables: EasyEmailProVariable[] | undefined,\n): Record<string, string> {\n const out: Record<string, string> = {};\n if (!variables) return out;\n for (const entry of variables) {\n if (typeof entry.name !== \"string\" || entry.name === \"\") continue;\n if (typeof entry.value !== \"string\") continue;\n out[entry.name] = entry.value;\n }\n return out;\n}\n\n/**\n * Replace `$var(name)` tokens via a linear scan (no regex — CodeQL\n * `js/polynomial-redos`). Unknown names are left in place and flagged.\n */\nexport function substituteVars(\n value: string,\n variables: Record<string, string>,\n): { value: string; resolved: number; unresolved: number } {\n let out = \"\";\n let i = 0;\n let resolved = 0;\n let unresolved = 0;\n\n while (i < value.length) {\n const start = value.indexOf(VAR_PREFIX, i);\n if (start === -1) {\n out += value.slice(i);\n break;\n }\n out += value.slice(i, start);\n const nameStart = start + VAR_PREFIX.length;\n const nameEnd = findMatchingParen(value, nameStart);\n if (nameEnd === -1) {\n // Unclosed `$var(` — keep the rest verbatim and mark unresolved.\n out += value.slice(start);\n unresolved += 1;\n break;\n }\n const name = value.slice(nameStart, nameEnd);\n if (Object.prototype.hasOwnProperty.call(variables, name)) {\n out += variables[name];\n resolved += 1;\n } else {\n out += value.slice(start, nameEnd + 1);\n unresolved += 1;\n }\n i = nameEnd + 1;\n }\n\n return { value: out, resolved, unresolved };\n}\n\n/** Walk from `openIdx` (char after `(`) to the matching `)`. */\nfunction findMatchingParen(value: string, openIdx: number): number {\n let depth = 1;\n for (let i = openIdx; i < value.length; i++) {\n const ch = value[i];\n if (ch === \"(\") depth += 1;\n else if (ch === \")\") {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\nexport function contextFromPage(page: EasyEmailProNode): ResolveContext {\n const data = isPlainObject(page.data) ? page.data : {};\n return {\n variables: buildVariableTable(\n Array.isArray(data.variables)\n ? (data.variables as EasyEmailProVariable[])\n : undefined,\n ),\n blockAttributes: asNestedAttrMap(data.blockAttributes),\n categoryAttributes: asNestedAttrMap(data.categoryAttributes),\n globalAttributes: asAttrMap(data.globalAttributes),\n page,\n stats: { resolved: 0, unresolved: 0 },\n attrCache: new WeakMap(),\n };\n}\n\n/** Widget `data.input` overrides page variables for `$var` resolution. */\nexport function withWidgetInput(\n ctx: ResolveContext,\n input: Record<string, unknown> | undefined,\n): ResolveContext {\n if (!input) return ctx;\n const variables = { ...ctx.variables };\n for (const [key, value] of Object.entries(input)) {\n if (typeof value === \"string\") variables[key] = value;\n }\n // New cache: widget `data.input` can resolve the same node keys differently.\n return { ...ctx, variables, attrCache: new WeakMap() };\n}\n\n/**\n * Cascade: element → blockAttributes[type] → categoryAttributes[cat] →\n * globalAttributes. Missing keys fall through. String hits run through\n * `substituteVars`. Keys stay kebab-case MJML.\n */\nexport function readAttr(\n node: EasyEmailProNode,\n key: string,\n ctx: ResolveContext,\n): unknown {\n let cached = ctx.attrCache.get(node);\n if (cached?.has(key)) return cached.get(key);\n\n const raw = lookupAttr(node, key, ctx);\n let value: unknown = raw;\n if (typeof raw === \"string\") {\n const result = substituteVars(raw, ctx.variables);\n ctx.stats.resolved += result.resolved;\n ctx.stats.unresolved += result.unresolved;\n value = result.value;\n }\n\n if (!cached) {\n cached = new Map();\n ctx.attrCache.set(node, cached);\n }\n cached.set(key, value);\n return value;\n}\n\nfunction lookupAttr(\n node: EasyEmailProNode,\n key: string,\n ctx: ResolveContext,\n): unknown {\n const fromElement = node.attributes?.[key];\n if (fromElement !== undefined) return fromElement;\n\n if (typeof node.type === \"string\") {\n const fromBlock = ctx.blockAttributes[node.type]?.[key];\n if (fromBlock !== undefined) return fromBlock;\n\n const category = CATEGORY_BY_TYPE[node.type];\n if (category) {\n const fromCategory = ctx.categoryAttributes[category]?.[key];\n if (fromCategory !== undefined) return fromCategory;\n }\n }\n\n return ctx.globalAttributes[key];\n}\n\n/**\n * Envelope unwrap. Never throws — the converter owns invalid-shape errors.\n * Returns `{ page, subject }` when a page is visible; caller checks\n * `page.type === \"page\"`.\n */\nexport function unwrapDocument(root: unknown): {\n page: EasyEmailProNode;\n subject?: string;\n} {\n if (!isPlainObject(root)) {\n return { page: {} };\n }\n\n if (root.type === \"page\") {\n return { page: root as EasyEmailProNode };\n }\n\n const doc = root as EasyEmailProDocument;\n const page = isPlainObject(doc.content)\n ? (doc.content as EasyEmailProNode)\n : (root as EasyEmailProNode);\n const subject = typeof doc.subject === \"string\" ? doc.subject : undefined;\n return subject === undefined ? { page } : { page, subject };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asAttrMap(value: unknown): Record<string, unknown> {\n return isPlainObject(value) ? value : {};\n}\n\nfunction asNestedAttrMap(\n value: unknown,\n): Record<string, Record<string, unknown>> {\n if (!isPlainObject(value)) return {};\n const out: Record<string, Record<string, unknown>> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (isPlainObject(entry)) out[key] = entry;\n }\n return out;\n}\n","import { isUnset } from \"./normalize\";\n\n/** Strip a trailing `px` (case-insensitive) and coerce to number. Unset/NaN → undefined. */\nexport function parsePx(value: unknown): number | undefined {\n if (isUnset(value)) return undefined;\n let raw = String(value).trim();\n if (raw.toLowerCase().endsWith(\"px\")) raw = raw.slice(0, -2).trimEnd();\n const n = Number(raw);\n return Number.isNaN(n) ? undefined : n;\n}\n\n/**\n * Strip a trailing `%` and coerce to number. Unset/NaN → `null` so\n * `matchColumnLayout` can tell \"missing width\" from `0%`.\n */\nexport function parsePercent(value: unknown): number | null {\n if (isUnset(value)) return null;\n let raw = String(value).trim();\n if (raw.endsWith(\"%\")) raw = raw.slice(0, -1).trimEnd();\n const n = Number(raw);\n return Number.isNaN(n) ? null : n;\n}\n\n/** Return the colour string as-is when set. No named-colour expansion. */\nexport function parseColor(value: unknown): string | undefined {\n if (isUnset(value)) return undefined;\n if (typeof value !== \"string\") return undefined;\n return value;\n}\n\n/** Read the four padding sides via a callback (typically wrapping `readAttr`). Missing → 0. */\nexport function readPadding(read: (key: string) => unknown): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const side = (key: string) => parsePx(read(key)) ?? 0;\n return {\n top: side(\"padding-top\"),\n right: side(\"padding-right\"),\n bottom: side(\"padding-bottom\"),\n left: side(\"padding-left\"),\n };\n}\n","import type { EasyEmailProNode, EasyEmailProTextNode } from \"./types\";\n\n/** Escape `& < > \"` for text content and attribute values. */\nfunction escapeHtml(value: string): string {\n let out = \"\";\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === \"&\") out += \"&amp;\";\n else if (ch === \"<\") out += \"&lt;\";\n else if (ch === \">\") out += \"&gt;\";\n else if (ch === '\"') out += \"&quot;\";\n else out += ch;\n }\n return out;\n}\n\nfunction isTextNode(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProTextNode {\n return typeof (node as EasyEmailProTextNode).text === \"string\";\n}\n\n/**\n * Nesting (outer → inner): link → color/bgColor span → underline → strike →\n * em → strong. Matches the Task 4 brief / design §8.1 mark order.\n */\nfunction serialiseTextNode(node: EasyEmailProTextNode): string {\n let html = escapeHtml(node.text);\n\n if (node.bold) html = `<strong>${html}</strong>`;\n if (node.italic) html = `<em>${html}</em>`;\n if (node.strikethrough) html = `<s>${html}</s>`;\n if (node.underline) html = `<u>${html}</u>`;\n\n const styles: string[] = [];\n if (typeof node.color === \"string\" && node.color !== \"\") {\n styles.push(`color: ${escapeHtml(node.color)}`);\n }\n if (typeof node.bgColor === \"string\" && node.bgColor !== \"\") {\n styles.push(`background-color: ${escapeHtml(node.bgColor)}`);\n }\n if (styles.length > 0) {\n html = `<span style=\"${styles.join(\"; \")}\">${html}</span>`;\n }\n\n if (node.link) {\n const href = escapeHtml(node.link.href ?? \"\");\n const blank = node.link.blank === true ? ' target=\"_blank\"' : \"\";\n html = `<a href=\"${href}\"${blank}>${html}</a>`;\n }\n\n return html;\n}\n\n/** Easy Email Pro's `HTML_NODE_VOID_TAGS`: rendered `<tag />`, children ignored. */\nconst VOID_TAGS = new Set([\n \"img\",\n \"br\",\n \"hr\",\n \"area\",\n \"base\",\n \"col\",\n \"embed\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n]);\n\nconst TAG_NAME = /^[a-z][a-z0-9-]*$/;\nconst ATTRIBUTE_NAME = /^[a-z_:][a-z0-9_:.-]*$/i;\n\n/**\n * An inline `html-node` renders as `<tagName attrs>children</tagName>`. Only\n * non-empty string attributes are written, as Easy Email Pro does. A tag or\n * attribute name that could break out of the markup is dropped, the tag\n * falling back to `span`.\n */\nfunction serialiseHtmlNode(node: EasyEmailProNode): string {\n const rawTag = node.data?.[\"tagName\"];\n const lowered = typeof rawTag === \"string\" ? rawTag.toLowerCase() : \"\";\n const tagName = TAG_NAME.test(lowered) ? lowered : \"span\";\n\n let attrs = \"\";\n for (const [name, value] of Object.entries(node.attributes ?? {})) {\n if (typeof value !== \"string\" || !ATTRIBUTE_NAME.test(name)) continue;\n const trimmed = value.trim();\n if (trimmed === \"\") continue;\n attrs += ` ${name}=\"${escapeHtml(trimmed)}\"`;\n }\n\n if (VOID_TAGS.has(tagName)) return `<${tagName}${attrs} />`;\n return `<${tagName}${attrs}>${serialiseChildren(node.children)}</${tagName}>`;\n}\n\n/**\n * Easy Email Pro renders a `mergetag` as `{{ name }}`, the name being its\n * first text child and the marks on that child applying to the token.\n * `data.default` is not part of that output. A blank name has no variable to\n * keep.\n */\nfunction serialiseMergetag(node: EasyEmailProNode): string {\n const first = node.children?.[0];\n if (!first || !isTextNode(first)) return \"\";\n const name = first.text.trim();\n if (name === \"\") return \"\";\n return serialiseTextNode({ ...first, text: `{{ ${name} }}` });\n}\n\nfunction serialiseNode(node: EasyEmailProNode | EasyEmailProTextNode): string {\n if (node.type === \"line-break\") return \"<br>\";\n\n if (node.type === \"html-block-node\") {\n const element = node as EasyEmailProNode;\n const rawTag = element.data?.[\"tagName\"];\n const tagName =\n typeof rawTag === \"string\" && rawTag !== \"\" ? rawTag : \"div\";\n const inner = serialiseChildren(element.children);\n return `<${tagName}>${inner}</${tagName}>`;\n }\n\n if (node.type === \"html-node\") {\n return serialiseHtmlNode(node as EasyEmailProNode);\n }\n\n if (node.type === \"mergetag\")\n return serialiseMergetag(node as EasyEmailProNode);\n\n // Any other typed element has no inline rendering here and is dropped.\n if (typeof node.type === \"string\" && node.type !== \"\") return \"\";\n\n if (isTextNode(node)) return serialiseTextNode(node);\n\n return \"\";\n}\n\n/** Slate children → HTML for TitleBlock / ParagraphBlock content. */\nexport function serialiseChildren(\n children: Array<EasyEmailProNode | EasyEmailProTextNode> | undefined,\n): string {\n if (!children || children.length === 0) return \"\";\n let out = \"\";\n for (const child of children) {\n out += serialiseNode(child);\n }\n return out;\n}\n","import {\n createButtonBlock,\n createDividerBlock,\n createHtmlBlock,\n createImageBlock,\n createMenuBlock,\n createParagraphBlock,\n createSocialIconsBlock,\n createSpacerBlock,\n createTableBlock,\n createTitleBlock,\n createVideoBlock,\n findOpenTagEnd,\n generateId,\n} from \"@templatical/types\";\nimport type {\n Block,\n BlockStyles,\n BlockVisibility,\n ButtonBlock,\n DividerBlock,\n HeadingLevel,\n MenuItemData,\n SocialIcon,\n SocialPlatform,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport {\n parseColor,\n parsePercent,\n parsePx,\n readPadding,\n} from \"./attribute-parser\";\nimport { isUnset, readAttr, type ResolveContext } from \"./normalize\";\nimport { serialiseChildren } from \"./rich-text\";\nimport type {\n ConversionStatus,\n EasyEmailProNode,\n EasyEmailProTextNode,\n ImportReportEntry,\n} from \"./types\";\n\nexport interface MapContext {\n resolve: ResolveContext;\n warnings: string[];\n /**\n * Width in px of the box the current node lays out in: the page, the inside\n * of a wrapper, band or hero, or a column's content box. Absent where the\n * converter cannot tell, and then no width is judged against it.\n */\n contentWidth?: number;\n}\n\nexport interface Converted {\n blocks: Block[];\n entries: ImportReportEntry[];\n}\n\nconst LEAF_TYPES = new Set([\n \"standard-paragraph\",\n \"standard-text\",\n \"standard-h1\",\n \"standard-h2\",\n \"standard-h3\",\n \"standard-h4\",\n \"standard-button\",\n \"standard-image\",\n \"standard-divider\",\n \"standard-spacer\",\n \"standard-navbar\",\n \"standard-social\",\n \"standard-table2\",\n \"marketing-countdown\",\n \"placeholder\",\n \"common-video\",\n \"raw\",\n]);\n\nconst SOCIAL_PLATFORMS = [\n \"facebook\",\n \"instagram\",\n \"twitter\",\n \"linkedin\",\n \"youtube\",\n \"tiktok\",\n \"pinterest\",\n \"whatsapp\",\n \"telegram\",\n \"github\",\n] as const satisfies ReadonlyArray<SocialPlatform>;\n\nconst ENTITIES: Record<string, string> = {\n \"&amp;\": \"&\",\n \"&lt;\": \"<\",\n \"&gt;\": \">\",\n \"&quot;\": '\"',\n \"&#39;\": \"'\",\n \"&apos;\": \"'\",\n \"&nbsp;\": \" \",\n};\n\n/**\n * Easy Email Pro writes `standard-table2-tr` / `standard-table2-td`; the plain\n * `tr` / `td` / `th` shape is read too.\n */\nconst TABLE_CELL_TYPES = new Set([\"standard-table2-td\", \"td\", \"th\"]);\n\nconst COUNTDOWN_NOTE =\n \"marketing-countdown GIF is the timer; Templatical countdown is Cloud-only\";\n\nexport function isLeafType(type: string | undefined): boolean {\n if (typeof type !== \"string\") return false;\n if (LEAF_TYPES.has(type)) return true;\n return headingLevelFromType(type) !== null;\n}\n\nexport function convertLeaf(\n node: EasyEmailProNode,\n map: MapContext,\n): Converted {\n const type = node.type;\n if (type === \"placeholder\") return empty();\n if (type === \"standard-paragraph\" || type === \"standard-text\") {\n return convertParagraph(node, map);\n }\n\n const heading = typeof type === \"string\" ? headingLevelFromType(type) : null;\n if (heading) return convertHeading(node, map, heading);\n\n switch (type) {\n case \"standard-button\":\n return convertButton(node, map);\n case \"standard-image\":\n return convertImage(node, map);\n case \"standard-divider\":\n return convertDivider(node, map);\n case \"standard-spacer\":\n return convertSpacer(node, map);\n case \"standard-navbar\":\n return convertNavbar(node, map);\n case \"standard-social\":\n return convertSocial(node, map);\n case \"standard-table2\":\n return convertTable(node, map);\n case \"marketing-countdown\":\n return convertCountdown(node, map);\n case \"common-video\":\n return convertVideo(node, map);\n case \"raw\":\n return convertRaw(node, map);\n default:\n return convertUnknown(node, map);\n }\n}\n\n/**\n * `standard-text` (the default text block) and `standard-paragraph` share one\n * shape and one MJML renderer, so both land here.\n */\nfunction convertParagraph(node: EasyEmailProNode, map: MapContext): Converted {\n return finish(\n createParagraphBlock({\n content: paragraphHtml(node, map),\n ...leafStyles(node, map),\n }),\n node,\n report(node.type ?? \"standard-paragraph\", \"paragraph\", \"converted\"),\n );\n}\n\n/**\n * ParagraphBlock has no color / fontSize / textAlign. Bake the cascaded\n * values into a wrapping `<p style>` (single wrapper, Unlayer/BeeFree class).\n */\nfunction paragraphHtml(node: EasyEmailProNode, map: MapContext): string {\n const html = serialiseChildren(node.children);\n const parts: string[] = [];\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n if (color) parts.push(`color: ${escapeAttr(color)}`);\n const fontSize = setString(readAttr(node, \"font-size\", map.resolve));\n if (fontSize) parts.push(`font-size: ${escapeAttr(fontSize)}`);\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n if (align) parts.push(`text-align: ${align}`);\n if (parts.length === 0) return html;\n return `<p style=\"${parts.join(\"; \")}\">${html}</p>`;\n}\n\nfunction escapeAttr(value: string): string {\n let out = \"\";\n for (let i = 0; i < value.length; i++) {\n const ch = value[i];\n if (ch === \"&\") out += \"&amp;\";\n else if (ch === \"<\") out += \"&lt;\";\n else if (ch === \">\") out += \"&gt;\";\n else if (ch === '\"') out += \"&quot;\";\n else out += ch;\n }\n return out;\n}\n\nfunction convertHeading(\n node: EasyEmailProNode,\n map: MapContext,\n heading: { level: HeadingLevel; source: number },\n): Converted {\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const clamped = heading.source > 4;\n return finish(\n createTitleBlock({\n content: serialiseChildren(node.children),\n level: heading.level,\n ...(color ? { color } : {}),\n ...(align ? { textAlign: align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n clamped\n ? report(\n node.type ?? \"standard-h4\",\n \"title\",\n \"approximated\",\n `Heading level h${heading.source} clamped to 4 — Templatical titles support h1-h4.`,\n )\n : report(node.type ?? \"standard-h1\", \"title\", \"converted\"),\n );\n}\n\nfunction convertButton(node: EasyEmailProNode, map: MapContext): Converted {\n const fill = parseColor(readAttr(node, \"background-color\", map.resolve));\n const textColor = parseColor(readAttr(node, \"color\", map.resolve));\n const href = readAttr(node, \"href\", map.resolve);\n const width = buttonWidth(readAttr(node, \"width\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const outlined =\n isBorderEnabled(readAttr(node, \"border-enabled\", map.resolve)) &&\n isUnset(readAttr(node, \"background-color\", map.resolve));\n\n const block: ButtonBlock = createButtonBlock({\n text: stripTags(serialiseChildren(node.children)),\n url: setString(href) ?? \"\",\n ...(fill ? { backgroundColor: fill } : {}),\n ...(textColor ? { textColor } : {}),\n ...(width !== undefined ? { width } : {}),\n ...(align ? { align } : {}),\n ...leafStyles(node, map),\n });\n // createButtonBlock defaults fill to #333333; an unset Pro fill with\n // border-enabled is an outlined button, which has no Templatical equivalent.\n if (outlined) block.backgroundColor = \"#ffffff\";\n\n return finish(\n block,\n node,\n outlined\n ? report(\n \"standard-button\",\n \"button\",\n \"approximated\",\n \"outlined button has no Templatical equivalent\",\n )\n : report(\"standard-button\", \"button\", \"converted\"),\n );\n}\n\nfunction convertImage(node: EasyEmailProNode, map: MapContext): Converted {\n const src = setString(readAttr(node, \"src\", map.resolve)) ?? \"\";\n const alt = setString(readAttr(node, \"alt\", map.resolve)) ?? \"\";\n const href = readAttr(node, \"href\", map.resolve);\n const linkUrl = setString(href);\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const target = setString(readAttr(node, \"target\", map.resolve));\n\n return finish(\n createImageBlock({\n src,\n alt,\n ...(align ? { align } : {}),\n ...(radius !== undefined && radius > 0 ? { borderRadius: radius } : {}),\n ...(linkUrl ? { linkUrl } : {}),\n ...(linkUrl && target === \"_blank\" ? { linkOpenInNewTab: true } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-image\", \"image\", \"converted\"),\n );\n}\n\nfunction convertDivider(node: EasyEmailProNode, map: MapContext): Converted {\n const color = parseColor(readAttr(node, \"border-color\", map.resolve));\n const thickness = parsePx(readAttr(node, \"border-width\", map.resolve));\n const lineStyle = asLineStyle(readAttr(node, \"border-style\", map.resolve));\n const styles = leafStyles(node, map);\n const { left, right } = styles.styles.padding;\n const room =\n map.contentWidth === undefined\n ? undefined\n : map.contentWidth - left - right;\n return finish(\n createDividerBlock({\n ...(color ? { color } : {}),\n ...(thickness !== undefined ? { thickness } : {}),\n ...(lineStyle ? { lineStyle } : {}),\n width: dividerWidth(readAttr(node, \"width\", map.resolve), room),\n ...styles,\n }),\n node,\n report(\"standard-divider\", \"divider\", \"converted\"),\n );\n}\n\n/**\n * A missing or `100%` width is `\"full\"`. Any other percentage stays a share of\n * the column, clamped to 0-100 and rounded to two decimals so it stays a\n * `DividerPercentWidth`. A px width stays px until it fills `room`, the space\n * the column leaves the line once the divider's own padding is off, and then\n * it is `\"full\"`. An unreadable width falls back to `\"full\"`, MJML's default.\n */\nfunction dividerWidth(\n value: unknown,\n room: number | undefined,\n): DividerBlock[\"width\"] {\n if (isUnset(value)) return \"full\";\n if (typeof value === \"string\" && value.trim().endsWith(\"%\")) {\n const percent = parsePercent(value);\n if (percent === null) return \"full\";\n const clamped = Math.min(100, Math.max(0, percent));\n const rounded = Math.round(clamped * 100) / 100;\n return rounded >= 100 ? \"full\" : `${rounded}%`;\n }\n const px = parsePx(value);\n if (px === undefined) return \"full\";\n return room !== undefined && px >= room ? \"full\" : px;\n}\n\nfunction convertSpacer(node: EasyEmailProNode, map: MapContext): Converted {\n const height = parsePx(readAttr(node, \"height\", map.resolve));\n return finish(\n createSpacerBlock({\n ...(height !== undefined ? { height } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-spacer\", \"spacer\", \"converted\"),\n );\n}\n\nfunction convertNavbar(node: EasyEmailProNode, map: MapContext): Converted {\n const items: MenuItemData[] = [];\n for (const child of node.children ?? []) {\n if (!isElement(child) || child.type !== \"standard-navbar-link\") continue;\n const href = readAttr(child, \"href\", map.resolve);\n const target = setString(readAttr(child, \"target\", map.resolve));\n const color = parseColor(readAttr(child, \"color\", map.resolve));\n const weight = setString(readAttr(child, \"font-weight\", map.resolve));\n const deco = setString(readAttr(child, \"text-decoration\", map.resolve));\n items.push({\n id: generateId(),\n text: stripTags(serialiseChildren(child.children)),\n url: setString(href) ?? \"\",\n openInNewTab: target === \"_blank\",\n bold: weight === \"bold\" || weight === \"700\",\n underline: deco !== undefined && deco.includes(\"underline\"),\n ...(color ? { color } : {}),\n });\n }\n if (items.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n return finish(\n createMenuBlock({\n items,\n ...(align ? { textAlign: align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-navbar\", \"menu\", \"converted\"),\n );\n}\n\nfunction convertSocial(node: EasyEmailProNode, map: MapContext): Converted {\n const icons: SocialIcon[] = [];\n let customSrc = false;\n for (const child of node.children ?? []) {\n if (!isElement(child) || child.type !== \"standard-social-element\") continue;\n const href = readAttr(child, \"href\", map.resolve);\n const src = readAttr(child, \"src\", map.resolve);\n if (typeof src === \"string\" && !isUnset(src)) customSrc = true;\n icons.push({\n id: generateId(),\n platform: inferPlatform(href, src),\n url: setString(href) ?? \"\",\n });\n }\n if (icons.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n return finish(\n createSocialIconsBlock({\n icons,\n ...(align ? { align } : {}),\n ...leafStyles(node, map),\n }),\n node,\n customSrc\n ? report(\n \"standard-social\",\n \"social\",\n \"approximated\",\n \"custom src was present; SocialIconsBlock has no custom PNG\",\n )\n : report(\"standard-social\", \"social\", \"converted\"),\n );\n}\n\nfunction convertTable(node: EasyEmailProNode, map: MapContext): Converted {\n const rows: TableRowData[] = [];\n let hasHeaderRow = false;\n let first = true;\n for (const row of collectRows(node)) {\n const cells: TableCellData[] = [];\n let rowHasTh = false;\n for (const cell of row.children ?? []) {\n if (!isElement(cell) || !TABLE_CELL_TYPES.has(cell.type ?? \"\")) continue;\n if (cell.type === \"th\") rowHasTh = true;\n cells.push({\n id: generateId(),\n content: serialiseChildren(cell.children),\n });\n }\n if (cells.length === 0) continue;\n if (first) {\n hasHeaderRow = rowHasTh;\n first = false;\n }\n rows.push({ id: generateId(), cells });\n }\n if (rows.length === 0) return empty();\n\n const align = parseAlign(readAttr(node, \"align\", map.resolve));\n const color = parseColor(readAttr(node, \"color\", map.resolve));\n return finish(\n createTableBlock({\n rows,\n hasHeaderRow,\n ...(align ? { textAlign: align } : {}),\n ...(color ? { color } : {}),\n ...leafStyles(node, map),\n }),\n node,\n report(\"standard-table2\", \"table\", \"converted\"),\n );\n}\n\nfunction convertCountdown(node: EasyEmailProNode, map: MapContext): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n const remapped = remapOverlay(child);\n if (!remapped) continue;\n const inner = convertLeaf(remapped, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) {\n entries.push({\n sourceTag: \"marketing-countdown\",\n templaticalBlockType: entry.templaticalBlockType,\n status: \"approximated\",\n note: entry.note ? `${entry.note} marketing-countdown` : COUNTDOWN_NOTE,\n });\n }\n }\n\n const src = readAttr(node, \"src\", map.resolve);\n if (typeof src === \"string\" && !isUnset(src)) {\n const image = createImageBlock({\n src,\n ...leafStyles(node, map),\n });\n const visibility = readVisibility(node);\n if (visibility) image.visibility = visibility;\n blocks.push(image);\n entries.push({\n sourceTag: \"marketing-countdown\",\n templaticalBlockType: \"image\",\n status: \"approximated\",\n note: COUNTDOWN_NOTE,\n });\n }\n\n return { blocks, entries };\n}\n\nfunction convertVideo(node: EasyEmailProNode, map: MapContext): Converted {\n const url = firstUrl(node, map);\n if (!url) return convertUnknown(node, map);\n return finish(\n createVideoBlock({\n url,\n ...leafStyles(node, map),\n }),\n node,\n report(\"common-video\", \"video\", \"converted\"),\n );\n}\n\n/** `raw` is `<mj-raw>{data.content}</mj-raw>`: the markup moves across as is. */\nfunction convertRaw(node: EasyEmailProNode, map: MapContext): Converted {\n const content = node.data?.[\"content\"];\n if (typeof content !== \"string\") return convertUnknown(node, map);\n return finish(\n createHtmlBlock({\n content,\n ...leafStyles(node, map),\n }),\n node,\n report(\"raw\", \"html\", \"converted\"),\n );\n}\n\nfunction convertUnknown(node: EasyEmailProNode, map: MapContext): Converted {\n const sourceTag = node.type ?? \"unknown\";\n return finish(\n createHtmlBlock({\n content: JSON.stringify(node),\n ...leafStyles(node, map),\n }),\n node,\n report(\n sourceTag,\n \"html\",\n \"html-fallback\",\n `Unknown Easy Email Pro type \"${sourceTag}\"; preserved as HTML.`,\n ),\n );\n}\n\nfunction remapOverlay(child: EasyEmailProNode): EasyEmailProNode | null {\n if (child.type === \"text\") {\n return { ...child, type: \"standard-paragraph\" };\n }\n if (child.type === \"standard-paragraph\" || child.type === \"standard-text\") {\n return child;\n }\n if (typeof child.type === \"string\" && headingLevelFromType(child.type)) {\n return child;\n }\n return null;\n}\n\nfunction collectRows(node: EasyEmailProNode): EasyEmailProNode[] {\n const rows: EasyEmailProNode[] = [];\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n if (isTableRow(child)) {\n rows.push(child);\n continue;\n }\n for (const inner of child.children ?? []) {\n if (isElement(inner) && isTableRow(inner)) rows.push(inner);\n }\n }\n return rows;\n}\n\nfunction isTableRow(node: EasyEmailProNode): boolean {\n return node.type === \"standard-table2-tr\" || node.type === \"tr\";\n}\n\nfunction firstUrl(node: EasyEmailProNode, map: MapContext): string | undefined {\n for (const key of [\"src\", \"href\", \"url\"] as const) {\n const value = readAttr(node, key, map.resolve);\n if (typeof value === \"string\" && !isUnset(value)) return value;\n }\n return undefined;\n}\n\nfunction inferPlatform(href: unknown, src: unknown): SocialPlatform {\n const host = hostname(typeof href === \"string\" ? href : \"\");\n for (const platform of SOCIAL_PLATFORMS) {\n if (host.includes(platform)) return platform;\n }\n const path = pathOf(typeof src === \"string\" ? src : \"\");\n for (const platform of SOCIAL_PLATFORMS) {\n if (path.includes(platform)) return platform;\n }\n return \"website\";\n}\n\nfunction hostname(href: string): string {\n const lower = href.toLowerCase();\n const scheme = lower.indexOf(\"://\");\n const rest = scheme === -1 ? lower : lower.slice(scheme + 3);\n const slash = rest.indexOf(\"/\");\n const hostport = slash === -1 ? rest : rest.slice(0, slash);\n const colon = hostport.indexOf(\":\");\n return colon === -1 ? hostport : hostport.slice(0, colon);\n}\n\nfunction pathOf(src: string): string {\n const lower = src.toLowerCase();\n const scheme = lower.indexOf(\"://\");\n const rest = scheme === -1 ? lower : lower.slice(scheme + 3);\n const slash = rest.indexOf(\"/\");\n return slash === -1 ? rest : rest.slice(slash);\n}\n\nfunction headingLevelFromType(\n type: string,\n): { level: HeadingLevel; source: number } | null {\n if (!type.startsWith(\"standard-h\")) return null;\n const raw = type.slice(\"standard-h\".length);\n if (raw.length === 0) return null;\n for (let i = 0; i < raw.length; i++) {\n const code = raw.charCodeAt(i);\n if (code < 48 || code > 57) return null;\n }\n const source = Number(raw);\n if (!Number.isFinite(source) || source < 1) return null;\n const level = (source > 4 ? 4 : source) as HeadingLevel;\n return { level, source };\n}\n\nfunction leafStyles(\n node: EasyEmailProNode,\n map: MapContext,\n): { styles: BlockStyles } {\n return {\n styles: {\n padding: readPadding((key) => readAttr(node, key, map.resolve)),\n },\n };\n}\n\nfunction finish(\n block: Block,\n node: EasyEmailProNode,\n entry: ImportReportEntry,\n): Converted {\n const visibility = readVisibility(node);\n if (visibility) block.visibility = visibility;\n return { blocks: [block], entries: [entry] };\n}\n\nfunction readVisibility(node: EasyEmailProNode): BlockVisibility | undefined {\n if (node.visible === \"desktop\") return { desktop: true, mobile: false };\n if (node.visible === \"mobile\") return { desktop: false, mobile: true };\n return undefined;\n}\n\nfunction report(\n sourceTag: string,\n templaticalBlockType: string,\n status: ConversionStatus,\n note?: string,\n): ImportReportEntry {\n return {\n sourceTag,\n templaticalBlockType,\n status,\n ...(note ? { note } : {}),\n };\n}\n\nfunction empty(): Converted {\n return { blocks: [], entries: [] };\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction isBorderEnabled(value: unknown): boolean {\n return value === true || value === \"true\";\n}\n\nfunction buttonWidth(value: unknown): number | \"full\" | undefined {\n if (isUnset(value)) return undefined;\n if (typeof value === \"string\" && value.trim() === \"100%\") return \"full\";\n return parsePx(value);\n}\n\nfunction parseAlign(value: unknown): \"left\" | \"center\" | \"right\" | undefined {\n if (typeof value !== \"string\") return undefined;\n const align = value.trim().toLowerCase();\n if (align === \"left\" || align === \"center\" || align === \"right\") return align;\n return undefined;\n}\n\nfunction asLineStyle(\n value: unknown,\n): \"solid\" | \"dashed\" | \"dotted\" | undefined {\n if (typeof value !== \"string\") return undefined;\n const style = value.trim().toLowerCase();\n if (style === \"solid\" || style === \"dashed\" || style === \"dotted\") {\n return style;\n }\n return undefined;\n}\n\nfunction setString(value: unknown): string | undefined {\n if (typeof value !== \"string\" || isUnset(value)) return undefined;\n return value;\n}\n\n/** Button labels are plain text — strip tags the serialiser may have emitted. */\nfunction stripTags(html: string): string {\n if (html === \"\") return \"\";\n return decodeEntities(removeTags(html)).trim();\n}\n\nfunction removeTags(html: string): string {\n let out = \"\";\n let i = 0;\n while (i < html.length) {\n const open = html.indexOf(\"<\", i);\n if (open === -1) {\n out += html.slice(i);\n break;\n }\n out += html.slice(i, open);\n const tagEnd = findOpenTagEnd(html, open + 1);\n if (tagEnd === -1) {\n out += html.slice(open);\n break;\n }\n i = tagEnd + 1;\n }\n return out;\n}\n\nfunction decodeEntities(text: string): string {\n let out = \"\";\n let i = 0;\n while (i < text.length) {\n if (text[i] === \"&\") {\n const semi = text.indexOf(\";\", i + 1);\n if (semi !== -1 && semi - i <= 6) {\n const mapped = ENTITIES[text.slice(i, semi + 1)];\n if (mapped !== undefined) {\n out += mapped;\n i = semi + 1;\n continue;\n }\n }\n }\n out += text[i];\n i++;\n }\n return out;\n}\n","import type { ColumnLayout } from \"@templatical/types\";\n\nconst LAYOUT_SHAPES: Array<{ layout: ColumnLayout; percents: number[] }> = [\n { layout: \"1\", percents: [100] },\n { layout: \"2\", percents: [50, 50] },\n { layout: \"1-2\", percents: [33.33, 66.67] },\n { layout: \"2-1\", percents: [66.67, 33.33] },\n { layout: \"3\", percents: [33.33, 33.33, 33.34] },\n];\n\nconst WIDTH_TOLERANCE = 2;\n\nexport const COLUMN_COUNT: Record<ColumnLayout, number> = {\n \"1\": 1,\n \"2\": 2,\n \"3\": 3,\n \"2-1\": 2,\n \"1-2\": 2,\n};\n\nexport function columnPixels(\n layout: ColumnLayout,\n container: number,\n): number[] {\n switch (layout) {\n case \"2\":\n return [container / 2, container / 2];\n case \"3\":\n return [container / 3, container / 3, container / 3];\n case \"1-2\":\n return [container / 3, (container * 2) / 3];\n case \"2-1\":\n return [(container * 2) / 3, container / 3];\n default:\n return [container];\n }\n}\n\n/**\n * Resolve column widths to one of the five layouts `ColumnLayout` allows.\n *\n * `exact: false` means the caller reports `approximated` — Chamaileon permits\n * any column count at any width and this union permits five shapes, so this is\n * the importer's main irreducible loss.\n */\nexport function matchColumnLayout(percents: Array<number | null>): {\n layout: ColumnLayout;\n exact: boolean;\n} {\n const count = percents.length;\n if (count === 0) return { layout: \"1\", exact: true };\n\n // All-unset is \"distribute equally\", which is exactly what the n-column\n // layout means — not a missing value to approximate around.\n if (percents.every((p) => p === null)) {\n if (count === 1) return { layout: \"1\", exact: true };\n if (count === 2) return { layout: \"2\", exact: true };\n if (count === 3) return { layout: \"3\", exact: true };\n return { layout: \"3\", exact: false };\n }\n\n const known = percents.filter((p): p is number => p !== null);\n const unsetCount = count - known.length;\n const remainder =\n unsetCount === 0\n ? 0\n : Math.max(0, 100 - known.reduce((a, b) => a + b, 0)) / unsetCount;\n const resolved = percents.map((p) => p ?? remainder);\n\n const sameCount = LAYOUT_SHAPES.filter((s) => s.percents.length === count);\n\n for (const shape of sameCount) {\n if (\n shape.percents.every(\n (want, i) => Math.abs(want - resolved[i]) <= WIDTH_TOLERANCE,\n )\n ) {\n return { layout: shape.layout, exact: true };\n }\n }\n\n const candidates =\n sameCount.length > 0\n ? sameCount\n : LAYOUT_SHAPES.filter((s) => s.layout === \"3\");\n\n let best = candidates[0];\n let bestError = Infinity;\n for (const shape of candidates) {\n const error = shape.percents.reduce(\n (sum, want, i) => sum + Math.abs(want - (resolved[i] ?? 0)),\n 0,\n );\n if (error < bestError) {\n bestError = error;\n best = shape;\n }\n }\n\n return { layout: best.layout, exact: false };\n}\n\n/** Convert absolute px column widths to percents of `bodyWidth`. */\nexport function widthsToPercents(\n widthsPx: Array<number | undefined>,\n bodyWidth: number,\n): Array<number | null> {\n return widthsPx.map((w) => (w === undefined ? null : (w / bodyWidth) * 100));\n}\n","import { createImageBlock, createSectionBlock } from \"@templatical/types\";\nimport type {\n Block,\n BlockVisibility,\n SectionBlock,\n SectionWrapper,\n} from \"@templatical/types\";\nimport {\n parseColor,\n parsePercent,\n parsePx,\n readPadding,\n} from \"./attribute-parser\";\nimport { convertLeaf, type Converted, type MapContext } from \"./block-mapper\";\nimport { COLUMN_COUNT, columnPixels, matchColumnLayout } from \"./column-layout\";\nimport { isUnset, readAttr } from \"./normalize\";\nimport type {\n ConversionStatus,\n EasyEmailProNode,\n EasyEmailProTextNode,\n ImportReportEntry,\n} from \"./types\";\n\nexport function buildTopLevel(\n node: EasyEmailProNode,\n map: MapContext,\n): Converted {\n switch (node.type) {\n case \"standard-wrapper\":\n return buildWrapper(node, map);\n case \"standard-hero\":\n return buildHero(node, map);\n case \"standard-section\":\n return buildSection(node, map);\n default:\n return empty();\n }\n}\n\nfunction buildSection(node: EasyEmailProNode, map: MapContext): Converted {\n const plan = collectSlots(node);\n const rawWidths = plan.columns.map((column) =>\n readAttr(column, \"width\", map.resolve),\n );\n const { layout, exact } = matchColumnLayout(\n rawWidths.map((width) => parsePercent(width)),\n );\n const slots = COLUMN_COUNT[layout];\n const sectionWidth = insideWidth(map.contentWidth, node, map);\n const slotWidths =\n sectionWidth === undefined ? undefined : columnPixels(layout, sectionWidth);\n const leftover = convertFlow(\n plan.leftovers,\n withContentWidth(map, slotWidths?.[0]),\n );\n const children: Block[][] = Array.from({ length: slots }, () => []);\n if (leftover.blocks.length > 0) {\n children[0].push(...leftover.blocks);\n }\n\n const entries = leftover.entries;\n plan.columns.forEach((column, index) => {\n const slot = Math.min(index, slots - 1);\n const inner = convertFlow(\n column.children ?? [],\n withContentWidth(map, insideWidth(slotWidths?.[slot], column, map)),\n );\n children[slot].push(...inner.blocks);\n for (const entry of inner.entries) entries.push(entry);\n });\n\n const reasons: string[] = [];\n if (plan.mixed) {\n reasons.push(\n \"Section mixed columns and groups; flattened into the column flow.\",\n );\n }\n if (!exact) {\n if (plan.columns.length > slots) {\n reasons.push(\n `${plan.columns.length}-column layout has no exact Templatical equivalent; folded onto \"${layout}\".`,\n );\n } else {\n const shown = rawWidths\n .map((width) =>\n typeof width === \"string\" && !isUnset(width) ? width : \"auto\",\n )\n .join(\", \");\n reasons.push(\n `Column widths ${shown} have no exact Templatical layout; resolved to \"${layout}\".`,\n );\n }\n }\n\n const section = createSectionBlock();\n section.columns = layout;\n section.children = children;\n paintSection(section, node, map, {\n stackOnMobile: plan.grouped ? false : undefined,\n });\n entries.unshift(\n sectionEntry(\n node,\n reasons.length > 0 ? \"approximated\" : \"converted\",\n reasons.length > 0 ? reasons.join(\" \") : undefined,\n ),\n );\n return { blocks: [section], entries };\n}\n\nfunction buildHero(node: EasyEmailProNode, map: MapContext): Converted {\n const slot: Block[] = [];\n const entries: ImportReportEntry[] = [];\n const url = readAttr(node, \"background-url\", map.resolve);\n if (typeof url === \"string\" && !isUnset(url)) {\n slot.push(createImageBlock({ src: url }));\n entries.push({\n sourceTag: \"standard-hero\",\n templaticalBlockType: \"image\",\n status: \"approximated\",\n note: \"hero background-url stacked as a leading image (overlay is the loss)\",\n });\n }\n const inner = convertFlow(\n node.children ?? [],\n withContentWidth(map, insideWidth(map.contentWidth, node, map)),\n );\n slot.push(...inner.blocks);\n for (const entry of inner.entries) entries.push(entry);\n\n const section = createSectionBlock();\n section.columns = \"1\";\n section.children = [slot];\n paintSection(section, node, map);\n entries.unshift({\n sourceTag: \"standard-hero\",\n templaticalBlockType: \"section\",\n status: \"approximated\",\n note: \"standard-hero overlay is stacked as a 1-column section; background-url becomes a leading image.\",\n });\n return { blocks: [section], entries };\n}\n\nfunction buildWrapper(node: EasyEmailProNode, map: MapContext): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n const childMap = withContentWidth(\n map,\n insideWidth(map.contentWidth, node, map),\n );\n for (const child of elements(node)) {\n if (child.type === \"placeholder\") continue;\n const inner = buildTopLevel(child, childMap);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n }\n\n if (!blocks.some(isSection)) {\n return {\n blocks: [],\n entries: [\n {\n sourceTag: \"standard-wrapper\",\n templaticalBlockType: null,\n status: \"skipped\",\n note: \"An empty standard-wrapper produces nothing.\",\n },\n ],\n };\n }\n\n applyWrapper(node, blocks, entries, map);\n return { blocks, entries };\n}\n\n/**\n * Paint `node`'s `mj-wrapper` band onto every section in `blocks`. A section\n * holds one band, so a band around several sections is copied onto each and\n * their `converted` entries become `approximated`.\n */\nexport function applyWrapper(\n node: EasyEmailProNode,\n blocks: Block[],\n entries: ImportReportEntry[],\n map: MapContext,\n): void {\n const sections = blocks.filter(isSection);\n if (sections.length === 0) return;\n\n const wrapper = readWrapper(node, map);\n for (const section of sections) {\n section.wrapper = { ...wrapper };\n }\n if (sections.length > 1) {\n const note = `${node.type ?? \"standard-wrapper\"} holding ${sections.length} sections was applied to each of them — Templatical has no multi-section band.`;\n for (const entry of entries) {\n if (entry.templaticalBlockType !== \"section\") continue;\n if (entry.status !== \"converted\") continue;\n entry.status = \"approximated\";\n entry.note = note;\n }\n }\n}\n\n/** `width` less `node`'s own left and right padding; unknown stays unknown. */\nexport function insideWidth(\n width: number | undefined,\n node: EasyEmailProNode,\n map: MapContext,\n): number | undefined {\n if (width === undefined) return undefined;\n const padding = readPadding((key) => readAttr(node, key, map.resolve));\n return Math.max(0, width - padding.left - padding.right);\n}\n\nexport function withContentWidth(\n map: MapContext,\n contentWidth: number | undefined,\n): MapContext {\n return { ...map, contentWidth };\n}\n\nfunction isSection(block: Block): block is SectionBlock {\n return block.type === \"section\";\n}\n\n/**\n * Direct columns become slots. A sole `standard-group` of columns becomes\n * those slots with `stackOnMobile: false`. Mixed group + columns flatten\n * into the slot list and are approximated.\n */\nfunction collectSlots(section: EasyEmailProNode): {\n columns: EasyEmailProNode[];\n leftovers: EasyEmailProNode[];\n grouped: boolean;\n mixed: boolean;\n} {\n const kids = structuralChildren(section);\n\n if (kids.every((kid) => kid.type === \"standard-column\")) {\n return { columns: kids, leftovers: [], grouped: false, mixed: false };\n }\n\n if (kids.length === 1 && kids[0].type === \"standard-group\") {\n const groupKids = structuralChildren(kids[0]);\n if (groupKids.every((kid) => kid.type === \"standard-column\")) {\n return {\n columns: groupKids,\n leftovers: [],\n grouped: true,\n mixed: false,\n };\n }\n }\n\n const columns: EasyEmailProNode[] = [];\n const leftovers: EasyEmailProNode[] = [];\n let grouped = false;\n for (const kid of kids) {\n if (kid.type === \"standard-column\") {\n columns.push(kid);\n continue;\n }\n if (kid.type === \"standard-group\") {\n grouped = true;\n for (const inner of structuralChildren(kid)) {\n if (inner.type === \"standard-column\") columns.push(inner);\n else leftovers.push(inner);\n }\n continue;\n }\n leftovers.push(kid);\n }\n return { columns, leftovers, grouped, mixed: true };\n}\n\nfunction convertFlow(\n nodes: Array<EasyEmailProNode | EasyEmailProTextNode> | undefined,\n map: MapContext,\n): Converted {\n const blocks: Block[] = [];\n const entries: ImportReportEntry[] = [];\n for (const child of nodes ?? []) {\n if (!isElement(child)) continue;\n if (child.type === \"placeholder\") continue;\n if (child.type === \"standard-group\") {\n const innerColumns = structuralChildren(child).filter(\n (node) => node.type === \"standard-column\",\n );\n entries.push({\n sourceTag: \"standard-group\",\n templaticalBlockType: null,\n status: \"approximated\",\n note: `nested group flattened (${innerColumns.length} columns)`,\n });\n for (const column of innerColumns) {\n const inner = convertFlow(column.children, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n }\n continue;\n }\n if (child.type === \"standard-column\") {\n const inner = convertFlow(child.children, map);\n for (const block of inner.blocks) blocks.push(block);\n for (const entry of inner.entries) entries.push(entry);\n continue;\n }\n const converted = convertLeaf(child, map);\n for (const block of converted.blocks) blocks.push(block);\n for (const entry of converted.entries) entries.push(entry);\n }\n return { blocks, entries };\n}\n\n/** `createSectionBlock()` first, then mutate fill — never a partial `styles`. */\nfunction paintSection(\n section: SectionBlock,\n node: EasyEmailProNode,\n map: MapContext,\n extras?: { stackOnMobile?: boolean },\n): void {\n section.styles.padding = readPadding((key) =>\n readAttr(node, key, map.resolve),\n );\n const fill = sectionFill(node, map);\n if (fill) section.styles.backgroundColor = fill;\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n if (radius !== undefined && radius > 0) section.borderRadius = radius;\n if (extras?.stackOnMobile === false) section.stackOnMobile = false;\n const visibility = readVisibility(node);\n if (visibility) section.visibility = visibility;\n}\n\nfunction sectionFill(\n node: EasyEmailProNode,\n map: MapContext,\n): string | undefined {\n const own = parseColor(readAttr(node, \"background-color\", map.resolve));\n if (own) return own;\n return parseColor(\n readAttr(map.resolve.page, \"content-background-color\", map.resolve),\n );\n}\n\nfunction readWrapper(node: EasyEmailProNode, map: MapContext): SectionWrapper {\n const backgroundColor = parseColor(\n readAttr(node, \"background-color\", map.resolve),\n );\n const padding = readPadding((key) => readAttr(node, key, map.resolve));\n const radius = parsePx(readAttr(node, \"border-radius\", map.resolve));\n return {\n ...(backgroundColor ? { backgroundColor } : {}),\n padding,\n ...(radius !== undefined && radius > 0 ? { borderRadius: radius } : {}),\n };\n}\n\nfunction sectionEntry(\n node: EasyEmailProNode,\n status: ConversionStatus,\n note?: string,\n): ImportReportEntry {\n return {\n sourceTag: node.type ?? \"standard-section\",\n templaticalBlockType: \"section\",\n status,\n ...(note ? { note } : {}),\n };\n}\n\nfunction readVisibility(node: EasyEmailProNode): BlockVisibility | undefined {\n if (node.visible === \"desktop\") return { desktop: true, mobile: false };\n if (node.visible === \"mobile\") return { desktop: false, mobile: true };\n return undefined;\n}\n\nfunction structuralChildren(node: EasyEmailProNode): EasyEmailProNode[] {\n return elements(node).filter((child) => child.type !== \"placeholder\");\n}\n\nfunction elements(node: EasyEmailProNode): EasyEmailProNode[] {\n const out: EasyEmailProNode[] = [];\n for (const child of node.children ?? []) {\n if (isElement(child)) out.push(child);\n }\n return out;\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction empty(): Converted {\n return { blocks: [], entries: [] };\n}\n","import {\n createDefaultTemplateContent,\n createSectionBlock,\n DEFAULT_TEMPLATE_DEFAULTS,\n} from \"@templatical/types\";\nimport type {\n Block,\n TemplateContent,\n TemplateSettings,\n} from \"@templatical/types\";\nimport { parseColor, parsePx } from \"./attribute-parser\";\nimport { convertLeaf, isLeafType, type MapContext } from \"./block-mapper\";\nimport {\n contextFromPage,\n isUnset,\n readAttr,\n unwrapDocument,\n withWidgetInput,\n} from \"./normalize\";\nimport {\n applyWrapper,\n buildTopLevel,\n insideWidth,\n withContentWidth,\n} from \"./section-builder\";\nimport type {\n EasyEmailProDocument,\n EasyEmailProNode,\n EasyEmailProPage,\n EasyEmailProTextNode,\n ImportReportEntry,\n ImportResult,\n} from \"./types\";\n\nconst INVALID_MESSAGE =\n \"Invalid Easy Email Pro template: expected a page JSON object (EmailTemplate { subject, content } or the page element).\";\nconst OSS_MESSAGE =\n \"Invalid Easy Email Pro template: this looks like open-source Easy Email JSON, not Easy Email Pro (expected standard-* children).\";\nconst EMPTY_WARNING =\n \"No convertible content was found in the Easy Email Pro page. Check that page.children holds at least one standard-section.\";\nconst MOBILE_WARNING =\n \"mobileAttributes were dropped; Templatical has no per-viewport padding.\";\n\nfunction varWarning(resolved: number, unresolved: number): string {\n return `Resolved ${resolved} $var() values, ${unresolved} left unresolved.`;\n}\n\nconst OSS_CHILD_TYPES = new Set([\n \"section\",\n \"column\",\n \"text\",\n \"button\",\n \"image\",\n \"wrapper\",\n \"hero\",\n \"navbar\",\n \"social\",\n]);\n\nconst STRUCTURE_TYPES = new Set([\n \"standard-section\",\n \"standard-wrapper\",\n \"standard-hero\",\n]);\n\nconst WIDGET_TYPES = new Set([\"section_widget\", \"wrapper_widget\"]);\n\n/**\n * Header and footer bands. Easy Email Pro renders each as an `mj-wrapper`\n * carrying the node's own attributes around `data.content`, whose items are\n * page children.\n */\nconst PAGE_BAND_TYPES = new Set([\"page-header\", \"page-footer\"]);\n\n/**\n * `DEFAULT_TEMPLATE_DEFAULTS` is typed `Partial<TemplateSettings>` so a\n * consumer can override any subset, but its own literal always sets the six\n * required fields. Narrowing once here is what lets each fallback come out as\n * `number`/`string`/`boolean` rather than `| undefined`.\n */\nconst REQUIRED_DEFAULTS = DEFAULT_TEMPLATE_DEFAULTS as Required<\n Pick<\n TemplateSettings,\n | \"width\"\n | \"backgroundColor\"\n | \"textColor\"\n | \"linkUnderline\"\n | \"fontFamily\"\n | \"locale\"\n >\n>;\n\n/**\n * Convert an Easy Email Pro page (or EmailTemplate envelope) into a\n * Templatical template. Accepts a bare `page` element, an\n * `EmailTemplate { subject, content }` envelope, or a JSON string of either.\n */\nexport function convertEasyEmailProTemplate(\n doc: EasyEmailProDocument | EasyEmailProPage | string,\n): ImportResult {\n const root: unknown = typeof doc === \"string\" ? safeParse(doc) : doc;\n\n if (typeof root !== \"object\" || root === null || Array.isArray(root)) {\n throw new Error(INVALID_MESSAGE);\n }\n\n const unwrapped = unwrapDocument(root);\n if (unwrapped.page.type !== \"page\") {\n throw new Error(INVALID_MESSAGE);\n }\n const page = unwrapped.page as EasyEmailProPage;\n const subject = unwrapped.subject;\n assertNotOss(page);\n\n const warnings: string[] = [];\n const entries: ImportReportEntry[] = [];\n const blocks: Block[] = [];\n const resolve = contextFromPage(page);\n const data = isPlainObject(page.data) ? page.data : {};\n const settings = readSettings(page, data, resolve);\n const map: MapContext = { resolve, warnings, contentWidth: settings.width };\n\n const subjectRaw = typeof subject === \"string\" ? subject.trim() : \"\";\n if (subjectRaw) {\n warnings.push(\n `Document subject \"${subject}\" has no TemplateSettings field and was dropped.`,\n );\n }\n if (Array.isArray(data.fonts) && data.fonts.length > 0) {\n warnings.push(\n \"Dropped fonts[] — Templatical templates have no document-level font table.\",\n );\n }\n if (isNonEmptyHeadStyles(data.headStyles)) {\n warnings.push(\n \"Dropped headStyles — Templatical templates have no document-level head-style table.\",\n );\n }\n if (\n isPlainObject(data.classAttributes) &&\n Object.keys(data.classAttributes).length > 0\n ) {\n warnings.push(\n \"Dropped classAttributes — class-based MJML attribute cascade is not applied.\",\n );\n }\n\n for (const child of page.children ?? []) {\n if (!isElement(child)) continue;\n walkNode(child, map, entries, blocks);\n }\n\n if (hasMobileAttributes(page)) {\n warnings.push(MOBILE_WARNING);\n }\n\n if (resolve.stats.resolved > 0 || resolve.stats.unresolved > 0) {\n warnings.push(varWarning(resolve.stats.resolved, resolve.stats.unresolved));\n }\n\n if (blocks.length === 0) {\n warnings.push(EMPTY_WARNING);\n }\n\n const defaults = createDefaultTemplateContent();\n const content: TemplateContent = {\n ...defaults,\n blocks,\n settings: {\n ...defaults.settings,\n ...settings,\n },\n };\n\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 return { content, report: { entries, warnings, summary } };\n}\n\nfunction readSettings(\n page: EasyEmailProPage,\n data: Record<string, unknown>,\n resolve: MapContext[\"resolve\"],\n): TemplateSettings {\n const width =\n parsePx(readAttr(page, \"width\", resolve)) ?? REQUIRED_DEFAULTS.width;\n // Page `background-color` is the viewport band. `content-background-color`\n // is the section fill fallback (section-builder), never a settings field.\n const backgroundColor =\n parseColor(readAttr(page, \"background-color\", resolve)) ??\n REQUIRED_DEFAULTS.backgroundColor;\n const fontFamilyRaw = readAttr(page, \"font-family\", resolve);\n const fontFamily =\n typeof fontFamilyRaw === \"string\" && !isUnset(fontFamilyRaw)\n ? fontFamilyRaw\n : REQUIRED_DEFAULTS.fontFamily;\n const textColor =\n parseColor(readAttr(page, \"color\", resolve)) ?? REQUIRED_DEFAULTS.textColor;\n const linkColor = parseColor(readAttr(page, \"link-color\", resolve));\n const deco = readAttr(page, \"link-text-decoration\", resolve);\n const linkUnderline =\n deco === \"underline\"\n ? true\n : deco === \"none\"\n ? false\n : REQUIRED_DEFAULTS.linkUnderline;\n const preheaderRaw =\n typeof data.preheader === \"string\" ? data.preheader.trim() : \"\";\n\n return {\n width,\n backgroundColor,\n textColor,\n fontFamily,\n linkUnderline,\n locale: REQUIRED_DEFAULTS.locale,\n ...(linkColor ? { linkColor } : {}),\n ...(preheaderRaw ? { preheaderText: preheaderRaw } : {}),\n };\n}\n\nfunction walkNode(\n node: EasyEmailProNode,\n map: MapContext,\n entries: ImportReportEntry[],\n blocks: Block[],\n): void {\n if (node.type === \"placeholder\") return;\n\n const logic = node.logic;\n const logicOn = hasLogic(logic);\n if (logicOn && !hasTypedContent(node)) {\n entries.push({\n sourceTag: node.type ?? \"logic\",\n templaticalBlockType: null,\n status: \"skipped\",\n note: `Skipped empty logic (${logicExpression(logic)})`,\n });\n return;\n }\n\n if (typeof node.type === \"string\" && WIDGET_TYPES.has(node.type)) {\n const from = entries.length;\n const data = isPlainObject(node.data) ? node.data : {};\n const input = isPlainObject(data.input) ? data.input : undefined;\n const inner: MapContext = {\n ...map,\n resolve: withWidgetInput(map.resolve, input),\n };\n for (const child of node.children ?? []) {\n if (!isElement(child)) continue;\n walkNode(child, inner, entries, blocks);\n }\n markApproximated(\n entries,\n from,\n `${node.type} flattened; $var resolved from data.input`,\n {\n sourceTag: node.type,\n },\n );\n if (logicOn) {\n markApproximated(entries, from, `logic ${logicExpression(logic)}`);\n }\n return;\n }\n\n const from = entries.length;\n if (typeof node.type === \"string\" && STRUCTURE_TYPES.has(node.type)) {\n const converted = buildTopLevel(node, map);\n for (const block of converted.blocks) blocks.push(block);\n for (const entry of converted.entries) entries.push(entry);\n } else if (isPageBand(node)) {\n walkBand(node, map, entries, blocks);\n } else {\n const converted = convertLeaf(node, map);\n for (const entry of converted.entries) entries.push(entry);\n if (converted.blocks.length > 0) {\n if (isLeafType(node.type)) {\n blocks.push(wrapInSection(converted.blocks));\n } else {\n blocks.push(...converted.blocks);\n }\n }\n }\n\n if (logicOn) {\n markApproximated(entries, from, `logic ${logicExpression(logic)}`);\n }\n}\n\n/**\n * A band's content walks as page children, so sections, wrappers, widgets and\n * bare leaves all convert as they would at the top. The band's own paint then\n * lands on the resulting sections, the way a `standard-wrapper`'s does.\n */\nfunction walkBand(\n node: PageBand,\n map: MapContext,\n entries: ImportReportEntry[],\n blocks: Block[],\n): void {\n const inner = withContentWidth(map, insideWidth(map.contentWidth, node, map));\n const bandBlocks: Block[] = [];\n const bandEntries: ImportReportEntry[] = [];\n for (const child of bandContent(node)) {\n walkNode(child, inner, bandEntries, bandBlocks);\n }\n if (bandBlocks.length === 0 && bandEntries.length === 0) {\n entries.push({\n sourceTag: node.type,\n templaticalBlockType: null,\n status: \"skipped\",\n note: `An empty ${node.type} produces nothing.`,\n });\n return;\n }\n applyWrapper(node, bandBlocks, bandEntries, map);\n for (const block of bandBlocks) blocks.push(block);\n for (const entry of bandEntries) entries.push(entry);\n}\n\ntype PageBand = EasyEmailProNode & { type: string };\n\nfunction isPageBand(node: EasyEmailProNode): node is PageBand {\n return typeof node.type === \"string\" && PAGE_BAND_TYPES.has(node.type);\n}\n\nfunction bandContent(node: EasyEmailProNode): EasyEmailProNode[] {\n const content = isPlainObject(node.data) ? node.data.content : undefined;\n if (!Array.isArray(content)) return [];\n return content.filter(\n (child): child is EasyEmailProNode =>\n isPlainObject(child) && isElement(child as EasyEmailProNode),\n );\n}\n\nfunction wrapInSection(blocks: Block[]): Block {\n const section = createSectionBlock();\n section.columns = \"1\";\n section.children = [blocks];\n return section;\n}\n\nfunction markApproximated(\n entries: ImportReportEntry[],\n from: number,\n extra: string,\n rewrite?: { sourceTag: string },\n): void {\n for (let i = from; i < entries.length; i++) {\n const entry = entries[i];\n if (rewrite) entry.sourceTag = rewrite.sourceTag;\n if (entry.status === \"converted\") entry.status = \"approximated\";\n entry.note = entry.note ? `${entry.note} ${extra}` : extra;\n }\n}\n\nfunction hasLogic(\n logic: EasyEmailProNode[\"logic\"],\n): logic is NonNullable<EasyEmailProNode[\"logic\"]> {\n if (logic == null || typeof logic !== \"object\") return false;\n return logic.condition != null || logic.iteration != null;\n}\n\nfunction logicExpression(\n logic: NonNullable<EasyEmailProNode[\"logic\"]>,\n): string {\n const parts: string[] = [];\n if (logic.condition != null) {\n parts.push(\n typeof logic.condition === \"string\"\n ? logic.condition\n : JSON.stringify(logic.condition),\n );\n }\n if (logic.iteration != null) {\n const iteration = logic.iteration;\n if (typeof iteration === \"string\") {\n parts.push(iteration);\n } else if (\n isPlainObject(iteration) &&\n typeof iteration.dataSource === \"string\"\n ) {\n parts.push(iteration.dataSource);\n } else {\n parts.push(JSON.stringify(iteration));\n }\n }\n return parts.join(\" \");\n}\n\nfunction hasTypedContent(node: EasyEmailProNode): boolean {\n const kids = isPageBand(node) ? bandContent(node) : (node.children ?? []);\n for (const child of kids) {\n if (!isElement(child)) continue;\n if (child.type === \"placeholder\") continue;\n return true;\n }\n return false;\n}\n\nfunction hasMobileAttributes(node: unknown): boolean {\n if (!isPlainObject(node)) return false;\n const mobile = node.mobileAttributes;\n if (isPlainObject(mobile) && Object.keys(mobile).length > 0) return true;\n const band = isPageBand(node as EasyEmailProNode)\n ? bandContent(node as EasyEmailProNode)\n : [];\n const children = Array.isArray(node.children) ? node.children : [];\n for (const child of [...children, ...band]) {\n if (hasMobileAttributes(child)) return true;\n }\n return false;\n}\n\nfunction assertNotOss(page: EasyEmailProPage): void {\n const children = page.children ?? [];\n for (const child of children) {\n if (!child || typeof child !== \"object\") continue;\n const type = (child as EasyEmailProNode).type;\n if (typeof type !== \"string\") continue;\n if (OSS_CHILD_TYPES.has(type)) {\n throw new Error(OSS_MESSAGE);\n }\n break;\n }\n}\n\nfunction isElement(\n node: EasyEmailProNode | EasyEmailProTextNode,\n): node is EasyEmailProNode {\n return typeof node.type === \"string\" && node.type !== \"\";\n}\n\nfunction isNonEmptyHeadStyles(value: unknown): boolean {\n if (Array.isArray(value)) return value.length > 0;\n return isPlainObject(value) && Object.keys(value).length > 0;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction safeParse(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n throw new Error(INVALID_MESSAGE);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@templatical/import-easy-email-pro",
3
3
  "description": "Convert Easy Email Pro email templates to Templatical format",
4
- "version": "0.42.0",
4
+ "version": "0.43.0",
5
5
  "bugs": "https://github.com/templatical/sdk/issues",
6
6
  "dependencies": {
7
- "@templatical/types": "0.42.0"
7
+ "@templatical/types": "0.43.0"
8
8
  },
9
9
  "devDependencies": {
10
10
  "@types/node": "^25.9.8",
11
11
  "typescript": "^6.0.3",
12
- "vitest": "^5.0.1"
12
+ "vitest": "^5.0.2"
13
13
  },
14
14
  "exports": {
15
15
  ".": {