@templatical/import-unlayer 0.9.1 → 0.10.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/converter.ts","../src/block-mapper.ts","../src/style-parser.ts"],"sourcesContent":["import {\n createSectionBlock,\n createDefaultTemplateContent,\n} from \"@templatical/types\";\nimport type { Block, ColumnLayout, TemplateContent } from \"@templatical/types\";\nimport type {\n UnlayerTemplate,\n UnlayerRow,\n UnlayerColumn,\n ImportResult,\n ImportReport,\n ImportReportEntry,\n} from \"./types\";\nimport { convertContent } from \"./block-mapper\";\nimport {\n parsePxValue,\n parseColor,\n parseFontFamily,\n parsePaddingShorthand,\n} from \"./style-parser\";\n\nfunction resolveColumnLayout(\n cells: number[],\n warnings: string[],\n): ColumnLayout {\n if (cells.length <= 1) return \"1\";\n if (cells.length === 3) return \"3\";\n\n if (cells.length === 2) {\n const left = cells[0] ?? 1;\n const right = cells[1] ?? 1;\n const total = left + right;\n const ratio = left / total;\n\n if (ratio > 0.58) return \"2-1\";\n if (ratio < 0.42) return \"1-2\";\n return \"2\";\n }\n\n warnings.push(\n `Row with ${cells.length} columns was flattened to a single column. Unlayer supports arbitrary columns, but Templatical supports up to 3.`,\n );\n return \"1\";\n}\n\n/**\n * Converts all contents in a column to Templatical blocks.\n */\nfunction convertColumnContents(\n column: UnlayerColumn,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const blocks: Block[] = [];\n\n for (const content of column.contents ?? []) {\n const { block, entry } = convertContent(content, warnings);\n blocks.push(block);\n entries.push(entry);\n }\n\n return blocks;\n}\n\n/**\n * Processes a single Unlayer row into one or more Templatical blocks.\n */\nfunction processRow(\n row: UnlayerRow,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const columns = row.columns;\n if (!columns || columns.length === 0) return [];\n\n const cells = row.cells ?? columns.map(() => 1);\n const layout = resolveColumnLayout(cells, warnings);\n\n let children: Block[][];\n if (layout === \"1\") {\n const merged: Block[] = [];\n for (const column of columns) {\n merged.push(...convertColumnContents(column, entries, warnings));\n }\n children = [merged];\n } else {\n children = columns.map((col) =>\n convertColumnContents(col, entries, warnings),\n );\n }\n\n const rowBg = parseColor(row.values?.backgroundColor);\n const padding = parsePaddingShorthand(row.values?.padding);\n\n const section = createSectionBlock({\n columns: layout,\n children,\n styles: {\n padding,\n margin: { top: 0, right: 0, bottom: 0, left: 0 },\n ...(rowBg ? { backgroundColor: rowBg } : {}),\n },\n });\n\n return [section];\n}\n\n/**\n * Extracts template-level settings from the Unlayer body values.\n */\nfunction extractSettings(\n template: UnlayerTemplate,\n): TemplateContent[\"settings\"] {\n const values = template.body?.values ?? {};\n\n const width = parsePxValue(values.contentWidth);\n const bgColor = parseColor(values.backgroundColor) || \"#ffffff\";\n const fontFamily = parseFontFamily(values.fontFamily) || \"Arial\";\n\n return {\n width: width || 600,\n backgroundColor: bgColor,\n fontFamily,\n locale: \"en\",\n };\n}\n\n/**\n * Converts an Unlayer design JSON to Templatical TemplateContent.\n *\n * @param template - The parsed Unlayer JSON object (the result of `editor.saveDesign(...)`)\n * @returns An ImportResult with the converted content and a detailed report\n *\n * @example\n * ```ts\n * import { convertUnlayerTemplate } from '@templatical/import-unlayer';\n *\n * const unlayerJson = JSON.parse(fileContent);\n * const { content, report } = convertUnlayerTemplate(unlayerJson);\n *\n * const editor = init({ container: '#editor', content });\n *\n * console.log(report.summary);\n * console.log(report.warnings);\n * ```\n */\nexport function convertUnlayerTemplate(\n template: UnlayerTemplate,\n): ImportResult {\n if (!template?.body?.rows) {\n throw new Error(\n \"Invalid Unlayer template: missing body.rows. Ensure you are passing a valid Unlayer JSON design (the output of editor.saveDesign).\",\n );\n }\n\n const entries: ImportReportEntry[] = [];\n const warnings: string[] = [];\n const blocks: Block[] = [];\n\n const headers = template.body.headers ?? [];\n const footers = template.body.footers ?? [];\n\n if (headers.length > 0) {\n warnings.push(\n `${headers.length} Unlayer header row(s) were imported as regular rows at the top of the template.`,\n );\n for (const row of headers) {\n blocks.push(...processRow(row, entries, warnings));\n }\n }\n\n for (const row of template.body.rows) {\n blocks.push(...processRow(row, entries, warnings));\n }\n\n if (footers.length > 0) {\n warnings.push(\n `${footers.length} Unlayer footer row(s) were imported as regular rows at the bottom of the template.`,\n );\n for (const row of footers) {\n blocks.push(...processRow(row, entries, warnings));\n }\n }\n\n const content: TemplateContent = {\n ...createDefaultTemplateContent(),\n blocks,\n settings: extractSettings(template),\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 const report: ImportReport = { entries, warnings, summary };\n\n return { content, report };\n}\n","import {\n createTitleBlock,\n createParagraphBlock,\n createImageBlock,\n createButtonBlock,\n createDividerBlock,\n createSpacerBlock,\n createHtmlBlock,\n createSocialIconsBlock,\n createMenuBlock,\n createVideoBlock,\n generateId,\n} from \"@templatical/types\";\nimport type {\n Block,\n HeadingLevel,\n SocialPlatform,\n SocialIcon,\n MenuItemData,\n SpacingValue,\n} from \"@templatical/types\";\nimport type {\n UnlayerContent,\n UnlayerContentValues,\n ImportReportEntry,\n} from \"./types\";\nimport {\n parsePxValue,\n parseColor,\n parseFontFamily,\n parsePaddingShorthand,\n parseBorderObject,\n parseWidthPercent,\n} from \"./style-parser\";\n\nconst SOCIAL_PLATFORM_MAP: Record<string, SocialPlatform> = {\n facebook: \"facebook\",\n twitter: \"twitter\",\n x: \"twitter\",\n instagram: \"instagram\",\n linkedin: \"linkedin\",\n youtube: \"youtube\",\n tiktok: \"tiktok\",\n pinterest: \"pinterest\",\n email: \"email\",\n mail: \"email\",\n whatsapp: \"whatsapp\",\n telegram: \"telegram\",\n discord: \"discord\",\n snapchat: \"snapchat\",\n reddit: \"reddit\",\n github: \"github\",\n dribbble: \"dribbble\",\n behance: \"behance\",\n};\n\ntype Align = \"left\" | \"center\" | \"right\";\ntype LineStyle = \"solid\" | \"dashed\" | \"dotted\";\n\nfunction toAlign(value: string | undefined, fallback: Align = \"left\"): Align {\n if (value === \"left\" || value === \"center\" || value === \"right\") return value;\n return fallback;\n}\n\n/**\n * Strip every `<…>` from a button label and reject any stray `<` or `>`\n * left behind (e.g. a truncated `<script`). The original\n * `value.replace(/<[^>]*>/g, \"\")` was both polynomial-ReDoS over\n * `<<<<…` inputs and an incomplete sanitizer — an unterminated `<script`\n * would survive the strip. Downstream HTML-escapes the label at render\n * time, but stripping here keeps the imported JSON clean.\n */\nfunction stripTagsPlain(text: string): string {\n let out = \"\";\n let i = 0;\n while (i < text.length) {\n if (text[i] === \"<\") {\n const close = text.indexOf(\">\", i + 1);\n if (close === -1) {\n // Unterminated tag — discard the rest.\n break;\n }\n i = close + 1;\n continue;\n }\n if (text[i] === \">\") {\n i++;\n continue;\n }\n out += text[i];\n i++;\n }\n return out;\n}\n\nfunction toLineStyle(\n value: string | undefined,\n fallback: LineStyle = \"solid\",\n): LineStyle {\n if (value === \"solid\" || value === \"dashed\" || value === \"dotted\")\n return value;\n return fallback;\n}\n\nfunction defaultMargin(): SpacingValue {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction makeStyles(values: UnlayerContentValues): Block[\"styles\"] {\n const padding = parsePaddingShorthand(values.containerPadding);\n return {\n padding,\n margin: defaultMargin(),\n };\n}\n\n/**\n * Apply Unlayer text-level styles as TipTap-compatible inline markup.\n * Mirrors the BeeFree importer's helper but reads from Unlayer's flat\n * values shape rather than a CSS style record.\n */\nfunction inlineStylesToHtml(\n html: string,\n values: UnlayerContentValues,\n): string {\n const spanParts: string[] = [];\n const fontSize = parsePxValue(values.fontSize);\n if (fontSize && fontSize !== 16) spanParts.push(`font-size: ${fontSize}px`);\n const color = parseColor(values.color);\n if (color && color !== \"#1a1a1a\") spanParts.push(`color: ${color}`);\n const fontWeight = values.fontWeight;\n if (\n fontWeight !== undefined &&\n fontWeight !== null &&\n String(fontWeight) !== \"normal\" &&\n String(fontWeight) !== \"400\"\n ) {\n spanParts.push(`font-weight: ${fontWeight}`);\n }\n const fontFamily = parseFontFamily(values.fontFamily);\n if (fontFamily) spanParts.push(`font-family: ${fontFamily}`);\n\n const textAlign = values.textAlign;\n const pStyle =\n textAlign && textAlign !== \"left\" ? `text-align: ${textAlign}` : \"\";\n\n if (!pStyle && spanParts.length === 0) return html;\n\n const spanStyle = spanParts.join(\"; \");\n let result = html;\n\n if (pStyle) {\n result = result\n .replace(/<p style=\"([^\"]*)\">/g, `<p style=\"$1; ${pStyle}\">`)\n .replaceAll(\"<p>\", `<p style=\"${pStyle}\">`);\n }\n\n if (spanStyle) {\n result = wrapParagraphInner(result, spanStyle);\n }\n\n return result;\n}\n\n/**\n * Wrap the inner content of every `<p …>…</p>` with `<span style=\"…\">…</span>`.\n *\n * Hand-rolled linear scanner instead of `/<p([^>]*)>([\\s\\S]*?)<\\/p>/g` because\n * the regex is polynomial-ReDoS: the engine retries `[\\s\\S]*?<\\/p>` at every\n * `<p` start, so inputs like `<p>a<p>a<p>a…` (no `</p>` ever) cost O(n²).\n * The scanner advances `i` monotonically via `indexOf`, keeping the work\n * strictly O(n).\n */\nfunction wrapParagraphInner(html: string, spanStyle: string): string {\n let out = \"\";\n let i = 0;\n while (i < html.length) {\n const open = html.indexOf(\"<p\", i);\n if (open === -1) {\n out += html.substring(i);\n break;\n }\n const afterTagName = html[open + 2];\n if (\n afterTagName !== \">\" &&\n afterTagName !== \" \" &&\n afterTagName !== \"\\t\" &&\n afterTagName !== \"\\n\" &&\n afterTagName !== \"\\r\" &&\n afterTagName !== \"/\"\n ) {\n out += html.substring(i, open + 2);\n i = open + 2;\n continue;\n }\n const openEnd = html.indexOf(\">\", open + 2);\n if (openEnd === -1) {\n out += html.substring(i);\n break;\n }\n const closeStart = html.indexOf(\"</p>\", openEnd + 1);\n if (closeStart === -1) {\n out += html.substring(i);\n break;\n }\n const inner = html.substring(openEnd + 1, closeStart);\n out += html.substring(i, openEnd + 1);\n out += `<span style=\"${spanStyle}\">${inner}</span></p>`;\n i = closeStart + 4;\n }\n return out;\n}\n\nfunction ensureParagraphWrapped(html: string): string {\n if (!html) return \"<p></p>\";\n if (/<p[\\s>]/i.test(html)) return html;\n return `<p>${html}</p>`;\n}\n\nfunction convertText(values: UnlayerContentValues): Block {\n const html = ensureParagraphWrapped(values.text ?? \"\");\n\n return createParagraphBlock({\n content: inlineStylesToHtml(html, values),\n styles: makeStyles(values),\n });\n}\n\nfunction parseHeadingLevel(tag: string | undefined): HeadingLevel {\n if (!tag) return 2;\n const match = tag.match(/^h(\\d)$/i);\n if (match) {\n const num = Number(match[1]);\n if (num >= 1 && num <= 4) return num as HeadingLevel;\n }\n return 2;\n}\n\nfunction convertHeading(values: UnlayerContentValues): Block {\n const text = values.text ?? \"\";\n const stripped = text.replace(/^<h\\d[^>]*>|<\\/h\\d>$/gi, \"\");\n const content = stripped ? `<p>${stripped}</p>` : \"<p></p>\";\n\n return createTitleBlock({\n content,\n level: parseHeadingLevel(values.headingType),\n color: parseColor(values.color) || \"#1a1a1a\",\n textAlign: toAlign(values.textAlign),\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n styles: makeStyles(values),\n });\n}\n\nfunction convertImage(values: UnlayerContentValues): Block {\n const src = values.src;\n const action = values.action?.values;\n\n return createImageBlock({\n src: src?.url || \"\",\n alt: values.altText || \"\",\n width: src?.width ? Math.round(src.width) : 600,\n align: toAlign(values.textAlign, \"center\"),\n linkUrl: action?.href || undefined,\n linkOpenInNewTab: action?.target === \"_blank\" || undefined,\n styles: makeStyles(values),\n });\n}\n\nfunction convertButton(values: UnlayerContentValues): Block {\n const colors = values.buttonColors ?? {};\n const padding = values.padding\n ? parsePaddingShorthand(values.padding)\n : { top: 12, right: 24, bottom: 12, left: 24 };\n const label = stripTagsPlain(values.text ?? \"Button\");\n const linkValues = values.href?.values;\n\n return createButtonBlock({\n text: label,\n url: linkValues?.href || \"#\",\n openInNewTab: linkValues?.target === \"_blank\" || undefined,\n backgroundColor: parseColor(colors.backgroundColor) || \"#4f46e5\",\n textColor: parseColor(colors.color) || \"#ffffff\",\n borderRadius: parsePxValue(values.borderRadius),\n fontSize: parsePxValue(values.fontSize) || 16,\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n buttonPadding: padding,\n styles: makeStyles(values),\n });\n}\n\nfunction convertDivider(values: UnlayerContentValues): Block {\n const border = parseBorderObject(values.border);\n\n return createDividerBlock({\n lineStyle: toLineStyle(border.style),\n color: border.color,\n thickness: border.width || 1,\n width: parseWidthPercent(values.width),\n styles: makeStyles(values),\n });\n}\n\nfunction convertSpacer(values: UnlayerContentValues): Block {\n const padding = parsePaddingShorthand(values.containerPadding);\n const height =\n parsePxValue((values as { height?: string | number }).height) ||\n padding.top + padding.bottom ||\n 24;\n\n return createSpacerBlock({\n height,\n styles: makeStyles(values),\n });\n}\n\nfunction convertHtml(values: UnlayerContentValues): Block {\n return createHtmlBlock({\n content: values.html ?? \"\",\n styles: makeStyles(values),\n });\n}\n\nfunction convertSocial(\n values: UnlayerContentValues,\n warnings: string[],\n): Block {\n const iconList = values.icons?.icons ?? [];\n const icons: SocialIcon[] = [];\n\n for (const unlayerIcon of iconList) {\n const id = (unlayerIcon.name ?? \"\").toLowerCase();\n const platform = SOCIAL_PLATFORM_MAP[id];\n\n if (!platform) {\n warnings.push(\n `Unrecognized social icon \"${unlayerIcon.name || id}\" was skipped.`,\n );\n continue;\n }\n\n icons.push({\n id: generateId(),\n platform,\n url: unlayerIcon.url || \"#\",\n });\n }\n\n return createSocialIconsBlock({\n icons,\n align: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertVideo(values: UnlayerContentValues): Block {\n return createVideoBlock({\n url: values.videoUrl || \"\",\n thumbnailUrl: values.thumbnailUrl || \"\",\n alt: values.altText || \"\",\n width: 600,\n align: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertMenu(values: UnlayerContentValues): Block {\n const menu = values.menu;\n const items: MenuItemData[] = (menu?.items ?? []).map((item) => ({\n id: generateId(),\n text: item.text || \"\",\n url: item.link?.values?.href || \"#\",\n openInNewTab: item.link?.values?.target === \"_blank\",\n bold: false,\n underline: false,\n }));\n\n return createMenuBlock({\n items,\n separator: values.separator || \"|\",\n separatorColor: \"#999999\",\n fontSize: parsePxValue(values.fontSize) || 14,\n color: parseColor(values.color) || \"#1a1a1a\",\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n textAlign: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertHtmlFallback(content: UnlayerContent, comment: string): Block {\n const safe = comment.replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n return createHtmlBlock({\n content: `<div style=\"padding:12px;border:1px dashed #d1d5db;border-radius:6px;background:#fafafa;color:#6b7280;font-family:sans-serif;font-size:13px;\">${safe}</div>`,\n styles: makeStyles(content.values),\n });\n}\n\n/**\n * Converts a single Unlayer content node to a Templatical block.\n * Returns the block and a report entry.\n */\nexport function convertContent(\n content: UnlayerContent,\n warnings: string[],\n): { block: Block; entry: ImportReportEntry } {\n const type = content.type;\n const values = content.values ?? ({} as UnlayerContentValues);\n\n switch (type) {\n case \"text\":\n return {\n block: convertText(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"paragraph\",\n status: \"converted\",\n },\n };\n case \"heading\":\n return {\n block: convertHeading(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"title\",\n status: \"converted\",\n },\n };\n case \"image\":\n return {\n block: convertImage(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"image\",\n status: \"converted\",\n },\n };\n case \"button\":\n return {\n block: convertButton(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"button\",\n status: \"converted\",\n },\n };\n case \"divider\":\n return {\n block: convertDivider(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"divider\",\n status: \"converted\",\n },\n };\n case \"spacer\":\n return {\n block: convertSpacer(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"spacer\",\n status: \"converted\",\n },\n };\n case \"html\":\n return {\n block: convertHtml(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"converted\",\n },\n };\n case \"menu\":\n return {\n block: convertMenu(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"menu\",\n status: \"approximated\",\n note: \"Menu styles map approximately; review spacing and separator color.\",\n },\n };\n case \"social\":\n return {\n block: convertSocial(values, warnings),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"social\",\n status: \"converted\",\n },\n };\n case \"video\":\n return {\n block: convertVideo(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"video\",\n status: \"converted\",\n },\n };\n case \"timer\":\n return {\n block: convertHtmlFallback(\n content,\n \"Unlayer timer block: rebuild manually in Templatical\",\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: \"Timer modules have no direct Templatical equivalent; placeholder HTML inserted.\",\n },\n };\n case \"form\":\n return {\n block: convertHtmlFallback(\n content,\n \"Unlayer form block: not supported in Templatical (most email clients block form submission)\",\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: null,\n status: \"skipped\",\n note: \"Unlayer forms have no Templatical equivalent and are skipped. Most email clients block form submission anyway.\",\n },\n };\n default:\n return {\n block: convertHtmlFallback(\n content,\n `Unsupported Unlayer content type: ${type}`,\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: `Unknown content type \"${type}\" converted to HTML block.`,\n },\n };\n }\n}\n","import type { SpacingValue } from \"@templatical/types\";\nimport type { UnlayerBorder, UnlayerFontFamily } from \"./types\";\n\n/**\n * Parses CSS-like style values from Unlayer content values.\n */\n\nexport function parsePxValue(value: string | number | undefined): number {\n if (value === undefined || value === null) return 0;\n if (typeof value === \"number\") return Math.round(value);\n // Single trailing-whitespace group with an optional `px` inside it. The\n // original `\\s*(?:px)?\\s*` had two `\\s*` quantifiers around an optional\n // group, which is the textbook polynomial-ReDoS shape (\"how should the\n // whitespace split across the two quantifiers?\") over inputs like\n // `9 ` with no `px`.\n const match = value.match(/^(-?\\d+(?:\\.\\d+)?)\\s*(?:px\\s*)?$/);\n return match ? Math.round(parseFloat(match[1])) : 0;\n}\n\nexport function parseColor(value: string | undefined): string {\n if (!value || value === \"transparent\") return \"\";\n\n const trimmed = value.trim();\n\n if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) return trimmed.toLowerCase();\n\n if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {\n const r = trimmed[1];\n const g = trimmed[2];\n const b = trimmed[3];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n\n return trimmed;\n}\n\nexport function parsePaddingShorthand(value: string | undefined): SpacingValue {\n if (!value) return { top: 0, right: 0, bottom: 0, left: 0 };\n\n const parts = value.trim().split(/\\s+/);\n const values = parts.map((p) => parsePxValue(p));\n\n switch (values.length) {\n case 1:\n return {\n top: values[0],\n right: values[0],\n bottom: values[0],\n left: values[0],\n };\n case 2:\n return {\n top: values[0],\n right: values[1],\n bottom: values[0],\n left: values[1],\n };\n case 3:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[1],\n };\n default:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[3],\n };\n }\n}\n\nexport function parseBorderObject(border: UnlayerBorder | undefined): {\n width: number;\n style: string;\n color: string;\n} {\n if (!border) return { width: 0, style: \"solid\", color: \"#000000\" };\n return {\n width: parsePxValue(border.borderTopWidth),\n style: border.borderTopStyle || \"solid\",\n color: parseColor(border.borderTopColor) || \"#000000\",\n };\n}\n\nexport function parseWidthPercent(value: string | undefined): number {\n if (!value) return 100;\n const match = value.match(/^(\\d+(?:\\.\\d+)?)\\s*%/);\n if (match) return Math.round(parseFloat(match[1]));\n return 100;\n}\n\nexport function parseFontFamily(\n value: UnlayerFontFamily | string | undefined,\n): string {\n if (!value) return \"\";\n if (typeof value === \"string\") {\n return value.split(\",\")[0].trim().replace(/['\"]/g, \"\");\n }\n if (value.value) return value.value.split(\",\")[0].trim().replace(/['\"]/g, \"\");\n if (value.label) return value.label;\n return \"\";\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/style-parser.ts","../src/block-mapper.ts","../src/converter.ts"],"sourcesContent":["import type { SpacingValue } from \"@templatical/types\";\nimport type { UnlayerBorder, UnlayerFontFamily } from \"./types\";\n\n/**\n * Parses CSS-like style values from Unlayer content values.\n */\n\nexport function parsePxValue(value: string | number | undefined): number {\n if (value === undefined || value === null) return 0;\n if (typeof value === \"number\") return Math.round(value);\n // Single trailing-whitespace group with an optional `px` inside it. The\n // original `\\s*(?:px)?\\s*` had two `\\s*` quantifiers around an optional\n // group, which is the textbook polynomial-ReDoS shape (\"how should the\n // whitespace split across the two quantifiers?\") over inputs like\n // `9 ` with no `px`.\n const match = value.match(/^(-?\\d+(?:\\.\\d+)?)\\s*(?:px\\s*)?$/);\n return match ? Math.round(parseFloat(match[1])) : 0;\n}\n\nexport function parseColor(value: string | undefined): string {\n if (!value || value === \"transparent\") return \"\";\n\n const trimmed = value.trim();\n\n if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) return trimmed.toLowerCase();\n\n if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {\n const r = trimmed[1];\n const g = trimmed[2];\n const b = trimmed[3];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n\n return trimmed;\n}\n\nexport function parsePaddingShorthand(value: string | undefined): SpacingValue {\n if (!value) return { top: 0, right: 0, bottom: 0, left: 0 };\n\n const parts = value.trim().split(/\\s+/);\n const values = parts.map((p) => parsePxValue(p));\n\n switch (values.length) {\n case 1:\n return {\n top: values[0],\n right: values[0],\n bottom: values[0],\n left: values[0],\n };\n case 2:\n return {\n top: values[0],\n right: values[1],\n bottom: values[0],\n left: values[1],\n };\n case 3:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[1],\n };\n default:\n return {\n top: values[0],\n right: values[1],\n bottom: values[2],\n left: values[3],\n };\n }\n}\n\nexport function parseBorderObject(border: UnlayerBorder | undefined): {\n width: number;\n style: string;\n color: string;\n} {\n if (!border) return { width: 0, style: \"solid\", color: \"#000000\" };\n return {\n width: parsePxValue(border.borderTopWidth),\n style: border.borderTopStyle || \"solid\",\n color: parseColor(border.borderTopColor) || \"#000000\",\n };\n}\n\nexport function parseWidthPercent(value: string | undefined): number {\n if (!value) return 100;\n const match = value.match(/^(\\d+(?:\\.\\d+)?)\\s*%/);\n if (match) return Math.round(parseFloat(match[1]));\n return 100;\n}\n\nexport function parseFontFamily(\n value: UnlayerFontFamily | string | undefined,\n): string {\n if (!value) return \"\";\n if (typeof value === \"string\") {\n return value.split(\",\")[0].trim().replace(/['\"]/g, \"\");\n }\n if (value.value) return value.value.split(\",\")[0].trim().replace(/['\"]/g, \"\");\n if (value.label) return value.label;\n return \"\";\n}\n","import {\n createTitleBlock,\n createParagraphBlock,\n createImageBlock,\n createButtonBlock,\n createDividerBlock,\n createSpacerBlock,\n createHtmlBlock,\n createSocialIconsBlock,\n createMenuBlock,\n createVideoBlock,\n generateId,\n} from \"@templatical/types\";\nimport type {\n Block,\n HeadingLevel,\n SocialPlatform,\n SocialIcon,\n MenuItemData,\n} from \"@templatical/types\";\nimport type {\n UnlayerContent,\n UnlayerContentValues,\n ImportReportEntry,\n} from \"./types\";\nimport {\n parsePxValue,\n parseColor,\n parseFontFamily,\n parsePaddingShorthand,\n parseBorderObject,\n parseWidthPercent,\n} from \"./style-parser\";\n\nconst SOCIAL_PLATFORM_MAP: Record<string, SocialPlatform> = {\n facebook: \"facebook\",\n twitter: \"twitter\",\n x: \"twitter\",\n instagram: \"instagram\",\n linkedin: \"linkedin\",\n youtube: \"youtube\",\n tiktok: \"tiktok\",\n pinterest: \"pinterest\",\n email: \"email\",\n mail: \"email\",\n whatsapp: \"whatsapp\",\n telegram: \"telegram\",\n discord: \"discord\",\n snapchat: \"snapchat\",\n reddit: \"reddit\",\n github: \"github\",\n dribbble: \"dribbble\",\n behance: \"behance\",\n};\n\ntype Align = \"left\" | \"center\" | \"right\";\ntype LineStyle = \"solid\" | \"dashed\" | \"dotted\";\n\nfunction toAlign(value: string | undefined, fallback: Align = \"left\"): Align {\n if (value === \"left\" || value === \"center\" || value === \"right\") return value;\n return fallback;\n}\n\n/**\n * Strip every `<…>` from a button label and reject any stray `<` or `>`\n * left behind (e.g. a truncated `<script`). The original\n * `value.replace(/<[^>]*>/g, \"\")` was both polynomial-ReDoS over\n * `<<<<…` inputs and an incomplete sanitizer — an unterminated `<script`\n * would survive the strip. Downstream HTML-escapes the label at render\n * time, but stripping here keeps the imported JSON clean.\n */\nfunction stripTagsPlain(text: string): string {\n let out = \"\";\n let i = 0;\n while (i < text.length) {\n if (text[i] === \"<\") {\n const close = text.indexOf(\">\", i + 1);\n if (close === -1) {\n // Unterminated tag — discard the rest.\n break;\n }\n i = close + 1;\n continue;\n }\n if (text[i] === \">\") {\n i++;\n continue;\n }\n out += text[i];\n i++;\n }\n return out;\n}\n\nfunction toLineStyle(\n value: string | undefined,\n fallback: LineStyle = \"solid\",\n): LineStyle {\n if (value === \"solid\" || value === \"dashed\" || value === \"dotted\")\n return value;\n return fallback;\n}\n\nfunction makeStyles(values: UnlayerContentValues): Block[\"styles\"] {\n const padding = parsePaddingShorthand(values.containerPadding);\n return {\n padding,\n };\n}\n\n/**\n * Apply Unlayer text-level styles as TipTap-compatible inline markup.\n * Mirrors the BeeFree importer's helper but reads from Unlayer's flat\n * values shape rather than a CSS style record.\n */\nfunction inlineStylesToHtml(\n html: string,\n values: UnlayerContentValues,\n): string {\n const spanParts: string[] = [];\n const fontSize = parsePxValue(values.fontSize);\n if (fontSize && fontSize !== 16) spanParts.push(`font-size: ${fontSize}px`);\n const color = parseColor(values.color);\n if (color && color !== \"#1a1a1a\") spanParts.push(`color: ${color}`);\n const fontWeight = values.fontWeight;\n if (\n fontWeight !== undefined &&\n fontWeight !== null &&\n String(fontWeight) !== \"normal\" &&\n String(fontWeight) !== \"400\"\n ) {\n spanParts.push(`font-weight: ${fontWeight}`);\n }\n const fontFamily = parseFontFamily(values.fontFamily);\n if (fontFamily) spanParts.push(`font-family: ${fontFamily}`);\n\n const textAlign = values.textAlign;\n const pStyle =\n textAlign && textAlign !== \"left\" ? `text-align: ${textAlign}` : \"\";\n\n if (!pStyle && spanParts.length === 0) return html;\n\n const spanStyle = spanParts.join(\"; \");\n let result = html;\n\n if (pStyle) {\n result = result\n .replace(/<p style=\"([^\"]*)\">/g, `<p style=\"$1; ${pStyle}\">`)\n .replaceAll(\"<p>\", `<p style=\"${pStyle}\">`);\n }\n\n if (spanStyle) {\n result = wrapParagraphInner(result, spanStyle);\n }\n\n return result;\n}\n\n/**\n * Wrap the inner content of every `<p …>…</p>` with `<span style=\"…\">…</span>`.\n *\n * Hand-rolled linear scanner instead of `/<p([^>]*)>([\\s\\S]*?)<\\/p>/g` because\n * the regex is polynomial-ReDoS: the engine retries `[\\s\\S]*?<\\/p>` at every\n * `<p` start, so inputs like `<p>a<p>a<p>a…` (no `</p>` ever) cost O(n²).\n * The scanner advances `i` monotonically via `indexOf`, keeping the work\n * strictly O(n).\n */\nfunction wrapParagraphInner(html: string, spanStyle: string): string {\n let out = \"\";\n let i = 0;\n while (i < html.length) {\n const open = html.indexOf(\"<p\", i);\n if (open === -1) {\n out += html.substring(i);\n break;\n }\n const afterTagName = html[open + 2];\n if (\n afterTagName !== \">\" &&\n afterTagName !== \" \" &&\n afterTagName !== \"\\t\" &&\n afterTagName !== \"\\n\" &&\n afterTagName !== \"\\r\" &&\n afterTagName !== \"/\"\n ) {\n out += html.substring(i, open + 2);\n i = open + 2;\n continue;\n }\n const openEnd = html.indexOf(\">\", open + 2);\n if (openEnd === -1) {\n out += html.substring(i);\n break;\n }\n const closeStart = html.indexOf(\"</p>\", openEnd + 1);\n if (closeStart === -1) {\n out += html.substring(i);\n break;\n }\n const inner = html.substring(openEnd + 1, closeStart);\n out += html.substring(i, openEnd + 1);\n out += `<span style=\"${spanStyle}\">${inner}</span></p>`;\n i = closeStart + 4;\n }\n return out;\n}\n\nfunction ensureParagraphWrapped(html: string): string {\n if (!html) return \"<p></p>\";\n if (/<p[\\s>]/i.test(html)) return html;\n return `<p>${html}</p>`;\n}\n\nfunction convertText(values: UnlayerContentValues): Block {\n const html = ensureParagraphWrapped(values.text ?? \"\");\n\n return createParagraphBlock({\n content: inlineStylesToHtml(html, values),\n styles: makeStyles(values),\n });\n}\n\nfunction parseHeadingLevel(tag: string | undefined): HeadingLevel {\n if (!tag) return 2;\n const match = tag.match(/^h(\\d)$/i);\n if (match) {\n const num = Number(match[1]);\n if (num >= 1 && num <= 4) return num as HeadingLevel;\n }\n return 2;\n}\n\nfunction convertHeading(values: UnlayerContentValues): Block {\n const text = values.text ?? \"\";\n const stripped = text.replace(/^<h\\d[^>]*>|<\\/h\\d>$/gi, \"\");\n const content = stripped ? `<p>${stripped}</p>` : \"<p></p>\";\n\n return createTitleBlock({\n content,\n level: parseHeadingLevel(values.headingType),\n color: parseColor(values.color) || \"#1a1a1a\",\n textAlign: toAlign(values.textAlign),\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n styles: makeStyles(values),\n });\n}\n\nfunction convertImage(values: UnlayerContentValues): Block {\n const src = values.src;\n const action = values.action?.values;\n\n return createImageBlock({\n src: src?.url || \"\",\n alt: values.altText || \"\",\n width: src?.width ? Math.round(src.width) : 600,\n align: toAlign(values.textAlign, \"center\"),\n linkUrl: action?.href || undefined,\n linkOpenInNewTab: action?.target === \"_blank\" || undefined,\n styles: makeStyles(values),\n });\n}\n\nfunction convertButton(values: UnlayerContentValues): Block {\n const colors = values.buttonColors ?? {};\n const padding = values.padding\n ? parsePaddingShorthand(values.padding)\n : { top: 12, right: 24, bottom: 12, left: 24 };\n const label = stripTagsPlain(values.text ?? \"Button\");\n const linkValues = values.href?.values;\n\n return createButtonBlock({\n text: label,\n url: linkValues?.href || \"#\",\n openInNewTab: linkValues?.target === \"_blank\" || undefined,\n backgroundColor: parseColor(colors.backgroundColor) || \"#4f46e5\",\n textColor: parseColor(colors.color) || \"#ffffff\",\n borderRadius: parsePxValue(values.borderRadius),\n fontSize: parsePxValue(values.fontSize) || 16,\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n buttonPadding: padding,\n styles: makeStyles(values),\n });\n}\n\nfunction convertDivider(values: UnlayerContentValues): Block {\n const border = parseBorderObject(values.border);\n\n return createDividerBlock({\n lineStyle: toLineStyle(border.style),\n color: border.color,\n thickness: border.width || 1,\n width: parseWidthPercent(values.width),\n styles: makeStyles(values),\n });\n}\n\nfunction convertSpacer(values: UnlayerContentValues): Block {\n const padding = parsePaddingShorthand(values.containerPadding);\n const height =\n parsePxValue((values as { height?: string | number }).height) ||\n padding.top + padding.bottom ||\n 24;\n\n return createSpacerBlock({\n height,\n styles: makeStyles(values),\n });\n}\n\nfunction convertHtml(values: UnlayerContentValues): Block {\n return createHtmlBlock({\n content: values.html ?? \"\",\n styles: makeStyles(values),\n });\n}\n\nfunction convertSocial(\n values: UnlayerContentValues,\n warnings: string[],\n): Block {\n const iconList = values.icons?.icons ?? [];\n const icons: SocialIcon[] = [];\n\n for (const unlayerIcon of iconList) {\n const id = (unlayerIcon.name ?? \"\").toLowerCase();\n const platform = SOCIAL_PLATFORM_MAP[id];\n\n if (!platform) {\n warnings.push(\n `Unrecognized social icon \"${unlayerIcon.name || id}\" was skipped.`,\n );\n continue;\n }\n\n icons.push({\n id: generateId(),\n platform,\n url: unlayerIcon.url || \"#\",\n });\n }\n\n return createSocialIconsBlock({\n icons,\n align: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertVideo(values: UnlayerContentValues): Block {\n return createVideoBlock({\n url: values.videoUrl || \"\",\n thumbnailUrl: values.thumbnailUrl || \"\",\n alt: values.altText || \"\",\n width: 600,\n align: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertMenu(values: UnlayerContentValues): Block {\n const menu = values.menu;\n const items: MenuItemData[] = (menu?.items ?? []).map((item) => ({\n id: generateId(),\n text: item.text || \"\",\n url: item.link?.values?.href || \"#\",\n openInNewTab: item.link?.values?.target === \"_blank\",\n bold: false,\n underline: false,\n }));\n\n return createMenuBlock({\n items,\n separator: values.separator || \"|\",\n separatorColor: \"#999999\",\n fontSize: parsePxValue(values.fontSize) || 14,\n color: parseColor(values.color) || \"#1a1a1a\",\n fontFamily: parseFontFamily(values.fontFamily) || undefined,\n textAlign: toAlign(values.textAlign, \"center\"),\n styles: makeStyles(values),\n });\n}\n\nfunction convertHtmlFallback(content: UnlayerContent, comment: string): Block {\n const safe = comment.replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n return createHtmlBlock({\n content: `<div style=\"padding:12px;border:1px dashed #d1d5db;border-radius:6px;background:#fafafa;color:#6b7280;font-family:sans-serif;font-size:13px;\">${safe}</div>`,\n styles: makeStyles(content.values),\n });\n}\n\n/**\n * Converts a single Unlayer content node to a Templatical block.\n * Returns the block and a report entry.\n */\nexport function convertContent(\n content: UnlayerContent,\n warnings: string[],\n): { block: Block; entry: ImportReportEntry } {\n const type = content.type;\n const values = content.values ?? ({} as UnlayerContentValues);\n\n switch (type) {\n case \"text\":\n return {\n block: convertText(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"paragraph\",\n status: \"converted\",\n },\n };\n case \"heading\":\n return {\n block: convertHeading(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"title\",\n status: \"converted\",\n },\n };\n case \"image\":\n return {\n block: convertImage(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"image\",\n status: \"converted\",\n },\n };\n case \"button\":\n return {\n block: convertButton(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"button\",\n status: \"converted\",\n },\n };\n case \"divider\":\n return {\n block: convertDivider(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"divider\",\n status: \"converted\",\n },\n };\n case \"spacer\":\n return {\n block: convertSpacer(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"spacer\",\n status: \"converted\",\n },\n };\n case \"html\":\n return {\n block: convertHtml(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"converted\",\n },\n };\n case \"menu\":\n return {\n block: convertMenu(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"menu\",\n status: \"approximated\",\n note: \"Menu styles map approximately; review spacing and separator color.\",\n },\n };\n case \"social\":\n return {\n block: convertSocial(values, warnings),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"social\",\n status: \"converted\",\n },\n };\n case \"video\":\n return {\n block: convertVideo(values),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"video\",\n status: \"converted\",\n },\n };\n case \"timer\":\n return {\n block: convertHtmlFallback(\n content,\n \"Unlayer timer block: rebuild manually in Templatical\",\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: \"Timer modules have no direct Templatical equivalent; placeholder HTML inserted.\",\n },\n };\n case \"form\":\n return {\n block: convertHtmlFallback(\n content,\n \"Unlayer form block: not supported in Templatical (most email clients block form submission)\",\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: null,\n status: \"skipped\",\n note: \"Unlayer forms have no Templatical equivalent and are skipped. Most email clients block form submission anyway.\",\n },\n };\n default:\n return {\n block: convertHtmlFallback(\n content,\n `Unsupported Unlayer content type: ${type}`,\n ),\n entry: {\n unlayerContentType: type,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: `Unknown content type \"${type}\" converted to HTML block.`,\n },\n };\n }\n}\n","import {\n createSectionBlock,\n createDefaultTemplateContent,\n} from \"@templatical/types\";\nimport type { Block, ColumnLayout, TemplateContent } from \"@templatical/types\";\nimport type {\n UnlayerTemplate,\n UnlayerRow,\n UnlayerColumn,\n ImportResult,\n ImportReport,\n ImportReportEntry,\n} from \"./types\";\nimport { convertContent } from \"./block-mapper\";\nimport {\n parsePxValue,\n parseColor,\n parseFontFamily,\n parsePaddingShorthand,\n} from \"./style-parser\";\n\nfunction resolveColumnLayout(\n cells: number[],\n warnings: string[],\n): ColumnLayout {\n if (cells.length <= 1) return \"1\";\n if (cells.length === 3) return \"3\";\n\n if (cells.length === 2) {\n const left = cells[0] ?? 1;\n const right = cells[1] ?? 1;\n const total = left + right;\n const ratio = left / total;\n\n if (ratio > 0.58) return \"2-1\";\n if (ratio < 0.42) return \"1-2\";\n return \"2\";\n }\n\n warnings.push(\n `Row with ${cells.length} columns was flattened to a single column. Unlayer supports arbitrary columns, but Templatical supports up to 3.`,\n );\n return \"1\";\n}\n\n/**\n * Converts all contents in a column to Templatical blocks.\n */\nfunction convertColumnContents(\n column: UnlayerColumn,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const blocks: Block[] = [];\n\n for (const content of column.contents ?? []) {\n const { block, entry } = convertContent(content, warnings);\n blocks.push(block);\n entries.push(entry);\n }\n\n return blocks;\n}\n\n/**\n * Processes a single Unlayer row into one or more Templatical blocks.\n */\nfunction processRow(\n row: UnlayerRow,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const columns = row.columns;\n if (!columns || columns.length === 0) return [];\n\n const cells = row.cells ?? columns.map(() => 1);\n const layout = resolveColumnLayout(cells, warnings);\n\n let children: Block[][];\n if (layout === \"1\") {\n const merged: Block[] = [];\n for (const column of columns) {\n merged.push(...convertColumnContents(column, entries, warnings));\n }\n children = [merged];\n } else {\n children = columns.map((col) =>\n convertColumnContents(col, entries, warnings),\n );\n }\n\n const rowBg = parseColor(row.values?.backgroundColor);\n const padding = parsePaddingShorthand(row.values?.padding);\n\n const section = createSectionBlock({\n columns: layout,\n children,\n styles: {\n padding,\n ...(rowBg ? { backgroundColor: rowBg } : {}),\n },\n });\n\n return [section];\n}\n\n/**\n * Extracts template-level settings from the Unlayer body values.\n */\nfunction extractSettings(\n template: UnlayerTemplate,\n): TemplateContent[\"settings\"] {\n const values = template.body?.values ?? {};\n\n const width = parsePxValue(values.contentWidth);\n const bgColor = parseColor(values.backgroundColor) || \"#ffffff\";\n const fontFamily = parseFontFamily(values.fontFamily) || \"Arial\";\n\n return {\n width: width || 600,\n backgroundColor: bgColor,\n fontFamily,\n locale: \"en\",\n };\n}\n\n/**\n * Converts an Unlayer design JSON to Templatical TemplateContent.\n *\n * @param template - The parsed Unlayer JSON object (the result of `editor.saveDesign(...)`)\n * @returns An ImportResult with the converted content and a detailed report\n *\n * @example\n * ```ts\n * import { convertUnlayerTemplate } from '@templatical/import-unlayer';\n *\n * const unlayerJson = JSON.parse(fileContent);\n * const { content, report } = convertUnlayerTemplate(unlayerJson);\n *\n * const editor = init({ container: '#editor', content });\n *\n * console.log(report.summary);\n * console.log(report.warnings);\n * ```\n */\nexport function convertUnlayerTemplate(\n template: UnlayerTemplate,\n): ImportResult {\n if (!template?.body?.rows) {\n throw new Error(\n \"Invalid Unlayer template: missing body.rows. Ensure you are passing a valid Unlayer JSON design (the output of editor.saveDesign).\",\n );\n }\n\n const entries: ImportReportEntry[] = [];\n const warnings: string[] = [];\n const blocks: Block[] = [];\n\n const headers = template.body.headers ?? [];\n const footers = template.body.footers ?? [];\n\n if (headers.length > 0) {\n warnings.push(\n `${headers.length} Unlayer header row(s) were imported as regular rows at the top of the template.`,\n );\n for (const row of headers) {\n blocks.push(...processRow(row, entries, warnings));\n }\n }\n\n for (const row of template.body.rows) {\n blocks.push(...processRow(row, entries, warnings));\n }\n\n if (footers.length > 0) {\n warnings.push(\n `${footers.length} Unlayer footer row(s) were imported as regular rows at the bottom of the template.`,\n );\n for (const row of footers) {\n blocks.push(...processRow(row, entries, warnings));\n }\n }\n\n const content: TemplateContent = {\n ...createDefaultTemplateContent(),\n blocks,\n settings: extractSettings(template),\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 const report: ImportReport = { entries, warnings, summary };\n\n return { content, report };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@templatical/import-unlayer",
3
3
  "description": "Convert Unlayer email templates to Templatical format",
4
- "version": "0.9.1",
4
+ "version": "0.10.1",
5
5
  "bugs": "https://github.com/templatical/sdk/issues",
6
6
  "dependencies": {
7
- "@templatical/types": "0.9.1"
7
+ "@templatical/types": "0.10.1"
8
8
  },
9
9
  "devDependencies": {
10
- "tsup": "^8.5.1",
11
10
  "typescript": "^6.0.3",
12
11
  "vitest": "^4.1.7"
13
12
  },
@@ -42,7 +41,7 @@
42
41
  "type": "module",
43
42
  "types": "./dist/index.d.ts",
44
43
  "scripts": {
45
- "build": "tsup",
44
+ "build": "tsdown",
46
45
  "test": "vitest run --config vitest.config.ts",
47
46
  "typecheck": "tsc --noEmit"
48
47
  }