@pyreon/document 0.7.0 → 0.9.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/README.md +68 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/{confluence-Va8e7RxQ.js → confluence-Bd3ua1Ut.js} +6 -4
- package/lib/confluence-Bd3ua1Ut.js.map +1 -0
- package/lib/{csv-2c38ub-Y.js → csv-COrS4qdy.js} +1 -1
- package/lib/{csv-2c38ub-Y.js.map → csv-COrS4qdy.js.map} +1 -1
- package/lib/{discord-DAoUZqvE.js → discord-BLUnkEh9.js} +6 -4
- package/lib/discord-BLUnkEh9.js.map +1 -0
- package/lib/{docx-CorFwEH9.js → docx-BEBOihjl.js} +27 -26
- package/lib/docx-BEBOihjl.js.map +1 -0
- package/lib/{email-Bn_Brjdp.js → email-D0bbfWq4.js} +15 -13
- package/lib/email-D0bbfWq4.js.map +1 -0
- package/lib/{google-chat-B6I017I1.js → google-chat-CkKCBUWC.js} +6 -4
- package/lib/google-chat-CkKCBUWC.js.map +1 -0
- package/lib/{html-De_iS_f0.js → html-B5biprN2.js} +15 -13
- package/lib/html-B5biprN2.js.map +1 -0
- package/lib/index.js +44 -42
- package/lib/index.js.map +1 -1
- package/lib/{markdown-BYC_3C9i.js → markdown-CdtlFGC0.js} +6 -4
- package/lib/markdown-CdtlFGC0.js.map +1 -0
- package/lib/{notion-DHaQHO6P.js → notion-iG2C5bEY.js} +6 -4
- package/lib/notion-iG2C5bEY.js.map +1 -0
- package/lib/{pdf-CDPc5Itc.js → pdf-DIUQUEdj.js} +1 -1
- package/lib/{pdf-CDPc5Itc.js.map → pdf-DIUQUEdj.js.map} +1 -1
- package/lib/{pptx-DKQU6bjq.js → pptx-Dd33oL3_.js} +13 -11
- package/lib/pptx-Dd33oL3_.js.map +1 -0
- package/lib/sanitize-O_3j1mNJ.js +73 -0
- package/lib/sanitize-O_3j1mNJ.js.map +1 -0
- package/lib/{slack-CJRJgkag.js → slack-BI3EQwYm.js} +6 -4
- package/lib/slack-BI3EQwYm.js.map +1 -0
- package/lib/{svg-BM8biZmL.js → svg-BKxumy-p.js} +14 -12
- package/lib/svg-BKxumy-p.js.map +1 -0
- package/lib/{teams-S99tonRG.js → teams-Cwz9lce0.js} +6 -4
- package/lib/teams-Cwz9lce0.js.map +1 -0
- package/lib/{telegram-CbEO_2PN.js → telegram-gYFqyMXb.js} +5 -3
- package/lib/telegram-gYFqyMXb.js.map +1 -0
- package/lib/{text-B5U8ucRr.js → text-l1XNXBOC.js} +1 -1
- package/lib/{text-B5U8ucRr.js.map → text-l1XNXBOC.js.map} +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/{whatsapp-DJ2D1jGG.js → whatsapp-CjSGoOKx.js} +5 -3
- package/lib/whatsapp-CjSGoOKx.js.map +1 -0
- package/lib/{xlsx-D47x-gZ5.js → xlsx-Bb5TWyXQ.js} +1 -1
- package/lib/{xlsx-D47x-gZ5.js.map → xlsx-Bb5TWyXQ.js.map} +1 -1
- package/package.json +3 -3
- package/src/builder.ts +6 -6
- package/src/download.ts +6 -0
- package/src/nodes.ts +5 -0
- package/src/renderers/confluence.ts +4 -3
- package/src/renderers/discord.ts +4 -3
- package/src/renderers/docx.ts +44 -32
- package/src/renderers/email.ts +15 -12
- package/src/renderers/google-chat.ts +4 -3
- package/src/renderers/html.ts +20 -12
- package/src/renderers/markdown.ts +4 -3
- package/src/renderers/notion.ts +4 -3
- package/src/renderers/pptx.ts +11 -10
- package/src/renderers/slack.ts +4 -3
- package/src/renderers/svg.ts +12 -11
- package/src/renderers/teams.ts +4 -3
- package/src/renderers/telegram.ts +3 -2
- package/src/renderers/whatsapp.ts +3 -2
- package/src/sanitize.ts +88 -0
- package/lib/confluence-Va8e7RxQ.js.map +0 -1
- package/lib/discord-DAoUZqvE.js.map +0 -1
- package/lib/docx-CorFwEH9.js.map +0 -1
- package/lib/email-Bn_Brjdp.js.map +0 -1
- package/lib/google-chat-B6I017I1.js.map +0 -1
- package/lib/html-De_iS_f0.js.map +0 -1
- package/lib/markdown-BYC_3C9i.js.map +0 -1
- package/lib/notion-DHaQHO6P.js.map +0 -1
- package/lib/pptx-DKQU6bjq.js.map +0 -1
- package/lib/slack-CJRJgkag.js.map +0 -1
- package/lib/svg-BM8biZmL.js.map +0 -1
- package/lib/teams-S99tonRG.js.map +0 -1
- package/lib/telegram-CbEO_2PN.js.map +0 -1
- package/lib/whatsapp-DJ2D1jGG.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-
|
|
1
|
+
{"version":3,"file":"text-l1XNXBOC.js","names":[],"sources":["../src/renderers/text.ts"],"sourcesContent":["import type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction renderChild(child: DocChild): string {\n if (typeof child === 'string') return child\n return renderNode(child)\n}\n\nfunction renderChildren(children: DocChild[]): string {\n return children.map(renderChild).join('')\n}\n\nfunction pad(\n str: string,\n width: number,\n align: 'left' | 'center' | 'right' = 'left',\n): string {\n if (str.length >= width) return str.slice(0, width)\n const diff = width - str.length\n if (align === 'center') {\n const left = Math.floor(diff / 2)\n return ' '.repeat(left) + str + ' '.repeat(diff - left)\n }\n if (align === 'right') return ' '.repeat(diff) + str\n return str + ' '.repeat(diff)\n}\n\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case 'document':\n return renderChildren(node.children)\n\n case 'page':\n return renderChildren(node.children)\n\n case 'section':\n case 'row':\n case 'column':\n return renderChildren(node.children)\n\n case 'heading': {\n const text = renderChildren(node.children)\n const level = (p.level as number) ?? 1\n if (level === 1)\n return `${text.toUpperCase()}\\n${'='.repeat(text.length)}\\n\\n`\n if (level === 2) return `${text}\\n${'-'.repeat(text.length)}\\n\\n`\n return `${text}\\n\\n`\n }\n\n case 'text':\n return `${renderChildren(node.children)}\\n\\n`\n\n case 'link':\n return `${renderChildren(node.children)} (${p.href})`\n\n case 'image': {\n const alt = (p.alt as string) ?? 'Image'\n const caption = p.caption ? ` — ${p.caption}` : ''\n return `[${alt}${caption}]\\n\\n`\n }\n\n case 'table': {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(\n resolveColumn,\n )\n const rows = (p.rows ?? []) as (string | number)[][]\n\n if (columns.length === 0) return ''\n\n // Calculate column widths\n const widths = columns.map((col, i) => {\n const headerLen = col.header.length\n const maxDataLen = rows.reduce(\n (max, row) => Math.max(max, String(row[i] ?? '').length),\n 0,\n )\n return Math.max(headerLen, maxDataLen, 3)\n })\n\n // Header\n const header = columns\n .map((col, i) => pad(col.header, widths[i] ?? 3, col.align))\n .join(' | ')\n const separator = widths.map((w) => '-'.repeat(w ?? 3)).join('-+-')\n\n // Rows\n const body = rows\n .map((row) =>\n columns\n .map((col, i) =>\n pad(String(row[i] ?? ''), widths[i] ?? 3, col.align),\n )\n .join(' | '),\n )\n .join('\\n')\n\n let result = `${header}\\n${separator}\\n${body}\\n\\n`\n if (p.caption) result = `${p.caption}\\n\\n${result}`\n return result\n }\n\n case 'list': {\n const ordered = p.ordered as boolean | undefined\n return `${node.children\n .filter((c): c is DocNode => typeof c !== 'string')\n .map((item, i) => {\n const prefix = ordered ? `${i + 1}.` : '*'\n return ` ${prefix} ${renderChildren(item.children)}`\n })\n .join('\\n')}\\n\\n`\n }\n\n case 'list-item':\n return renderChildren(node.children)\n\n case 'code':\n return `${renderChildren(node.children)}\\n\\n`\n\n case 'divider':\n return `${'─'.repeat(40)}\\n\\n`\n\n case 'page-break':\n return `\\n${'═'.repeat(40)}\\n\\n`\n\n case 'spacer':\n return '\\n'\n\n case 'button':\n return `[${renderChildren(node.children)}] → ${p.href}\\n\\n`\n\n case 'quote':\n return ` \"${renderChildren(node.children)}\"\\n\\n`\n\n default:\n return renderChildren(node.children)\n }\n}\n\nexport const textRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n return `${renderNode(node).trim()}\\n`\n },\n}\n"],"mappings":"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"}
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/builder.ts","../../../src/nodes.ts","../../../src/download.ts","../../../src/render.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/builder.ts","../../../src/nodes.ts","../../../src/download.ts","../../../src/render.ts"],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { n as sanitizeHref } from "./sanitize-O_3j1mNJ.js";
|
|
2
|
+
|
|
1
3
|
//#region src/renderers/whatsapp.ts
|
|
2
4
|
/**
|
|
3
5
|
* WhatsApp renderer — outputs formatted text using WhatsApp's markup.
|
|
@@ -26,7 +28,7 @@ function renderNode(node) {
|
|
|
26
28
|
return `${text}\n\n`;
|
|
27
29
|
}
|
|
28
30
|
case "link": {
|
|
29
|
-
const href = p.href;
|
|
31
|
+
const href = sanitizeHref(p.href);
|
|
30
32
|
return `${getTextContent(node.children)}: ${href}\n\n`;
|
|
31
33
|
}
|
|
32
34
|
case "image": return "";
|
|
@@ -48,7 +50,7 @@ function renderNode(node) {
|
|
|
48
50
|
case "page-break": return "───────────\n\n";
|
|
49
51
|
case "spacer": return "\n";
|
|
50
52
|
case "button": {
|
|
51
|
-
const href = p.href;
|
|
53
|
+
const href = sanitizeHref(p.href);
|
|
52
54
|
return `*${getTextContent(node.children)}*: ${href}\n\n`;
|
|
53
55
|
}
|
|
54
56
|
case "quote": return `> ${getTextContent(node.children)}\n\n`;
|
|
@@ -61,4 +63,4 @@ const whatsappRenderer = { async render(node, _options) {
|
|
|
61
63
|
|
|
62
64
|
//#endregion
|
|
63
65
|
export { whatsappRenderer };
|
|
64
|
-
//# sourceMappingURL=whatsapp-
|
|
66
|
+
//# sourceMappingURL=whatsapp-CjSGoOKx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp-CjSGoOKx.js","names":[],"sources":["../src/renderers/whatsapp.ts"],"sourcesContent":["import { sanitizeHref } from '../sanitize'\nimport type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\n/**\n * WhatsApp renderer — outputs formatted text using WhatsApp's markup.\n * WhatsApp supports: *bold*, _italic_, ~strikethrough~, ```code```, > quote\n */\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction getTextContent(children: DocChild[]): string {\n return children\n .map((c) =>\n typeof c === 'string' ? c : getTextContent((c as DocNode).children),\n )\n .join('')\n}\n\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case 'document':\n case 'page':\n case 'section':\n case 'row':\n case 'column':\n return node.children\n .map((c) => (typeof c === 'string' ? c : renderNode(c)))\n .join('')\n\n case 'heading': {\n const text = getTextContent(node.children)\n return `*${text}*\\n\\n`\n }\n\n case 'text': {\n let text = getTextContent(node.children)\n if (p.bold) text = `*${text}*`\n if (p.italic) text = `_${text}_`\n if (p.strikethrough) text = `~${text}~`\n return `${text}\\n\\n`\n }\n\n case 'link': {\n const href = sanitizeHref(p.href as string)\n const text = getTextContent(node.children)\n return `${text}: ${href}\\n\\n`\n }\n\n case 'image':\n // WhatsApp doesn't support inline images in text\n return ''\n\n case 'table': {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(\n resolveColumn,\n )\n const rows = (p.rows ?? []) as (string | number)[][]\n\n const header = columns.map((c) => `*${c.header}*`).join(' | ')\n const body = rows\n .map((row) => row.map((c) => String(c ?? '')).join(' | '))\n .join('\\n')\n\n let result = `${header}\\n${body}\\n\\n`\n if (p.caption) result = `_${p.caption}_\\n${result}`\n return result\n }\n\n case 'list': {\n const ordered = p.ordered as boolean | undefined\n return `${node.children\n .filter((c): c is DocNode => typeof c !== 'string')\n .map((item, i) => {\n const prefix = ordered ? `${i + 1}.` : '•'\n return `${prefix} ${getTextContent(item.children)}`\n })\n .join('\\n')}\\n\\n`\n }\n\n case 'code': {\n const text = getTextContent(node.children)\n return `\\`\\`\\`${text}\\`\\`\\`\\n\\n`\n }\n\n case 'divider':\n case 'page-break':\n return '───────────\\n\\n'\n\n case 'spacer':\n return '\\n'\n\n case 'button': {\n const href = sanitizeHref(p.href as string)\n const text = getTextContent(node.children)\n return `*${text}*: ${href}\\n\\n`\n }\n\n case 'quote': {\n const text = getTextContent(node.children)\n return `> ${text}\\n\\n`\n }\n\n default:\n return ''\n }\n}\n\nexport const whatsappRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n return renderNode(node).trim()\n },\n}\n"],"mappings":";;;;;;;AAcA,SAAS,cAAc,KAAwC;AAC7D,QAAO,OAAO,QAAQ,WAAW,EAAE,QAAQ,KAAK,GAAG;;AAGrD,SAAS,eAAe,UAA8B;AACpD,QAAO,SACJ,KAAK,MACJ,OAAO,MAAM,WAAW,IAAI,eAAgB,EAAc,SAAS,CACpE,CACA,KAAK,GAAG;;AAGb,SAAS,WAAW,MAAuB;CACzC,MAAM,IAAI,KAAK;AAEf,SAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO,KAAK,SACT,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,WAAW,EAAE,CAAE,CACvD,KAAK,GAAG;EAEb,KAAK,UAEH,QAAO,IADM,eAAe,KAAK,SAAS,CAC1B;EAGlB,KAAK,QAAQ;GACX,IAAI,OAAO,eAAe,KAAK,SAAS;AACxC,OAAI,EAAE,KAAM,QAAO,IAAI,KAAK;AAC5B,OAAI,EAAE,OAAQ,QAAO,IAAI,KAAK;AAC9B,OAAI,EAAE,cAAe,QAAO,IAAI,KAAK;AACrC,UAAO,GAAG,KAAK;;EAGjB,KAAK,QAAQ;GACX,MAAM,OAAO,aAAa,EAAE,KAAe;AAE3C,UAAO,GADM,eAAe,KAAK,SAAS,CAC3B,IAAI,KAAK;;EAG1B,KAAK,QAEH,QAAO;EAET,KAAK,SAAS;GACZ,MAAM,WAAY,EAAE,WAAW,EAAE,EAA+B,IAC9D,cACD;GACD,MAAM,OAAQ,EAAE,QAAQ,EAAE;GAO1B,IAAI,SAAS,GALE,QAAQ,KAAK,MAAM,IAAI,EAAE,OAAO,GAAG,CAAC,KAAK,MAAM,CAKvC,IAJV,KACV,KAAK,QAAQ,IAAI,KAAK,MAAM,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,CACzD,KAAK,KAAK,CAEmB;AAChC,OAAI,EAAE,QAAS,UAAS,IAAI,EAAE,QAAQ,KAAK;AAC3C,UAAO;;EAGT,KAAK,QAAQ;GACX,MAAM,UAAU,EAAE;AAClB,UAAO,GAAG,KAAK,SACZ,QAAQ,MAAoB,OAAO,MAAM,SAAS,CAClD,KAAK,MAAM,MAAM;AAEhB,WAAO,GADQ,UAAU,GAAG,IAAI,EAAE,KAAK,IACtB,GAAG,eAAe,KAAK,SAAS;KACjD,CACD,KAAK,KAAK,CAAC;;EAGhB,KAAK,OAEH,QAAO,SADM,eAAe,KAAK,SAAS,CACrB;EAGvB,KAAK;EACL,KAAK,aACH,QAAO;EAET,KAAK,SACH,QAAO;EAET,KAAK,UAAU;GACb,MAAM,OAAO,aAAa,EAAE,KAAe;AAE3C,UAAO,IADM,eAAe,KAAK,SAAS,CAC1B,KAAK,KAAK;;EAG5B,KAAK,QAEH,QAAO,KADM,eAAe,KAAK,SAAS,CACzB;EAGnB,QACE,QAAO;;;AAIb,MAAa,mBAAqC,EAChD,MAAM,OAAO,MAAe,UAA2C;AACrE,QAAO,WAAW,KAAK,CAAC,MAAM;GAEjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xlsx-D47x-gZ5.js","names":[],"sources":["../src/renderers/xlsx.ts"],"sourcesContent":["import type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\n/**\n * XLSX renderer — lazy-loads ExcelJS on first use.\n * Extracts tables from the document and renders each as a worksheet.\n * Non-table content (headings, text) becomes header rows.\n */\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction getTextContent(children: DocChild[]): string {\n return children\n .map((c) =>\n typeof c === 'string' ? c : getTextContent((c as DocNode).children),\n )\n .join('')\n}\n\ninterface ExtractedSheet {\n name: string\n headings: string[]\n tables: DocNode[]\n}\n\n/** Walk the tree and group content into sheets (one per page, or one global). */\nfunction extractSheets(node: DocNode): ExtractedSheet[] {\n const sheets: ExtractedSheet[] = []\n let currentSheet: ExtractedSheet = {\n name: 'Sheet 1',\n headings: [],\n tables: [],\n }\n\n function walk(n: DocNode): void {\n switch (n.type) {\n case 'document':\n walkChildren(n)\n break\n\n case 'page':\n pushCurrentSheet()\n currentSheet = {\n name: `Sheet ${sheets.length + 1}`,\n headings: [],\n tables: [],\n }\n walkChildren(n)\n break\n\n case 'heading':\n addHeading(n)\n break\n\n case 'table':\n currentSheet.tables.push(n)\n break\n\n default:\n walkChildren(n)\n }\n }\n\n function walkChildren(n: DocNode): void {\n for (const child of n.children) {\n if (typeof child !== 'string') walk(child)\n }\n }\n\n function pushCurrentSheet(): void {\n if (currentSheet.tables.length > 0 || currentSheet.headings.length > 0) {\n sheets.push(currentSheet)\n }\n }\n\n function addHeading(n: DocNode): void {\n const text = getTextContent(n.children)\n currentSheet.headings.push(text)\n if (currentSheet.headings.length === 1) {\n currentSheet.name = text.slice(0, 31) // Excel sheet name max 31 chars\n }\n }\n\n walk(node)\n pushCurrentSheet()\n\n return sheets\n}\n\n/** Parse a cell value, handling currencies, percentages, and plain numbers. */\nfunction parseCellValue(value: string | number | undefined): string | number {\n if (value == null) return ''\n if (typeof value === 'number') return value\n\n const trimmed = value.trim()\n\n // Percentage: \"45%\" or \"12.5%\"\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) {\n return Number.parseFloat(trimmed) / 100\n }\n\n // Currency: \"$1,234.56\", \"$1234\", \"-$500\"\n const currencyMatch = trimmed.match(/^-?\\$[\\d,]+(\\.\\d+)?$/)\n if (currencyMatch) {\n return Number.parseFloat(trimmed.replace(/[$,]/g, ''))\n }\n\n // Plain number: \"1,234.56\", \"1234\", \"-500.5\"\n const plainNum = Number(trimmed.replace(/,/g, ''))\n if (!Number.isNaN(plainNum) && /^-?[\\d,]+(\\.\\d+)?$/.test(trimmed)) {\n return plainNum\n }\n\n return value\n}\n\n/** Get ExcelJS number format string for a value. */\nfunction getCellFormat(\n originalValue: string | number | undefined,\n): string | undefined {\n if (typeof originalValue !== 'string') return undefined\n const trimmed = originalValue.trim()\n\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) return '0.00%'\n if (/^-?\\$/.test(trimmed)) return '$#,##0.00'\n return undefined\n}\n\n/** Map alignment string to ExcelJS horizontal alignment. */\nfunction mapAlignment(align?: string): 'left' | 'center' | 'right' | undefined {\n if (align === 'left' || align === 'center' || align === 'right') return align\n return undefined\n}\n\n/** Thin border style for ExcelJS. */\nfunction thinBorder(): { style: 'thin'; color: { argb: string } } {\n return { style: 'thin', color: { argb: 'FFDDDDDD' } }\n}\n\n/** Apply header styling to a cell. */\nfunction styleHeaderCell(\n cell: {\n font: unknown\n fill: unknown\n alignment: unknown\n border: unknown\n value: unknown\n },\n col: TableColumn,\n hs: { background?: string; color?: string } | undefined,\n bordered: boolean,\n): void {\n cell.value = col.header\n cell.font = {\n bold: true,\n color: { argb: hs?.color?.replace('#', 'FF') ?? 'FF000000' },\n }\n if (hs?.background) {\n cell.fill = {\n type: 'pattern',\n pattern: 'solid',\n fgColor: { argb: hs.background.replace('#', 'FF') },\n }\n }\n cell.alignment = { horizontal: mapAlignment(col.align) ?? 'left' }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Apply data cell value and styling. */\nfunction styleDataCell(\n cell: {\n value: unknown\n numFmt: unknown\n alignment: unknown\n fill: unknown\n border: unknown\n },\n rawValue: string | number | undefined,\n col: TableColumn,\n striped: boolean,\n isOddRow: boolean,\n bordered: boolean,\n): void {\n cell.value = parseCellValue(rawValue)\n const fmt = getCellFormat(rawValue)\n if (fmt) cell.numFmt = fmt\n cell.alignment = { horizontal: mapAlignment(col.align) ?? 'left' }\n if (striped && isOddRow) {\n cell.fill = {\n type: 'pattern',\n pattern: 'solid',\n fgColor: { argb: 'FFF9F9F9' },\n }\n }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Render a single table node into the worksheet starting at the given row. Returns the next row number. */\nfunction renderTable(\n ws: {\n getRow: (n: number) => { getCell: (n: number) => Record<string, unknown> }\n columns: unknown[]\n },\n tableNode: DocNode,\n startRow: number,\n): number {\n let rowNum = startRow\n const columns = (\n (tableNode.props.columns ?? []) as (string | TableColumn)[]\n ).map(resolveColumn)\n const rows = (tableNode.props.rows ?? []) as (string | number)[][]\n const hs = tableNode.props.headerStyle as\n | { background?: string; color?: string }\n | undefined\n const bordered = (tableNode.props.bordered as boolean) ?? false\n\n // Caption\n if (tableNode.props.caption) {\n const captionRow = ws.getRow(rowNum)\n const captionCell = captionRow.getCell(1)\n captionCell.value = tableNode.props.caption as string\n captionCell.font = { italic: true, size: 10 }\n rowNum++\n }\n\n // Header row\n const headerRow = ws.getRow(rowNum)\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n if (!col) continue\n styleHeaderCell(headerRow.getCell(i + 1) as any, col, hs, bordered)\n }\n rowNum++\n\n // Data rows\n for (let r = 0; r < rows.length; r++) {\n const dataRow = ws.getRow(rowNum)\n for (let c = 0; c < columns.length; c++) {\n const col = columns[c]\n if (!col) continue\n styleDataCell(\n dataRow.getCell(c + 1) as any,\n rows[r]?.[c],\n col,\n (tableNode.props.striped as boolean) ?? false,\n r % 2 === 1,\n bordered,\n )\n }\n rowNum++\n }\n\n return rowNum + 1 // gap after table\n}\n\n/** Auto-fit column widths based on content. */\nfunction autoFitColumns(ws: {\n columns: {\n width: number\n eachCell?: (\n opts: { includeEmpty: boolean },\n cb: (cell: { value: unknown }) => void,\n ) => void\n }[]\n}): void {\n for (const col of ws.columns) {\n let maxLen = 10\n col.eachCell?.({ includeEmpty: false }, (cell) => {\n const len = String(cell.value ?? '').length\n if (len > maxLen) maxLen = len\n })\n col.width = Math.min(maxLen + 2, 50)\n }\n}\n\nexport const xlsxRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<Uint8Array> {\n const ExcelJS = await import('exceljs')\n const workbook = new ExcelJS.default.Workbook()\n\n workbook.creator = (node.props.author as string) ?? ''\n workbook.title = (node.props.title as string) ?? ''\n\n const sheets = extractSheets(node)\n\n if (sheets.length === 0) {\n workbook.addWorksheet('Sheet 1')\n }\n\n for (const sheet of sheets) {\n const ws = workbook.addWorksheet(sheet.name)\n\n let rowNum = 1\n\n // Add headings as title rows\n for (const heading of sheet.headings) {\n const row = ws.getRow(rowNum)\n row.getCell(1).value = heading\n row.getCell(1).font = { bold: true, size: 14 }\n rowNum++\n }\n\n if (sheet.headings.length > 0) rowNum++ // gap after headings\n\n // Add tables\n for (const tableNode of sheet.tables) {\n rowNum = renderTable(\n ws as unknown as Parameters<typeof renderTable>[0],\n tableNode,\n rowNum,\n )\n }\n\n // Auto-fit columns (approximate)\n autoFitColumns(ws as unknown as Parameters<typeof autoFitColumns>[0])\n }\n\n const buffer = await workbook.xlsx.writeBuffer()\n return new Uint8Array(buffer as ArrayBuffer)\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"xlsx-Bb5TWyXQ.js","names":[],"sources":["../src/renderers/xlsx.ts"],"sourcesContent":["import type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\n/**\n * XLSX renderer — lazy-loads ExcelJS on first use.\n * Extracts tables from the document and renders each as a worksheet.\n * Non-table content (headings, text) becomes header rows.\n */\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction getTextContent(children: DocChild[]): string {\n return children\n .map((c) =>\n typeof c === 'string' ? c : getTextContent((c as DocNode).children),\n )\n .join('')\n}\n\ninterface ExtractedSheet {\n name: string\n headings: string[]\n tables: DocNode[]\n}\n\n/** Walk the tree and group content into sheets (one per page, or one global). */\nfunction extractSheets(node: DocNode): ExtractedSheet[] {\n const sheets: ExtractedSheet[] = []\n let currentSheet: ExtractedSheet = {\n name: 'Sheet 1',\n headings: [],\n tables: [],\n }\n\n function walk(n: DocNode): void {\n switch (n.type) {\n case 'document':\n walkChildren(n)\n break\n\n case 'page':\n pushCurrentSheet()\n currentSheet = {\n name: `Sheet ${sheets.length + 1}`,\n headings: [],\n tables: [],\n }\n walkChildren(n)\n break\n\n case 'heading':\n addHeading(n)\n break\n\n case 'table':\n currentSheet.tables.push(n)\n break\n\n default:\n walkChildren(n)\n }\n }\n\n function walkChildren(n: DocNode): void {\n for (const child of n.children) {\n if (typeof child !== 'string') walk(child)\n }\n }\n\n function pushCurrentSheet(): void {\n if (currentSheet.tables.length > 0 || currentSheet.headings.length > 0) {\n sheets.push(currentSheet)\n }\n }\n\n function addHeading(n: DocNode): void {\n const text = getTextContent(n.children)\n currentSheet.headings.push(text)\n if (currentSheet.headings.length === 1) {\n currentSheet.name = text.slice(0, 31) // Excel sheet name max 31 chars\n }\n }\n\n walk(node)\n pushCurrentSheet()\n\n return sheets\n}\n\n/** Parse a cell value, handling currencies, percentages, and plain numbers. */\nfunction parseCellValue(value: string | number | undefined): string | number {\n if (value == null) return ''\n if (typeof value === 'number') return value\n\n const trimmed = value.trim()\n\n // Percentage: \"45%\" or \"12.5%\"\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) {\n return Number.parseFloat(trimmed) / 100\n }\n\n // Currency: \"$1,234.56\", \"$1234\", \"-$500\"\n const currencyMatch = trimmed.match(/^-?\\$[\\d,]+(\\.\\d+)?$/)\n if (currencyMatch) {\n return Number.parseFloat(trimmed.replace(/[$,]/g, ''))\n }\n\n // Plain number: \"1,234.56\", \"1234\", \"-500.5\"\n const plainNum = Number(trimmed.replace(/,/g, ''))\n if (!Number.isNaN(plainNum) && /^-?[\\d,]+(\\.\\d+)?$/.test(trimmed)) {\n return plainNum\n }\n\n return value\n}\n\n/** Get ExcelJS number format string for a value. */\nfunction getCellFormat(\n originalValue: string | number | undefined,\n): string | undefined {\n if (typeof originalValue !== 'string') return undefined\n const trimmed = originalValue.trim()\n\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) return '0.00%'\n if (/^-?\\$/.test(trimmed)) return '$#,##0.00'\n return undefined\n}\n\n/** Map alignment string to ExcelJS horizontal alignment. */\nfunction mapAlignment(align?: string): 'left' | 'center' | 'right' | undefined {\n if (align === 'left' || align === 'center' || align === 'right') return align\n return undefined\n}\n\n/** Thin border style for ExcelJS. */\nfunction thinBorder(): { style: 'thin'; color: { argb: string } } {\n return { style: 'thin', color: { argb: 'FFDDDDDD' } }\n}\n\n/** Apply header styling to a cell. */\nfunction styleHeaderCell(\n cell: {\n font: unknown\n fill: unknown\n alignment: unknown\n border: unknown\n value: unknown\n },\n col: TableColumn,\n hs: { background?: string; color?: string } | undefined,\n bordered: boolean,\n): void {\n cell.value = col.header\n cell.font = {\n bold: true,\n color: { argb: hs?.color?.replace('#', 'FF') ?? 'FF000000' },\n }\n if (hs?.background) {\n cell.fill = {\n type: 'pattern',\n pattern: 'solid',\n fgColor: { argb: hs.background.replace('#', 'FF') },\n }\n }\n cell.alignment = { horizontal: mapAlignment(col.align) ?? 'left' }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Apply data cell value and styling. */\nfunction styleDataCell(\n cell: {\n value: unknown\n numFmt: unknown\n alignment: unknown\n fill: unknown\n border: unknown\n },\n rawValue: string | number | undefined,\n col: TableColumn,\n striped: boolean,\n isOddRow: boolean,\n bordered: boolean,\n): void {\n cell.value = parseCellValue(rawValue)\n const fmt = getCellFormat(rawValue)\n if (fmt) cell.numFmt = fmt\n cell.alignment = { horizontal: mapAlignment(col.align) ?? 'left' }\n if (striped && isOddRow) {\n cell.fill = {\n type: 'pattern',\n pattern: 'solid',\n fgColor: { argb: 'FFF9F9F9' },\n }\n }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Render a single table node into the worksheet starting at the given row. Returns the next row number. */\nfunction renderTable(\n ws: {\n getRow: (n: number) => { getCell: (n: number) => Record<string, unknown> }\n columns: unknown[]\n },\n tableNode: DocNode,\n startRow: number,\n): number {\n let rowNum = startRow\n const columns = (\n (tableNode.props.columns ?? []) as (string | TableColumn)[]\n ).map(resolveColumn)\n const rows = (tableNode.props.rows ?? []) as (string | number)[][]\n const hs = tableNode.props.headerStyle as\n | { background?: string; color?: string }\n | undefined\n const bordered = (tableNode.props.bordered as boolean) ?? false\n\n // Caption\n if (tableNode.props.caption) {\n const captionRow = ws.getRow(rowNum)\n const captionCell = captionRow.getCell(1)\n captionCell.value = tableNode.props.caption as string\n captionCell.font = { italic: true, size: 10 }\n rowNum++\n }\n\n // Header row\n const headerRow = ws.getRow(rowNum)\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n if (!col) continue\n styleHeaderCell(headerRow.getCell(i + 1) as any, col, hs, bordered)\n }\n rowNum++\n\n // Data rows\n for (let r = 0; r < rows.length; r++) {\n const dataRow = ws.getRow(rowNum)\n for (let c = 0; c < columns.length; c++) {\n const col = columns[c]\n if (!col) continue\n styleDataCell(\n dataRow.getCell(c + 1) as any,\n rows[r]?.[c],\n col,\n (tableNode.props.striped as boolean) ?? false,\n r % 2 === 1,\n bordered,\n )\n }\n rowNum++\n }\n\n return rowNum + 1 // gap after table\n}\n\n/** Auto-fit column widths based on content. */\nfunction autoFitColumns(ws: {\n columns: {\n width: number\n eachCell?: (\n opts: { includeEmpty: boolean },\n cb: (cell: { value: unknown }) => void,\n ) => void\n }[]\n}): void {\n for (const col of ws.columns) {\n let maxLen = 10\n col.eachCell?.({ includeEmpty: false }, (cell) => {\n const len = String(cell.value ?? '').length\n if (len > maxLen) maxLen = len\n })\n col.width = Math.min(maxLen + 2, 50)\n }\n}\n\nexport const xlsxRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<Uint8Array> {\n const ExcelJS = await import('exceljs')\n const workbook = new ExcelJS.default.Workbook()\n\n workbook.creator = (node.props.author as string) ?? ''\n workbook.title = (node.props.title as string) ?? ''\n\n const sheets = extractSheets(node)\n\n if (sheets.length === 0) {\n workbook.addWorksheet('Sheet 1')\n }\n\n for (const sheet of sheets) {\n const ws = workbook.addWorksheet(sheet.name)\n\n let rowNum = 1\n\n // Add headings as title rows\n for (const heading of sheet.headings) {\n const row = ws.getRow(rowNum)\n row.getCell(1).value = heading\n row.getCell(1).font = { bold: true, size: 14 }\n rowNum++\n }\n\n if (sheet.headings.length > 0) rowNum++ // gap after headings\n\n // Add tables\n for (const tableNode of sheet.tables) {\n rowNum = renderTable(\n ws as unknown as Parameters<typeof renderTable>[0],\n tableNode,\n rowNum,\n )\n }\n\n // Auto-fit columns (approximate)\n autoFitColumns(ws as unknown as Parameters<typeof autoFitColumns>[0])\n }\n\n const buffer = await workbook.xlsx.writeBuffer()\n return new Uint8Array(buffer as ArrayBuffer)\n },\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/document",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Universal document rendering for Pyreon — one template, every output format (HTML, PDF, DOCX, email, XLSX, Markdown, and more)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"typecheck": "tsc --noEmit"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@pyreon/core": ">=0.
|
|
44
|
-
"@pyreon/reactivity": ">=0.
|
|
43
|
+
"@pyreon/core": ">=0.7.0 <0.8.0",
|
|
44
|
+
"@pyreon/reactivity": ">=0.7.0 <0.8.0"
|
|
45
45
|
},
|
|
46
46
|
"optionalDependencies": {
|
|
47
47
|
"docx": "^9.6.0",
|
package/src/builder.ts
CHANGED
|
@@ -136,9 +136,9 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
136
136
|
sections.push(
|
|
137
137
|
Image({
|
|
138
138
|
src: dataUrl,
|
|
139
|
-
width: p
|
|
140
|
-
height: p
|
|
141
|
-
caption: p
|
|
139
|
+
...(p?.width != null ? { width: p.width } : {}),
|
|
140
|
+
...(p?.height != null ? { height: p.height } : {}),
|
|
141
|
+
...(p?.caption != null ? { caption: p.caption } : {}),
|
|
142
142
|
}),
|
|
143
143
|
)
|
|
144
144
|
} else {
|
|
@@ -164,9 +164,9 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
164
164
|
sections.push(
|
|
165
165
|
Image({
|
|
166
166
|
src: `data:image/svg+xml,${encodeURIComponent(svg)}`,
|
|
167
|
-
width: p
|
|
168
|
-
height: p
|
|
169
|
-
caption: p
|
|
167
|
+
...(p?.width != null ? { width: p.width } : {}),
|
|
168
|
+
...(p?.height != null ? { height: p.height } : {}),
|
|
169
|
+
...(p?.caption != null ? { caption: p.caption } : {}),
|
|
170
170
|
}),
|
|
171
171
|
)
|
|
172
172
|
} else {
|
package/src/download.ts
CHANGED
|
@@ -67,6 +67,12 @@ export async function download(
|
|
|
67
67
|
type: MIME_TYPES[format] ?? 'application/octet-stream',
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
+
if (typeof document === 'undefined') {
|
|
71
|
+
throw new Error(
|
|
72
|
+
'[@pyreon/document] download() requires a browser environment.',
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
const url = URL.createObjectURL(blob)
|
|
71
77
|
const a = document.createElement('a')
|
|
72
78
|
a.href = url
|
package/src/nodes.ts
CHANGED
|
@@ -37,6 +37,11 @@ function normalizeChildren(children: unknown): DocChild[] {
|
|
|
37
37
|
if (typeof children === 'number') return [String(children)]
|
|
38
38
|
if (Array.isArray(children)) return children.flatMap(normalizeChildren)
|
|
39
39
|
if (isDocNode(children)) return [children]
|
|
40
|
+
if (typeof children === 'object') {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'[@pyreon/document] Invalid child: plain objects are not valid document children. Use a document node (Text, Heading, etc.) instead.',
|
|
43
|
+
)
|
|
44
|
+
}
|
|
40
45
|
return [String(children)]
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeHref, sanitizeImageSrc } from '../sanitize'
|
|
1
2
|
import type {
|
|
2
3
|
DocChild,
|
|
3
4
|
DocNode,
|
|
@@ -81,7 +82,7 @@ function nodeToAdf(node: DocNode): AdfNode[] {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
case 'link': {
|
|
84
|
-
const href = p.href as string
|
|
85
|
+
const href = sanitizeHref(p.href as string)
|
|
85
86
|
const text = getTextContent(node.children)
|
|
86
87
|
result.push({
|
|
87
88
|
type: 'paragraph',
|
|
@@ -91,7 +92,7 @@ function nodeToAdf(node: DocNode): AdfNode[] {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
case 'image': {
|
|
94
|
-
const src = p.src as string
|
|
95
|
+
const src = sanitizeImageSrc(p.src as string)
|
|
95
96
|
if (src.startsWith('http')) {
|
|
96
97
|
result.push({
|
|
97
98
|
type: 'mediaSingle',
|
|
@@ -191,7 +192,7 @@ function nodeToAdf(node: DocNode): AdfNode[] {
|
|
|
191
192
|
break
|
|
192
193
|
|
|
193
194
|
case 'button': {
|
|
194
|
-
const href = p.href as string
|
|
195
|
+
const href = sanitizeHref(p.href as string)
|
|
195
196
|
const text = getTextContent(node.children)
|
|
196
197
|
result.push({
|
|
197
198
|
type: 'paragraph',
|
package/src/renderers/discord.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeHref, sanitizeImageSrc } from '../sanitize'
|
|
1
2
|
import type {
|
|
2
3
|
DocChild,
|
|
3
4
|
DocNode,
|
|
@@ -36,7 +37,7 @@ function extractMeta(node: DocNode): { title?: string; imageUrl?: string } {
|
|
|
36
37
|
if (level === 1) return { title: getTextContent(node.children) }
|
|
37
38
|
}
|
|
38
39
|
if (node.type === 'image') {
|
|
39
|
-
const src = node.props.src as string
|
|
40
|
+
const src = sanitizeImageSrc(node.props.src as string)
|
|
40
41
|
if (src.startsWith('http')) return { imageUrl: src }
|
|
41
42
|
}
|
|
42
43
|
for (const child of node.children) {
|
|
@@ -92,7 +93,7 @@ function nodeToMarkdown(
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
case 'link': {
|
|
95
|
-
const href = p.href as string
|
|
96
|
+
const href = sanitizeHref(p.href as string)
|
|
96
97
|
const text = getTextContent(node.children)
|
|
97
98
|
content += `[${text}](${href})\n\n`
|
|
98
99
|
break
|
|
@@ -157,7 +158,7 @@ function nodeToMarkdown(
|
|
|
157
158
|
break
|
|
158
159
|
|
|
159
160
|
case 'button': {
|
|
160
|
-
const href = p.href as string
|
|
161
|
+
const href = sanitizeHref(p.href as string)
|
|
161
162
|
const text = getTextContent(node.children)
|
|
162
163
|
content += `[**${text}**](${href})\n\n`
|
|
163
164
|
break
|
package/src/renderers/docx.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeHref, sanitizeXmlColor } from '../sanitize'
|
|
1
2
|
import type {
|
|
2
3
|
DocChild,
|
|
3
4
|
DocNode,
|
|
@@ -122,7 +123,7 @@ function renderHeading(ctx: DocxCtx, n: DocNode): void {
|
|
|
122
123
|
new docx.TextRun({
|
|
123
124
|
text: getTextContent(n.children),
|
|
124
125
|
bold: true,
|
|
125
|
-
color: (p.color as string)
|
|
126
|
+
color: sanitizeXmlColor(p.color as string),
|
|
126
127
|
}),
|
|
127
128
|
],
|
|
128
129
|
alignment: alignmentMap(p.align as string) as any,
|
|
@@ -138,12 +139,14 @@ function renderTextNode(ctx: DocxCtx, n: DocNode): void {
|
|
|
138
139
|
children: [
|
|
139
140
|
new docx.TextRun({
|
|
140
141
|
text: getTextContent(n.children),
|
|
141
|
-
bold: p.bold as boolean
|
|
142
|
-
italics: p.italic as boolean
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
...(p.bold != null ? { bold: p.bold as boolean } : {}),
|
|
143
|
+
...(p.italic != null ? { italics: p.italic as boolean } : {}),
|
|
144
|
+
...(p.underline ? { underline: {} } : {}),
|
|
145
|
+
...(p.strikethrough != null
|
|
146
|
+
? { strike: p.strikethrough as boolean }
|
|
147
|
+
: {}),
|
|
148
|
+
...(p.size != null ? { size: (p.size as number) * 2 } : {}),
|
|
149
|
+
color: sanitizeXmlColor(p.color as string, '333333'),
|
|
147
150
|
}),
|
|
148
151
|
],
|
|
149
152
|
alignment: alignmentMap(p.align as string) as any,
|
|
@@ -159,11 +162,11 @@ function renderLink(ctx: DocxCtx, n: DocNode): void {
|
|
|
159
162
|
new docx.Paragraph({
|
|
160
163
|
children: [
|
|
161
164
|
new docx.ExternalHyperlink({
|
|
162
|
-
link: p.href as string,
|
|
165
|
+
link: sanitizeHref(p.href as string),
|
|
163
166
|
children: [
|
|
164
167
|
new docx.TextRun({
|
|
165
168
|
text: getTextContent(n.children),
|
|
166
|
-
color: (p.color as string
|
|
169
|
+
color: sanitizeXmlColor(p.color as string, '4f46e5'),
|
|
167
170
|
underline: { type: docx.UnderlineType.SINGLE },
|
|
168
171
|
}),
|
|
169
172
|
],
|
|
@@ -261,19 +264,21 @@ function renderDocxTable(ctx: DocxCtx, n: DocNode): void {
|
|
|
261
264
|
new docx.TextRun({
|
|
262
265
|
text: col.header,
|
|
263
266
|
bold: true,
|
|
264
|
-
color: hs?.color
|
|
267
|
+
color: sanitizeXmlColor(hs?.color),
|
|
265
268
|
}),
|
|
266
269
|
],
|
|
267
270
|
alignment: alignmentMap(col.align) as any,
|
|
268
271
|
}),
|
|
269
272
|
],
|
|
270
|
-
|
|
273
|
+
...(hs?.background
|
|
271
274
|
? {
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
shading: {
|
|
276
|
+
fill: sanitizeXmlColor(hs.background),
|
|
277
|
+
type: docx.ShadingType.SOLID,
|
|
278
|
+
},
|
|
274
279
|
}
|
|
275
|
-
:
|
|
276
|
-
borders: cellBorders,
|
|
280
|
+
: {}),
|
|
281
|
+
...(cellBorders != null ? { borders: cellBorders } : {}),
|
|
277
282
|
width: getColumnWidth(col.width as string | undefined) as any,
|
|
278
283
|
}),
|
|
279
284
|
),
|
|
@@ -293,11 +298,10 @@ function renderDocxTable(ctx: DocxCtx, n: DocNode): void {
|
|
|
293
298
|
alignment: alignmentMap(col.align) as any,
|
|
294
299
|
}),
|
|
295
300
|
],
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
borders: cellBorders,
|
|
301
|
+
...(p.striped && rowIdx % 2 === 1
|
|
302
|
+
? { shading: { fill: 'F9F9F9', type: docx.ShadingType.SOLID } }
|
|
303
|
+
: {}),
|
|
304
|
+
...(cellBorders != null ? { borders: cellBorders } : {}),
|
|
301
305
|
width: getColumnWidth(col.width as string | undefined) as any,
|
|
302
306
|
}),
|
|
303
307
|
),
|
|
@@ -345,7 +349,7 @@ function renderButtonOrQuote(ctx: DocxCtx, n: DocNode): void {
|
|
|
345
349
|
new docx.Paragraph({
|
|
346
350
|
children: [
|
|
347
351
|
new docx.ExternalHyperlink({
|
|
348
|
-
link: p.href as string,
|
|
352
|
+
link: sanitizeHref(p.href as string),
|
|
349
353
|
children: [
|
|
350
354
|
new docx.TextRun({
|
|
351
355
|
text,
|
|
@@ -368,7 +372,7 @@ function renderButtonOrQuote(ctx: DocxCtx, n: DocNode): void {
|
|
|
368
372
|
left: {
|
|
369
373
|
style: docx.BorderStyle.SINGLE,
|
|
370
374
|
size: 6,
|
|
371
|
-
color: (p.borderColor as string
|
|
375
|
+
color: sanitizeXmlColor(p.borderColor as string, 'DDDDDD'),
|
|
372
376
|
},
|
|
373
377
|
},
|
|
374
378
|
spacing: { after: 120 },
|
|
@@ -416,8 +420,9 @@ export const docxRenderer: DocumentRenderer = {
|
|
|
416
420
|
children: [
|
|
417
421
|
new docx.TextRun({ text: getTextContent(textChildren) }),
|
|
418
422
|
],
|
|
419
|
-
|
|
420
|
-
|
|
423
|
+
...(ordered
|
|
424
|
+
? { numbering: { reference: listRef, level } }
|
|
425
|
+
: { bullet: { level } }),
|
|
421
426
|
}),
|
|
422
427
|
)
|
|
423
428
|
if (nestedList) {
|
|
@@ -494,8 +499,7 @@ export const docxRenderer: DocumentRenderer = {
|
|
|
494
499
|
bottom: {
|
|
495
500
|
style: docx.BorderStyle.SINGLE,
|
|
496
501
|
size: (n.props.thickness as number | undefined) ?? 1,
|
|
497
|
-
color:
|
|
498
|
-
(n.props.color as string)?.replace('#', '') ?? 'DDDDDD',
|
|
502
|
+
color: sanitizeXmlColor(n.props.color as string, 'DDDDDD'),
|
|
499
503
|
},
|
|
500
504
|
},
|
|
501
505
|
spacing: { before: 120, after: 120 },
|
|
@@ -595,12 +599,20 @@ export const docxRenderer: DocumentRenderer = {
|
|
|
595
599
|
sections: [
|
|
596
600
|
{
|
|
597
601
|
properties: sectionProperties,
|
|
598
|
-
|
|
599
|
-
? {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
602
|
+
...(headerContent
|
|
603
|
+
? {
|
|
604
|
+
headers: {
|
|
605
|
+
default: new docx.Header({ children: headerContent as any }),
|
|
606
|
+
},
|
|
607
|
+
}
|
|
608
|
+
: {}),
|
|
609
|
+
...(footerContent
|
|
610
|
+
? {
|
|
611
|
+
footers: {
|
|
612
|
+
default: new docx.Footer({ children: footerContent as any }),
|
|
613
|
+
},
|
|
614
|
+
}
|
|
615
|
+
: {}),
|
|
604
616
|
children: children as any,
|
|
605
617
|
},
|
|
606
618
|
],
|
package/src/renderers/email.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeColor, sanitizeHref, sanitizeImageSrc } from '../sanitize'
|
|
1
2
|
import type {
|
|
2
3
|
DocChild,
|
|
3
4
|
DocNode,
|
|
@@ -60,7 +61,9 @@ function renderNode(node: DocNode): string {
|
|
|
60
61
|
return renderChildren(node.children)
|
|
61
62
|
|
|
62
63
|
case 'section': {
|
|
63
|
-
const bg = p.background
|
|
64
|
+
const bg = p.background
|
|
65
|
+
? `background-color:${sanitizeColor(p.background as string)};`
|
|
66
|
+
: ''
|
|
64
67
|
const pad = p.padding
|
|
65
68
|
? `padding:${typeof p.padding === 'number' ? `${p.padding}px` : Array.isArray(p.padding) ? (p.padding as number[]).map((v) => `${v}px`).join(' ') : '0'}`
|
|
66
69
|
: 'padding:0'
|
|
@@ -100,14 +103,14 @@ function renderNode(node: DocNode): string {
|
|
|
100
103
|
6: 14,
|
|
101
104
|
}
|
|
102
105
|
const size = sizes[level] ?? 24
|
|
103
|
-
const color = (p.color as string) ?? '#000000'
|
|
106
|
+
const color = sanitizeColor((p.color as string) ?? '#000000')
|
|
104
107
|
const align = (p.align as string) ?? 'left'
|
|
105
108
|
return `<h${level} style="margin:0 0 12px 0;font-size:${size}px;color:${color};text-align:${align};font-weight:bold;line-height:1.3">${renderChildren(node.children)}</h${level}>`
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
case 'text': {
|
|
109
112
|
const size = (p.size as number) ?? 14
|
|
110
|
-
const color = (p.color as string) ?? '#333333'
|
|
113
|
+
const color = sanitizeColor((p.color as string) ?? '#333333')
|
|
111
114
|
const weight = p.bold ? 'bold' : 'normal'
|
|
112
115
|
const style = p.italic ? 'italic' : 'normal'
|
|
113
116
|
const decoration = p.underline
|
|
@@ -121,11 +124,11 @@ function renderNode(node: DocNode): string {
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
case 'link':
|
|
124
|
-
return `<a href="${esc(p.href as string)}" style="color:${(p.color as string) ?? '#4f46e5'};text-decoration:underline" target="_blank">${renderChildren(node.children)}</a>`
|
|
127
|
+
return `<a href="${esc(sanitizeHref(p.href as string))}" style="color:${sanitizeColor((p.color as string) ?? '#4f46e5')};text-decoration:underline" target="_blank">${renderChildren(node.children)}</a>`
|
|
125
128
|
|
|
126
129
|
case 'image': {
|
|
127
130
|
const align = (p.align as string) ?? 'left'
|
|
128
|
-
const img = `<img src="${esc(p.src as string)}"${p.width ? ` width="${p.width}"` : ''}${p.height ? ` height="${p.height}"` : ''} alt="${esc((p.alt as string) ?? '')}" style="display:block;outline:none;border:none;text-decoration:none${p.width ? `;max-width:${p.width}px` : ''}" />`
|
|
131
|
+
const img = `<img src="${esc(sanitizeImageSrc(p.src as string))}"${p.width ? ` width="${p.width}"` : ''}${p.height ? ` height="${p.height}"` : ''} alt="${esc((p.alt as string) ?? '')}" style="display:block;outline:none;border:none;text-decoration:none${p.width ? `;max-width:${p.width}px` : ''}" />`
|
|
129
132
|
if (p.caption) {
|
|
130
133
|
return `<table cellpadding="0" cellspacing="0" border="0"${align === 'center' ? ' align="center"' : ''}><tr><td>${img}</td></tr><tr><td style="font-size:12px;color:#666;padding-top:4px;text-align:center">${esc(p.caption as string)}</td></tr></table>`
|
|
131
134
|
}
|
|
@@ -153,9 +156,9 @@ function renderNode(node: DocNode): string {
|
|
|
153
156
|
html += '<tr>'
|
|
154
157
|
for (const col of columns) {
|
|
155
158
|
const bg = hs?.background
|
|
156
|
-
? `background-color:${hs.background};`
|
|
159
|
+
? `background-color:${sanitizeColor(hs.background)};`
|
|
157
160
|
: 'background-color:#f5f5f5;'
|
|
158
|
-
const color = hs?.color ? `color:${hs.color};` : ''
|
|
161
|
+
const color = hs?.color ? `color:${sanitizeColor(hs.color)};` : ''
|
|
159
162
|
const align = col.align ? `text-align:${col.align};` : ''
|
|
160
163
|
const width = col.width
|
|
161
164
|
? `width:${typeof col.width === 'number' ? `${col.width}px` : col.width};`
|
|
@@ -190,7 +193,7 @@ function renderNode(node: DocNode): string {
|
|
|
190
193
|
return `<pre style="background-color:#f5f5f5;padding:12px;border-radius:4px;font-family:Courier New,monospace;font-size:13px;color:#333;overflow-x:auto;margin:0 0 12px 0"><code>${esc(renderChildren(node.children))}</code></pre>`
|
|
191
194
|
|
|
192
195
|
case 'divider': {
|
|
193
|
-
const color = (p.color as string) ?? '#dddddd'
|
|
196
|
+
const color = sanitizeColor((p.color as string) ?? '#dddddd')
|
|
194
197
|
const thickness = (p.thickness as number) ?? 1
|
|
195
198
|
return `<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:16px 0"><tr><td style="border-top:${thickness}px solid ${color};font-size:0;line-height:0"> </td></tr></table>`
|
|
196
199
|
}
|
|
@@ -202,10 +205,10 @@ function renderNode(node: DocNode): string {
|
|
|
202
205
|
return `<div style="height:${p.height}px;line-height:${p.height}px;font-size:0"> </div>`
|
|
203
206
|
|
|
204
207
|
case 'button': {
|
|
205
|
-
const bg = (p.background as string) ?? '#4f46e5'
|
|
206
|
-
const color = (p.color as string) ?? '#ffffff'
|
|
208
|
+
const bg = sanitizeColor((p.background as string) ?? '#4f46e5')
|
|
209
|
+
const color = sanitizeColor((p.color as string) ?? '#ffffff')
|
|
207
210
|
const radius = (p.borderRadius as number) ?? 4
|
|
208
|
-
const href = esc(p.href as string)
|
|
211
|
+
const href = esc(sanitizeHref(p.href as string))
|
|
209
212
|
const text = renderChildren(node.children)
|
|
210
213
|
const align = (p.align as string) ?? 'left'
|
|
211
214
|
|
|
@@ -214,7 +217,7 @@ function renderNode(node: DocNode): string {
|
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
case 'quote': {
|
|
217
|
-
const borderColor = (p.borderColor as string) ?? '#dddddd'
|
|
220
|
+
const borderColor = sanitizeColor((p.borderColor as string) ?? '#dddddd')
|
|
218
221
|
return `<table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:12px 0"><tr><td style="border-left:4px solid ${borderColor};padding:12px 20px;color:#555555;font-style:italic">${renderChildren(node.children)}</td></tr></table>`
|
|
219
222
|
}
|
|
220
223
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sanitizeHref, sanitizeImageSrc } from '../sanitize'
|
|
1
2
|
import type {
|
|
2
3
|
DocChild,
|
|
3
4
|
DocNode,
|
|
@@ -68,7 +69,7 @@ function nodeToWidgets(node: DocNode): CardWidget[] {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
case 'link': {
|
|
71
|
-
const href = p.href as string
|
|
72
|
+
const href = sanitizeHref(p.href as string)
|
|
72
73
|
const text = getTextContent(node.children)
|
|
73
74
|
widgets.push({
|
|
74
75
|
textParagraph: { text: `<a href="${href}">${text}</a>` },
|
|
@@ -77,7 +78,7 @@ function nodeToWidgets(node: DocNode): CardWidget[] {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
case 'image': {
|
|
80
|
-
const src = p.src as string
|
|
81
|
+
const src = sanitizeImageSrc(p.src as string)
|
|
81
82
|
if (src.startsWith('http')) {
|
|
82
83
|
widgets.push({
|
|
83
84
|
image: {
|
|
@@ -142,7 +143,7 @@ function nodeToWidgets(node: DocNode): CardWidget[] {
|
|
|
142
143
|
break
|
|
143
144
|
|
|
144
145
|
case 'button': {
|
|
145
|
-
const href = p.href as string
|
|
146
|
+
const href = sanitizeHref(p.href as string)
|
|
146
147
|
const text = getTextContent(node.children)
|
|
147
148
|
widgets.push({
|
|
148
149
|
buttonList: {
|