@templatical/import-html
Advanced tools
+9
-28
@@ -299,5 +299,2 @@ // src/converter.ts | ||
| var TEXT_TAGS = /* @__PURE__ */ new Set(["p", "span", "div"]); | ||
| function emptyMargin() { | ||
| return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| } | ||
| function emptyPadding() { | ||
@@ -342,4 +339,3 @@ return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| styles: { | ||
| padding: readPaddingFromStyles(styles), | ||
| margin: emptyMargin() | ||
| padding: readPaddingFromStyles(styles) | ||
| } | ||
@@ -379,4 +375,3 @@ }); | ||
| styles: { | ||
| padding: readPaddingFromStyles(styles), | ||
| margin: emptyMargin() | ||
| padding: readPaddingFromStyles(styles) | ||
| } | ||
@@ -398,4 +393,3 @@ }); | ||
| styles: { | ||
| padding: readPaddingFromStyles(styles), | ||
| margin: emptyMargin() | ||
| padding: readPaddingFromStyles(styles) | ||
| } | ||
@@ -430,4 +424,3 @@ }); | ||
| styles: { | ||
| padding: emptyPadding(), | ||
| margin: emptyMargin() | ||
| padding: emptyPadding() | ||
| } | ||
@@ -446,4 +439,3 @@ }); | ||
| styles: { | ||
| padding: readPaddingFromStyles(styles), | ||
| margin: emptyMargin() | ||
| padding: readPaddingFromStyles(styles) | ||
| } | ||
@@ -459,4 +451,3 @@ }); | ||
| styles: { | ||
| padding: readPaddingFromStyles(styles), | ||
| margin: emptyMargin() | ||
| padding: readPaddingFromStyles(styles) | ||
| } | ||
@@ -573,5 +564,2 @@ }); | ||
| } from "@templatical/types"; | ||
| function emptyMargin2() { | ||
| return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| } | ||
| function emptyPadding2() { | ||
@@ -600,4 +588,3 @@ return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| styles: { | ||
| padding: emptyPadding2(), | ||
| margin: emptyMargin2() | ||
| padding: emptyPadding2() | ||
| } | ||
@@ -612,4 +599,3 @@ }); | ||
| styles: { | ||
| padding: emptyPadding2(), | ||
| margin: emptyMargin2() | ||
| padding: emptyPadding2() | ||
| } | ||
@@ -754,3 +740,2 @@ }); | ||
| padding, | ||
| margin: emptyMargin2(), | ||
| ...bgColor ? { backgroundColor: bgColor } : {} | ||
@@ -765,5 +750,2 @@ } | ||
| // src/converter.ts | ||
| function emptyMargin3() { | ||
| return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| } | ||
| function emptyPadding3() { | ||
@@ -806,4 +788,3 @@ return { top: 0, right: 0, bottom: 0, left: 0 }; | ||
| styles: { | ||
| padding: emptyPadding3(), | ||
| margin: emptyMargin3() | ||
| padding: emptyPadding3() | ||
| } | ||
@@ -810,0 +791,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/converter.ts","../src/style-parser.ts","../src/css-resolver.ts","../src/block-mapper.ts","../src/section-builder.ts"],"sourcesContent":["import { load } from \"cheerio\";\nimport type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element } from \"domhandler\";\nimport {\n createDefaultTemplateContent,\n createSectionBlock,\n} from \"@templatical/types\";\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport { resolveCssStyles } from \"./css-resolver\";\nimport { convertElement } from \"./block-mapper\";\nimport { processTable } from \"./section-builder\";\nimport {\n parseColor,\n parseFontFamily,\n parsePxValue,\n parseStyleAttribute,\n} from \"./style-parser\";\nimport type { ImportReport, ImportReportEntry, ImportResult } from \"./types\";\n\nfunction emptyMargin() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction emptyPadding() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction readPreheader($: CheerioAPI): string | undefined {\n // Convention: a hidden <div> at the very top with `display:none` containing\n // the preheader text. Match if it appears in the first ~3 children of body.\n const candidates = $(\"body\")\n .children()\n .slice(0, 5)\n .filter((_, el) => {\n const styles = parseStyleAttribute($(el).attr(\"style\"));\n return (styles.display ?? \"\").toLowerCase() === \"none\";\n });\n if (candidates.length === 0) return undefined;\n const text = $(candidates[0]).text().trim();\n return text || undefined;\n}\n\nfunction extractSettings($: CheerioAPI): TemplateContent[\"settings\"] {\n const $body = $(\"body\");\n const bodyStyles = parseStyleAttribute($body.attr(\"style\"));\n const fontFamily = parseFontFamily(bodyStyles[\"font-family\"]) || \"Arial\";\n const backgroundColor =\n parseColor(bodyStyles[\"background-color\"]) ||\n parseColor(bodyStyles.background) ||\n \"#ffffff\";\n\n // Width heuristic: outermost table width attr, or \".container\" width style.\n const $outerTable = $body.find(\"table\").first();\n const widthAttr = parsePxValue($outerTable.attr(\"width\"));\n const widthStyle = parsePxValue(\n parseStyleAttribute($outerTable.attr(\"style\")).width,\n );\n const width = widthAttr || widthStyle || 600;\n\n const preheaderText = readPreheader($);\n\n return {\n width,\n backgroundColor,\n fontFamily,\n locale: \"en\",\n ...(preheaderText ? { preheaderText } : {}),\n };\n}\n\n/**\n * Wrap a list of free-floating blocks (those produced by top-level non-table\n * elements) in a single one-column section.\n */\nfunction wrapInSection(blocks: Block[]): Block {\n return createSectionBlock({\n columns: \"1\",\n children: [blocks],\n styles: {\n padding: emptyPadding(),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Walk top-level body children. Tables become sections; loose content\n * elements are accumulated and wrapped in a single one-column section.\n */\nfunction processBody(\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const blocks: Block[] = [];\n const $body = $(\"body\");\n const children = $body.children().toArray();\n\n let pendingLoose: Block[] = [];\n\n const flushLoose = () => {\n if (pendingLoose.length > 0) {\n blocks.push(wrapInSection(pendingLoose));\n pendingLoose = [];\n }\n };\n\n for (const childEl of children) {\n const tag = childEl.tagName?.toLowerCase() ?? \"\";\n const $child = $(childEl) as unknown as Cheerio<Element>;\n\n if (tag === \"table\") {\n flushLoose();\n blocks.push(...processTable($child, $, entries, warnings, false));\n continue;\n }\n\n // Skip hidden preheader divs — already captured in settings.\n const childStyles = parseStyleAttribute($child.attr(\"style\"));\n if ((childStyles.display ?? \"\").toLowerCase() === \"none\") continue;\n\n // Containers like a wrapping <div> with table children: recurse.\n if (\n (tag === \"div\" || tag === \"center\" || tag === \"main\") &&\n $child.find(\"table\").length > 0\n ) {\n flushLoose();\n $child.children().each((_, innerEl) => {\n const innerTag = innerEl.tagName?.toLowerCase() ?? \"\";\n const $inner = $(innerEl) as unknown as Cheerio<Element>;\n if (innerTag === \"table\") {\n blocks.push(...processTable($inner, $, entries, warnings, false));\n } else {\n const r = convertElement($inner, $);\n if (r) {\n entries.push(r.entry);\n pendingLoose.push(r.block);\n }\n }\n });\n flushLoose();\n continue;\n }\n\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n pendingLoose.push(r.block);\n }\n }\n\n flushLoose();\n return blocks;\n}\n\n/**\n * Converts an HTML email template to Templatical TemplateContent.\n *\n * Designed for table-based marketing email HTML (output of MJML, Mailchimp,\n * SendGrid, Campaign Monitor, hand-coded emails). Modern HTML using flex/grid\n * layouts is preserved via HTML-fallback blocks.\n *\n * @param html - The raw HTML string (full document or body fragment).\n * @returns An ImportResult with the converted content and a detailed report.\n *\n * @example\n * ```ts\n * import { convertHtmlTemplate } from '@templatical/import-html';\n *\n * const html = await fetch('/email.html').then((r) => r.text());\n * const { content, report } = convertHtmlTemplate(html);\n *\n * const editor = init({ container: '#editor', content });\n *\n * console.log(report.summary);\n * console.log(report.warnings);\n * ```\n */\nexport function convertHtmlTemplate(html: string): ImportResult {\n if (typeof html !== \"string\") {\n throw new Error(\n \"Invalid HTML template: expected a string. Pass the raw HTML source as a string.\",\n );\n }\n if (html.trim().length === 0) {\n throw new Error(\n \"Invalid HTML template: input is empty. Pass the raw HTML source of an email.\",\n );\n }\n\n const $ = load(html);\n resolveCssStyles($);\n\n // Drop tags that are never useful in the editor canvas.\n $(\"script, noscript, link, meta, title\").remove();\n\n const entries: ImportReportEntry[] = [];\n const warnings: string[] = [];\n\n const blocks = processBody($, entries, warnings);\n\n if (blocks.length === 0) {\n warnings.push(\n \"No convertible content was found in the HTML. The email may use a non-table layout — modern HTML support is limited.\",\n );\n }\n\n const content: TemplateContent = {\n ...createDefaultTemplateContent(),\n blocks,\n settings: extractSettings($),\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 type { SpacingValue } from \"@templatical/types\";\n\n/**\n * Parses a CSS `style=\"...\"` attribute string into a flat key/value record.\n * Keys are lowercased; values are trimmed. Quotes around values are not stripped.\n */\nexport function parseStyleAttribute(\n styleAttr: string | undefined,\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!styleAttr) return result;\n\n for (const decl of styleAttr.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const key = decl.slice(0, idx).trim().toLowerCase();\n const value = decl.slice(idx + 1).trim();\n if (key && value) result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Serializes a flat key/value record back to a `style` attribute string.\n */\nexport function serializeStyleAttribute(\n styles: Record<string, string>,\n): string {\n return Object.entries(styles)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\"; \");\n}\n\n/**\n * Parses a px-like CSS value (`\"12px\"`, `\"12\"`, `12`) into a rounded integer.\n * Returns 0 for missing or unparseable input. Ignores em/% units.\n */\nexport function parsePxValue(value: string | number | undefined): number {\n if (value === undefined || value === null || value === \"\") return 0;\n if (typeof value === \"number\") return Math.round(value);\n const match = value.match(/^(-?\\d+(?:\\.\\d+)?)\\s*(?:px)?\\s*$/);\n return match ? Math.round(parseFloat(match[1])) : 0;\n}\n\n/**\n * Parses a width value that may be a percentage. Returns the numeric percent\n * (0-100). For non-percent values, returns 100.\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\nconst NAMED_COLORS: Record<string, string> = {\n black: \"#000000\",\n white: \"#ffffff\",\n red: \"#ff0000\",\n green: \"#008000\",\n blue: \"#0000ff\",\n yellow: \"#ffff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n gray: \"#808080\",\n grey: \"#808080\",\n silver: \"#c0c0c0\",\n maroon: \"#800000\",\n olive: \"#808000\",\n lime: \"#00ff00\",\n aqua: \"#00ffff\",\n teal: \"#008080\",\n navy: \"#000080\",\n fuchsia: \"#ff00ff\",\n purple: \"#800080\",\n orange: \"#ffa500\",\n pink: \"#ffc0cb\",\n};\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n const hex = (n: number) => clamp(n).toString(16).padStart(2, \"0\");\n return `#${hex(r)}${hex(g)}${hex(b)}`;\n}\n\n/**\n * Normalizes a CSS color value to a 6-digit lowercase hex string.\n * - 3-digit hex expands to 6-digit\n * - rgb()/rgba() converts to hex (alpha is dropped)\n * - Named colors map via lookup\n * - \"transparent\" / unknown returns \"\"\n */\nexport function parseColor(value: string | undefined): string {\n if (!value) return \"\";\n const trimmed = value.trim().toLowerCase();\n if (trimmed === \"transparent\" || trimmed === \"inherit\" || trimmed === \"none\")\n return \"\";\n\n if (/^#[0-9a-f]{6}$/.test(trimmed)) return trimmed;\n\n if (/^#[0-9a-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}`;\n }\n\n const rgbMatch = trimmed.match(\n /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*[\\d.]+\\s*)?\\)$/,\n );\n if (rgbMatch) {\n return rgbToHex(\n parseInt(rgbMatch[1], 10),\n parseInt(rgbMatch[2], 10),\n parseInt(rgbMatch[3], 10),\n );\n }\n\n if (NAMED_COLORS[trimmed]) return NAMED_COLORS[trimmed];\n\n return \"\";\n}\n\n/**\n * Parses a CSS `padding` shorthand (1-4 values) into a SpacingValue.\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\n/**\n * Reads CSS padding from a style record, preferring the longhand props\n * (padding-top/right/bottom/left) and falling back to the `padding` shorthand.\n */\nexport function readPaddingFromStyles(\n styles: Record<string, string>,\n): SpacingValue {\n const shorthand = parsePaddingShorthand(styles.padding);\n return {\n top: parsePxValue(styles[\"padding-top\"]) || shorthand.top,\n right: parsePxValue(styles[\"padding-right\"]) || shorthand.right,\n bottom: parsePxValue(styles[\"padding-bottom\"]) || shorthand.bottom,\n left: parsePxValue(styles[\"padding-left\"]) || shorthand.left,\n };\n}\n\n/**\n * Strips quotes and returns the first font in a font-family stack.\n */\nexport function parseFontFamily(value: string | undefined): string {\n if (!value) return \"\";\n return value\n .split(\",\")[0]\n .trim()\n .replace(/^['\"]|['\"]$/g, \"\");\n}\n\n/**\n * Normalizes a font-weight value to a string CSS keyword/number that\n * the editor accepts. Returns \"\" when the value is the default (normal/400).\n */\nexport function parseFontWeight(value: string | undefined): string {\n if (!value) return \"\";\n const trimmed = value.trim().toLowerCase();\n if (trimmed === \"normal\" || trimmed === \"400\") return \"\";\n return trimmed;\n}\n\n/**\n * Parses CSS text-align to one of the allowed editor alignments.\n */\nexport function parseAlignment(\n value: string | undefined,\n fallback: \"left\" | \"center\" | \"right\" = \"left\",\n): \"left\" | \"center\" | \"right\" {\n const v = (value ?? \"\").trim().toLowerCase();\n if (v === \"left\" || v === \"center\" || v === \"right\") return v;\n return fallback;\n}\n\n/**\n * Parses a CSS `border` shorthand (`\"1px solid #ccc\"`) into width/style/color.\n * Order-tolerant: each token is classified by content.\n */\nexport function parseBorderShorthand(value: string | undefined): {\n width: number;\n style: string;\n color: string;\n} {\n const fallback = { width: 0, style: \"solid\", color: \"#000000\" };\n if (!value) return fallback;\n\n const styleKeywords = new Set([\n \"none\",\n \"hidden\",\n \"dotted\",\n \"dashed\",\n \"solid\",\n \"double\",\n \"groove\",\n \"ridge\",\n \"inset\",\n \"outset\",\n ]);\n\n let width = 0;\n let style = \"solid\";\n let color = \"#000000\";\n\n for (const token of value.trim().split(/\\s+/)) {\n const lower = token.toLowerCase();\n if (styleKeywords.has(lower)) {\n style = lower;\n } else if (/^-?\\d+(?:\\.\\d+)?(?:px)?$/i.test(lower)) {\n width = parsePxValue(lower);\n } else {\n const c = parseColor(lower);\n if (c) color = c;\n }\n }\n\n return { width, style, color };\n}\n","import type { CheerioAPI } from \"cheerio\";\nimport { parseStyleAttribute, serializeStyleAttribute } from \"./style-parser\";\n\ninterface CssRule {\n selectors: string[];\n declarations: Record<string, string>;\n}\n\n/**\n * Strips all CSS comments. Handles nested-looking content safely.\n */\nfunction stripComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n}\n\n/**\n * Strips at-rule blocks (@media, @font-face, @keyframes, @supports, etc.)\n * and their nested content. Leaves top-level rules in place.\n *\n * Email HTML rarely benefits from @media (we render at one viewport),\n * and resolving it onto elements would not be visually faithful anyway.\n */\nfunction stripAtRules(css: string): string {\n let result = \"\";\n let i = 0;\n while (i < css.length) {\n if (css[i] === \"@\") {\n // skip until matching `{...}` block or terminating `;`\n const semiIdx = css.indexOf(\";\", i);\n const braceIdx = css.indexOf(\"{\", i);\n\n if (braceIdx === -1 || (semiIdx !== -1 && semiIdx < braceIdx)) {\n i = semiIdx === -1 ? css.length : semiIdx + 1;\n continue;\n }\n\n // skip the entire `{...}` block, accounting for nesting\n let depth = 0;\n let j = braceIdx;\n for (; j < css.length; j++) {\n if (css[j] === \"{\") depth++;\n else if (css[j] === \"}\") {\n depth--;\n if (depth === 0) {\n j++;\n break;\n }\n }\n }\n i = j;\n } else {\n result += css[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Parses a CSS declarations block (`color: red; font-size: 14px`) into\n * a flat record. `!important` markers are dropped.\n */\nfunction parseDeclarations(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const decl of text.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const key = decl.slice(0, idx).trim().toLowerCase();\n let value = decl.slice(idx + 1).trim();\n value = value.replace(/!important\\s*$/i, \"\").trim();\n if (key && value) result[key] = value;\n }\n return result;\n}\n\n/**\n * A selector is \"supported\" by cheerio's matcher if it has no pseudo-classes\n * or pseudo-elements. Resolving e.g. `a:hover` onto an inline style would be\n * wrong (it would always apply), so we skip such rules entirely.\n */\nfunction isSupportedSelector(selector: string): boolean {\n if (!selector) return false;\n if (selector.includes(\":\")) return false;\n if (selector.includes(\"@\")) return false;\n return true;\n}\n\n/**\n * Parses the full content of one or more `<style>` tags into a list of rules.\n * Skips at-rules and selectors with pseudo-classes.\n */\nexport function parseStyleSheet(css: string): CssRule[] {\n const rules: CssRule[] = [];\n const cleaned = stripAtRules(stripComments(css));\n\n // Greedily walk top-level `selectors { decls }` blocks.\n const blockRe = /([^{}]+)\\{([^{}]*)\\}/g;\n let match: RegExpExecArray | null;\n while ((match = blockRe.exec(cleaned)) !== null) {\n const selectorPart = match[1].trim();\n const declarationPart = match[2];\n if (!selectorPart) continue;\n\n const selectors = selectorPart\n .split(\",\")\n .map((s) => s.trim())\n .filter(isSupportedSelector);\n if (selectors.length === 0) continue;\n\n const declarations = parseDeclarations(declarationPart);\n if (Object.keys(declarations).length === 0) continue;\n\n rules.push({ selectors, declarations });\n }\n\n return rules;\n}\n\n/**\n * Reads all `<style>` tags from the document, parses them into rules,\n * applies each rule's declarations to matching elements (merging with\n * existing inline `style=\"\"` attributes — inline always wins), and removes\n * the `<style>` tags from the document.\n *\n * No specificity is computed; rules are applied in source order, with later\n * rules overriding earlier ones. Inline styles always override resolved\n * rules. This is sufficient for typical email HTML, where authors already\n * inline most styles.\n */\nexport function resolveCssStyles($: CheerioAPI): void {\n const styleTags = $(\"style\");\n if (styleTags.length === 0) return;\n\n const allRules: CssRule[] = [];\n styleTags.each((_, el) => {\n const css = $(el).text();\n if (css) allRules.push(...parseStyleSheet(css));\n });\n\n // First pass: capture each element's original inline styles, so we can\n // distinguish \"author wrote this inline\" from \"we just resolved a rule into it\".\n const inlineByEl = new WeakMap<object, Record<string, string>>();\n $(\"[style]\").each((_, el) => {\n inlineByEl.set(el as object, parseStyleAttribute($(el).attr(\"style\")));\n });\n\n // Second pass: apply rules in source order. Later rules override earlier\n // resolved ones; original inline always wins at the end.\n const resolvedByEl = new WeakMap<object, Record<string, string>>();\n\n for (const rule of allRules) {\n for (const selector of rule.selectors) {\n let matched: ReturnType<CheerioAPI>;\n try {\n matched = $(selector);\n } catch {\n // cheerio threw on an exotic selector — skip the rule.\n continue;\n }\n matched.each((_, el) => {\n const key = el as object;\n const current = resolvedByEl.get(key) ?? {};\n for (const [k, v] of Object.entries(rule.declarations)) {\n current[k] = v;\n }\n resolvedByEl.set(key, current);\n });\n }\n }\n\n // Third pass: merge resolved + original inline (inline wins) and write back.\n $(\"*\").each((_, el) => {\n const key = el as object;\n const resolved = resolvedByEl.get(key);\n if (!resolved) return;\n const inline = inlineByEl.get(key) ?? {};\n const merged: Record<string, string> = { ...resolved };\n for (const [k, v] of Object.entries(inline)) merged[k] = v;\n $(el).attr(\"style\", serializeStyleAttribute(merged));\n });\n\n styleTags.remove();\n}\n","import type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element, AnyNode } from \"domhandler\";\nimport {\n createTitleBlock,\n createParagraphBlock,\n createImageBlock,\n createButtonBlock,\n createDividerBlock,\n createSpacerBlock,\n createHtmlBlock,\n} from \"@templatical/types\";\nimport type { Block, HeadingLevel, SpacingValue } from \"@templatical/types\";\nimport type { ImportReportEntry } from \"./types\";\nimport {\n parseAlignment,\n parseBorderShorthand,\n parseColor,\n parseFontFamily,\n parseFontWeight,\n parsePxValue,\n parseStyleAttribute,\n readPaddingFromStyles,\n} from \"./style-parser\";\n\nconst HEADING_TAGS = new Set([\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"]);\nconst TEXT_TAGS = new Set([\"p\", \"span\", \"div\"]);\n\nfunction emptyMargin(): SpacingValue {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction emptyPadding(): SpacingValue {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction tagOf(el: Element | AnyNode): string {\n if (\"tagName\" in el && typeof el.tagName === \"string\")\n return el.tagName.toLowerCase();\n return \"\";\n}\n\nfunction getStyles($el: Cheerio<Element>): Record<string, string> {\n return parseStyleAttribute($el.attr(\"style\"));\n}\n\n/**\n * Returns the inner HTML of `$el`.\n */\nexport function getInnerHtml($el: Cheerio<Element>): string {\n return $el.html() ?? \"\";\n}\n\nfunction ensureParagraphWrapped(html: string): string {\n if (!html.trim()) return \"<p></p>\";\n if (/<(p|h[1-6]|ul|ol|blockquote)[\\s>]/i.test(html)) return html;\n return `<p>${html}</p>`;\n}\n\nfunction safeHtmlComment(message: string, raw: string): string {\n const escapedMessage = message\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n return `<!-- ${escapedMessage} -->\\n${raw}`;\n}\n\n/**\n * Heading element (h1-h6) → Title block.\n */\nfunction convertHeading($el: Cheerio<Element>): Block {\n const tag = tagOf($el[0]);\n const styles = getStyles($el);\n const levelMatch = tag.match(/^h(\\d)$/);\n const rawLevel = levelMatch ? Number(levelMatch[1]) : 2;\n const level: HeadingLevel = (\n rawLevel >= 1 && rawLevel <= 4 ? rawLevel : Math.min(rawLevel, 4)\n ) as HeadingLevel;\n\n const innerHtml = getInnerHtml($el);\n const content = innerHtml.trim() ? `<p>${innerHtml}</p>` : \"<p></p>\";\n\n return createTitleBlock({\n content,\n level,\n color: parseColor(styles.color) || \"#1a1a1a\",\n textAlign: parseAlignment(styles[\"text-align\"]),\n fontFamily: parseFontFamily(styles[\"font-family\"]) || undefined,\n styles: {\n padding: readPaddingFromStyles(styles),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Paragraph or block-level text container → Paragraph block.\n */\nfunction convertParagraph($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const innerHtml = getInnerHtml($el);\n const wrapped = ensureParagraphWrapped(innerHtml);\n\n // Apply container-level styles to the wrapping <p>.\n const fontParts: string[] = [];\n const fontSize = parsePxValue(styles[\"font-size\"]);\n if (fontSize && fontSize !== 16) fontParts.push(`font-size: ${fontSize}px`);\n const color = parseColor(styles.color);\n if (color && color !== \"#1a1a1a\") fontParts.push(`color: ${color}`);\n const fontWeight = parseFontWeight(styles[\"font-weight\"]);\n if (fontWeight) fontParts.push(`font-weight: ${fontWeight}`);\n const fontFamily = parseFontFamily(styles[\"font-family\"]);\n if (fontFamily) fontParts.push(`font-family: ${fontFamily}`);\n const textAlign = styles[\"text-align\"];\n\n let result = wrapped;\n if (textAlign && textAlign !== \"left\") {\n result = result\n .replace(\n /<p style=\"([^\"]*)\">/g,\n `<p style=\"$1; text-align: ${textAlign}\">`,\n )\n .replaceAll(\"<p>\", `<p style=\"text-align: ${textAlign}\">`);\n }\n if (fontParts.length > 0) {\n const span = fontParts.join(\"; \");\n result = result.replace(\n /<p([^>]*)>([\\s\\S]*?)<\\/p>/g,\n `<p$1><span style=\"${span}\">$2</span></p>`,\n );\n }\n\n return createParagraphBlock({\n content: result,\n styles: {\n padding: readPaddingFromStyles(styles),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * <img> → Image block.\n */\nfunction convertImage($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const src = $el.attr(\"src\") ?? \"\";\n const alt = $el.attr(\"alt\") ?? \"\";\n const widthAttr = $el.attr(\"width\");\n const widthStyle = styles.width;\n const width = parsePxValue(widthAttr) || parsePxValue(widthStyle) || 600;\n\n return createImageBlock({\n src,\n alt,\n width,\n align: parseAlignment(styles[\"text-align\"], \"center\"),\n styles: {\n padding: readPaddingFromStyles(styles),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * <a> styled as a button → Button block.\n *\n * Heuristic: a single `<a>` with a non-transparent background-color OR padding\n * OR border-radius OR display: inline-block / block is treated as a button.\n */\nexport function looksLikeButton(styles: Record<string, string>): boolean {\n if (parseColor(styles[\"background-color\"]) || parseColor(styles.background))\n return true;\n if (\n styles.padding ||\n styles[\"padding-top\"] ||\n styles[\"padding-bottom\"] ||\n styles[\"padding-left\"] ||\n styles[\"padding-right\"]\n )\n return true;\n if (parsePxValue(styles[\"border-radius\"])) return true;\n const display = (styles.display ?? \"\").toLowerCase();\n if (display === \"inline-block\" || display === \"block\") return true;\n return false;\n}\n\nfunction convertButton($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const text = ($el.text() ?? \"Button\").trim() || \"Button\";\n const url = $el.attr(\"href\") ?? \"#\";\n const target = $el.attr(\"target\");\n\n return createButtonBlock({\n text,\n url,\n openInNewTab: target === \"_blank\" || undefined,\n backgroundColor:\n parseColor(styles[\"background-color\"]) ||\n parseColor(styles.background) ||\n \"#4f46e5\",\n textColor: parseColor(styles.color) || \"#ffffff\",\n borderRadius: parsePxValue(styles[\"border-radius\"]),\n fontSize: parsePxValue(styles[\"font-size\"]) || 16,\n fontFamily: parseFontFamily(styles[\"font-family\"]) || undefined,\n buttonPadding: readPaddingFromStyles(styles),\n styles: {\n padding: emptyPadding(),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * <hr> → Divider block.\n */\nfunction convertDivider($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const border = parseBorderShorthand(styles[\"border-top\"] ?? styles.border);\n const lineStyle =\n border.style === \"dashed\" || border.style === \"dotted\"\n ? border.style\n : \"solid\";\n\n return createDividerBlock({\n lineStyle: lineStyle as \"solid\" | \"dashed\" | \"dotted\",\n color: border.color || \"#e5e7eb\",\n thickness: border.width || 1,\n width: 100,\n styles: {\n padding: readPaddingFromStyles(styles),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Empty `<td>` with explicit height → Spacer block.\n */\nfunction convertSpacer($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const heightAttr = $el.attr(\"height\");\n const height =\n parsePxValue(heightAttr) ||\n parsePxValue(styles.height) ||\n parsePxValue(styles[\"line-height\"]) ||\n 24;\n\n return createSpacerBlock({\n height,\n styles: {\n padding: emptyPadding(),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Wraps the element's outerHTML in an HTML block (the lossless fallback).\n */\nexport function convertHtmlFallback(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n note?: string,\n): Block {\n const outer = $.html($el) ?? \"\";\n const content = note ? safeHtmlComment(note, outer) : outer;\n const styles = getStyles($el);\n\n return createHtmlBlock({\n content,\n styles: {\n padding: readPaddingFromStyles(styles),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Decides whether a `<td>` looks like a vertical spacer:\n * empty (or only ` `) AND has an explicit height.\n */\nexport function isSpacerCell($el: Cheerio<Element>): boolean {\n const text = ($el.text() ?? \"\").replace(/\\s| /g, \"\");\n if (text !== \"\") return false;\n if ($el.find(\"img, a, hr\").length > 0) return false;\n\n const styles = getStyles($el);\n const hasHeight =\n parsePxValue($el.attr(\"height\")) > 0 ||\n parsePxValue(styles.height) > 0 ||\n parsePxValue(styles[\"line-height\"]) > 0;\n return hasHeight;\n}\n\n/**\n * Decides whether a `<td>` is a button container — i.e. has exactly one\n * `<a>` inside that itself looks like a button.\n */\nexport function isButtonCell(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n): { match: boolean; anchor?: Cheerio<Element> } {\n const anchors = $el.find(\"a\");\n if (anchors.length !== 1) return { match: false };\n const anchor = $(anchors[0]);\n if (looksLikeButton(getStyles(anchor))) return { match: true, anchor };\n // Cell-level styling (bg, padding) wrapping a plain anchor reads as a\n // button only when the anchor actually has an href. Without one, the\n // anchor is a decorative styled span and should fall through to the\n // text-conversion path; otherwise convertButton defaults href to \"#\"\n // and the import becomes a clickable button to nowhere.\n if (looksLikeButton(getStyles($el))) {\n const href = (anchor.attr(\"href\") ?? \"\").trim();\n if (href !== \"\") {\n return { match: true, anchor };\n }\n }\n return { match: false };\n}\n\n/**\n * Converts a single content-bearing element (heading / paragraph / image /\n * anchor-as-button / divider) to a Templatical block.\n *\n * Returns `null` for elements that do not contain any meaningful content\n * (the caller should skip them).\n */\nexport function convertElement(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n): { block: Block; entry: ImportReportEntry } | null {\n const tag = tagOf($el[0]);\n if (!tag) return null;\n\n if (HEADING_TAGS.has(tag)) {\n return {\n block: convertHeading($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"title\",\n status: \"converted\",\n },\n };\n }\n\n if (tag === \"img\") {\n return {\n block: convertImage($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"image\",\n status: \"converted\",\n },\n };\n }\n\n if (tag === \"a\") {\n if (looksLikeButton(getStyles($el))) {\n return {\n block: convertButton($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"button\",\n status: \"converted\",\n },\n };\n }\n // Plain anchor — wrap as paragraph.\n return {\n block: convertParagraph($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"paragraph\",\n status: \"approximated\",\n note: \"Inline anchor wrapped in a paragraph block.\",\n },\n };\n }\n\n if (tag === \"hr\") {\n return {\n block: convertDivider($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"divider\",\n status: \"converted\",\n },\n };\n }\n\n if (TEXT_TAGS.has(tag)) {\n const text = ($el.text() ?? \"\").trim();\n if (!text && $el.find(\"img, a\").length === 0) return null;\n return {\n block: convertParagraph($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"paragraph\",\n status: \"converted\",\n },\n };\n }\n\n // Unknown element — preserve as HTML.\n return {\n block: convertHtmlFallback(\n $el,\n $,\n `Unsupported element <${tag}>: preserved as raw HTML`,\n ),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: `Unknown element \"${tag}\" preserved as HTML block.`,\n },\n };\n}\n\n/**\n * Helpers exported for tests.\n */\nexport const _internal = {\n convertButton,\n convertDivider,\n convertHeading,\n convertImage,\n convertParagraph,\n convertSpacer,\n ensureParagraphWrapped,\n};\n","import type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element } from \"domhandler\";\nimport {\n createSectionBlock,\n createButtonBlock,\n createSpacerBlock,\n} from \"@templatical/types\";\nimport type { Block, ColumnLayout } from \"@templatical/types\";\nimport {\n convertElement,\n convertHtmlFallback,\n isButtonCell,\n isSpacerCell,\n looksLikeButton,\n} from \"./block-mapper\";\nimport {\n parseColor,\n parsePxValue,\n parseStyleAttribute,\n readPaddingFromStyles,\n} from \"./style-parser\";\nimport type { ImportReportEntry } from \"./types\";\n\nfunction emptyMargin() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction emptyPadding() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction getStyles($el: Cheerio<Element>): Record<string, string> {\n return parseStyleAttribute($el.attr(\"style\"));\n}\n\nfunction buildCellButton(\n $cell: Cheerio<Element>,\n $anchor: Cheerio<Element>,\n): Block {\n const cellStyles = getStyles($cell);\n const aStyles = getStyles($anchor);\n // Anchor styles win when they overlap (typical: anchor sets text color, cell sets bg).\n const merged = { ...cellStyles, ...aStyles };\n const text = ($anchor.text() ?? \"Button\").trim() || \"Button\";\n const url = $anchor.attr(\"href\") ?? \"#\";\n const target = $anchor.attr(\"target\");\n\n return createButtonBlock({\n text,\n url,\n openInNewTab: target === \"_blank\" || undefined,\n backgroundColor:\n parseColor(merged[\"background-color\"]) ||\n parseColor(merged.background) ||\n \"#4f46e5\",\n textColor: parseColor(merged.color) || \"#ffffff\",\n borderRadius: parsePxValue(merged[\"border-radius\"]),\n fontSize: parsePxValue(merged[\"font-size\"]) || 16,\n buttonPadding: readPaddingFromStyles(merged),\n styles: {\n padding: emptyPadding(),\n margin: emptyMargin(),\n },\n });\n}\n\nfunction buildSpacerFromCell($cell: Cheerio<Element>): Block {\n const cellStyles = getStyles($cell);\n const height =\n parsePxValue($cell.attr(\"height\")) ||\n parsePxValue(cellStyles.height) ||\n parsePxValue(cellStyles[\"line-height\"]) ||\n 24;\n return createSpacerBlock({\n height,\n styles: {\n padding: emptyPadding(),\n margin: emptyMargin(),\n },\n });\n}\n\n/**\n * Returns the direct child `<tr>` rows of a table, including those one level\n * inside `<thead>`, `<tbody>`, or `<tfoot>` (which the HTML parser inserts\n * automatically).\n */\nfunction getDirectRows(\n $table: Cheerio<Element>,\n $: CheerioAPI,\n): Cheerio<Element>[] {\n const rows: Cheerio<Element>[] = [];\n $table.children(\"tr\").each((_, el) => {\n rows.push($(el) as unknown as Cheerio<Element>);\n });\n $table.children(\"thead, tbody, tfoot\").each((_, group) => {\n $(group)\n .children(\"tr\")\n .each((_i, el) => {\n rows.push($(el) as unknown as Cheerio<Element>);\n });\n });\n return rows;\n}\n\nfunction getDirectCells(\n $row: Cheerio<Element>,\n $: CheerioAPI,\n): Cheerio<Element>[] {\n const cells: Cheerio<Element>[] = [];\n $row.children(\"td, th\").each((_, el) => {\n cells.push($(el) as unknown as Cheerio<Element>);\n });\n return cells;\n}\n\nfunction isLayoutTable($table: Cheerio<Element>, $: CheerioAPI): boolean {\n // A table is \"layout\" if any descendant carries content email blocks rely on,\n // OR if any cell contains a non-text element (custom tags, semantic blocks,\n // etc. — those should be preserved as html-fallback at the element level\n // rather than collapsing the entire table into one html block).\n // A bare data table (cells contain only text) is preserved as HTML.\n if (\n $table.find(\n \"img, a, h1, h2, h3, h4, h5, h6, table, hr, p, div, span, ul, ol, li, blockquote, video, iframe\",\n ).length > 0\n )\n return true;\n\n let hasNonStandardChild = false;\n $table.find(\"td, th\").each((_, td) => {\n if (hasNonStandardChild) return;\n if ($(td).children().length > 0) hasNonStandardChild = true;\n });\n return hasNonStandardChild;\n}\n\nfunction resolveColumnLayout(\n cellCount: number,\n warnings: string[],\n): ColumnLayout {\n if (cellCount <= 1) return \"1\";\n if (cellCount === 2) return \"2\";\n if (cellCount === 3) return \"3\";\n warnings.push(\n `Row with ${cellCount} columns was flattened to a single column. Templatical supports up to 3 columns per section.`,\n );\n return \"1\";\n}\n\nfunction extractCellBlocks(\n $cell: Cheerio<Element>,\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n if (isSpacerCell($cell)) {\n entries.push({\n sourceTag: \"td\",\n templaticalBlockType: \"spacer\",\n status: \"converted\",\n });\n return [buildSpacerFromCell($cell)];\n }\n\n const btn = isButtonCell($cell, $);\n if (btn.match && btn.anchor) {\n entries.push({\n sourceTag: \"td\",\n templaticalBlockType: \"button\",\n status: \"converted\",\n });\n return [buildCellButton($cell, btn.anchor)];\n }\n\n const blocks: Block[] = [];\n const childEls = $cell.children().toArray();\n\n if (childEls.length === 0) {\n const text = ($cell.text() ?? \"\").trim();\n if (!text) return [];\n const r = convertElement($cell, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n return blocks;\n }\n\n for (const childEl of childEls) {\n const $child = $(childEl) as unknown as Cheerio<Element>;\n const tag = childEl.tagName?.toLowerCase() ?? \"\";\n\n if (tag === \"table\") {\n const inner = processTable($child, $, entries, warnings, true);\n blocks.push(...inner);\n continue;\n }\n\n if (tag === \"a\" && looksLikeButton(getStyles($child))) {\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n continue;\n }\n\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n }\n\n return blocks;\n}\n\n/**\n * Walk a `<table>` and produce Section blocks (one per row).\n *\n * @param flattenInline - When true (used for nested tables), drop the section\n * wrapper and return the flat block list. Templatical sections cannot nest,\n * so nested layout-tables are merged into their parent cell.\n */\nexport function processTable(\n $table: Cheerio<Element>,\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n flattenInline = false,\n): Block[] {\n if (!isLayoutTable($table, $)) {\n entries.push({\n sourceTag: \"table\",\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: \"Data table preserved as HTML block.\",\n });\n return [convertHtmlFallback($table, $, \"Data table preserved as HTML\")];\n }\n\n const rows = getDirectRows($table, $);\n if (rows.length === 0) return [];\n\n const sections: Block[] = [];\n\n for (const $row of rows) {\n const cells = getDirectCells($row, $);\n if (cells.length === 0) continue;\n\n const layout = resolveColumnLayout(cells.length, warnings);\n\n let columnsBlocks: Block[][];\n if (layout === \"1\") {\n const merged: Block[] = [];\n for (const $cell of cells) {\n merged.push(...extractCellBlocks($cell, $, entries, warnings));\n }\n columnsBlocks = [merged];\n } else {\n columnsBlocks = cells.map(($cell) =>\n extractCellBlocks($cell, $, entries, warnings),\n );\n }\n\n if (flattenInline) {\n for (const col of columnsBlocks) sections.push(...col);\n continue;\n }\n\n const rowStyles = getStyles($row);\n const bgColor =\n parseColor(rowStyles[\"background-color\"]) ||\n parseColor(rowStyles.background);\n const padding = readPaddingFromStyles(rowStyles);\n\n sections.push(\n createSectionBlock({\n columns: layout,\n children: columnsBlocks,\n styles: {\n padding,\n margin: emptyMargin(),\n ...(bgColor ? { backgroundColor: bgColor } : {}),\n },\n }),\n );\n }\n\n return sections;\n}\n"],"mappings":"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","names":["createSectionBlock","createButtonBlock","createSpacerBlock","emptyMargin","emptyPadding","getStyles","createButtonBlock","createSpacerBlock","r","emptyMargin","emptyPadding","createSectionBlock","r"]} | ||
| {"version":3,"sources":["../src/converter.ts","../src/style-parser.ts","../src/css-resolver.ts","../src/block-mapper.ts","../src/section-builder.ts"],"sourcesContent":["import { load } from \"cheerio\";\nimport type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element } from \"domhandler\";\nimport {\n createDefaultTemplateContent,\n createSectionBlock,\n} from \"@templatical/types\";\nimport type { Block, TemplateContent } from \"@templatical/types\";\nimport { resolveCssStyles } from \"./css-resolver\";\nimport { convertElement } from \"./block-mapper\";\nimport { processTable } from \"./section-builder\";\nimport {\n parseColor,\n parseFontFamily,\n parsePxValue,\n parseStyleAttribute,\n} from \"./style-parser\";\nimport type { ImportReport, ImportReportEntry, ImportResult } from \"./types\";\n\nfunction emptyPadding() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction readPreheader($: CheerioAPI): string | undefined {\n // Convention: a hidden <div> at the very top with `display:none` containing\n // the preheader text. Match if it appears in the first ~3 children of body.\n const candidates = $(\"body\")\n .children()\n .slice(0, 5)\n .filter((_, el) => {\n const styles = parseStyleAttribute($(el).attr(\"style\"));\n return (styles.display ?? \"\").toLowerCase() === \"none\";\n });\n if (candidates.length === 0) return undefined;\n const text = $(candidates[0]).text().trim();\n return text || undefined;\n}\n\nfunction extractSettings($: CheerioAPI): TemplateContent[\"settings\"] {\n const $body = $(\"body\");\n const bodyStyles = parseStyleAttribute($body.attr(\"style\"));\n const fontFamily = parseFontFamily(bodyStyles[\"font-family\"]) || \"Arial\";\n const backgroundColor =\n parseColor(bodyStyles[\"background-color\"]) ||\n parseColor(bodyStyles.background) ||\n \"#ffffff\";\n\n // Width heuristic: outermost table width attr, or \".container\" width style.\n const $outerTable = $body.find(\"table\").first();\n const widthAttr = parsePxValue($outerTable.attr(\"width\"));\n const widthStyle = parsePxValue(\n parseStyleAttribute($outerTable.attr(\"style\")).width,\n );\n const width = widthAttr || widthStyle || 600;\n\n const preheaderText = readPreheader($);\n\n return {\n width,\n backgroundColor,\n fontFamily,\n locale: \"en\",\n ...(preheaderText ? { preheaderText } : {}),\n };\n}\n\n/**\n * Wrap a list of free-floating blocks (those produced by top-level non-table\n * elements) in a single one-column section.\n */\nfunction wrapInSection(blocks: Block[]): Block {\n return createSectionBlock({\n columns: \"1\",\n children: [blocks],\n styles: {\n padding: emptyPadding(),\n },\n });\n}\n\n/**\n * Walk top-level body children. Tables become sections; loose content\n * elements are accumulated and wrapped in a single one-column section.\n */\nfunction processBody(\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n const blocks: Block[] = [];\n const $body = $(\"body\");\n const children = $body.children().toArray();\n\n let pendingLoose: Block[] = [];\n\n const flushLoose = () => {\n if (pendingLoose.length > 0) {\n blocks.push(wrapInSection(pendingLoose));\n pendingLoose = [];\n }\n };\n\n for (const childEl of children) {\n const tag = childEl.tagName?.toLowerCase() ?? \"\";\n const $child = $(childEl) as unknown as Cheerio<Element>;\n\n if (tag === \"table\") {\n flushLoose();\n blocks.push(...processTable($child, $, entries, warnings, false));\n continue;\n }\n\n // Skip hidden preheader divs — already captured in settings.\n const childStyles = parseStyleAttribute($child.attr(\"style\"));\n if ((childStyles.display ?? \"\").toLowerCase() === \"none\") continue;\n\n // Containers like a wrapping <div> with table children: recurse.\n if (\n (tag === \"div\" || tag === \"center\" || tag === \"main\") &&\n $child.find(\"table\").length > 0\n ) {\n flushLoose();\n $child.children().each((_, innerEl) => {\n const innerTag = innerEl.tagName?.toLowerCase() ?? \"\";\n const $inner = $(innerEl) as unknown as Cheerio<Element>;\n if (innerTag === \"table\") {\n blocks.push(...processTable($inner, $, entries, warnings, false));\n } else {\n const r = convertElement($inner, $);\n if (r) {\n entries.push(r.entry);\n pendingLoose.push(r.block);\n }\n }\n });\n flushLoose();\n continue;\n }\n\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n pendingLoose.push(r.block);\n }\n }\n\n flushLoose();\n return blocks;\n}\n\n/**\n * Converts an HTML email template to Templatical TemplateContent.\n *\n * Designed for table-based marketing email HTML (output of MJML, Mailchimp,\n * SendGrid, Campaign Monitor, hand-coded emails). Modern HTML using flex/grid\n * layouts is preserved via HTML-fallback blocks.\n *\n * @param html - The raw HTML string (full document or body fragment).\n * @returns An ImportResult with the converted content and a detailed report.\n *\n * @example\n * ```ts\n * import { convertHtmlTemplate } from '@templatical/import-html';\n *\n * const html = await fetch('/email.html').then((r) => r.text());\n * const { content, report } = convertHtmlTemplate(html);\n *\n * const editor = init({ container: '#editor', content });\n *\n * console.log(report.summary);\n * console.log(report.warnings);\n * ```\n */\nexport function convertHtmlTemplate(html: string): ImportResult {\n if (typeof html !== \"string\") {\n throw new Error(\n \"Invalid HTML template: expected a string. Pass the raw HTML source as a string.\",\n );\n }\n if (html.trim().length === 0) {\n throw new Error(\n \"Invalid HTML template: input is empty. Pass the raw HTML source of an email.\",\n );\n }\n\n const $ = load(html);\n resolveCssStyles($);\n\n // Drop tags that are never useful in the editor canvas.\n $(\"script, noscript, link, meta, title\").remove();\n\n const entries: ImportReportEntry[] = [];\n const warnings: string[] = [];\n\n const blocks = processBody($, entries, warnings);\n\n if (blocks.length === 0) {\n warnings.push(\n \"No convertible content was found in the HTML. The email may use a non-table layout — modern HTML support is limited.\",\n );\n }\n\n const content: TemplateContent = {\n ...createDefaultTemplateContent(),\n blocks,\n settings: extractSettings($),\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 type { SpacingValue } from \"@templatical/types\";\n\n/**\n * Parses a CSS `style=\"...\"` attribute string into a flat key/value record.\n * Keys are lowercased; values are trimmed. Quotes around values are not stripped.\n */\nexport function parseStyleAttribute(\n styleAttr: string | undefined,\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!styleAttr) return result;\n\n for (const decl of styleAttr.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const key = decl.slice(0, idx).trim().toLowerCase();\n const value = decl.slice(idx + 1).trim();\n if (key && value) result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Serializes a flat key/value record back to a `style` attribute string.\n */\nexport function serializeStyleAttribute(\n styles: Record<string, string>,\n): string {\n return Object.entries(styles)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\"; \");\n}\n\n/**\n * Parses a px-like CSS value (`\"12px\"`, `\"12\"`, `12`) into a rounded integer.\n * Returns 0 for missing or unparseable input. Ignores em/% units.\n */\nexport function parsePxValue(value: string | number | undefined): number {\n if (value === undefined || value === null || value === \"\") return 0;\n if (typeof value === \"number\") return Math.round(value);\n const match = value.match(/^(-?\\d+(?:\\.\\d+)?)\\s*(?:px)?\\s*$/);\n return match ? Math.round(parseFloat(match[1])) : 0;\n}\n\n/**\n * Parses a width value that may be a percentage. Returns the numeric percent\n * (0-100). For non-percent values, returns 100.\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\nconst NAMED_COLORS: Record<string, string> = {\n black: \"#000000\",\n white: \"#ffffff\",\n red: \"#ff0000\",\n green: \"#008000\",\n blue: \"#0000ff\",\n yellow: \"#ffff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n gray: \"#808080\",\n grey: \"#808080\",\n silver: \"#c0c0c0\",\n maroon: \"#800000\",\n olive: \"#808000\",\n lime: \"#00ff00\",\n aqua: \"#00ffff\",\n teal: \"#008080\",\n navy: \"#000080\",\n fuchsia: \"#ff00ff\",\n purple: \"#800080\",\n orange: \"#ffa500\",\n pink: \"#ffc0cb\",\n};\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n const hex = (n: number) => clamp(n).toString(16).padStart(2, \"0\");\n return `#${hex(r)}${hex(g)}${hex(b)}`;\n}\n\n/**\n * Normalizes a CSS color value to a 6-digit lowercase hex string.\n * - 3-digit hex expands to 6-digit\n * - rgb()/rgba() converts to hex (alpha is dropped)\n * - Named colors map via lookup\n * - \"transparent\" / unknown returns \"\"\n */\nexport function parseColor(value: string | undefined): string {\n if (!value) return \"\";\n const trimmed = value.trim().toLowerCase();\n if (trimmed === \"transparent\" || trimmed === \"inherit\" || trimmed === \"none\")\n return \"\";\n\n if (/^#[0-9a-f]{6}$/.test(trimmed)) return trimmed;\n\n if (/^#[0-9a-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}`;\n }\n\n const rgbMatch = trimmed.match(\n /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*[\\d.]+\\s*)?\\)$/,\n );\n if (rgbMatch) {\n return rgbToHex(\n parseInt(rgbMatch[1], 10),\n parseInt(rgbMatch[2], 10),\n parseInt(rgbMatch[3], 10),\n );\n }\n\n if (NAMED_COLORS[trimmed]) return NAMED_COLORS[trimmed];\n\n return \"\";\n}\n\n/**\n * Parses a CSS `padding` shorthand (1-4 values) into a SpacingValue.\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\n/**\n * Reads CSS padding from a style record, preferring the longhand props\n * (padding-top/right/bottom/left) and falling back to the `padding` shorthand.\n */\nexport function readPaddingFromStyles(\n styles: Record<string, string>,\n): SpacingValue {\n const shorthand = parsePaddingShorthand(styles.padding);\n return {\n top: parsePxValue(styles[\"padding-top\"]) || shorthand.top,\n right: parsePxValue(styles[\"padding-right\"]) || shorthand.right,\n bottom: parsePxValue(styles[\"padding-bottom\"]) || shorthand.bottom,\n left: parsePxValue(styles[\"padding-left\"]) || shorthand.left,\n };\n}\n\n/**\n * Strips quotes and returns the first font in a font-family stack.\n */\nexport function parseFontFamily(value: string | undefined): string {\n if (!value) return \"\";\n return value\n .split(\",\")[0]\n .trim()\n .replace(/^['\"]|['\"]$/g, \"\");\n}\n\n/**\n * Normalizes a font-weight value to a string CSS keyword/number that\n * the editor accepts. Returns \"\" when the value is the default (normal/400).\n */\nexport function parseFontWeight(value: string | undefined): string {\n if (!value) return \"\";\n const trimmed = value.trim().toLowerCase();\n if (trimmed === \"normal\" || trimmed === \"400\") return \"\";\n return trimmed;\n}\n\n/**\n * Parses CSS text-align to one of the allowed editor alignments.\n */\nexport function parseAlignment(\n value: string | undefined,\n fallback: \"left\" | \"center\" | \"right\" = \"left\",\n): \"left\" | \"center\" | \"right\" {\n const v = (value ?? \"\").trim().toLowerCase();\n if (v === \"left\" || v === \"center\" || v === \"right\") return v;\n return fallback;\n}\n\n/**\n * Parses a CSS `border` shorthand (`\"1px solid #ccc\"`) into width/style/color.\n * Order-tolerant: each token is classified by content.\n */\nexport function parseBorderShorthand(value: string | undefined): {\n width: number;\n style: string;\n color: string;\n} {\n const fallback = { width: 0, style: \"solid\", color: \"#000000\" };\n if (!value) return fallback;\n\n const styleKeywords = new Set([\n \"none\",\n \"hidden\",\n \"dotted\",\n \"dashed\",\n \"solid\",\n \"double\",\n \"groove\",\n \"ridge\",\n \"inset\",\n \"outset\",\n ]);\n\n let width = 0;\n let style = \"solid\";\n let color = \"#000000\";\n\n for (const token of value.trim().split(/\\s+/)) {\n const lower = token.toLowerCase();\n if (styleKeywords.has(lower)) {\n style = lower;\n } else if (/^-?\\d+(?:\\.\\d+)?(?:px)?$/i.test(lower)) {\n width = parsePxValue(lower);\n } else {\n const c = parseColor(lower);\n if (c) color = c;\n }\n }\n\n return { width, style, color };\n}\n","import type { CheerioAPI } from \"cheerio\";\nimport { parseStyleAttribute, serializeStyleAttribute } from \"./style-parser\";\n\ninterface CssRule {\n selectors: string[];\n declarations: Record<string, string>;\n}\n\n/**\n * Strips all CSS comments. Handles nested-looking content safely.\n */\nfunction stripComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n}\n\n/**\n * Strips at-rule blocks (@media, @font-face, @keyframes, @supports, etc.)\n * and their nested content. Leaves top-level rules in place.\n *\n * Email HTML rarely benefits from @media (we render at one viewport),\n * and resolving it onto elements would not be visually faithful anyway.\n */\nfunction stripAtRules(css: string): string {\n let result = \"\";\n let i = 0;\n while (i < css.length) {\n if (css[i] === \"@\") {\n // skip until matching `{...}` block or terminating `;`\n const semiIdx = css.indexOf(\";\", i);\n const braceIdx = css.indexOf(\"{\", i);\n\n if (braceIdx === -1 || (semiIdx !== -1 && semiIdx < braceIdx)) {\n i = semiIdx === -1 ? css.length : semiIdx + 1;\n continue;\n }\n\n // skip the entire `{...}` block, accounting for nesting\n let depth = 0;\n let j = braceIdx;\n for (; j < css.length; j++) {\n if (css[j] === \"{\") depth++;\n else if (css[j] === \"}\") {\n depth--;\n if (depth === 0) {\n j++;\n break;\n }\n }\n }\n i = j;\n } else {\n result += css[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Parses a CSS declarations block (`color: red; font-size: 14px`) into\n * a flat record. `!important` markers are dropped.\n */\nfunction parseDeclarations(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const decl of text.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const key = decl.slice(0, idx).trim().toLowerCase();\n let value = decl.slice(idx + 1).trim();\n value = value.replace(/!important\\s*$/i, \"\").trim();\n if (key && value) result[key] = value;\n }\n return result;\n}\n\n/**\n * A selector is \"supported\" by cheerio's matcher if it has no pseudo-classes\n * or pseudo-elements. Resolving e.g. `a:hover` onto an inline style would be\n * wrong (it would always apply), so we skip such rules entirely.\n */\nfunction isSupportedSelector(selector: string): boolean {\n if (!selector) return false;\n if (selector.includes(\":\")) return false;\n if (selector.includes(\"@\")) return false;\n return true;\n}\n\n/**\n * Parses the full content of one or more `<style>` tags into a list of rules.\n * Skips at-rules and selectors with pseudo-classes.\n */\nexport function parseStyleSheet(css: string): CssRule[] {\n const rules: CssRule[] = [];\n const cleaned = stripAtRules(stripComments(css));\n\n // Greedily walk top-level `selectors { decls }` blocks.\n const blockRe = /([^{}]+)\\{([^{}]*)\\}/g;\n let match: RegExpExecArray | null;\n while ((match = blockRe.exec(cleaned)) !== null) {\n const selectorPart = match[1].trim();\n const declarationPart = match[2];\n if (!selectorPart) continue;\n\n const selectors = selectorPart\n .split(\",\")\n .map((s) => s.trim())\n .filter(isSupportedSelector);\n if (selectors.length === 0) continue;\n\n const declarations = parseDeclarations(declarationPart);\n if (Object.keys(declarations).length === 0) continue;\n\n rules.push({ selectors, declarations });\n }\n\n return rules;\n}\n\n/**\n * Reads all `<style>` tags from the document, parses them into rules,\n * applies each rule's declarations to matching elements (merging with\n * existing inline `style=\"\"` attributes — inline always wins), and removes\n * the `<style>` tags from the document.\n *\n * No specificity is computed; rules are applied in source order, with later\n * rules overriding earlier ones. Inline styles always override resolved\n * rules. This is sufficient for typical email HTML, where authors already\n * inline most styles.\n */\nexport function resolveCssStyles($: CheerioAPI): void {\n const styleTags = $(\"style\");\n if (styleTags.length === 0) return;\n\n const allRules: CssRule[] = [];\n styleTags.each((_, el) => {\n const css = $(el).text();\n if (css) allRules.push(...parseStyleSheet(css));\n });\n\n // First pass: capture each element's original inline styles, so we can\n // distinguish \"author wrote this inline\" from \"we just resolved a rule into it\".\n const inlineByEl = new WeakMap<object, Record<string, string>>();\n $(\"[style]\").each((_, el) => {\n inlineByEl.set(el as object, parseStyleAttribute($(el).attr(\"style\")));\n });\n\n // Second pass: apply rules in source order. Later rules override earlier\n // resolved ones; original inline always wins at the end.\n const resolvedByEl = new WeakMap<object, Record<string, string>>();\n\n for (const rule of allRules) {\n for (const selector of rule.selectors) {\n let matched: ReturnType<CheerioAPI>;\n try {\n matched = $(selector);\n } catch {\n // cheerio threw on an exotic selector — skip the rule.\n continue;\n }\n matched.each((_, el) => {\n const key = el as object;\n const current = resolvedByEl.get(key) ?? {};\n for (const [k, v] of Object.entries(rule.declarations)) {\n current[k] = v;\n }\n resolvedByEl.set(key, current);\n });\n }\n }\n\n // Third pass: merge resolved + original inline (inline wins) and write back.\n $(\"*\").each((_, el) => {\n const key = el as object;\n const resolved = resolvedByEl.get(key);\n if (!resolved) return;\n const inline = inlineByEl.get(key) ?? {};\n const merged: Record<string, string> = { ...resolved };\n for (const [k, v] of Object.entries(inline)) merged[k] = v;\n $(el).attr(\"style\", serializeStyleAttribute(merged));\n });\n\n styleTags.remove();\n}\n","import type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element, AnyNode } from \"domhandler\";\nimport {\n createTitleBlock,\n createParagraphBlock,\n createImageBlock,\n createButtonBlock,\n createDividerBlock,\n createSpacerBlock,\n createHtmlBlock,\n} from \"@templatical/types\";\nimport type { Block, HeadingLevel, SpacingValue } from \"@templatical/types\";\nimport type { ImportReportEntry } from \"./types\";\nimport {\n parseAlignment,\n parseBorderShorthand,\n parseColor,\n parseFontFamily,\n parseFontWeight,\n parsePxValue,\n parseStyleAttribute,\n readPaddingFromStyles,\n} from \"./style-parser\";\n\nconst HEADING_TAGS = new Set([\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"]);\nconst TEXT_TAGS = new Set([\"p\", \"span\", \"div\"]);\n\nfunction emptyPadding(): SpacingValue {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction tagOf(el: Element | AnyNode): string {\n if (\"tagName\" in el && typeof el.tagName === \"string\")\n return el.tagName.toLowerCase();\n return \"\";\n}\n\nfunction getStyles($el: Cheerio<Element>): Record<string, string> {\n return parseStyleAttribute($el.attr(\"style\"));\n}\n\n/**\n * Returns the inner HTML of `$el`.\n */\nexport function getInnerHtml($el: Cheerio<Element>): string {\n return $el.html() ?? \"\";\n}\n\nfunction ensureParagraphWrapped(html: string): string {\n if (!html.trim()) return \"<p></p>\";\n if (/<(p|h[1-6]|ul|ol|blockquote)[\\s>]/i.test(html)) return html;\n return `<p>${html}</p>`;\n}\n\nfunction safeHtmlComment(message: string, raw: string): string {\n const escapedMessage = message\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n return `<!-- ${escapedMessage} -->\\n${raw}`;\n}\n\n/**\n * Heading element (h1-h6) → Title block.\n */\nfunction convertHeading($el: Cheerio<Element>): Block {\n const tag = tagOf($el[0]);\n const styles = getStyles($el);\n const levelMatch = tag.match(/^h(\\d)$/);\n const rawLevel = levelMatch ? Number(levelMatch[1]) : 2;\n const level: HeadingLevel = (\n rawLevel >= 1 && rawLevel <= 4 ? rawLevel : Math.min(rawLevel, 4)\n ) as HeadingLevel;\n\n const innerHtml = getInnerHtml($el);\n const content = innerHtml.trim() ? `<p>${innerHtml}</p>` : \"<p></p>\";\n\n return createTitleBlock({\n content,\n level,\n color: parseColor(styles.color) || \"#1a1a1a\",\n textAlign: parseAlignment(styles[\"text-align\"]),\n fontFamily: parseFontFamily(styles[\"font-family\"]) || undefined,\n styles: {\n padding: readPaddingFromStyles(styles),\n },\n });\n}\n\n/**\n * Paragraph or block-level text container → Paragraph block.\n */\nfunction convertParagraph($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const innerHtml = getInnerHtml($el);\n const wrapped = ensureParagraphWrapped(innerHtml);\n\n // Apply container-level styles to the wrapping <p>.\n const fontParts: string[] = [];\n const fontSize = parsePxValue(styles[\"font-size\"]);\n if (fontSize && fontSize !== 16) fontParts.push(`font-size: ${fontSize}px`);\n const color = parseColor(styles.color);\n if (color && color !== \"#1a1a1a\") fontParts.push(`color: ${color}`);\n const fontWeight = parseFontWeight(styles[\"font-weight\"]);\n if (fontWeight) fontParts.push(`font-weight: ${fontWeight}`);\n const fontFamily = parseFontFamily(styles[\"font-family\"]);\n if (fontFamily) fontParts.push(`font-family: ${fontFamily}`);\n const textAlign = styles[\"text-align\"];\n\n let result = wrapped;\n if (textAlign && textAlign !== \"left\") {\n result = result\n .replace(\n /<p style=\"([^\"]*)\">/g,\n `<p style=\"$1; text-align: ${textAlign}\">`,\n )\n .replaceAll(\"<p>\", `<p style=\"text-align: ${textAlign}\">`);\n }\n if (fontParts.length > 0) {\n const span = fontParts.join(\"; \");\n result = result.replace(\n /<p([^>]*)>([\\s\\S]*?)<\\/p>/g,\n `<p$1><span style=\"${span}\">$2</span></p>`,\n );\n }\n\n return createParagraphBlock({\n content: result,\n styles: {\n padding: readPaddingFromStyles(styles),\n },\n });\n}\n\n/**\n * <img> → Image block.\n */\nfunction convertImage($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const src = $el.attr(\"src\") ?? \"\";\n const alt = $el.attr(\"alt\") ?? \"\";\n const widthAttr = $el.attr(\"width\");\n const widthStyle = styles.width;\n const width = parsePxValue(widthAttr) || parsePxValue(widthStyle) || 600;\n\n return createImageBlock({\n src,\n alt,\n width,\n align: parseAlignment(styles[\"text-align\"], \"center\"),\n styles: {\n padding: readPaddingFromStyles(styles),\n },\n });\n}\n\n/**\n * <a> styled as a button → Button block.\n *\n * Heuristic: a single `<a>` with a non-transparent background-color OR padding\n * OR border-radius OR display: inline-block / block is treated as a button.\n */\nexport function looksLikeButton(styles: Record<string, string>): boolean {\n if (parseColor(styles[\"background-color\"]) || parseColor(styles.background))\n return true;\n if (\n styles.padding ||\n styles[\"padding-top\"] ||\n styles[\"padding-bottom\"] ||\n styles[\"padding-left\"] ||\n styles[\"padding-right\"]\n )\n return true;\n if (parsePxValue(styles[\"border-radius\"])) return true;\n const display = (styles.display ?? \"\").toLowerCase();\n if (display === \"inline-block\" || display === \"block\") return true;\n return false;\n}\n\nfunction convertButton($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const text = ($el.text() ?? \"Button\").trim() || \"Button\";\n const url = $el.attr(\"href\") ?? \"#\";\n const target = $el.attr(\"target\");\n\n return createButtonBlock({\n text,\n url,\n openInNewTab: target === \"_blank\" || undefined,\n backgroundColor:\n parseColor(styles[\"background-color\"]) ||\n parseColor(styles.background) ||\n \"#4f46e5\",\n textColor: parseColor(styles.color) || \"#ffffff\",\n borderRadius: parsePxValue(styles[\"border-radius\"]),\n fontSize: parsePxValue(styles[\"font-size\"]) || 16,\n fontFamily: parseFontFamily(styles[\"font-family\"]) || undefined,\n buttonPadding: readPaddingFromStyles(styles),\n styles: {\n padding: emptyPadding(),\n },\n });\n}\n\n/**\n * <hr> → Divider block.\n */\nfunction convertDivider($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const border = parseBorderShorthand(styles[\"border-top\"] ?? styles.border);\n const lineStyle =\n border.style === \"dashed\" || border.style === \"dotted\"\n ? border.style\n : \"solid\";\n\n return createDividerBlock({\n lineStyle: lineStyle as \"solid\" | \"dashed\" | \"dotted\",\n color: border.color || \"#e5e7eb\",\n thickness: border.width || 1,\n width: 100,\n styles: {\n padding: readPaddingFromStyles(styles),\n },\n });\n}\n\n/**\n * Empty `<td>` with explicit height → Spacer block.\n */\nfunction convertSpacer($el: Cheerio<Element>): Block {\n const styles = getStyles($el);\n const heightAttr = $el.attr(\"height\");\n const height =\n parsePxValue(heightAttr) ||\n parsePxValue(styles.height) ||\n parsePxValue(styles[\"line-height\"]) ||\n 24;\n\n return createSpacerBlock({\n height,\n styles: {\n padding: emptyPadding(),\n },\n });\n}\n\n/**\n * Wraps the element's outerHTML in an HTML block (the lossless fallback).\n */\nexport function convertHtmlFallback(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n note?: string,\n): Block {\n const outer = $.html($el) ?? \"\";\n const content = note ? safeHtmlComment(note, outer) : outer;\n const styles = getStyles($el);\n\n return createHtmlBlock({\n content,\n styles: {\n padding: readPaddingFromStyles(styles),\n },\n });\n}\n\n/**\n * Decides whether a `<td>` looks like a vertical spacer:\n * empty (or only ` `) AND has an explicit height.\n */\nexport function isSpacerCell($el: Cheerio<Element>): boolean {\n const text = ($el.text() ?? \"\").replace(/\\s| /g, \"\");\n if (text !== \"\") return false;\n if ($el.find(\"img, a, hr\").length > 0) return false;\n\n const styles = getStyles($el);\n const hasHeight =\n parsePxValue($el.attr(\"height\")) > 0 ||\n parsePxValue(styles.height) > 0 ||\n parsePxValue(styles[\"line-height\"]) > 0;\n return hasHeight;\n}\n\n/**\n * Decides whether a `<td>` is a button container — i.e. has exactly one\n * `<a>` inside that itself looks like a button.\n */\nexport function isButtonCell(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n): { match: boolean; anchor?: Cheerio<Element> } {\n const anchors = $el.find(\"a\");\n if (anchors.length !== 1) return { match: false };\n const anchor = $(anchors[0]);\n if (looksLikeButton(getStyles(anchor))) return { match: true, anchor };\n // Cell-level styling (bg, padding) wrapping a plain anchor reads as a\n // button only when the anchor actually has an href. Without one, the\n // anchor is a decorative styled span and should fall through to the\n // text-conversion path; otherwise convertButton defaults href to \"#\"\n // and the import becomes a clickable button to nowhere.\n if (looksLikeButton(getStyles($el))) {\n const href = (anchor.attr(\"href\") ?? \"\").trim();\n if (href !== \"\") {\n return { match: true, anchor };\n }\n }\n return { match: false };\n}\n\n/**\n * Converts a single content-bearing element (heading / paragraph / image /\n * anchor-as-button / divider) to a Templatical block.\n *\n * Returns `null` for elements that do not contain any meaningful content\n * (the caller should skip them).\n */\nexport function convertElement(\n $el: Cheerio<Element>,\n $: CheerioAPI,\n): { block: Block; entry: ImportReportEntry } | null {\n const tag = tagOf($el[0]);\n if (!tag) return null;\n\n if (HEADING_TAGS.has(tag)) {\n return {\n block: convertHeading($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"title\",\n status: \"converted\",\n },\n };\n }\n\n if (tag === \"img\") {\n return {\n block: convertImage($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"image\",\n status: \"converted\",\n },\n };\n }\n\n if (tag === \"a\") {\n if (looksLikeButton(getStyles($el))) {\n return {\n block: convertButton($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"button\",\n status: \"converted\",\n },\n };\n }\n // Plain anchor — wrap as paragraph.\n return {\n block: convertParagraph($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"paragraph\",\n status: \"approximated\",\n note: \"Inline anchor wrapped in a paragraph block.\",\n },\n };\n }\n\n if (tag === \"hr\") {\n return {\n block: convertDivider($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"divider\",\n status: \"converted\",\n },\n };\n }\n\n if (TEXT_TAGS.has(tag)) {\n const text = ($el.text() ?? \"\").trim();\n if (!text && $el.find(\"img, a\").length === 0) return null;\n return {\n block: convertParagraph($el),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"paragraph\",\n status: \"converted\",\n },\n };\n }\n\n // Unknown element — preserve as HTML.\n return {\n block: convertHtmlFallback(\n $el,\n $,\n `Unsupported element <${tag}>: preserved as raw HTML`,\n ),\n entry: {\n sourceTag: tag,\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: `Unknown element \"${tag}\" preserved as HTML block.`,\n },\n };\n}\n\n/**\n * Helpers exported for tests.\n */\nexport const _internal = {\n convertButton,\n convertDivider,\n convertHeading,\n convertImage,\n convertParagraph,\n convertSpacer,\n ensureParagraphWrapped,\n};\n","import type { CheerioAPI, Cheerio } from \"cheerio\";\nimport type { Element } from \"domhandler\";\nimport {\n createSectionBlock,\n createButtonBlock,\n createSpacerBlock,\n} from \"@templatical/types\";\nimport type { Block, ColumnLayout } from \"@templatical/types\";\nimport {\n convertElement,\n convertHtmlFallback,\n isButtonCell,\n isSpacerCell,\n looksLikeButton,\n} from \"./block-mapper\";\nimport {\n parseColor,\n parsePxValue,\n parseStyleAttribute,\n readPaddingFromStyles,\n} from \"./style-parser\";\nimport type { ImportReportEntry } from \"./types\";\n\nfunction emptyPadding() {\n return { top: 0, right: 0, bottom: 0, left: 0 };\n}\n\nfunction getStyles($el: Cheerio<Element>): Record<string, string> {\n return parseStyleAttribute($el.attr(\"style\"));\n}\n\nfunction buildCellButton(\n $cell: Cheerio<Element>,\n $anchor: Cheerio<Element>,\n): Block {\n const cellStyles = getStyles($cell);\n const aStyles = getStyles($anchor);\n // Anchor styles win when they overlap (typical: anchor sets text color, cell sets bg).\n const merged = { ...cellStyles, ...aStyles };\n const text = ($anchor.text() ?? \"Button\").trim() || \"Button\";\n const url = $anchor.attr(\"href\") ?? \"#\";\n const target = $anchor.attr(\"target\");\n\n return createButtonBlock({\n text,\n url,\n openInNewTab: target === \"_blank\" || undefined,\n backgroundColor:\n parseColor(merged[\"background-color\"]) ||\n parseColor(merged.background) ||\n \"#4f46e5\",\n textColor: parseColor(merged.color) || \"#ffffff\",\n borderRadius: parsePxValue(merged[\"border-radius\"]),\n fontSize: parsePxValue(merged[\"font-size\"]) || 16,\n buttonPadding: readPaddingFromStyles(merged),\n styles: {\n padding: emptyPadding(),\n },\n });\n}\n\nfunction buildSpacerFromCell($cell: Cheerio<Element>): Block {\n const cellStyles = getStyles($cell);\n const height =\n parsePxValue($cell.attr(\"height\")) ||\n parsePxValue(cellStyles.height) ||\n parsePxValue(cellStyles[\"line-height\"]) ||\n 24;\n return createSpacerBlock({\n height,\n styles: {\n padding: emptyPadding(),\n },\n });\n}\n\n/**\n * Returns the direct child `<tr>` rows of a table, including those one level\n * inside `<thead>`, `<tbody>`, or `<tfoot>` (which the HTML parser inserts\n * automatically).\n */\nfunction getDirectRows(\n $table: Cheerio<Element>,\n $: CheerioAPI,\n): Cheerio<Element>[] {\n const rows: Cheerio<Element>[] = [];\n $table.children(\"tr\").each((_, el) => {\n rows.push($(el) as unknown as Cheerio<Element>);\n });\n $table.children(\"thead, tbody, tfoot\").each((_, group) => {\n $(group)\n .children(\"tr\")\n .each((_i, el) => {\n rows.push($(el) as unknown as Cheerio<Element>);\n });\n });\n return rows;\n}\n\nfunction getDirectCells(\n $row: Cheerio<Element>,\n $: CheerioAPI,\n): Cheerio<Element>[] {\n const cells: Cheerio<Element>[] = [];\n $row.children(\"td, th\").each((_, el) => {\n cells.push($(el) as unknown as Cheerio<Element>);\n });\n return cells;\n}\n\nfunction isLayoutTable($table: Cheerio<Element>, $: CheerioAPI): boolean {\n // A table is \"layout\" if any descendant carries content email blocks rely on,\n // OR if any cell contains a non-text element (custom tags, semantic blocks,\n // etc. — those should be preserved as html-fallback at the element level\n // rather than collapsing the entire table into one html block).\n // A bare data table (cells contain only text) is preserved as HTML.\n if (\n $table.find(\n \"img, a, h1, h2, h3, h4, h5, h6, table, hr, p, div, span, ul, ol, li, blockquote, video, iframe\",\n ).length > 0\n )\n return true;\n\n let hasNonStandardChild = false;\n $table.find(\"td, th\").each((_, td) => {\n if (hasNonStandardChild) return;\n if ($(td).children().length > 0) hasNonStandardChild = true;\n });\n return hasNonStandardChild;\n}\n\nfunction resolveColumnLayout(\n cellCount: number,\n warnings: string[],\n): ColumnLayout {\n if (cellCount <= 1) return \"1\";\n if (cellCount === 2) return \"2\";\n if (cellCount === 3) return \"3\";\n warnings.push(\n `Row with ${cellCount} columns was flattened to a single column. Templatical supports up to 3 columns per section.`,\n );\n return \"1\";\n}\n\nfunction extractCellBlocks(\n $cell: Cheerio<Element>,\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n): Block[] {\n if (isSpacerCell($cell)) {\n entries.push({\n sourceTag: \"td\",\n templaticalBlockType: \"spacer\",\n status: \"converted\",\n });\n return [buildSpacerFromCell($cell)];\n }\n\n const btn = isButtonCell($cell, $);\n if (btn.match && btn.anchor) {\n entries.push({\n sourceTag: \"td\",\n templaticalBlockType: \"button\",\n status: \"converted\",\n });\n return [buildCellButton($cell, btn.anchor)];\n }\n\n const blocks: Block[] = [];\n const childEls = $cell.children().toArray();\n\n if (childEls.length === 0) {\n const text = ($cell.text() ?? \"\").trim();\n if (!text) return [];\n const r = convertElement($cell, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n return blocks;\n }\n\n for (const childEl of childEls) {\n const $child = $(childEl) as unknown as Cheerio<Element>;\n const tag = childEl.tagName?.toLowerCase() ?? \"\";\n\n if (tag === \"table\") {\n const inner = processTable($child, $, entries, warnings, true);\n blocks.push(...inner);\n continue;\n }\n\n if (tag === \"a\" && looksLikeButton(getStyles($child))) {\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n continue;\n }\n\n const r = convertElement($child, $);\n if (r) {\n entries.push(r.entry);\n blocks.push(r.block);\n }\n }\n\n return blocks;\n}\n\n/**\n * Walk a `<table>` and produce Section blocks (one per row).\n *\n * @param flattenInline - When true (used for nested tables), drop the section\n * wrapper and return the flat block list. Templatical sections cannot nest,\n * so nested layout-tables are merged into their parent cell.\n */\nexport function processTable(\n $table: Cheerio<Element>,\n $: CheerioAPI,\n entries: ImportReportEntry[],\n warnings: string[],\n flattenInline = false,\n): Block[] {\n if (!isLayoutTable($table, $)) {\n entries.push({\n sourceTag: \"table\",\n templaticalBlockType: \"html\",\n status: \"html-fallback\",\n note: \"Data table preserved as HTML block.\",\n });\n return [convertHtmlFallback($table, $, \"Data table preserved as HTML\")];\n }\n\n const rows = getDirectRows($table, $);\n if (rows.length === 0) return [];\n\n const sections: Block[] = [];\n\n for (const $row of rows) {\n const cells = getDirectCells($row, $);\n if (cells.length === 0) continue;\n\n const layout = resolveColumnLayout(cells.length, warnings);\n\n let columnsBlocks: Block[][];\n if (layout === \"1\") {\n const merged: Block[] = [];\n for (const $cell of cells) {\n merged.push(...extractCellBlocks($cell, $, entries, warnings));\n }\n columnsBlocks = [merged];\n } else {\n columnsBlocks = cells.map(($cell) =>\n extractCellBlocks($cell, $, entries, warnings),\n );\n }\n\n if (flattenInline) {\n for (const col of columnsBlocks) sections.push(...col);\n continue;\n }\n\n const rowStyles = getStyles($row);\n const bgColor =\n parseColor(rowStyles[\"background-color\"]) ||\n parseColor(rowStyles.background);\n const padding = readPaddingFromStyles(rowStyles);\n\n sections.push(\n createSectionBlock({\n columns: layout,\n children: columnsBlocks,\n styles: {\n padding,\n ...(bgColor ? { backgroundColor: bgColor } : {}),\n },\n }),\n );\n }\n\n return sections;\n}\n"],"mappings":"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","names":["createSectionBlock","createButtonBlock","createSpacerBlock","emptyPadding","getStyles","createButtonBlock","createSpacerBlock","r","emptyPadding","createSectionBlock","r"]} |
+2
-2
| { | ||
| "name": "@templatical/import-html", | ||
| "description": "Convert HTML email templates to Templatical format", | ||
| "version": "0.9.1", | ||
| "version": "0.10.0", | ||
| "bugs": "https://github.com/templatical/sdk/issues", | ||
@@ -9,3 +9,3 @@ "dependencies": { | ||
| "domhandler": "^6.0.1", | ||
| "@templatical/types": "0.9.1" | ||
| "@templatical/types": "0.10.0" | ||
| }, | ||
@@ -12,0 +12,0 @@ "devDependencies": { |
92090
-1.71%927
-2.01%+ Added
- Removed
Updated