payload-plugin-newsletter 0.16.10 → 0.17.1

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.
@@ -950,62 +950,73 @@ async function convertToEmailSafeHtml(editorState, options) {
950
950
  if (!editorState) {
951
951
  return "";
952
952
  }
953
- const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl);
953
+ const rawHtml = await lexicalToEmailHtml(editorState, options?.mediaUrl, options?.customBlockConverter);
954
954
  const sanitizedHtml = DOMPurify.sanitize(rawHtml, EMAIL_SAFE_CONFIG);
955
955
  if (options?.wrapInTemplate) {
956
956
  return wrapInEmailTemplate(sanitizedHtml, options.preheader);
957
957
  }
958
958
  return sanitizedHtml;
959
959
  }
960
- async function lexicalToEmailHtml(editorState, mediaUrl) {
960
+ async function lexicalToEmailHtml(editorState, mediaUrl, customBlockConverter) {
961
961
  const { root } = editorState;
962
962
  if (!root || !root.children) {
963
963
  return "";
964
964
  }
965
- const html = root.children.map((node) => convertNode(node, mediaUrl)).join("");
966
- return html;
965
+ const htmlParts = await Promise.all(
966
+ root.children.map((node) => convertNode(node, mediaUrl, customBlockConverter))
967
+ );
968
+ return htmlParts.join("");
967
969
  }
968
- function convertNode(node, mediaUrl) {
970
+ async function convertNode(node, mediaUrl, customBlockConverter) {
969
971
  switch (node.type) {
970
972
  case "paragraph":
971
- return convertParagraph(node, mediaUrl);
973
+ return convertParagraph(node, mediaUrl, customBlockConverter);
972
974
  case "heading":
973
- return convertHeading(node, mediaUrl);
975
+ return convertHeading(node, mediaUrl, customBlockConverter);
974
976
  case "list":
975
- return convertList(node, mediaUrl);
977
+ return convertList(node, mediaUrl, customBlockConverter);
976
978
  case "listitem":
977
- return convertListItem(node, mediaUrl);
979
+ return convertListItem(node, mediaUrl, customBlockConverter);
978
980
  case "blockquote":
979
- return convertBlockquote(node, mediaUrl);
981
+ return convertBlockquote(node, mediaUrl, customBlockConverter);
980
982
  case "text":
981
983
  return convertText(node);
982
984
  case "link":
983
- return convertLink(node, mediaUrl);
985
+ return convertLink(node, mediaUrl, customBlockConverter);
984
986
  case "linebreak":
985
987
  return "<br>";
986
988
  case "upload":
987
989
  return convertUpload(node, mediaUrl);
988
990
  case "block":
989
- return convertBlock(node, mediaUrl);
991
+ return await convertBlock(node, mediaUrl, customBlockConverter);
990
992
  default:
991
993
  if (node.children) {
992
- return node.children.map((child) => convertNode(child, mediaUrl)).join("");
994
+ const childParts = await Promise.all(
995
+ node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
996
+ );
997
+ return childParts.join("");
993
998
  }
994
999
  return "";
995
1000
  }
996
1001
  }
997
- function convertParagraph(node, mediaUrl) {
1002
+ async function convertParagraph(node, mediaUrl, customBlockConverter) {
998
1003
  const align = getAlignment(node.format);
999
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1004
+ const childParts = await Promise.all(
1005
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1006
+ );
1007
+ const children = childParts.join("");
1000
1008
  if (!children.trim()) {
1001
1009
  return '<p style="margin: 0 0 16px 0; min-height: 1em;">&nbsp;</p>';
1002
1010
  }
1003
1011
  return `<p style="margin: 0 0 16px 0; text-align: ${align};">${children}</p>`;
1004
1012
  }
1005
- function convertHeading(node, mediaUrl) {
1013
+ async function convertHeading(node, mediaUrl, customBlockConverter) {
1006
1014
  const tag = node.tag || "h1";
1007
1015
  const align = getAlignment(node.format);
1008
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1016
+ const childParts = await Promise.all(
1017
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1018
+ );
1019
+ const children = childParts.join("");
1009
1020
  const styles2 = {
1010
1021
  h1: "font-size: 32px; font-weight: 700; margin: 0 0 24px 0; line-height: 1.2;",
1011
1022
  h2: "font-size: 24px; font-weight: 600; margin: 0 0 16px 0; line-height: 1.3;",
@@ -1014,18 +1025,27 @@ function convertHeading(node, mediaUrl) {
1014
1025
  const style = `${styles2[tag] || styles2.h3} text-align: ${align};`;
1015
1026
  return `<${tag} style="${style}">${children}</${tag}>`;
1016
1027
  }
1017
- function convertList(node, mediaUrl) {
1028
+ async function convertList(node, mediaUrl, customBlockConverter) {
1018
1029
  const tag = node.listType === "number" ? "ol" : "ul";
1019
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1030
+ const childParts = await Promise.all(
1031
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1032
+ );
1033
+ const children = childParts.join("");
1020
1034
  const style = tag === "ul" ? "margin: 0 0 16px 0; padding-left: 24px; list-style-type: disc;" : "margin: 0 0 16px 0; padding-left: 24px; list-style-type: decimal;";
1021
1035
  return `<${tag} style="${style}">${children}</${tag}>`;
1022
1036
  }
1023
- function convertListItem(node, mediaUrl) {
1024
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1037
+ async function convertListItem(node, mediaUrl, customBlockConverter) {
1038
+ const childParts = await Promise.all(
1039
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1040
+ );
1041
+ const children = childParts.join("");
1025
1042
  return `<li style="margin: 0 0 8px 0;">${children}</li>`;
1026
1043
  }
1027
- function convertBlockquote(node, mediaUrl) {
1028
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1044
+ async function convertBlockquote(node, mediaUrl, customBlockConverter) {
1045
+ const childParts = await Promise.all(
1046
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1047
+ );
1048
+ const children = childParts.join("");
1029
1049
  const style = "margin: 0 0 16px 0; padding-left: 16px; border-left: 4px solid #e5e7eb; color: #6b7280;";
1030
1050
  return `<blockquote style="${style}">${children}</blockquote>`;
1031
1051
  }
@@ -1045,8 +1065,11 @@ function convertText(node) {
1045
1065
  }
1046
1066
  return text;
1047
1067
  }
1048
- function convertLink(node, mediaUrl) {
1049
- const children = node.children?.map((child) => convertNode(child, mediaUrl)).join("") || "";
1068
+ async function convertLink(node, mediaUrl, customBlockConverter) {
1069
+ const childParts = await Promise.all(
1070
+ (node.children || []).map((child) => convertNode(child, mediaUrl, customBlockConverter))
1071
+ );
1072
+ const children = childParts.join("");
1050
1073
  const url = node.fields?.url || "#";
1051
1074
  const newTab = node.fields?.newTab ?? false;
1052
1075
  const targetAttr = newTab ? ' target="_blank"' : "";
@@ -1077,8 +1100,18 @@ function convertUpload(node, mediaUrl) {
1077
1100
  }
1078
1101
  return `<div style="margin: 0 0 16px 0; text-align: center;">${imgHtml}</div>`;
1079
1102
  }
1080
- function convertBlock(node, mediaUrl) {
1081
- const blockType = node.fields?.blockName;
1103
+ async function convertBlock(node, mediaUrl, customBlockConverter) {
1104
+ const blockType = node.fields?.blockName || node.blockName;
1105
+ if (customBlockConverter) {
1106
+ try {
1107
+ const customHtml = await customBlockConverter(node, mediaUrl);
1108
+ if (customHtml) {
1109
+ return customHtml;
1110
+ }
1111
+ } catch (error) {
1112
+ console.error(`Custom block converter error for ${blockType}:`, error);
1113
+ }
1114
+ }
1082
1115
  switch (blockType) {
1083
1116
  case "button":
1084
1117
  return convertButtonBlock(node.fields);
@@ -1086,7 +1119,10 @@ function convertBlock(node, mediaUrl) {
1086
1119
  return convertDividerBlock(node.fields);
1087
1120
  default:
1088
1121
  if (node.children) {
1089
- return node.children.map((child) => convertNode(child, mediaUrl)).join("");
1122
+ const childParts = await Promise.all(
1123
+ node.children.map((child) => convertNode(child, mediaUrl, customBlockConverter))
1124
+ );
1125
+ return childParts.join("");
1090
1126
  }
1091
1127
  return "";
1092
1128
  }
@@ -1460,7 +1496,9 @@ var createBroadcastsCollection = (pluginConfig) => {
1460
1496
  const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1461
1497
  const provider = new BroadcastApiProvider2(providerConfig);
1462
1498
  req.payload.logger.info("Converting content to HTML...");
1463
- const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
1499
+ const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content, {
1500
+ customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
1501
+ });
1464
1502
  if (!htmlContent || htmlContent.trim() === "") {
1465
1503
  req.payload.logger.info("Skipping provider sync - content is empty after conversion");
1466
1504
  return doc;
@@ -1557,7 +1595,9 @@ var createBroadcastsCollection = (pluginConfig) => {
1557
1595
  return doc;
1558
1596
  }
1559
1597
  req.payload.logger.info("Creating broadcast in provider (deferred from initial create)...");
1560
- const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
1598
+ const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content, {
1599
+ customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
1600
+ });
1561
1601
  if (!htmlContent || htmlContent.trim() === "") {
1562
1602
  req.payload.logger.info("Skipping provider sync - content is empty after conversion");
1563
1603
  return doc;
@@ -1616,7 +1656,9 @@ var createBroadcastsCollection = (pluginConfig) => {
1616
1656
  updates.preheader = doc.contentSection?.preheader;
1617
1657
  }
1618
1658
  if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
1619
- updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
1659
+ updates.content = await convertToEmailSafeHtml(doc.contentSection?.content, {
1660
+ customBlockConverter: pluginConfig.customizations?.broadcasts?.customBlockConverter
1661
+ });
1620
1662
  }
1621
1663
  if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
1622
1664
  updates.trackOpens = doc.settings.trackOpens;