@samva/markup 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -0
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/ast-DAhnAvQN.d.ts +120 -0
- package/dist/ast.d.ts +2 -0
- package/dist/ast.js +159 -0
- package/dist/ast.js.map +1 -0
- package/dist/candidate.d.ts +29 -0
- package/dist/candidate.js +88 -0
- package/dist/candidate.js.map +1 -0
- package/dist/checks-CW0nFj5n.js +58922 -0
- package/dist/checks-CW0nFj5n.js.map +1 -0
- package/dist/checks.d.ts +26 -0
- package/dist/checks.js +2 -0
- package/dist/classes.d.ts +27 -0
- package/dist/classes.js +184 -0
- package/dist/classes.js.map +1 -0
- package/dist/compile-sms.d.ts +28 -0
- package/dist/compile-sms.js +44 -0
- package/dist/compile-sms.js.map +1 -0
- package/dist/compile-whatsapp.d.ts +56 -0
- package/dist/compile-whatsapp.js +186 -0
- package/dist/compile-whatsapp.js.map +1 -0
- package/dist/compile.d.ts +93 -0
- package/dist/compile.js +938 -0
- package/dist/compile.js.map +1 -0
- package/dist/components.d.ts +289 -0
- package/dist/components.js +106 -0
- package/dist/components.js.map +1 -0
- package/dist/conditionals.d.ts +10 -0
- package/dist/conditionals.js +42 -0
- package/dist/conditionals.js.map +1 -0
- package/dist/diagnostics-oveE-laV.d.ts +79 -0
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +62 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/emit.d.ts +50 -0
- package/dist/emit.js +151 -0
- package/dist/emit.js.map +1 -0
- package/dist/footer.d.ts +66 -0
- package/dist/footer.js +90 -0
- package/dist/footer.js.map +1 -0
- package/dist/interpolate.d.ts +27 -0
- package/dist/interpolate.js +61 -0
- package/dist/interpolate.js.map +1 -0
- package/dist/jsx-dev-runtime.d.ts +10 -0
- package/dist/jsx-dev-runtime.js +7 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime-fSxcvlx4.d.ts +57 -0
- package/dist/jsx-runtime.d.ts +2 -0
- package/dist/jsx-runtime.js +102 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/lexer.d.ts +60 -0
- package/dist/lexer.js +269 -0
- package/dist/lexer.js.map +1 -0
- package/dist/model.d.ts +39 -0
- package/dist/model.js +90 -0
- package/dist/model.js.map +1 -0
- package/dist/ops.d.ts +208 -0
- package/dist/ops.js +546 -0
- package/dist/ops.js.map +1 -0
- package/dist/parser-yzjUOGhc.d.ts +387 -0
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +821 -0
- package/dist/parser.js.map +1 -0
- package/dist/predicate-DTtVtLK7.d.ts +49 -0
- package/dist/predicate.d.ts +2 -0
- package/dist/predicate.js +417 -0
- package/dist/predicate.js.map +1 -0
- package/dist/presets/category-mapping.d.ts +13 -0
- package/dist/presets/category-mapping.js +49 -0
- package/dist/presets/category-mapping.js.map +1 -0
- package/dist/presets/public-gallery.d.ts +490 -0
- package/dist/presets/public-gallery.js +563 -0
- package/dist/presets/public-gallery.js.map +1 -0
- package/dist/presets/public-handoff.d.ts +68 -0
- package/dist/presets/public-handoff.js +105 -0
- package/dist/presets/public-handoff.js.map +1 -0
- package/dist/presets/public-preview-artifacts.d.ts +41 -0
- package/dist/presets/public-preview-artifacts.js +109 -0
- package/dist/presets/public-preview-artifacts.js.map +1 -0
- package/dist/presets/public-preview.d.ts +1168 -0
- package/dist/presets/public-preview.js +1158 -0
- package/dist/presets/public-preview.js.map +1 -0
- package/dist/presets/sources.d.ts +7 -0
- package/dist/presets/sources.js +54 -0
- package/dist/presets/sources.js.map +1 -0
- package/dist/presets.d.ts +350 -0
- package/dist/presets.js +1251 -0
- package/dist/presets.js.map +1 -0
- package/dist/printer.d.ts +6 -0
- package/dist/printer.js +122 -0
- package/dist/printer.js.map +1 -0
- package/dist/registry.d.ts +42 -0
- package/dist/registry.js +646 -0
- package/dist/registry.js.map +1 -0
- package/dist/render-template.d.ts +42 -0
- package/dist/render-template.js +74 -0
- package/dist/render-template.js.map +1 -0
- package/dist/sms-segments-y4FpPilJ.d.ts +13 -0
- package/dist/sms-segments.d.ts +2 -0
- package/dist/sms-segments.js +46 -0
- package/dist/sms-segments.js.map +1 -0
- package/dist/theme-6ApjjiBo.d.ts +116 -0
- package/dist/theme-css.d.ts +24 -0
- package/dist/theme-css.js +509 -0
- package/dist/theme-css.js.map +1 -0
- package/dist/theme.d.ts +2 -0
- package/dist/theme.js +95 -0
- package/dist/theme.js.map +1 -0
- package/dist/tokens.d.ts +6 -0
- package/dist/tokens.js +417 -0
- package/dist/tokens.js.map +1 -0
- package/dist/variables-3NGLpNTB.d.ts +77 -0
- package/dist/variables.d.ts +2 -0
- package/dist/variables.js +141 -0
- package/dist/variables.js.map +1 -0
- package/dist/version-BITr58kk.js +96 -0
- package/dist/version-BITr58kk.js.map +1 -0
- package/generated/sml-agent-contract.json +917 -0
- package/generated/sml-email-agent-contract.json +790 -0
- package/generated/sml-email-language-reference.md +160 -0
- package/generated/sml-language-reference.md +220 -0
- package/package.json +203 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import { Result } from \"effect\";\n\nimport type { ElementNode, SmlDocument, SmlNode } from \"./ast\";\nimport { DEFAULT_EMAIL_WIDTH, isHiddenNode, numberAttribute, stringAttribute } from \"./ast\";\nimport type { ClassVariant, ResolvedClass } from \"./classes\";\nimport { resolveClasses } from \"./classes\";\nimport type { CompiledSms } from \"./compile-sms\";\nimport { compileSmsRoot } from \"./compile-sms\";\nimport type { CompiledWhatsApp } from \"./compile-whatsapp\";\nimport { compileWhatsAppRoot } from \"./compile-whatsapp\";\nimport type { Diagnostic } from \"./diagnostics\";\nimport { SmlCompileError, SmlParseError } from \"./diagnostics\";\nimport { parseSml } from \"./parser\";\nimport type { Declaration } from \"./registry\";\nimport {\n GRADIENT_DIRECTION_PROPERTY,\n GRADIENT_FROM_PROPERTY,\n GRADIENT_TO_PROPERTY,\n} from \"./registry\";\nimport type { ThemeLayers, TokenTable } from \"./theme\";\nimport { escapeCssString, resolveTokenTable } from \"./theme\";\nimport { baseTokens } from \"./tokens\";\nimport { COMPILER_VERSION } from \"./version\";\n\n/**\n * The SML compiler: pure functions, deterministic bytes for identical\n * (source, theme, compilerVersion, base) — content addressing in R2 depends on\n * it. No Date, no randomness, no platform APIs; runs identically in Worker,\n * browser, and CLI.\n *\n * Pipeline: AST → expand primitives → resolve tokens+classes → inline (base\n * declarations → style=\"\"; sm:/dark: → head <style> with !important) → lower\n * to tables + mso fallbacks → print artifact HTML + plain-text alt.\n *\n * Client-compat knowledge ported from react-email (see the design brief §3):\n * - Body: Yahoo/AOL strip body styles → outer table, inner td carries them.\n * - Container/Section: padding split off the table onto the inner td\n * (Klaviyo/Outlook).\n * - Outlook button: mso-padding-alt:0 + <!--[if mso]> hair-space runs with\n * mso-font-width (≤500%) / mso-text-raise (px→pt = ×3/4).\n * - Preview: hidden div padded to ~200 chars with zero-width characters,\n * marked data-skip-in-text for the plain-text pass.\n * - Img reset, hr as border-top, x-apple-disable-message-reformatting, XHTML\n * transitional doctype, width/align HTML attributes, role=\"presentation\".\n *\n * Two bases: \"outlook-safe\" (default — tables + mso conditionals + XHTML\n * doctype) and \"modern\" (HTML5 doctype, divs for Container/Section, real\n * padding on buttons, no conditional comments). Rows keep tables in both bases\n * — columns are genuinely tabular content.\n */\n\nexport { COMPILER_VERSION } from \"./version\";\n\nexport type CompileBase = \"outlook-safe\" | \"modern\";\n\n/** Email-only knobs; sms/whatsapp compile ignores both (no theming, no bases). */\nexport interface CompileOptions {\n readonly base?: CompileBase | undefined;\n readonly theme?:\n | (Omit<ThemeLayers, \"base\"> & { readonly base?: TokenTable | undefined })\n | undefined;\n /** Omit editor DOM instrumentation from exported/sent HTML artifacts. */\n readonly stripInstrumentation?: boolean | undefined;\n}\n\nexport interface CompiledEmail {\n readonly channel: \"email\";\n readonly html: string;\n /** Plain-text alternative ({{vars}} verbatim, envelope metadata skipped). */\n readonly text: string;\n /** Authored <Subject> text with interpolation tokens preserved; null when absent. */\n readonly subjectText: string | null;\n readonly warnings: ReadonlyArray<Diagnostic>;\n readonly base: CompileBase;\n readonly compilerVersion: string;\n}\n\n/**\n * The compile result, discriminated on `channel` — which the root tag\n * declares. email → HTML + plain-text artifacts; sms → body + segment math;\n * whatsapp → whatsapp_template_variants column shapes.\n */\nexport type CompiledTemplate = CompiledEmail | CompiledSms | CompiledWhatsApp;\n\n// ── Internal HTML tree ────────────────────────────────────────────────────────\n\nexport interface HtmlElement {\n readonly kind: \"element\";\n readonly tag: string;\n /** Ordered; serialized before style. */\n readonly attrs: ReadonlyArray<readonly [string, string]>;\n /** Serialized as style=\"\" in order — CSS last-wins handles overrides. */\n readonly style: ReadonlyArray<Declaration>;\n readonly children: ReadonlyArray<HtmlNode>;\n readonly void?: boolean | undefined;\n}\n\nexport type HtmlTextNode = { readonly kind: \"text\"; readonly value: string };\n\nexport type HtmlRawNode = {\n readonly kind: \"raw\";\n readonly html: string;\n};\n\n/** Children spliced into the parent's block flow — <If> lowers to one. */\nexport interface HtmlFragment {\n readonly kind: \"fragment\";\n readonly children: ReadonlyArray<HtmlNode>;\n}\n\nexport type HtmlNode = HtmlElement | HtmlTextNode | HtmlRawNode | HtmlFragment;\n\nexport interface CompileHeadStyleAsset {\n readonly kind: \"style\";\n readonly css: string;\n /** Serialized style asset as emitted in <head>. */\n readonly html: string;\n /** True for the Outlook-hidden webfont style block. */\n readonly outlookHidden: boolean;\n}\n\nexport interface CompileHeadAssets {\n readonly styles: ReadonlyArray<CompileHeadStyleAsset>;\n readonly links: ReadonlyArray<HtmlElement>;\n}\n\nexport type HtmlNodeIdMap = Readonly<Record<string, ReadonlyArray<HtmlElement>>>;\n\nexport interface CompiledEmailArtifact extends CompiledEmail {\n /** Resolved primary document width in pixels. */\n readonly width: number;\n readonly tree: HtmlElement;\n readonly doctype: string;\n readonly headAssets: CompileHeadAssets;\n readonly nodeIdMap: HtmlNodeIdMap;\n}\n\nexport type CompiledTemplateArtifact = CompiledEmailArtifact | CompiledSms | CompiledWhatsApp;\n\nconst element = (\n tag: string,\n attrs: Array<readonly [string, string]>,\n style: Array<Declaration>,\n children: Array<HtmlNode>,\n options?: { readonly void?: boolean | undefined },\n): HtmlElement => ({\n kind: \"element\",\n tag,\n attrs,\n style,\n children,\n void: options?.void === true ? true : undefined,\n});\n\nconst text = (value: string): HtmlNode => ({ kind: \"text\", value });\nconst raw = (html: string): HtmlNode => ({ kind: \"raw\", html });\n\n// ── Serialization ─────────────────────────────────────────────────────────────\n\nconst INLINE_HTML_TAGS = new Set([\"a\", \"span\", \"strong\", \"em\", \"i\", \"title\"]);\n\nconst escapeHtmlText = (value: string): string =>\n value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\");\n\nconst escapeHtmlAttribute = (value: string): string =>\n value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll('\"', \""\");\n\nconst serializeStyle = (style: ReadonlyArray<Declaration>): string =>\n style.map((declaration) => `${declaration.property}:${declaration.value}`).join(\";\");\n\nconst serializeOpenTag = (node: HtmlElement, selfClose: boolean): string => {\n let result = `<${node.tag}`;\n for (const [name, value] of node.attrs) {\n result += value === \"\" ? ` ${name}=\"\"` : ` ${name}=\"${escapeHtmlAttribute(value)}\"`;\n }\n if (node.style.length > 0)\n result += ` style=\"${escapeHtmlAttribute(serializeStyle(node.style))}\"`;\n return result + (selfClose ? \"/>\" : \">\");\n};\n\nconst isInlineNode = (node: HtmlNode): boolean =>\n node.kind === \"fragment\" ? false : node.kind !== \"element\" || INLINE_HTML_TAGS.has(node.tag);\n\nexport const serializeHtmlNode = (node: HtmlNode): string => {\n if (node.kind === \"text\") return escapeHtmlText(node.value);\n if (node.kind === \"raw\") return node.html;\n if (node.kind === \"fragment\") return node.children.map(serializeHtmlNode).join(\"\\n\");\n if (node.void === true || node.children.length === 0) {\n if (node.void === true) return serializeOpenTag(node, true);\n return `${serializeOpenTag(node, false)}</${node.tag}>`;\n }\n const inlineContent = node.children.every(isInlineNode);\n const children = node.children.map(serializeHtmlNode);\n if (inlineContent) return `${serializeOpenTag(node, false)}${children.join(\"\")}</${node.tag}>`;\n return `${serializeOpenTag(node, false)}\\n${children.join(\"\\n\")}\\n</${node.tag}>`;\n};\n\n// ── Class → style plumbing ────────────────────────────────────────────────────\n\ninterface HeadRule {\n readonly selector: string;\n readonly declarations: ReadonlyArray<Declaration>;\n}\n\ninterface CompileState {\n readonly base: CompileBase;\n readonly emailWidth: number;\n readonly theme: TokenTable;\n readonly stripInstrumentation: boolean;\n readonly errors: Array<Diagnostic>;\n readonly warnings: Array<Diagnostic>;\n readonly smRules: Array<HeadRule>;\n readonly darkRules: Array<HeadRule>;\n}\n\ninterface NodeStyles {\n readonly inline: Array<Declaration>;\n /** Present when the node needs a class attribute for head rules. */\n readonly className: string | null;\n}\n\n// ── Gradient combination ──────────────────────────────────────────────────────\n// The gradient family resolves to placeholder declarations (registry.ts); one\n// node's direction + from + to combine here into real background declarations.\n// Modern base: a CSS linear-gradient plus a solid from-color fallback.\n// Outlook-safe base: the solid from-color only — Outlook for Windows renders\n// no gradient, and a VML fill is not robust enough to be worth it.\n\nconst GRADIENT_PROPERTIES = new Set([\n GRADIENT_DIRECTION_PROPERTY,\n GRADIENT_FROM_PROPERTY,\n GRADIENT_TO_PROPERTY,\n]);\n\nconst isGradientClass = (resolved: ResolvedClass): boolean =>\n resolved.declarations.some((declaration) => GRADIENT_PROPERTIES.has(declaration.property));\n\nconst combineGradient = (\n state: CompileState,\n baseClasses: ReadonlyArray<ResolvedClass>,\n): Array<Declaration> => {\n const gradientClasses = baseClasses.filter(isGradientClass);\n if (gradientClasses.length === 0) {\n return baseClasses.flatMap((resolved) => [...resolved.declarations]);\n }\n\n // Last-wins per slot, like CSS.\n const slots = new Map<string, string>();\n for (const resolved of gradientClasses) {\n for (const declaration of resolved.declarations) {\n slots.set(declaration.property, declaration.value);\n }\n }\n const direction = slots.get(GRADIENT_DIRECTION_PROPERTY);\n const from = slots.get(GRADIENT_FROM_PROPERTY);\n const to = slots.get(GRADIENT_TO_PROPERTY);\n const anchor = gradientClasses[0]!;\n const missing: Array<string> = [];\n if (direction === undefined) missing.push(\"a direction (bg-gradient-to-b/t/l/r)\");\n if (from === undefined) missing.push(\"a start color (from-{color})\");\n if (to === undefined) missing.push(\"an end color (to-{color})\");\n if (missing.length > 0) {\n state.errors.push({\n code: \"incomplete-gradient\",\n message: `incomplete gradient near \"${anchor.raw}\" — add ${missing.join(\" and \")}`,\n span: anchor.span,\n });\n return baseClasses\n .filter((resolved) => !isGradientClass(resolved))\n .flatMap((resolved) => [...resolved.declarations]);\n }\n\n const combined: Array<Declaration> = [{ property: \"background-color\", value: from! }];\n if (state.base === \"modern\") {\n combined.push({\n property: \"background-image\",\n value: `linear-gradient(${direction},${from},${to})`,\n });\n }\n\n // The combined declarations take the FIRST gradient class's position so CSS\n // last-wins ordering against other background utilities stays authored.\n const out: Array<Declaration> = [];\n for (const resolved of baseClasses) {\n if (isGradientClass(resolved)) {\n if (resolved === anchor) out.push(...combined);\n continue;\n }\n out.push(...resolved.declarations);\n }\n return out;\n};\n\nconst resolveNodeStyles = (\n state: CompileState,\n node: ElementNode,\n defaults: {\n readonly inline: ReadonlyArray<Declaration>;\n readonly dark: ReadonlyArray<Declaration>;\n } = { inline: [], dark: [] },\n): NodeStyles => {\n const classAttribute = node.attributes.find((attribute) => attribute.name === \"class\");\n if (classAttribute === undefined || classAttribute.value.kind !== \"string\") {\n if (defaults.dark.length === 0) {\n return { inline: [...defaults.inline], className: null };\n }\n const className = `sml-${node.id}`;\n state.darkRules.push({ selector: `.${className}`, declarations: defaults.dark });\n return { inline: [...defaults.inline], className };\n }\n const resolution = resolveClasses(\n classAttribute.value.value,\n state.theme,\n classAttribute.valueSpan.start,\n );\n state.errors.push(...resolution.errors);\n state.warnings.push(...resolution.warnings);\n\n const byVariant = (variant: ClassVariant): Array<ResolvedClass> =>\n resolution.classes.filter((resolved) => resolved.variant === variant);\n\n const base = byVariant(\"base\");\n const inline = [...defaults.inline, ...combineGradient(state, base)];\n const smDeclarations = byVariant(\"sm\").flatMap((resolved) => [...resolved.declarations]);\n // Auto token-dark rules come BEFORE explicit dark: rules — both land in the\n // same node-classed head rule, so an explicit dark:bg-* wins by last-wins.\n const darkDeclarations = [\n ...defaults.dark,\n ...base.flatMap((resolved) => [...(resolved.darkDeclarations ?? [])]),\n ...byVariant(\"dark\").flatMap((resolved) => [...resolved.declarations]),\n ];\n\n if (smDeclarations.length === 0 && darkDeclarations.length === 0) {\n return { inline, className: null };\n }\n const className = `sml-${node.id}`;\n if (smDeclarations.length > 0) {\n state.smRules.push({\n selector: `.${className}`,\n declarations: smDeclarations,\n });\n }\n if (darkDeclarations.length > 0) {\n state.darkRules.push({\n selector: `.${className}`,\n declarations: darkDeclarations,\n });\n }\n return { inline, className };\n};\n\n/**\n * Inline anchors are deliberately a narrower surface than block primitives:\n * classes resolve through the exact same safe registry, but source IDs stay on\n * their block owner so the canvas does not expose links as independent nodes.\n *\n * A link always has a concrete color and decoration. That prevents each email\n * client from choosing its browser-default blue/underline treatment; authored\n * classes follow these defaults and therefore win by normal CSS last-wins.\n */\nconst resolveInlineLinkStyles = (state: CompileState, node: ElementNode): NodeStyles =>\n resolveNodeStyles(state, node, {\n inline: [\n { property: \"color\", value: state.theme.colors.brand! },\n { property: \"text-decoration\", value: \"underline\" },\n ],\n dark:\n state.theme.colorsDark.brand === undefined\n ? []\n : [{ property: \"color\", value: state.theme.colorsDark.brand }],\n });\n\nconst nodeAttrs = (\n state: CompileState,\n node: ElementNode,\n styles: NodeStyles,\n extra: Array<readonly [string, string]> = [],\n options?: { readonly extraClassName?: string | undefined },\n): Array<readonly [string, string]> => {\n const attrs: Array<readonly [string, string]> = state.stripInstrumentation\n ? [...extra]\n : [[\"data-sml-id\", node.id], ...extra];\n const classNames = [styles.className, options?.extraClassName].filter(\n (className): className is string => className !== null && className !== undefined,\n );\n if (classNames.length > 0) attrs.push([\"class\", classNames.join(\" \")]);\n return attrs;\n};\n\nconst isPaddingProperty = (property: string): boolean =>\n property === \"padding\" || property.startsWith(\"padding-\");\n\nconst splitPadding = (\n declarations: ReadonlyArray<Declaration>,\n): { padding: Array<Declaration>; rest: Array<Declaration> } => {\n const padding: Array<Declaration> = [];\n const rest: Array<Declaration> = [];\n for (const declaration of declarations) {\n (isPaddingProperty(declaration.property) ? padding : rest).push(declaration);\n }\n return { padding, rest };\n};\n\n// ── Background image (Section/Container) ─────────────────────────────────────\n// A background URL rides an attribute, not a class. The compiler emits\n// email-safe CSS — an escaped `url(...)`, position/size (defaults center/cover),\n// no-repeat — plus, on the outlook-safe base, the legacy `background` cell\n// attribute (honored by more clients than CSS alone). The URL is\n// attacker-adjacent input, so escapeCssString neutralizes a `'`/`\\`/`</style>`\n// breakout inside the url() string before the HTML serializer escapes the style\n// attribute around it. No VML `<v:rect>` fallback: a fill needs pixel width AND\n// height, but a Section's height is content-driven and unknown at compile time,\n// so Outlook Windows degrades to its background-color and checks warns via\n// css-background-image.\nconst BACKGROUND_POSITION_DEFAULT = \"center\";\nconst BACKGROUND_SIZE_DEFAULT = \"cover\";\n\nconst backgroundImageUrl = (node: ElementNode): string | undefined => {\n const url = stringAttribute(node, \"backgroundImage\");\n return url === undefined || url === \"\" ? undefined : url;\n};\n\nconst backgroundImageStyle = (node: ElementNode): Array<Declaration> => {\n const url = backgroundImageUrl(node);\n if (url === undefined) return [];\n return [\n { property: \"background-image\", value: `url('${escapeCssString(url)}')` },\n {\n property: \"background-position\",\n value: stringAttribute(node, \"backgroundPosition\") ?? BACKGROUND_POSITION_DEFAULT,\n },\n {\n property: \"background-size\",\n value: stringAttribute(node, \"backgroundSize\") ?? BACKGROUND_SIZE_DEFAULT,\n },\n { property: \"background-repeat\", value: \"no-repeat\" },\n ];\n};\n\n// ── Outlook button hack (ported from react-email button/utils) ───────────────\n\nconst MAX_FONT_WIDTH = 5;\n\nconst computeFontWidthAndSpaceCount = (expectedWidth: number): readonly [number, number] => {\n if (expectedWidth === 0) return [0, 0];\n let spaceCount = 0;\n const requiredFontWidth = (): number =>\n spaceCount > 0 ? expectedWidth / spaceCount / 2 : Number.POSITIVE_INFINITY;\n while (requiredFontWidth() > MAX_FONT_WIDTH) spaceCount++;\n return [requiredFontWidth(), spaceCount];\n};\n\nconst pxToPt = (px: number): number => (px * 3) / 4;\n\nconst paddingPx = (declarations: ReadonlyArray<Declaration>) => {\n const padding = { top: 0, right: 0, bottom: 0, left: 0 };\n const parse = (value: string): number => {\n const match = /^([\\d.]+)px$/.exec(value);\n return match === null ? 0 : Number.parseFloat(match[1]!);\n };\n for (const declaration of declarations) {\n switch (declaration.property) {\n case \"padding\": {\n const all = parse(declaration.value);\n padding.top = padding.right = padding.bottom = padding.left = all;\n break;\n }\n case \"padding-top\":\n padding.top = parse(declaration.value);\n break;\n case \"padding-right\":\n padding.right = parse(declaration.value);\n break;\n case \"padding-bottom\":\n padding.bottom = parse(declaration.value);\n break;\n case \"padding-left\":\n padding.left = parse(declaration.value);\n break;\n default:\n break;\n }\n }\n return padding;\n};\n\n// ── Preview (ported from react-email preview) ────────────────────────────────\n\nconst PREVIEW_MAX_LENGTH = 200;\nconst PREVIEW_WHITESPACE = \"\\xa0\\u200C\\u200B\\u200D\\u200E\\u200F\\uFEFF\";\n\n// ── Primitive expansion ───────────────────────────────────────────────────────\n\nconst TABLE_RESET: Array<readonly [string, string]> = [\n [\"border\", \"0\"],\n [\"cellpadding\", \"0\"],\n [\"cellspacing\", \"0\"],\n [\"role\", \"presentation\"],\n];\n\nconst expandInline = (state: CompileState, nodes: ReadonlyArray<SmlNode>): Array<HtmlNode> =>\n nodes.map((node) => {\n if (node.kind === \"text\") return text(node.value);\n if (node.kind === \"interpolation\") {\n return text(node.syntax === \"curly\" ? `{{${node.name}}}` : `\\${${node.name}}`);\n }\n if (node.tag === \"a\") {\n const styles = resolveInlineLinkStyles(state, node);\n const attrs: Array<readonly [string, string]> = [\n [\"href\", stringAttribute(node, \"href\") ?? \"\"],\n [\"target\", \"_blank\"],\n ];\n if (styles.className !== null) attrs.push([\"class\", styles.className]);\n return element(\"a\", attrs, styles.inline, expandInline(state, node.children));\n }\n return element(node.tag, [], [], expandInline(state, node.children));\n });\n\nconst expandBlock = (state: CompileState, node: ElementNode): HtmlNode => {\n const styles = resolveNodeStyles(state, node);\n switch (node.tag) {\n case \"Container\":\n case \"Section\": {\n const { padding, rest } = splitPadding(styles.inline);\n const bgStyle = backgroundImageStyle(node);\n const bgUrl = backgroundImageUrl(node);\n const children = node.children.flatMap((child) =>\n child.kind === \"element\" ? [expandBlock(state, child)] : [],\n );\n if (state.base === \"modern\") {\n const style: Array<Declaration> =\n node.tag === \"Container\"\n ? [\n { property: \"max-width\", value: `${state.emailWidth}px` },\n { property: \"margin-left\", value: \"auto\" },\n { property: \"margin-right\", value: \"auto\" },\n ]\n : [];\n // Background image sits after `rest` so a class-authored bg-color reads\n // as its load-failure fallback on the same element (CSS last-wins keeps\n // padding trailing).\n return element(\n \"div\",\n nodeAttrs(state, node, styles),\n [...style, ...rest, ...bgStyle, ...padding],\n children,\n );\n }\n const tableStyle: Array<Declaration> =\n node.tag === \"Container\"\n ? [{ property: \"max-width\", value: `${state.emailWidth}px` }, ...rest]\n : rest;\n // The background rides the inner td (where content sits): the CSS declarations\n // for modern clients and the legacy `background` attribute for the rest.\n const tdAttrs: Array<readonly [string, string]> =\n bgUrl === undefined ? [] : [[\"background\", bgUrl]];\n return element(\n \"table\",\n nodeAttrs(state, node, styles, [[\"align\", \"center\"], [\"width\", \"100%\"], ...TABLE_RESET]),\n tableStyle,\n [\n element(\n \"tbody\",\n [],\n [],\n [\n element(\n \"tr\",\n [],\n [{ property: \"width\", value: \"100%\" }],\n [element(\"td\", tdAttrs, [...bgStyle, ...padding], children)],\n ),\n ],\n ),\n ],\n );\n }\n case \"Row\": {\n const columns = node.children.flatMap((child) =>\n child.kind === \"element\" ? [expandBlock(state, child)] : [],\n );\n const { padding, rest } = splitPadding(styles.inline);\n const rowTable = (tableStyle: Array<Declaration>) =>\n element(\n \"table\",\n nodeAttrs(state, node, styles, [[\"align\", \"center\"], [\"width\", \"100%\"], ...TABLE_RESET]),\n tableStyle,\n [\n element(\n \"tbody\",\n [],\n [{ property: \"width\", value: \"100%\" }],\n [element(\"tr\", [], [{ property: \"width\", value: \"100%\" }], columns)],\n ),\n ],\n );\n // Modern clients honor padding on the table; Outlook drops it, so the\n // outlook-safe base carries it on a wrapping td like Container/Section.\n // Non-padding styles stay on the INNER table with nodeAttrs — sm:/dark:\n // head rules target the className there, and an override only wins when\n // it lives on the same element as the inline style it must beat.\n if (state.base === \"modern\" || padding.length === 0) {\n return rowTable(styles.inline);\n }\n return element(\n \"table\",\n [[\"align\", \"center\"], [\"width\", \"100%\"], ...TABLE_RESET],\n [],\n [\n element(\n \"tbody\",\n [],\n [],\n [\n element(\n \"tr\",\n [],\n [{ property: \"width\", value: \"100%\" }],\n [element(\"td\", [], padding, [rowTable(rest)])],\n ),\n ],\n ),\n ],\n );\n }\n case \"Column\": {\n const width = stringAttribute(node, \"width\");\n const valign = stringAttribute(node, \"valign\");\n const children = node.children.flatMap((child) =>\n child.kind === \"element\" ? [expandBlock(state, child)] : [],\n );\n // valign rides both the HTML attribute (Outlook honors the attribute,\n // not the CSS) and vertical-align (modern clients), on both bases.\n return element(\n \"td\",\n nodeAttrs(state, node, styles, [\n ...(width === undefined ? [] : [[\"width\", width] as const]),\n ...(valign === undefined ? [] : [[\"valign\", valign] as const]),\n ]),\n [\n ...(valign === undefined ? [] : [{ property: \"vertical-align\", value: valign }]),\n ...styles.inline,\n ],\n children,\n );\n }\n case \"Heading\": {\n const level = numberAttribute(node, \"level\") ?? 1;\n return element(\n `h${level}`,\n nodeAttrs(state, node, styles),\n styles.inline,\n expandInline(state, node.children),\n );\n }\n case \"Text\": {\n const defaults: Array<Declaration> = [\n { property: \"font-size\", value: \"14px\" },\n { property: \"line-height\", value: \"24px\" },\n { property: \"margin-top\", value: \"16px\" },\n { property: \"margin-bottom\", value: \"16px\" },\n ];\n return element(\n \"p\",\n nodeAttrs(state, node, styles),\n [...defaults, ...styles.inline],\n expandInline(state, node.children),\n );\n }\n case \"Button\":\n return expandButton(state, node, styles);\n case \"List\": {\n // Semantic <ul>/<ol> — supported everywhere, and lighter than table-based\n // lists. Spacing matches <Text> (16px block margins); the explicit\n // padding-left carries the bullet indent in clients that honor it, while\n // Outlook for Windows substitutes its own default indent (degrades to a\n // slightly wider gutter, never breaks).\n const tag = stringAttribute(node, \"type\") === \"ordered\" ? \"ol\" : \"ul\";\n const defaults: Array<Declaration> = [\n { property: \"margin-top\", value: \"16px\" },\n { property: \"margin-bottom\", value: \"16px\" },\n { property: \"padding-left\", value: \"24px\" },\n ];\n const items = node.children.flatMap((child) =>\n child.kind === \"element\" ? [expandBlock(state, child)] : [],\n );\n return element(tag, nodeAttrs(state, node, styles), [...defaults, ...styles.inline], items);\n }\n case \"ListItem\": {\n // Type/leading match <Text>; the small bottom margin separates items\n // without <Spacer> ceremony (margins on <li> hold up in Outlook).\n const defaults: Array<Declaration> = [\n { property: \"font-size\", value: \"14px\" },\n { property: \"line-height\", value: \"24px\" },\n { property: \"margin-top\", value: \"0px\" },\n { property: \"margin-bottom\", value: \"8px\" },\n ];\n return element(\n \"li\",\n nodeAttrs(state, node, styles),\n [...defaults, ...styles.inline],\n expandInline(state, node.children),\n );\n }\n case \"If\": {\n // Both branches survive into the artifact behind comment markers keyed\n // by the stable node id (unique per document, so nesting resolves by\n // exact-id matching). If renders no element of its own — the markers ARE\n // the node; resolveArtifactConditionals selects a branch at send time.\n const test = stringAttribute(node, \"test\") ?? \"\";\n const elements = node.children.filter(\n (child): child is ElementNode => child.kind === \"element\",\n );\n const elseNode = elements.find((child) => child.tag === \"Else\");\n const children: Array<HtmlNode> = [raw(`<!--sml:if:${node.id} ${test}-->`)];\n for (const child of elements) {\n if (child.tag !== \"Else\") children.push(expandBlock(state, child));\n }\n if (elseNode !== undefined) {\n children.push(raw(`<!--sml:else:${node.id}-->`));\n for (const child of elseNode.children) {\n if (child.kind === \"element\") children.push(expandBlock(state, child));\n }\n }\n children.push(raw(`<!--sml:endif:${node.id}-->`));\n return { kind: \"fragment\", children };\n }\n case \"Image\": {\n const width = numberAttribute(node, \"width\");\n const height = numberAttribute(node, \"height\");\n const src = stringAttribute(node, \"src\") ?? \"\";\n const alt = stringAttribute(node, \"alt\") ?? \"\";\n const darkSource = node.children.find(\n (child): child is ElementNode =>\n child.kind === \"element\" &&\n child.tag === \"Source\" &&\n stringAttribute(child, \"scheme\") === \"dark\",\n );\n const darkImageSource =\n darkSource === undefined ? undefined : stringAttribute(darkSource, \"src\");\n const attrs = nodeAttrs(state, node, styles, [\n [\"alt\", alt],\n [\"src\", src],\n ...(width === undefined ? [] : [[\"width\", String(width)] as const]),\n ...(height === undefined ? [] : [[\"height\", String(height)] as const]),\n ]);\n const reset: Array<Declaration> = [\n { property: \"display\", value: \"block\" },\n { property: \"outline\", value: \"none\" },\n { property: \"border\", value: \"none\" },\n { property: \"text-decoration\", value: \"none\" },\n ];\n if (darkImageSource !== undefined) {\n const lightClassName = `sml-${node.id}-theme-light`;\n const darkClassName = `sml-${node.id}-theme-dark`;\n state.darkRules.push(\n {\n selector: state.stripInstrumentation\n ? `.${lightClassName}`\n : `[data-sml-id=\"${node.id}\"][data-sml-theme=\"light\"]`,\n declarations: [{ property: \"display\", value: \"none\" }],\n },\n {\n selector: state.stripInstrumentation\n ? `.${darkClassName}`\n : `[data-sml-id=\"${node.id}\"][data-sml-theme=\"dark\"]`,\n declarations: [{ property: \"display\", value: \"block\" }],\n },\n );\n const themedAttrs = (theme: \"light\" | \"dark\", imageSrc: string) => {\n const themeClassName = theme === \"light\" ? lightClassName : darkClassName;\n return nodeAttrs(\n state,\n node,\n styles,\n [\n ...(state.stripInstrumentation ? [] : [[\"data-sml-theme\", theme] as const]),\n [\"alt\", alt],\n [\"src\", imageSrc],\n ...(width === undefined ? [] : [[\"width\", String(width)] as const]),\n ...(height === undefined ? [] : [[\"height\", String(height)] as const]),\n ],\n state.stripInstrumentation ? { extraClassName: themeClassName } : undefined,\n );\n };\n return {\n kind: \"fragment\",\n children: [\n element(\"img\", themedAttrs(\"light\", src), [...reset, ...styles.inline], [], {\n void: true,\n }),\n element(\n \"img\",\n themedAttrs(\"dark\", darkImageSource),\n [{ property: \"display\", value: \"none\" }, ...reset.slice(1), ...styles.inline],\n [],\n { void: true },\n ),\n ],\n };\n }\n return element(\"img\", attrs, [...reset, ...styles.inline], [], { void: true });\n }\n case \"Divider\": {\n const defaults: Array<Declaration> = [\n { property: \"width\", value: \"100%\" },\n { property: \"border\", value: \"none\" },\n { property: \"border-top\", value: \"1px solid #eaeaea\" },\n ];\n return element(\"hr\", nodeAttrs(state, node, styles), [...defaults, ...styles.inline], [], {\n void: true,\n });\n }\n case \"Spacer\": {\n const height = numberAttribute(node, \"height\") ?? 0;\n return element(\n \"div\",\n nodeAttrs(state, node, styles),\n [\n { property: \"height\", value: `${height}px` },\n { property: \"line-height\", value: `${height}px` },\n { property: \"font-size\", value: \"1px\" },\n ],\n [raw(\" \")],\n );\n }\n default:\n // Email/envelope metadata handled by the document expander; inline tags cannot be\n // block children (parser content model). Fail loudly if it ever happens.\n state.errors.push({\n code: \"internal-unexpected-node\",\n message: `unexpected <${node.tag}> during expansion`,\n span: node.span,\n });\n return raw(\"\");\n }\n};\n\nconst expandButton = (state: CompileState, node: ElementNode, styles: NodeStyles): HtmlNode => {\n const href = stringAttribute(node, \"href\") ?? \"\";\n const label = expandInline(state, node.children);\n const baseStyle: Array<Declaration> = [\n { property: \"line-height\", value: \"100%\" },\n { property: \"text-decoration\", value: \"none\" },\n { property: \"display\", value: \"inline-block\" },\n { property: \"max-width\", value: \"100%\" },\n { property: \"mso-padding-alt\", value: \"0px\" },\n ];\n const attrs = nodeAttrs(state, node, styles, [\n [\"href\", href],\n [\"target\", \"_blank\"],\n ]);\n\n if (state.base === \"modern\") {\n return element(\"a\", attrs, [...baseStyle, ...styles.inline], label);\n }\n\n const padding = paddingPx(styles.inline);\n const verticalPadding = padding.top + padding.bottom;\n const textRaise = pxToPt(verticalPadding);\n const [leftFontWidth, leftSpaceCount] = computeFontWidthAndSpaceCount(padding.left);\n const [rightFontWidth, rightSpaceCount] = computeFontWidthAndSpaceCount(padding.right);\n\n const innerSpanStyle: Array<Declaration> = [\n { property: \"max-width\", value: \"100%\" },\n { property: \"display\", value: \"inline-block\" },\n { property: \"line-height\", value: \"120%\" },\n { property: \"mso-padding-alt\", value: \"0px\" },\n ...(padding.bottom > 0\n ? // Unitless like the mso conditional — Word's engine reads unitless as pt.\n [{ property: \"mso-text-raise\", value: `${pxToPt(padding.bottom)}` }]\n : []),\n ];\n\n return element(\n \"a\",\n attrs,\n [...baseStyle, ...styles.inline],\n [\n element(\n \"span\",\n [],\n [],\n [\n raw(\n `<!--[if mso]><i style=\"mso-font-width:${leftFontWidth * 100}%;mso-text-raise:${textRaise}\" hidden>${\" \".repeat(leftSpaceCount)}</i><![endif]-->`,\n ),\n ],\n ),\n element(\"span\", [], innerSpanStyle, label),\n element(\n \"span\",\n [],\n [],\n [\n raw(\n `<!--[if mso]><i style=\"mso-font-width:${rightFontWidth * 100}%\" hidden>${\" \".repeat(rightSpaceCount)}​</i><![endif]-->`,\n ),\n ],\n ),\n ],\n );\n};\n\nconst expandPreview = (state: CompileState, node: ElementNode): HtmlNode => {\n let previewText = scalarElementText(node);\n let literalLength = 0;\n for (const child of node.children) {\n if (child.kind === \"text\") {\n literalLength += child.value.length;\n }\n }\n previewText = previewText.substring(0, PREVIEW_MAX_LENGTH);\n // Pad against the shortest possible rendered length: interpolations may\n // resolve to \"\" at send, so only literal text is guaranteed to occupy the\n // preview pane. Overshooting the pad is harmless — the run is hidden.\n const guaranteedLength = Math.min(literalLength, PREVIEW_MAX_LENGTH);\n const children: Array<HtmlNode> = [text(previewText)];\n if (guaranteedLength < PREVIEW_MAX_LENGTH) {\n children.push(\n element(\n \"div\",\n [],\n [],\n [text(PREVIEW_WHITESPACE.repeat(PREVIEW_MAX_LENGTH - guaranteedLength))],\n ),\n );\n }\n return element(\n \"div\",\n [\n ...(state.stripInstrumentation ? [] : [[\"data-sml-id\", node.id] as const]),\n [\"data-skip-in-text\", \"true\"],\n ],\n [\n { property: \"display\", value: \"none\" },\n { property: \"overflow\", value: \"hidden\" },\n { property: \"line-height\", value: \"1px\" },\n { property: \"opacity\", value: \"0\" },\n { property: \"max-height\", value: \"0\" },\n { property: \"max-width\", value: \"0\" },\n ],\n children,\n );\n};\n\n// ── Head <style> ──────────────────────────────────────────────────────────────\n\nconst serializeRules = (\n rules: ReadonlyArray<HeadRule>,\n indent: string,\n important: boolean,\n): string =>\n rules\n .map(\n (rule) =>\n `${indent}${rule.selector}{${rule.declarations\n .map((d) => `${d.property}:${d.value}${important ? \" !important\" : \"\"}`)\n .join(\";\")}}`,\n )\n .join(\"\\n\");\n\nconst isBackgroundProperty = (property: string): boolean => property.startsWith(\"background\");\n\nconst buildHeadStyle = (state: CompileState): string => {\n // Dark-mode hygiene: always declare color-scheme, even with zero dark:\n // classes, so honoring clients treat the email as scheme-aware.\n const parts: Array<string> = [\n \":root{color-scheme:light dark;supported-color-schemes:light dark}\",\n ];\n if (state.smRules.length > 0) {\n // sm: is progressive enhancement only — the inline baseline must already be\n // complete. !important beats the inline styles it adjusts.\n parts.push(`@media (min-width:640px){\\n${serializeRules(state.smRules, \" \", true)}\\n}`);\n }\n if (state.darkRules.length > 0) {\n parts.push(\n `@media (prefers-color-scheme:dark){\\n${serializeRules(state.darkRules, \" \", true)}\\n}`,\n );\n // Outlook.com ignores prefers-color-scheme and instead sets data-ogsc\n // (color) / data-ogsb (background) attributes — duplicate the rules.\n const ogscRules = state.darkRules.flatMap((rule) => {\n const declarations = rule.declarations.filter((d) => !isBackgroundProperty(d.property));\n return declarations.length > 0\n ? [{ selector: `[data-ogsc] ${rule.selector}`, declarations }]\n : [];\n });\n const ogsbRules = state.darkRules.flatMap((rule) => {\n const declarations = rule.declarations.filter((d) => isBackgroundProperty(d.property));\n return declarations.length > 0\n ? [{ selector: `[data-ogsb] ${rule.selector}`, declarations }]\n : [];\n });\n if (ogscRules.length > 0) parts.push(serializeRules(ogscRules, \"\", true));\n if (ogsbRules.length > 0) parts.push(serializeRules(ogsbRules, \"\", true));\n }\n return parts.join(\"\\n\");\n};\n\n/**\n * The @font-face block for self-hosted webfont tokens, emitted into a SEPARATE\n * <style> that is hidden from Outlook for Windows.\n *\n * Outlook-Windows must not SEE these rules: it would register an @font-face whose\n * family matches a used font, fail to load the distant src, and render the text in\n * Times New Roman — actively ignoring the inline font-family stack. Hiding the\n * block behind a downlevel-hidden conditional comment makes Outlook treat the font\n * as merely missing, so it falls through the inline stack to its generic instead.\n * (The head's OTHER <style> stays visible — it carries the Outlook.com [data-ogsc]\n * dark rules — so only this sibling is wrapped.) Empty when no token self-hosts.\n */\n// A `unicode-range` value is a token list, NOT a quoted string, so escaping is\n// meaningless for it. It is emitted unquoted into the rule (and thus into the\n// surrounding <style>), so a value carrying `}` or `</style>` would break out of\n// the rule or the style element and inject HTML into the delivered email. It\n// flows verbatim from the Google css2 API through storage with only\n// `Schema.optional(Schema.String)` validation, so validate the grammar at the\n// emit boundary and fail closed: emit the descriptor only when the value is a\n// comma-separated list of `U+`-prefixed codepoint tokens, else omit it entirely\n// (the face still loads, just without range-narrowing).\nconst UNICODE_RANGE = /^U\\+[0-9A-Fa-f?]{1,6}(-[0-9A-Fa-f]{1,6})?$/;\nconst isUnicodeRange = (value: string): boolean =>\n value.split(\",\").every((token) => UNICODE_RANGE.test(token.trim()) && token.trim() !== \"\");\n\nconst buildFontFaceCss = (state: CompileState): string => {\n const rules = Object.entries(state.theme.fontFaces)\n .flatMap(([name, set]) => {\n // Only emit faces still referenced by the resolved inline stack. Layering\n // merges fontFamilies and fontFaces independently, so overriding only\n // fontFamilies.<name> (a different family, no matching face override)\n // would otherwise leave the prior layer's face set orphaned — a dead\n // @font-face for a family the stack no longer leads with. Cross-checking\n // the leading family keeps the two namespaces structurally in sync.\n // The resolved inline stack leads with the escaped, single-quoted family\n // (flattenFontTokens), so compare against the escaped family too — a legit\n // quoted family still matches its own stack.\n const family = escapeCssString(set.family);\n const stack = state.theme.fontFamilies[name];\n if (stack === undefined || !stack.startsWith(`'${family}',`)) return [];\n return set.faces.map((face) => {\n const range =\n face.unicodeRange && isUnicodeRange(face.unicodeRange)\n ? `unicode-range:${face.unicodeRange};`\n : \"\";\n return `@font-face{font-family:'${family}';font-style:${face.style};font-weight:${face.weight};font-display:swap;src:url('${escapeCssString(face.src)}') format('woff2');${range}}`;\n });\n })\n .join(\"\");\n return rules;\n};\n\nconst buildFontFaceStyle = (css: string): string =>\n css === \"\" ? \"\" : `<!--[if !mso]><!--><style>${css}</style><!--<![endif]-->`;\n\n// ── Plain-text alternative ────────────────────────────────────────────────────\n\nconst inlinePlainText = (nodes: ReadonlyArray<SmlNode>): string => {\n let result = \"\";\n for (const node of nodes) {\n if (node.kind === \"text\") result += node.value;\n else if (node.kind === \"interpolation\") {\n result += node.syntax === \"curly\" ? `{{${node.name}}}` : `\\${${node.name}}`;\n } else if (node.kind === \"element\" && node.tag === \"a\") {\n const label = inlinePlainText(node.children);\n const href = stringAttribute(node, \"href\") ?? \"\";\n result += href === \"\" ? label : `${label} (${href})`;\n } else if (node.kind === \"element\") {\n result += inlinePlainText(node.children);\n }\n }\n return result;\n};\n\nconst blockPlainText = (node: ElementNode, blocks: Array<string>): void => {\n switch (node.tag) {\n case \"Subject\":\n case \"Preview\":\n return; // data-skip-in-text\n case \"Heading\":\n case \"Text\": {\n const value = inlinePlainText(node.children).trim();\n if (value.length > 0) blocks.push(value);\n return;\n }\n case \"Button\": {\n const label = inlinePlainText(node.children).trim();\n const href = stringAttribute(node, \"href\") ?? \"\";\n blocks.push(href === \"\" ? label : `${label} (${href})`);\n return;\n }\n case \"Image\": {\n const alt = stringAttribute(node, \"alt\");\n if (alt !== undefined && alt.length > 0) blocks.push(alt);\n return;\n }\n case \"Divider\":\n case \"Spacer\":\n return;\n case \"List\": {\n // One block, one line per item: \"- item\" (unordered) / \"1. item\" (ordered).\n // Ordinals are POSITIONAL (every ListItem counts, empty or not) so they\n // match the HTML artifact, which emits every <li> — an item emptied by a\n // send-time conditional must not renumber the items after it.\n const ordered = stringAttribute(node, \"type\") === \"ordered\";\n const lines: Array<string> = [];\n let position = 0;\n for (const child of node.children) {\n if (child.kind !== \"element\" || child.tag !== \"ListItem\") continue;\n position += 1;\n const value = inlinePlainText(child.children).trim();\n if (value.length === 0) continue;\n lines.push(ordered ? `${position}. ${value}` : `- ${value}`);\n }\n if (lines.length > 0) blocks.push(lines.join(\"\\n\"));\n return;\n }\n case \"If\": {\n // Same markers as the HTML artifact, as standalone blocks, so\n // resolveArtifactConditionals works identically on the plain-text alt.\n const test = stringAttribute(node, \"test\") ?? \"\";\n const elements = node.children.filter(\n (child): child is ElementNode => child.kind === \"element\",\n );\n const elseNode = elements.find((child) => child.tag === \"Else\");\n blocks.push(`<!--sml:if:${node.id} ${test}-->`);\n for (const child of elements) {\n if (child.tag !== \"Else\") blockPlainText(child, blocks);\n }\n if (elseNode !== undefined) {\n blocks.push(`<!--sml:else:${node.id}-->`);\n for (const child of elseNode.children) {\n if (child.kind === \"element\") blockPlainText(child, blocks);\n }\n }\n blocks.push(`<!--sml:endif:${node.id}-->`);\n return;\n }\n default: {\n for (const child of node.children) {\n if (child.kind === \"element\") blockPlainText(child, blocks);\n }\n }\n }\n};\n\n// ── Document assembly ─────────────────────────────────────────────────────────\n\nconst XHTML_DOCTYPE =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\n/** Compile a parsed document; the root tag routes to its channel backend. */\nexport const compileSml = (\n document: SmlDocument,\n options?: CompileOptions,\n): Result.Result<CompiledTemplate, SmlCompileError> => {\n switch (document.root.tag) {\n case \"Sms\":\n return Result.succeed(compileSmsRoot(document.root));\n case \"WhatsApp\":\n return compileWhatsAppRoot(document.root);\n default:\n return compileEmailSml(document, options);\n }\n};\n\nexport const compileSmlArtifact = (\n document: SmlDocument,\n options?: CompileOptions,\n): Result.Result<CompiledTemplateArtifact, SmlCompileError> => {\n switch (document.root.tag) {\n case \"Sms\":\n return Result.succeed(compileSmsRoot(document.root));\n case \"WhatsApp\":\n return compileWhatsAppRoot(document.root);\n default:\n return compileEmailSmlArtifact(document, options);\n }\n};\n\n/**\n * Drop `hidden=\"true\"` elements (and their subtrees) before expansion, so a\n * hidden node leaves the artifact exactly as if it were never authored — no\n * stray whitespace, and the surviving nodes keep their positional ids (hiding\n * one node never renumbers the rest). The root itself is never filtered.\n */\nconst stripHiddenNodes = (node: ElementNode): ElementNode => ({\n ...node,\n children: node.children.flatMap((child): SmlNode[] => {\n if (child.kind !== \"element\") return [child];\n return isHiddenNode(child) ? [] : [stripHiddenNodes(child)];\n }),\n});\n\nconst addNodeId = (\n map: Record<string, Array<HtmlElement>>,\n id: string,\n elementNode: HtmlElement,\n): void => {\n const current = map[id];\n if (current === undefined) map[id] = [elementNode];\n else current.push(elementNode);\n};\n\nconst collectNodeIdMap = (node: HtmlNode, map: Record<string, Array<HtmlElement>>): void => {\n if (node.kind === \"fragment\") {\n for (const child of node.children) collectNodeIdMap(child, map);\n return;\n }\n if (node.kind !== \"element\") return;\n const id = node.attrs.find(([name]) => name === \"data-sml-id\")?.[1];\n if (id !== undefined) addNodeId(map, id, node);\n for (const child of node.children) collectNodeIdMap(child, map);\n};\n\nconst createNodeIdMap = (tree: HtmlNode): HtmlNodeIdMap => {\n const map: Record<string, Array<HtmlElement>> = {};\n collectNodeIdMap(tree, map);\n return map;\n};\n\nconst compiledEmailFromArtifact = (artifact: CompiledEmailArtifact): CompiledEmail => ({\n channel: artifact.channel,\n html: artifact.html,\n text: artifact.text,\n subjectText: artifact.subjectText,\n warnings: artifact.warnings,\n base: artifact.base,\n compilerVersion: artifact.compilerVersion,\n});\n\nconst scalarElementText = (node: ElementNode): string => {\n let result = \"\";\n for (const child of node.children) {\n if (child.kind === \"text\") result += child.value;\n else if (child.kind === \"interpolation\") {\n result += child.syntax === \"curly\" ? `{{${child.name}}}` : `\\${${child.name}}`;\n }\n }\n return result;\n};\n\nconst compileEmailSml = (\n inputDocument: SmlDocument,\n options?: CompileOptions,\n): Result.Result<CompiledEmail, SmlCompileError> => {\n const artifact = compileEmailSmlArtifact(inputDocument, options);\n if (Result.isFailure(artifact)) return Result.fail(artifact.failure);\n return Result.succeed(compiledEmailFromArtifact(artifact.success));\n};\n\nconst compileEmailSmlArtifact = (\n inputDocument: SmlDocument,\n options?: CompileOptions,\n): Result.Result<CompiledEmailArtifact, SmlCompileError> => {\n const document: SmlDocument = {\n ...inputDocument,\n root: stripHiddenNodes(inputDocument.root),\n };\n const base = options?.base ?? \"outlook-safe\";\n const emailWidth = numberAttribute(document.root, \"width\") ?? DEFAULT_EMAIL_WIDTH;\n const theme = resolveTokenTable({\n base: options?.theme?.base ?? baseTokens,\n overrides: options?.theme?.overrides,\n });\n const state: CompileState = {\n base,\n emailWidth,\n theme,\n stripInstrumentation: options?.stripInstrumentation ?? false,\n errors: [],\n warnings: [],\n smRules: [],\n darkRules: [],\n };\n\n const root = document.root;\n const rootStyles = resolveNodeStyles(state, root);\n const lang = stringAttribute(root, \"lang\") ?? \"en\";\n const dir = stringAttribute(root, \"dir\") ?? \"ltr\";\n\n const preview = root.children.find(\n (child): child is ElementNode => child.kind === \"element\" && child.tag === \"Preview\",\n );\n const subject = root.children.find(\n (child): child is ElementNode => child.kind === \"element\" && child.tag === \"Subject\",\n );\n const blocks = root.children.flatMap((child) =>\n child.kind === \"element\" && child.tag !== \"Preview\" && child.tag !== \"Subject\"\n ? [expandBlock(state, child)]\n : [],\n );\n\n if (state.errors.length > 0) {\n return Result.fail(new SmlCompileError({ diagnostics: state.errors }));\n }\n\n // Yahoo/AOL strip <body> styles: body keeps only background + zeroed margin,\n // the inner td carries the real styles.\n const bodyBackground = rootStyles.inline.filter((d) => isBackgroundProperty(d.property));\n const bodyChildren: Array<HtmlNode> = [];\n if (preview !== undefined) bodyChildren.push(expandPreview(state, preview));\n bodyChildren.push(\n element(\n \"table\",\n [[\"align\", \"center\"], [\"width\", \"100%\"], ...TABLE_RESET],\n [],\n [\n element(\n \"tbody\",\n [],\n [],\n [\n element(\n \"tr\",\n [],\n [],\n [\n element(\n \"td\",\n [\n [\"dir\", dir],\n [\"lang\", lang],\n // The td carries the Email root's inline styles, so it must\n // also carry the className sm:/dark: head rules target —\n // an override only beats the inline style on its own element.\n ...(rootStyles.className !== null\n ? ([[\"class\", rootStyles.className]] as const)\n : []),\n ],\n [...rootStyles.inline],\n blocks,\n ),\n ],\n ),\n ],\n ),\n ],\n ),\n );\n\n const bodyAttrs: Array<readonly [string, string]> = [\n ...(state.stripInstrumentation ? [] : [[\"data-sml-id\", root.id] as const]),\n [\"dir\", dir],\n [\"lang\", lang],\n ];\n if (rootStyles.className !== null) bodyAttrs.push([\"class\", rootStyles.className]);\n const body = element(\n \"body\",\n bodyAttrs,\n [...bodyBackground, { property: \"margin\", value: \"0\" }],\n bodyChildren,\n );\n\n const headStyle = buildHeadStyle(state);\n const fontFaceCss = buildFontFaceCss(state);\n const fontFaceStyle = buildFontFaceStyle(fontFaceCss);\n const headAssets: CompileHeadAssets = {\n styles: [\n {\n kind: \"style\",\n css: headStyle,\n html: `<style>${headStyle}</style>`,\n outlookHidden: false,\n },\n ...(fontFaceCss === \"\"\n ? []\n : [\n {\n kind: \"style\" as const,\n css: fontFaceCss,\n html: fontFaceStyle,\n outlookHidden: true,\n },\n ]),\n ],\n links: [],\n };\n const head = element(\n \"head\",\n [],\n [],\n [\n element(\n \"meta\",\n [\n [\"content\", \"text/html; charset=UTF-8\"],\n [\"http-equiv\", \"Content-Type\"],\n ],\n [],\n [],\n { void: true },\n ),\n // Without this, WebKit mail clients lay out at a ~980px virtual viewport\n // and the sm: min-width rules fire unconditionally on phones.\n element(\n \"meta\",\n [\n [\"name\", \"viewport\"],\n [\"content\", \"width=device-width\"],\n ],\n [],\n [],\n { void: true },\n ),\n element(\"meta\", [[\"name\", \"x-apple-disable-message-reformatting\"]], [], [], { void: true }),\n element(\n \"meta\",\n [\n [\"name\", \"color-scheme\"],\n [\"content\", \"light dark\"],\n ],\n [],\n [],\n { void: true },\n ),\n element(\n \"meta\",\n [\n [\"name\", \"supported-color-schemes\"],\n [\"content\", \"light dark\"],\n ],\n [],\n [],\n {\n void: true,\n },\n ),\n element(\"style\", [], [], [raw(headStyle)]),\n // Self-hosted webfont @font-face, in its own Outlook-hidden <style> sibling.\n // Omitted entirely (not an empty node) when no token self-hosts, so the\n // system-font baseline stays byte-identical.\n ...(fontFaceStyle !== \"\" ? [raw(fontFaceStyle)] : []),\n ],\n );\n\n const htmlAttrs: Array<readonly [string, string]> =\n base === \"outlook-safe\"\n ? [\n [\"dir\", dir],\n [\"lang\", lang],\n [\"xmlns\", \"http://www.w3.org/1999/xhtml\"],\n [\"xmlns:o\", \"urn:schemas-microsoft-com:office:office\"],\n [\"xmlns:v\", \"urn:schemas-microsoft-com:vml\"],\n ]\n : [\n [\"dir\", dir],\n [\"lang\", lang],\n ];\n\n const doctype = base === \"outlook-safe\" ? XHTML_DOCTYPE : \"<!DOCTYPE html>\";\n const tree = element(\"html\", htmlAttrs, [], [head, body]);\n const html = `${doctype}\\n${serializeHtmlNode(tree)}\\n`;\n\n const textBlocks: Array<string> = [];\n for (const child of root.children) {\n if (child.kind === \"element\") blockPlainText(child, textBlocks);\n }\n\n return Result.succeed({\n channel: \"email\" as const,\n width: emailWidth,\n html,\n text: textBlocks.join(\"\\n\\n\"),\n subjectText: subject === undefined ? null : scalarElementText(subject),\n warnings: state.warnings,\n base,\n compilerVersion: COMPILER_VERSION,\n tree,\n doctype,\n headAssets,\n nodeIdMap: createNodeIdMap(tree),\n });\n};\n\nexport const compileSource = (\n source: string,\n options?: CompileOptions,\n): Result.Result<CompiledTemplate, SmlParseError | SmlCompileError> => {\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure);\n return compileSml(parsed.success, options);\n};\n\nexport const compileSourceArtifact = (\n source: string,\n options?: CompileOptions,\n): Result.Result<CompiledTemplateArtifact, SmlParseError | SmlCompileError> => {\n const parsed = parseSml(source);\n if (Result.isFailure(parsed)) return Result.fail(parsed.failure);\n return compileSmlArtifact(parsed.success, options);\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { m as ElementNode } from "./ast-DAhnAvQN.js";
|
|
2
|
+
import { i as EMAIL_TAG_SPECS, l as WHATSAPP_TAG_SPECS, o as TagSpec, t as AttributeSpec } from "./parser-yzjUOGhc.js";
|
|
3
|
+
import { r as PredicateRef, s as VariableRef } from "./variables-3NGLpNTB.js";
|
|
4
|
+
import { s as SmlChildren } from "./jsx-runtime-fSxcvlx4.js";
|
|
5
|
+
//#region src/components.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The scalar type an attribute spec accepts. `level` narrows to the Heading
|
|
8
|
+
* union; a spec with `values` narrows to that literal union (no VariableRef —
|
|
9
|
+
* enum attributes are structural, not send-time holes); other string
|
|
10
|
+
* attributes also take a VariableRef (href={v.ctaUrl}), which embeds as its
|
|
11
|
+
* send-time interpolation.
|
|
12
|
+
*/
|
|
13
|
+
type AttributeType<Name extends string, Spec extends AttributeSpec> = Name extends "level" ? HeadingLevel : Spec["type"] extends "number" ? number : Spec["values"] extends ReadonlyArray<string> ? Spec["values"][number] : string | VariableRef<string>;
|
|
14
|
+
/** Required attributes become required props; the rest are optional. */
|
|
15
|
+
type AttributeProps<Attributes extends Readonly<Record<string, AttributeSpec>>> = { [K in keyof Attributes as Attributes[K]["required"] extends true ? K : never]: AttributeType<K & string, Attributes[K]>; } & { [K in keyof Attributes as Attributes[K]["required"] extends true ? never : K]?: AttributeType<K & string, Attributes[K]>; };
|
|
16
|
+
type ChildrenProp<Content extends TagSpec["content"]> = Content extends "void" ? Record<never, never> : {
|
|
17
|
+
readonly children?: SmlChildren | undefined;
|
|
18
|
+
};
|
|
19
|
+
/** Full prop type for one spec: its attributes (typed/required) plus children where allowed. */
|
|
20
|
+
type PropsOf<Spec extends TagSpec> = AttributeProps<Spec["attributes"]> & ChildrenProp<Spec["content"]>;
|
|
21
|
+
/** Prop type for an email-table tag (the shared If/Else live here too). */
|
|
22
|
+
type PropsFor<Tag extends keyof typeof EMAIL_TAG_SPECS> = PropsOf<(typeof EMAIL_TAG_SPECS)[Tag]>;
|
|
23
|
+
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
24
|
+
declare const Email: (props: PropsOf<{
|
|
25
|
+
readonly attributes: {
|
|
26
|
+
readonly lang: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
};
|
|
29
|
+
readonly dir: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
};
|
|
32
|
+
readonly width: {
|
|
33
|
+
readonly type: "number";
|
|
34
|
+
readonly min: 1;
|
|
35
|
+
readonly max: 10000;
|
|
36
|
+
};
|
|
37
|
+
readonly class: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
readonly content: "email";
|
|
42
|
+
}>) => ElementNode;
|
|
43
|
+
declare const Subject: (props: PropsOf<{
|
|
44
|
+
readonly attributes: {};
|
|
45
|
+
readonly content: "preview";
|
|
46
|
+
}>) => ElementNode;
|
|
47
|
+
declare const Preview: (props: PropsOf<{
|
|
48
|
+
readonly attributes: {};
|
|
49
|
+
readonly content: "preview";
|
|
50
|
+
}>) => ElementNode;
|
|
51
|
+
declare const Container: (props: PropsOf<{
|
|
52
|
+
readonly attributes: {
|
|
53
|
+
readonly backgroundImage: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
};
|
|
56
|
+
readonly backgroundPosition: {
|
|
57
|
+
readonly type: "string";
|
|
58
|
+
readonly values: readonly ["center", "top", "bottom"];
|
|
59
|
+
};
|
|
60
|
+
readonly backgroundSize: {
|
|
61
|
+
readonly type: "string";
|
|
62
|
+
readonly values: readonly ["cover", "contain", "auto"];
|
|
63
|
+
};
|
|
64
|
+
readonly class: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
readonly content: "block";
|
|
69
|
+
}>) => ElementNode;
|
|
70
|
+
declare const Section: (props: PropsOf<{
|
|
71
|
+
readonly attributes: {
|
|
72
|
+
readonly backgroundImage: {
|
|
73
|
+
readonly type: "string";
|
|
74
|
+
};
|
|
75
|
+
readonly backgroundPosition: {
|
|
76
|
+
readonly type: "string";
|
|
77
|
+
readonly values: readonly ["center", "top", "bottom"];
|
|
78
|
+
};
|
|
79
|
+
readonly backgroundSize: {
|
|
80
|
+
readonly type: "string";
|
|
81
|
+
readonly values: readonly ["cover", "contain", "auto"];
|
|
82
|
+
};
|
|
83
|
+
readonly class: {
|
|
84
|
+
readonly type: "string";
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
readonly content: "block";
|
|
88
|
+
}>) => ElementNode;
|
|
89
|
+
declare const Row: (props: PropsOf<{
|
|
90
|
+
readonly attributes: {
|
|
91
|
+
readonly class: {
|
|
92
|
+
readonly type: "string";
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
readonly content: "columns";
|
|
96
|
+
}>) => ElementNode;
|
|
97
|
+
declare const Column: (props: PropsOf<{
|
|
98
|
+
readonly attributes: {
|
|
99
|
+
readonly class: {
|
|
100
|
+
readonly type: "string";
|
|
101
|
+
};
|
|
102
|
+
readonly width: {
|
|
103
|
+
readonly type: "string";
|
|
104
|
+
};
|
|
105
|
+
readonly valign: {
|
|
106
|
+
readonly type: "string";
|
|
107
|
+
readonly values: readonly ["top", "middle", "bottom"];
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
readonly content: "block";
|
|
111
|
+
}>) => ElementNode;
|
|
112
|
+
declare const Text: (props: PropsOf<{
|
|
113
|
+
readonly attributes: {
|
|
114
|
+
readonly class: {
|
|
115
|
+
readonly type: "string";
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
readonly content: "inline";
|
|
119
|
+
}>) => ElementNode;
|
|
120
|
+
declare const Image: (props: PropsOf<{
|
|
121
|
+
readonly attributes: {
|
|
122
|
+
readonly src: {
|
|
123
|
+
readonly type: "string";
|
|
124
|
+
readonly required: true;
|
|
125
|
+
};
|
|
126
|
+
readonly alt: {
|
|
127
|
+
readonly type: "string";
|
|
128
|
+
};
|
|
129
|
+
readonly width: {
|
|
130
|
+
readonly type: "number";
|
|
131
|
+
readonly min: 0;
|
|
132
|
+
readonly max: 10000;
|
|
133
|
+
};
|
|
134
|
+
readonly height: {
|
|
135
|
+
readonly type: "number";
|
|
136
|
+
readonly min: 0;
|
|
137
|
+
readonly max: 10000;
|
|
138
|
+
};
|
|
139
|
+
readonly class: {
|
|
140
|
+
readonly type: "string";
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
readonly content: "image";
|
|
144
|
+
}>) => ElementNode;
|
|
145
|
+
declare const Source: (props: PropsOf<{
|
|
146
|
+
readonly attributes: {
|
|
147
|
+
readonly scheme: {
|
|
148
|
+
readonly type: "string";
|
|
149
|
+
readonly required: true;
|
|
150
|
+
readonly values: readonly ["dark"];
|
|
151
|
+
};
|
|
152
|
+
readonly src: {
|
|
153
|
+
readonly type: "string";
|
|
154
|
+
readonly required: true;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
readonly content: "void";
|
|
158
|
+
}>) => ElementNode;
|
|
159
|
+
declare const Divider: (props: PropsOf<{
|
|
160
|
+
readonly attributes: {
|
|
161
|
+
readonly class: {
|
|
162
|
+
readonly type: "string";
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
readonly content: "void";
|
|
166
|
+
}>) => ElementNode;
|
|
167
|
+
declare const Spacer: (props: PropsOf<{
|
|
168
|
+
readonly attributes: {
|
|
169
|
+
readonly height: {
|
|
170
|
+
readonly type: "number";
|
|
171
|
+
readonly required: true;
|
|
172
|
+
readonly min: 1;
|
|
173
|
+
readonly max: 2000;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
readonly content: "void";
|
|
177
|
+
}>) => ElementNode;
|
|
178
|
+
declare const List: (props: PropsOf<{
|
|
179
|
+
readonly attributes: {
|
|
180
|
+
readonly type: {
|
|
181
|
+
readonly type: "string";
|
|
182
|
+
readonly values: readonly ["unordered", "ordered"];
|
|
183
|
+
};
|
|
184
|
+
readonly class: {
|
|
185
|
+
readonly type: "string";
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
readonly content: "list";
|
|
189
|
+
}>) => ElementNode;
|
|
190
|
+
declare const ListItem: (props: PropsOf<{
|
|
191
|
+
readonly attributes: {
|
|
192
|
+
readonly class: {
|
|
193
|
+
readonly type: "string";
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
readonly content: "inline";
|
|
197
|
+
}>) => ElementNode;
|
|
198
|
+
/** Heading — `level` is the literal 1–6 union the grammar bounds it to. */
|
|
199
|
+
declare const Heading: (props: PropsFor<"Heading">) => ElementNode;
|
|
200
|
+
/**
|
|
201
|
+
* `<Button>` is two tags in one name: the email link-button (`href` required)
|
|
202
|
+
* and the WhatsApp template button (`type` required, `url`/`phone` per type).
|
|
203
|
+
* The union keeps both authorable; the parser's per-channel table rejects the
|
|
204
|
+
* wrong one at render time.
|
|
205
|
+
*/
|
|
206
|
+
declare const Button: (props: PropsFor<"Button"> | PropsOf<(typeof WHATSAPP_TAG_SPECS)["Button"]>) => ElementNode;
|
|
207
|
+
declare const Strong: (props: PropsOf<{
|
|
208
|
+
readonly attributes: {};
|
|
209
|
+
readonly content: "inline-nolink";
|
|
210
|
+
}>) => ElementNode;
|
|
211
|
+
declare const Em: (props: PropsOf<{
|
|
212
|
+
readonly attributes: {};
|
|
213
|
+
readonly content: "inline-nolink";
|
|
214
|
+
}>) => ElementNode;
|
|
215
|
+
declare const A: (props: PropsOf<{
|
|
216
|
+
readonly attributes: {
|
|
217
|
+
readonly href: {
|
|
218
|
+
readonly type: "string";
|
|
219
|
+
readonly required: true;
|
|
220
|
+
};
|
|
221
|
+
readonly class: {
|
|
222
|
+
readonly type: "string";
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
readonly content: "inline-nolink";
|
|
226
|
+
}>) => ElementNode;
|
|
227
|
+
declare const Sms: (props: PropsOf<{
|
|
228
|
+
readonly attributes: {
|
|
229
|
+
readonly category: {
|
|
230
|
+
readonly type: "string";
|
|
231
|
+
readonly values: readonly ["transactional", "promotional", "service_implicit"];
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
readonly content: "verbatim";
|
|
235
|
+
}>) => ElementNode;
|
|
236
|
+
declare const WhatsApp: (props: PropsOf<{
|
|
237
|
+
readonly attributes: {
|
|
238
|
+
readonly name: {
|
|
239
|
+
readonly type: "string";
|
|
240
|
+
};
|
|
241
|
+
readonly language: {
|
|
242
|
+
readonly type: "string";
|
|
243
|
+
};
|
|
244
|
+
readonly category: {
|
|
245
|
+
readonly type: "string";
|
|
246
|
+
readonly required: true;
|
|
247
|
+
readonly values: readonly ["marketing", "utility", "authentication", "service"];
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
readonly content: "whatsapp";
|
|
251
|
+
}>) => ElementNode;
|
|
252
|
+
declare const Header: (props: PropsOf<{
|
|
253
|
+
readonly attributes: {
|
|
254
|
+
readonly type: {
|
|
255
|
+
readonly type: "string";
|
|
256
|
+
readonly values: readonly ["text", "image", "video", "document"];
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
readonly content: "verbatim";
|
|
260
|
+
}>) => ElementNode;
|
|
261
|
+
declare const Body: (props: PropsOf<{
|
|
262
|
+
readonly attributes: {};
|
|
263
|
+
readonly content: "verbatim";
|
|
264
|
+
}>) => ElementNode;
|
|
265
|
+
declare const Footer: (props: PropsOf<{
|
|
266
|
+
readonly attributes: {};
|
|
267
|
+
readonly content: "verbatim";
|
|
268
|
+
}>) => ElementNode;
|
|
269
|
+
declare const Buttons: (props: PropsOf<{
|
|
270
|
+
readonly attributes: {};
|
|
271
|
+
readonly content: "buttons";
|
|
272
|
+
}>) => ElementNode;
|
|
273
|
+
/**
|
|
274
|
+
* `<If test={v.plan.eq("pro").and(v.seats.gt(5))}>…<Else>…</Else></If>`. The
|
|
275
|
+
* predicate ref is printed to the closed predicate source the parser validates;
|
|
276
|
+
* a plain VariableRef (someone wrote `test={v.plan}`) is a common slip, so name
|
|
277
|
+
* the fix instead of coercing.
|
|
278
|
+
*/
|
|
279
|
+
declare const If: (props: {
|
|
280
|
+
readonly test: PredicateRef;
|
|
281
|
+
readonly children?: SmlChildren | undefined;
|
|
282
|
+
}) => ElementNode;
|
|
283
|
+
/** `<Else>` — the trailing branch of an `<If>`; children only. */
|
|
284
|
+
declare const Else: (props: {
|
|
285
|
+
readonly children?: SmlChildren | undefined;
|
|
286
|
+
}) => ElementNode;
|
|
287
|
+
//#endregion
|
|
288
|
+
export { A, Body, Button, Buttons, Column, Container, Divider, Else, Em, Email, Footer, Header, Heading, HeadingLevel, If, Image, List, ListItem, Preview, Row, Section, Sms, Source, Spacer, Strong, Subject, Text, WhatsApp };
|
|
289
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { VARIABLE_REF, isPredicateRef, isVariableRef, printPredicateRef } from "./variables.js";
|
|
2
|
+
import { elementDraft, normalizeChildren } from "./jsx-runtime.js";
|
|
3
|
+
import { EMAIL_TAG_SPECS, SMS_TAG_SPECS, WHATSAPP_TAG_SPECS } from "./parser.js";
|
|
4
|
+
//#region src/components.ts
|
|
5
|
+
const PLACEHOLDER_SPAN = {
|
|
6
|
+
start: 0,
|
|
7
|
+
end: 0
|
|
8
|
+
};
|
|
9
|
+
const attribute = (name, value) => ({
|
|
10
|
+
name,
|
|
11
|
+
value,
|
|
12
|
+
span: PLACEHOLDER_SPAN,
|
|
13
|
+
valueSpan: PLACEHOLDER_SPAN
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Maps a props object onto ordered attributes in AUTHORED order — object keys
|
|
17
|
+
* preserve JSX insertion order, and the printer preserves attribute order, so
|
|
18
|
+
* `<Column width="50%" class="pr-2">` round-trips as written rather than being
|
|
19
|
+
* reordered to the spec's declaration order. `children` is skipped; the spec's
|
|
20
|
+
* declared type per attribute picks the AttributeValue kind.
|
|
21
|
+
*/
|
|
22
|
+
const attributesFor = (spec, props) => {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const [name, value] of Object.entries(props)) {
|
|
25
|
+
if (name === "children" || value === void 0) continue;
|
|
26
|
+
const attributeSpec = spec.attributes[name];
|
|
27
|
+
if (isVariableRef(value)) {
|
|
28
|
+
out.push(attribute(name, {
|
|
29
|
+
kind: "string",
|
|
30
|
+
value: `{{${value[VARIABLE_REF].path}}}`
|
|
31
|
+
}));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (attributeSpec?.type === "number") out.push(attribute(name, {
|
|
35
|
+
kind: "number",
|
|
36
|
+
value
|
|
37
|
+
}));
|
|
38
|
+
else out.push(attribute(name, {
|
|
39
|
+
kind: "string",
|
|
40
|
+
value: String(value)
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
};
|
|
45
|
+
/** Builds a component for a tag whose attributes are all plain scalars. */
|
|
46
|
+
const scalarComponent = (tag, spec) => (props) => {
|
|
47
|
+
const record = props;
|
|
48
|
+
return elementDraft(tag, attributesFor(spec, record), spec.content === "void" ? [] : normalizeChildren(record.children));
|
|
49
|
+
};
|
|
50
|
+
const Email = scalarComponent("Email", EMAIL_TAG_SPECS.Email);
|
|
51
|
+
const Subject = scalarComponent("Subject", EMAIL_TAG_SPECS.Subject);
|
|
52
|
+
const Preview = scalarComponent("Preview", EMAIL_TAG_SPECS.Preview);
|
|
53
|
+
const Container = scalarComponent("Container", EMAIL_TAG_SPECS.Container);
|
|
54
|
+
const Section = scalarComponent("Section", EMAIL_TAG_SPECS.Section);
|
|
55
|
+
const Row = scalarComponent("Row", EMAIL_TAG_SPECS.Row);
|
|
56
|
+
const Column = scalarComponent("Column", EMAIL_TAG_SPECS.Column);
|
|
57
|
+
const Text = scalarComponent("Text", EMAIL_TAG_SPECS.Text);
|
|
58
|
+
const Image = scalarComponent("Image", EMAIL_TAG_SPECS.Image);
|
|
59
|
+
const Source = scalarComponent("Source", EMAIL_TAG_SPECS.Source);
|
|
60
|
+
const Divider = scalarComponent("Divider", EMAIL_TAG_SPECS.Divider);
|
|
61
|
+
const Spacer = scalarComponent("Spacer", EMAIL_TAG_SPECS.Spacer);
|
|
62
|
+
const List = scalarComponent("List", EMAIL_TAG_SPECS.List);
|
|
63
|
+
const ListItem = scalarComponent("ListItem", EMAIL_TAG_SPECS.ListItem);
|
|
64
|
+
/** Heading — `level` is the literal 1–6 union the grammar bounds it to. */
|
|
65
|
+
const Heading = (props) => scalarComponent("Heading", EMAIL_TAG_SPECS.Heading)(props);
|
|
66
|
+
/**
|
|
67
|
+
* `<Button>` is two tags in one name: the email link-button (`href` required)
|
|
68
|
+
* and the WhatsApp template button (`type` required, `url`/`phone` per type).
|
|
69
|
+
* The union keeps both authorable; the parser's per-channel table rejects the
|
|
70
|
+
* wrong one at render time.
|
|
71
|
+
*/
|
|
72
|
+
const Button = (props) => {
|
|
73
|
+
const record = props;
|
|
74
|
+
return elementDraft("Button", attributesFor(EMAIL_TAG_SPECS.Button, record), normalizeChildren(record.children));
|
|
75
|
+
};
|
|
76
|
+
const Strong = scalarComponent("strong", EMAIL_TAG_SPECS.strong);
|
|
77
|
+
const Em = scalarComponent("em", EMAIL_TAG_SPECS.em);
|
|
78
|
+
const A = scalarComponent("a", EMAIL_TAG_SPECS.a);
|
|
79
|
+
const Sms = scalarComponent("Sms", SMS_TAG_SPECS.Sms);
|
|
80
|
+
const WhatsApp = scalarComponent("WhatsApp", WHATSAPP_TAG_SPECS.WhatsApp);
|
|
81
|
+
const Header = scalarComponent("Header", WHATSAPP_TAG_SPECS.Header);
|
|
82
|
+
const Body = scalarComponent("Body", WHATSAPP_TAG_SPECS.Body);
|
|
83
|
+
const Footer = scalarComponent("Footer", WHATSAPP_TAG_SPECS.Footer);
|
|
84
|
+
const Buttons = scalarComponent("Buttons", WHATSAPP_TAG_SPECS.Buttons);
|
|
85
|
+
/**
|
|
86
|
+
* `<If test={v.plan.eq("pro").and(v.seats.gt(5))}>…<Else>…</Else></If>`. The
|
|
87
|
+
* predicate ref is printed to the closed predicate source the parser validates;
|
|
88
|
+
* a plain VariableRef (someone wrote `test={v.plan}`) is a common slip, so name
|
|
89
|
+
* the fix instead of coercing.
|
|
90
|
+
*/
|
|
91
|
+
const If = (props) => {
|
|
92
|
+
if (!isPredicateRef(props.test)) {
|
|
93
|
+
const hint = isVariableRef(props.test) ? `— a bare variable is not a condition; write test={var.exists()} or a comparison like .eq(...)` : "— build it with the variable predicate builders (eq/ne/gt/…, and/or/not)";
|
|
94
|
+
throw new TypeError(`<If test> must be a predicate ${hint}`);
|
|
95
|
+
}
|
|
96
|
+
return elementDraft("If", [attribute("test", {
|
|
97
|
+
kind: "string",
|
|
98
|
+
value: printPredicateRef(props.test)
|
|
99
|
+
})], normalizeChildren(props.children));
|
|
100
|
+
};
|
|
101
|
+
/** `<Else>` — the trailing branch of an `<If>`; children only. */
|
|
102
|
+
const Else = (props) => elementDraft("Else", [], normalizeChildren(props.children));
|
|
103
|
+
//#endregion
|
|
104
|
+
export { A, Body, Button, Buttons, Column, Container, Divider, Else, Em, Email, Footer, Header, Heading, If, Image, List, ListItem, Preview, Row, Section, Sms, Source, Spacer, Strong, Subject, Text, WhatsApp };
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","names":[],"sources":["../src/components.ts"],"sourcesContent":["import type { Attribute, AttributeValue, ElementNode, SmlTag } from \"./ast\";\nimport { elementDraft, normalizeChildren, type SmlChildren } from \"./jsx-runtime\";\nimport type { AttributeSpec, TagSpec } from \"./parser\";\nimport { EMAIL_TAG_SPECS, SMS_TAG_SPECS, WHATSAPP_TAG_SPECS } from \"./parser\";\nimport {\n isPredicateRef,\n isVariableRef,\n printPredicateRef,\n VARIABLE_REF,\n type PredicateRef,\n type VariableRef,\n} from \"./variables\";\n\n// Typed TSX components for the full SML vocabulary — email blocks + inline\n// tags, plus the sms/whatsapp channel tags (design brief §7b/§7f). Prop types\n// are DERIVED from the parser's channel spec tables at the type level — there\n// is no hand-written mirror of the grammar here; widening a table widens these\n// components' accepted props automatically. Each component maps its typed\n// props onto ordered AST attributes and normalized children, then hands off to\n// the jsx-runtime's element builder. The output is validated by renderTemplate's\n// print → parse pass, so components restrict *authoring* while the parser stays\n// the single source of grammatical truth.\n\n// ── Type-level prop derivation from the spec tables ─────────────────────────────\n\n/**\n * The scalar type an attribute spec accepts. `level` narrows to the Heading\n * union; a spec with `values` narrows to that literal union (no VariableRef —\n * enum attributes are structural, not send-time holes); other string\n * attributes also take a VariableRef (href={v.ctaUrl}), which embeds as its\n * send-time interpolation.\n */\ntype AttributeType<Name extends string, Spec extends AttributeSpec> = Name extends \"level\"\n ? HeadingLevel\n : Spec[\"type\"] extends \"number\"\n ? number\n : Spec[\"values\"] extends ReadonlyArray<string>\n ? Spec[\"values\"][number]\n : string | VariableRef<string>;\n\n/** Required attributes become required props; the rest are optional. */\ntype AttributeProps<Attributes extends Readonly<Record<string, AttributeSpec>>> = {\n [K in keyof Attributes as Attributes[K][\"required\"] extends true ? K : never]: AttributeType<\n K & string,\n Attributes[K]\n >;\n} & {\n [K in keyof Attributes as Attributes[K][\"required\"] extends true ? never : K]?: AttributeType<\n K & string,\n Attributes[K]\n >;\n};\n\ntype ChildrenProp<Content extends TagSpec[\"content\"]> = Content extends \"void\"\n ? Record<never, never>\n : { readonly children?: SmlChildren | undefined };\n\n/** Full prop type for one spec: its attributes (typed/required) plus children where allowed. */\ntype PropsOf<Spec extends TagSpec> = AttributeProps<Spec[\"attributes\"]> &\n ChildrenProp<Spec[\"content\"]>;\n\n/** Prop type for an email-table tag (the shared If/Else live here too). */\ntype PropsFor<Tag extends keyof typeof EMAIL_TAG_SPECS> = PropsOf<(typeof EMAIL_TAG_SPECS)[Tag]>;\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\n// ── Attribute assembly ──────────────────────────────────────────────────────────\n\nconst PLACEHOLDER_SPAN = { start: 0, end: 0 } as const;\n\nconst attribute = (name: string, value: AttributeValue): Attribute => ({\n name,\n value,\n span: PLACEHOLDER_SPAN,\n valueSpan: PLACEHOLDER_SPAN,\n});\n\n/**\n * Maps a props object onto ordered attributes in AUTHORED order — object keys\n * preserve JSX insertion order, and the printer preserves attribute order, so\n * `<Column width=\"50%\" class=\"pr-2\">` round-trips as written rather than being\n * reordered to the spec's declaration order. `children` is skipped; the spec's\n * declared type per attribute picks the AttributeValue kind.\n */\nconst attributesFor = (spec: TagSpec, props: Record<string, unknown>): Array<Attribute> => {\n const out: Array<Attribute> = [];\n for (const [name, value] of Object.entries(props)) {\n if (name === \"children\" || value === undefined) continue;\n const attributeSpec = spec.attributes[name];\n // A VariableRef in attribute position (href={v.ctaUrl}) embeds as its\n // send-time interpolation — String(ref) would trip the coercion guard.\n if (isVariableRef(value)) {\n out.push(attribute(name, { kind: \"string\", value: `{{${value[VARIABLE_REF].path}}}` }));\n continue;\n }\n // Unknown attributes are a type error at authoring; if one slips through a\n // loosely-typed caller, emit it as a string and let the parser reject it.\n if (attributeSpec?.type === \"number\") {\n out.push(attribute(name, { kind: \"number\", value: value as number }));\n } else {\n out.push(attribute(name, { kind: \"string\", value: String(value) }));\n }\n }\n return out;\n};\n\n/** Builds a component for a tag whose attributes are all plain scalars. */\nconst scalarComponent =\n <Tag extends SmlTag, Spec extends TagSpec>(tag: Tag, spec: Spec) =>\n (props: PropsOf<Spec>): ElementNode => {\n const record = props as Record<string, unknown>;\n return elementDraft(\n tag,\n attributesFor(spec, record),\n spec.content === \"void\" ? [] : normalizeChildren(record.children),\n );\n };\n\n// ── Email block components ──────────────────────────────────────────────────────\n\nexport const Email = scalarComponent(\"Email\", EMAIL_TAG_SPECS.Email);\nexport const Subject = scalarComponent(\"Subject\", EMAIL_TAG_SPECS.Subject);\nexport const Preview = scalarComponent(\"Preview\", EMAIL_TAG_SPECS.Preview);\nexport const Container = scalarComponent(\"Container\", EMAIL_TAG_SPECS.Container);\nexport const Section = scalarComponent(\"Section\", EMAIL_TAG_SPECS.Section);\nexport const Row = scalarComponent(\"Row\", EMAIL_TAG_SPECS.Row);\nexport const Column = scalarComponent(\"Column\", EMAIL_TAG_SPECS.Column);\nexport const Text = scalarComponent(\"Text\", EMAIL_TAG_SPECS.Text);\nexport const Image = scalarComponent(\"Image\", EMAIL_TAG_SPECS.Image);\nexport const Source = scalarComponent(\"Source\", EMAIL_TAG_SPECS.Source);\nexport const Divider = scalarComponent(\"Divider\", EMAIL_TAG_SPECS.Divider);\nexport const Spacer = scalarComponent(\"Spacer\", EMAIL_TAG_SPECS.Spacer);\nexport const List = scalarComponent(\"List\", EMAIL_TAG_SPECS.List);\nexport const ListItem = scalarComponent(\"ListItem\", EMAIL_TAG_SPECS.ListItem);\n\n/** Heading — `level` is the literal 1–6 union the grammar bounds it to. */\nexport const Heading = (props: PropsFor<\"Heading\">): ElementNode =>\n scalarComponent(\"Heading\", EMAIL_TAG_SPECS.Heading)(props);\n\n/**\n * `<Button>` is two tags in one name: the email link-button (`href` required)\n * and the WhatsApp template button (`type` required, `url`/`phone` per type).\n * The union keeps both authorable; the parser's per-channel table rejects the\n * wrong one at render time.\n */\nexport const Button = (\n props: PropsFor<\"Button\"> | PropsOf<(typeof WHATSAPP_TAG_SPECS)[\"Button\"]>,\n): ElementNode => {\n const record = props as Record<string, unknown>;\n return elementDraft(\n \"Button\",\n attributesFor(EMAIL_TAG_SPECS.Button, record),\n normalizeChildren(record.children),\n );\n};\n\n// ── Inline helpers ──────────────────────────────────────────────────────────────\n\nexport const Strong = scalarComponent(\"strong\", EMAIL_TAG_SPECS.strong);\nexport const Em = scalarComponent(\"em\", EMAIL_TAG_SPECS.em);\nexport const A = scalarComponent(\"a\", EMAIL_TAG_SPECS.a);\n\n// ── Channel roots + WhatsApp anatomy ────────────────────────────────────────────\n//\n// Text content inside <Sms>, <Header>/<Body>/<Footer>, and <Button> is\n// VERBATIM — the JSX transform trims JSX text per the JSX spec, so multi-line\n// bodies are authored as template-literal children: {`line one\\nline two`}.\n\nexport const Sms = scalarComponent(\"Sms\", SMS_TAG_SPECS.Sms);\nexport const WhatsApp = scalarComponent(\"WhatsApp\", WHATSAPP_TAG_SPECS.WhatsApp);\nexport const Header = scalarComponent(\"Header\", WHATSAPP_TAG_SPECS.Header);\nexport const Body = scalarComponent(\"Body\", WHATSAPP_TAG_SPECS.Body);\nexport const Footer = scalarComponent(\"Footer\", WHATSAPP_TAG_SPECS.Footer);\nexport const Buttons = scalarComponent(\"Buttons\", WHATSAPP_TAG_SPECS.Buttons);\n\n// ── Conditionals ────────────────────────────────────────────────────────────────\n\n/**\n * `<If test={v.plan.eq(\"pro\").and(v.seats.gt(5))}>…<Else>…</Else></If>`. The\n * predicate ref is printed to the closed predicate source the parser validates;\n * a plain VariableRef (someone wrote `test={v.plan}`) is a common slip, so name\n * the fix instead of coercing.\n */\nexport const If = (props: {\n readonly test: PredicateRef;\n readonly children?: SmlChildren | undefined;\n}): ElementNode => {\n if (!isPredicateRef(props.test)) {\n const hint = isVariableRef(props.test)\n ? `— a bare variable is not a condition; write test={${\"var\"}.exists()} or a comparison like .eq(...)`\n : \"— build it with the variable predicate builders (eq/ne/gt/…, and/or/not)\";\n // oxlint-disable-next-line samva/no-try-catch-or-throw, samva/no-error-constructor -- User-build authoring boundary: a non-predicate <If test> is a programmer error that must fail the user's TSX build loudly, not become a typed Effect failure.\n throw new TypeError(`<If test> must be a predicate ${hint}`);\n }\n return elementDraft(\n \"If\",\n [attribute(\"test\", { kind: \"string\", value: printPredicateRef(props.test) })],\n normalizeChildren(props.children),\n );\n};\n\n/** `<Else>` — the trailing branch of an `<If>`; children only. */\nexport const Else = (props: { readonly children?: SmlChildren | undefined }): ElementNode =>\n elementDraft(\"Else\", [], normalizeChildren(props.children));\n"],"mappings":";;;;AAoEA,MAAM,mBAAmB;CAAE,OAAO;CAAG,KAAK;AAAE;AAE5C,MAAM,aAAa,MAAc,WAAsC;CACrE;CACA;CACA,MAAM;CACN,WAAW;AACb;;;;;;;;AASA,MAAM,iBAAiB,MAAe,UAAqD;CACzF,MAAM,MAAwB,CAAC;CAC/B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,GAAG;EACjD,IAAI,SAAS,cAAc,UAAU,KAAA,GAAW;EAChD,MAAM,gBAAgB,KAAK,WAAW;EAGtC,IAAI,cAAc,KAAK,GAAG;GACxB,IAAI,KAAK,UAAU,MAAM;IAAE,MAAM;IAAU,OAAO,KAAK,MAAM,aAAa,CAAC,KAAK;GAAI,CAAC,CAAC;GACtF;EACF;EAGA,IAAI,eAAe,SAAS,UAC1B,IAAI,KAAK,UAAU,MAAM;GAAE,MAAM;GAAiB;EAAgB,CAAC,CAAC;OAEpE,IAAI,KAAK,UAAU,MAAM;GAAE,MAAM;GAAU,OAAO,OAAO,KAAK;EAAE,CAAC,CAAC;CAEtE;CACA,OAAO;AACT;;AAGA,MAAM,mBACuC,KAAU,UACpD,UAAsC;CACrC,MAAM,SAAS;CACf,OAAO,aACL,KACA,cAAc,MAAM,MAAM,GAC1B,KAAK,YAAY,SAAS,CAAC,IAAI,kBAAkB,OAAO,QAAQ,CAClE;AACF;AAIF,MAAa,QAAQ,gBAAgB,SAAS,gBAAgB,KAAK;AACnE,MAAa,UAAU,gBAAgB,WAAW,gBAAgB,OAAO;AACzE,MAAa,UAAU,gBAAgB,WAAW,gBAAgB,OAAO;AACzE,MAAa,YAAY,gBAAgB,aAAa,gBAAgB,SAAS;AAC/E,MAAa,UAAU,gBAAgB,WAAW,gBAAgB,OAAO;AACzE,MAAa,MAAM,gBAAgB,OAAO,gBAAgB,GAAG;AAC7D,MAAa,SAAS,gBAAgB,UAAU,gBAAgB,MAAM;AACtE,MAAa,OAAO,gBAAgB,QAAQ,gBAAgB,IAAI;AAChE,MAAa,QAAQ,gBAAgB,SAAS,gBAAgB,KAAK;AACnE,MAAa,SAAS,gBAAgB,UAAU,gBAAgB,MAAM;AACtE,MAAa,UAAU,gBAAgB,WAAW,gBAAgB,OAAO;AACzE,MAAa,SAAS,gBAAgB,UAAU,gBAAgB,MAAM;AACtE,MAAa,OAAO,gBAAgB,QAAQ,gBAAgB,IAAI;AAChE,MAAa,WAAW,gBAAgB,YAAY,gBAAgB,QAAQ;;AAG5E,MAAa,WAAW,UACtB,gBAAgB,WAAW,gBAAgB,OAAO,CAAC,CAAC,KAAK;;;;;;;AAQ3D,MAAa,UACX,UACgB;CAChB,MAAM,SAAS;CACf,OAAO,aACL,UACA,cAAc,gBAAgB,QAAQ,MAAM,GAC5C,kBAAkB,OAAO,QAAQ,CACnC;AACF;AAIA,MAAa,SAAS,gBAAgB,UAAU,gBAAgB,MAAM;AACtE,MAAa,KAAK,gBAAgB,MAAM,gBAAgB,EAAE;AAC1D,MAAa,IAAI,gBAAgB,KAAK,gBAAgB,CAAC;AAQvD,MAAa,MAAM,gBAAgB,OAAO,cAAc,GAAG;AAC3D,MAAa,WAAW,gBAAgB,YAAY,mBAAmB,QAAQ;AAC/E,MAAa,SAAS,gBAAgB,UAAU,mBAAmB,MAAM;AACzE,MAAa,OAAO,gBAAgB,QAAQ,mBAAmB,IAAI;AACnE,MAAa,SAAS,gBAAgB,UAAU,mBAAmB,MAAM;AACzE,MAAa,UAAU,gBAAgB,WAAW,mBAAmB,OAAO;;;;;;;AAU5E,MAAa,MAAM,UAGA;CACjB,IAAI,CAAC,eAAe,MAAM,IAAI,GAAG;EAC/B,MAAM,OAAO,cAAc,MAAM,IAAI,IACjC,kGACA;EAEJ,MAAM,IAAI,UAAU,iCAAiC,MAAM;CAC7D;CACA,OAAO,aACL,MACA,CAAC,UAAU,QAAQ;EAAE,MAAM;EAAU,OAAO,kBAAkB,MAAM,IAAI;CAAE,CAAC,CAAC,GAC5E,kBAAkB,MAAM,QAAQ,CAClC;AACF;;AAGA,MAAa,QAAQ,UACnB,aAAa,QAAQ,CAAC,GAAG,kBAAkB,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/conditionals.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Selects <If> branches in a compiled artifact (html or plain-text alt) for
|
|
4
|
+
* one recipient's variables and strips all sml: markers. Marker-free content
|
|
5
|
+
* passes through unchanged.
|
|
6
|
+
*/
|
|
7
|
+
declare const resolveArtifactConditionals: (content: string, variables: Record<string, unknown>) => string;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { resolveArtifactConditionals };
|
|
10
|
+
//# sourceMappingURL=conditionals.d.ts.map
|