@pyreon/document 0.10.0 → 0.11.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/lib/analysis/index.js.html +1 -1
- package/lib/confluence-Bd3ua1Ut.js.map +1 -1
- package/lib/csv-COrS4qdy.js.map +1 -1
- package/lib/discord-BLUnkEh9.js.map +1 -1
- package/lib/{dist-BsqdI2nY.js → dist-CYL41kqQ.js} +2 -2
- package/lib/dist-CYL41kqQ.js.map +1 -0
- package/lib/{docx-BEBOihjl.js → docx-uNAel545.js} +7 -2
- package/lib/docx-uNAel545.js.map +1 -0
- package/lib/email-D0bbfWq4.js.map +1 -1
- package/lib/{exceljs-BoIDUUaw.js → exceljs-BYETsesT.js} +314 -314
- package/lib/exceljs-BYETsesT.js.map +1 -0
- package/lib/google-chat-CkKCBUWC.js.map +1 -1
- package/lib/html-B5biprN2.js.map +1 -1
- package/lib/index.js +17 -8
- package/lib/index.js.map +1 -1
- package/lib/markdown-CdtlFGC0.js.map +1 -1
- package/lib/notion-iG2C5bEY.js.map +1 -1
- package/lib/{pdf-DIUQUEdj.js → pdf-IuBgTb3T.js} +9 -3
- package/lib/pdf-IuBgTb3T.js.map +1 -0
- package/lib/{pdfmake-DnmLxK4Q.js → pdfmake-CKMX5URW.js} +2 -4
- package/lib/pdfmake-CKMX5URW.js.map +1 -0
- package/lib/{pptx-Dd33oL3_.js → pptx-DXiMiYFM.js} +7 -2
- package/lib/pptx-DXiMiYFM.js.map +1 -0
- package/lib/{pptxgen.es-COcgXsyx.js → pptxgen.es-FsqHs8mD.js} +3 -6
- package/lib/pptxgen.es-FsqHs8mD.js.map +1 -0
- package/lib/sanitize-O_3j1mNJ.js.map +1 -1
- package/lib/slack-BI3EQwYm.js.map +1 -1
- package/lib/svg-BKxumy-p.js.map +1 -1
- package/lib/teams-Cwz9lce0.js.map +1 -1
- package/lib/telegram-gYFqyMXb.js.map +1 -1
- package/lib/text-l1XNXBOC.js.map +1 -1
- package/lib/types/index.d.ts +43 -39
- package/lib/types/index.d.ts.map +1 -1
- package/lib/{vfs_fonts-Df1kkZ4Y.js → vfs_fonts-Cap07Jg3.js} +2 -2
- package/lib/vfs_fonts-Cap07Jg3.js.map +1 -0
- package/lib/whatsapp-CjSGoOKx.js.map +1 -1
- package/lib/{xlsx-Bb5TWyXQ.js → xlsx-Cvu4LBNy.js} +8 -2
- package/lib/xlsx-Cvu4LBNy.js.map +1 -0
- package/package.json +19 -7
- package/src/builder.ts +53 -44
- package/src/download.ts +32 -36
- package/src/env.d.ts +3 -17
- package/src/index.ts +6 -8
- package/src/nodes.ts +45 -45
- package/src/render.ts +45 -118
- package/src/renderers/confluence.ts +64 -80
- package/src/renderers/csv.ts +11 -18
- package/src/renderers/discord.ts +38 -50
- package/src/renderers/docx.ts +78 -120
- package/src/renderers/email.ts +73 -92
- package/src/renderers/google-chat.ts +35 -47
- package/src/renderers/html.ts +78 -101
- package/src/renderers/markdown.ts +43 -53
- package/src/renderers/notion.ts +63 -85
- package/src/renderers/pdf.ts +92 -115
- package/src/renderers/pptx.ts +60 -66
- package/src/renderers/slack.ts +49 -61
- package/src/renderers/svg.ts +49 -63
- package/src/renderers/teams.ts +68 -80
- package/src/renderers/telegram.ts +40 -54
- package/src/renderers/text.ts +44 -66
- package/src/renderers/whatsapp.ts +34 -48
- package/src/renderers/xlsx.ts +47 -61
- package/src/sanitize.ts +21 -25
- package/src/tests/document.test.ts +1337 -1385
- package/src/tests/stress.test.ts +111 -111
- package/src/types.ts +66 -65
- package/lib/dist-BsqdI2nY.js.map +0 -1
- package/lib/docx-BEBOihjl.js.map +0 -1
- package/lib/exceljs-BoIDUUaw.js.map +0 -1
- package/lib/pdf-DIUQUEdj.js.map +0 -1
- package/lib/pdfmake-DnmLxK4Q.js.map +0 -1
- package/lib/pptx-Dd33oL3_.js.map +0 -1
- package/lib/pptxgen.es-COcgXsyx.js.map +0 -1
- package/lib/vfs_fonts-Df1kkZ4Y.js.map +0 -1
- package/lib/xlsx-Bb5TWyXQ.js.map +0 -1
package/src/renderers/html.ts
CHANGED
|
@@ -1,80 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
sanitizeHref,
|
|
4
|
-
sanitizeImageSrc,
|
|
5
|
-
sanitizeStyle,
|
|
6
|
-
} from '../sanitize'
|
|
7
|
-
import type {
|
|
8
|
-
DocChild,
|
|
9
|
-
DocNode,
|
|
10
|
-
DocumentRenderer,
|
|
11
|
-
RenderOptions,
|
|
12
|
-
TableColumn,
|
|
13
|
-
} from '../types'
|
|
1
|
+
import { sanitizeColor, sanitizeHref, sanitizeImageSrc, sanitizeStyle } from "../sanitize"
|
|
2
|
+
import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
|
|
14
3
|
|
|
15
4
|
function escapeHtml(str: string): string {
|
|
16
5
|
return str
|
|
17
|
-
.replace(/&/g,
|
|
18
|
-
.replace(/</g,
|
|
19
|
-
.replace(/>/g,
|
|
20
|
-
.replace(/"/g,
|
|
6
|
+
.replace(/&/g, "&")
|
|
7
|
+
.replace(/</g, "<")
|
|
8
|
+
.replace(/>/g, ">")
|
|
9
|
+
.replace(/"/g, """)
|
|
21
10
|
}
|
|
22
11
|
|
|
23
12
|
function resolveColumn(col: string | TableColumn): TableColumn {
|
|
24
|
-
return typeof col ===
|
|
13
|
+
return typeof col === "string" ? { header: col } : col
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
function styleStr(styles: Record<string, string | number | undefined>): string {
|
|
28
17
|
const parts: string[] = []
|
|
29
18
|
for (const [k, v] of Object.entries(styles)) {
|
|
30
|
-
if (v != null && v !==
|
|
31
|
-
const prop = k.replace(/([A-Z])/g,
|
|
32
|
-
parts.push(`${prop}:${typeof v ===
|
|
19
|
+
if (v != null && v !== "") {
|
|
20
|
+
const prop = k.replace(/([A-Z])/g, "-$1").toLowerCase()
|
|
21
|
+
parts.push(`${prop}:${typeof v === "number" ? `${v}px` : v}`)
|
|
33
22
|
}
|
|
34
23
|
}
|
|
35
|
-
return parts.length > 0 ? ` style="${parts.join(
|
|
24
|
+
return parts.length > 0 ? ` style="${parts.join(";")}"` : ""
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
function padStr(
|
|
39
28
|
pad: number | [number, number] | [number, number, number, number] | undefined,
|
|
40
29
|
): string | undefined {
|
|
41
30
|
if (pad == null) return undefined
|
|
42
|
-
if (typeof pad ===
|
|
31
|
+
if (typeof pad === "number") return `${pad}px`
|
|
43
32
|
if (pad.length === 2) return `${pad[0]}px ${pad[1]}px`
|
|
44
33
|
return `${pad[0]}px ${pad[1]}px ${pad[2]}px ${pad[3]}px`
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
function renderChild(child: DocChild): string {
|
|
48
|
-
if (typeof child ===
|
|
37
|
+
if (typeof child === "string") return escapeHtml(child)
|
|
49
38
|
return renderNode(child)
|
|
50
39
|
}
|
|
51
40
|
|
|
52
41
|
function renderChildren(children: DocChild[]): string {
|
|
53
|
-
return children.map(renderChild).join(
|
|
42
|
+
return children.map(renderChild).join("")
|
|
54
43
|
}
|
|
55
44
|
|
|
56
45
|
function renderNode(node: DocNode): string {
|
|
57
46
|
const p = node.props
|
|
58
47
|
|
|
59
48
|
switch (node.type) {
|
|
60
|
-
case
|
|
61
|
-
const lang = (p.language as string) ??
|
|
62
|
-
const title = p.title
|
|
63
|
-
? `<title>${escapeHtml(p.title as string)}</title>`
|
|
64
|
-
: ''
|
|
49
|
+
case "document": {
|
|
50
|
+
const lang = (p.language as string) ?? "en"
|
|
51
|
+
const title = p.title ? `<title>${escapeHtml(p.title as string)}</title>` : ""
|
|
65
52
|
return `<!DOCTYPE html><html lang="${lang}"><head><meta charset="utf-8">${title}<meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${renderChildren(node.children)}</body></html>`
|
|
66
53
|
}
|
|
67
54
|
|
|
68
|
-
case
|
|
55
|
+
case "page": {
|
|
69
56
|
const margin = padStr(p.margin as PageMargin)
|
|
70
|
-
return `<div${styleStr({ maxWidth:
|
|
57
|
+
return `<div${styleStr({ maxWidth: "800px", margin: margin ?? "0 auto", padding: margin ?? "40px" })}>${renderChildren(node.children)}</div>`
|
|
71
58
|
}
|
|
72
59
|
|
|
73
|
-
case
|
|
74
|
-
const dir = (p.direction as string) ??
|
|
60
|
+
case "section": {
|
|
61
|
+
const dir = (p.direction as string) ?? "column"
|
|
75
62
|
return `<div${styleStr({
|
|
76
|
-
display: dir ===
|
|
77
|
-
flexDirection: dir ===
|
|
63
|
+
display: dir === "row" ? "flex" : "block",
|
|
64
|
+
flexDirection: dir === "row" ? "row" : undefined,
|
|
78
65
|
gap: p.gap as number | undefined,
|
|
79
66
|
padding: padStr(p.padding as PageMargin),
|
|
80
67
|
background: sanitizeColor(p.background as string | undefined),
|
|
@@ -82,55 +69,49 @@ function renderNode(node: DocNode): string {
|
|
|
82
69
|
})}>${renderChildren(node.children)}</div>`
|
|
83
70
|
}
|
|
84
71
|
|
|
85
|
-
case
|
|
86
|
-
return `<div${styleStr({ display:
|
|
72
|
+
case "row":
|
|
73
|
+
return `<div${styleStr({ display: "flex", gap: p.gap as number | undefined, alignItems: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
|
|
87
74
|
|
|
88
|
-
case
|
|
89
|
-
return `<div${styleStr({ flex: p.width ? undefined :
|
|
75
|
+
case "column":
|
|
76
|
+
return `<div${styleStr({ flex: p.width ? undefined : "1", width: p.width as string | undefined, textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
|
|
90
77
|
|
|
91
|
-
case
|
|
78
|
+
case "heading": {
|
|
92
79
|
const level = (p.level as number) ?? 1
|
|
93
80
|
const tag = `h${Math.min(Math.max(level, 1), 6)}`
|
|
94
81
|
return `<${tag}${styleStr({ color: sanitizeColor(p.color as string | undefined), textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</${tag}>`
|
|
95
82
|
}
|
|
96
83
|
|
|
97
|
-
case
|
|
84
|
+
case "text": {
|
|
98
85
|
return `<p${styleStr({
|
|
99
86
|
fontSize: p.size as number | undefined,
|
|
100
87
|
color: sanitizeColor(p.color as string | undefined),
|
|
101
|
-
fontWeight: p.bold ?
|
|
102
|
-
fontStyle: p.italic ?
|
|
103
|
-
textDecoration: p.underline
|
|
104
|
-
? 'underline'
|
|
105
|
-
: p.strikethrough
|
|
106
|
-
? 'line-through'
|
|
107
|
-
: undefined,
|
|
88
|
+
fontWeight: p.bold ? "bold" : undefined,
|
|
89
|
+
fontStyle: p.italic ? "italic" : undefined,
|
|
90
|
+
textDecoration: p.underline ? "underline" : p.strikethrough ? "line-through" : undefined,
|
|
108
91
|
textAlign: p.align as string | undefined,
|
|
109
92
|
lineHeight: p.lineHeight as number | undefined,
|
|
110
93
|
})}>${renderChildren(node.children)}</p>`
|
|
111
94
|
}
|
|
112
95
|
|
|
113
|
-
case
|
|
96
|
+
case "link":
|
|
114
97
|
return `<a href="${escapeHtml(sanitizeHref(p.href as string))}"${styleStr({ color: sanitizeColor(p.color as string | undefined) })}>${renderChildren(node.children)}</a>`
|
|
115
98
|
|
|
116
|
-
case
|
|
99
|
+
case "image": {
|
|
117
100
|
const alignStyle =
|
|
118
|
-
p.align ===
|
|
119
|
-
?
|
|
120
|
-
: p.align ===
|
|
121
|
-
?
|
|
122
|
-
:
|
|
123
|
-
const img = `<img src="${escapeHtml(sanitizeImageSrc(p.src as string))}"${p.width ? ` width="${p.width}"` :
|
|
101
|
+
p.align === "center"
|
|
102
|
+
? "display:block;margin:0 auto"
|
|
103
|
+
: p.align === "right"
|
|
104
|
+
? "display:block;margin-left:auto"
|
|
105
|
+
: ""
|
|
106
|
+
const img = `<img src="${escapeHtml(sanitizeImageSrc(p.src as string))}"${p.width ? ` width="${p.width}"` : ""}${p.height ? ` height="${p.height}"` : ""}${p.alt ? ` alt="${escapeHtml(p.alt as string)}"` : ""}${alignStyle ? ` style="${sanitizeStyle(alignStyle)}"` : ""} />`
|
|
124
107
|
if (p.caption) {
|
|
125
|
-
return `<figure${p.align ===
|
|
108
|
+
return `<figure${p.align === "center" ? ' style="text-align:center"' : ""}>${img}<figcaption>${escapeHtml(p.caption as string)}</figcaption></figure>`
|
|
126
109
|
}
|
|
127
110
|
return img
|
|
128
111
|
}
|
|
129
112
|
|
|
130
|
-
case
|
|
131
|
-
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
|
|
132
|
-
resolveColumn,
|
|
133
|
-
)
|
|
113
|
+
case "table": {
|
|
114
|
+
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
|
|
134
115
|
const rows = (p.rows ?? []) as (string | number)[][]
|
|
135
116
|
const hs = p.headerStyle as
|
|
136
117
|
| { background?: string; color?: string; bold?: boolean }
|
|
@@ -138,80 +119,76 @@ function renderNode(node: DocNode): string {
|
|
|
138
119
|
const striped = p.striped as boolean | undefined
|
|
139
120
|
const bordered = p.bordered as boolean | undefined
|
|
140
121
|
const borderStyle = bordered
|
|
141
|
-
?
|
|
142
|
-
:
|
|
122
|
+
? "border:1px solid #ddd;border-collapse:collapse;"
|
|
123
|
+
: "border-collapse:collapse;"
|
|
143
124
|
|
|
144
125
|
let html = `<table style="width:100%;${borderStyle}">`
|
|
145
|
-
if (p.caption)
|
|
146
|
-
html += `<caption>${escapeHtml(p.caption as string)}</caption>`
|
|
126
|
+
if (p.caption) html += `<caption>${escapeHtml(p.caption as string)}</caption>`
|
|
147
127
|
|
|
148
|
-
html +=
|
|
128
|
+
html += "<thead><tr>"
|
|
149
129
|
for (const col of columns) {
|
|
150
|
-
const cellBorder = bordered ?
|
|
151
|
-
const bgStyle = hs?.background
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const fontStyle = hs?.bold !== false ? 'font-weight:bold;' : ''
|
|
156
|
-
const alignStyle = col.align ? `text-align:${col.align};` : ''
|
|
130
|
+
const cellBorder = bordered ? "border:1px solid #ddd;" : ""
|
|
131
|
+
const bgStyle = hs?.background ? `background:${sanitizeColor(hs.background)};` : ""
|
|
132
|
+
const colorStyle = hs?.color ? `color:${sanitizeColor(hs.color)};` : ""
|
|
133
|
+
const fontStyle = hs?.bold !== false ? "font-weight:bold;" : ""
|
|
134
|
+
const alignStyle = col.align ? `text-align:${col.align};` : ""
|
|
157
135
|
const widthStyle = col.width
|
|
158
|
-
? `width:${typeof col.width ===
|
|
159
|
-
:
|
|
136
|
+
? `width:${typeof col.width === "number" ? `${col.width}px` : col.width};`
|
|
137
|
+
: ""
|
|
160
138
|
html += `<th style="${cellBorder}${bgStyle}${colorStyle}${fontStyle}${alignStyle}${widthStyle}padding:8px">${escapeHtml(col.header)}</th>`
|
|
161
139
|
}
|
|
162
|
-
html +=
|
|
140
|
+
html += "</tr></thead>"
|
|
163
141
|
|
|
164
|
-
html +=
|
|
142
|
+
html += "<tbody>"
|
|
165
143
|
for (let i = 0; i < rows.length; i++) {
|
|
166
|
-
const rowBg =
|
|
167
|
-
striped && i % 2 === 1 ? ' style="background:#f9f9f9"' : ''
|
|
144
|
+
const rowBg = striped && i % 2 === 1 ? ' style="background:#f9f9f9"' : ""
|
|
168
145
|
html += `<tr${rowBg}>`
|
|
169
146
|
for (let j = 0; j < columns.length; j++) {
|
|
170
|
-
const cellBorder = bordered ?
|
|
147
|
+
const cellBorder = bordered ? "border:1px solid #ddd;" : ""
|
|
171
148
|
const col = columns[j]
|
|
172
|
-
const alignStyle = col?.align ? `text-align:${col.align};` :
|
|
173
|
-
html += `<td style="${cellBorder}${alignStyle}padding:8px">${escapeHtml(String(rows[i]?.[j] ??
|
|
149
|
+
const alignStyle = col?.align ? `text-align:${col.align};` : ""
|
|
150
|
+
html += `<td style="${cellBorder}${alignStyle}padding:8px">${escapeHtml(String(rows[i]?.[j] ?? ""))}</td>`
|
|
174
151
|
}
|
|
175
|
-
html +=
|
|
152
|
+
html += "</tr>"
|
|
176
153
|
}
|
|
177
|
-
html +=
|
|
154
|
+
html += "</tbody></table>"
|
|
178
155
|
return html
|
|
179
156
|
}
|
|
180
157
|
|
|
181
|
-
case
|
|
182
|
-
const tag = p.ordered ?
|
|
158
|
+
case "list": {
|
|
159
|
+
const tag = p.ordered ? "ol" : "ul"
|
|
183
160
|
return `<${tag}>${renderChildren(node.children)}</${tag}>`
|
|
184
161
|
}
|
|
185
162
|
|
|
186
|
-
case
|
|
163
|
+
case "list-item":
|
|
187
164
|
return `<li>${renderChildren(node.children)}</li>`
|
|
188
165
|
|
|
189
|
-
case
|
|
166
|
+
case "code":
|
|
190
167
|
return `<pre style="background:#f5f5f5;padding:12px;border-radius:4px;overflow-x:auto"><code>${escapeHtml(renderChildren(node.children))}</code></pre>`
|
|
191
168
|
|
|
192
|
-
case
|
|
193
|
-
const color = sanitizeColor((p.color as string) ??
|
|
169
|
+
case "divider": {
|
|
170
|
+
const color = sanitizeColor((p.color as string) ?? "#ddd")
|
|
194
171
|
const thickness = (p.thickness as number) ?? 1
|
|
195
172
|
return `<hr style="border:none;border-top:${thickness}px solid ${color};margin:16px 0" />`
|
|
196
173
|
}
|
|
197
174
|
|
|
198
|
-
case
|
|
175
|
+
case "page-break":
|
|
199
176
|
return '<div style="page-break-after:always;break-after:page"></div>'
|
|
200
177
|
|
|
201
|
-
case
|
|
178
|
+
case "spacer":
|
|
202
179
|
return `<div style="height:${p.height}px"></div>`
|
|
203
180
|
|
|
204
|
-
case
|
|
205
|
-
const bg = sanitizeColor((p.background as string) ??
|
|
206
|
-
const color = sanitizeColor((p.color as string) ??
|
|
181
|
+
case "button": {
|
|
182
|
+
const bg = sanitizeColor((p.background as string) ?? "#4f46e5")
|
|
183
|
+
const color = sanitizeColor((p.color as string) ?? "#fff")
|
|
207
184
|
const radius = (p.borderRadius as number) ?? 4
|
|
208
185
|
const pad = padStr((p.padding ?? [12, 24]) as [number, number])
|
|
209
|
-
const align = (p.align as string) ??
|
|
186
|
+
const align = (p.align as string) ?? "left"
|
|
210
187
|
return `<div style="text-align:${align}"><a href="${escapeHtml(sanitizeHref(p.href as string))}" style="display:inline-block;background:${bg};color:${color};padding:${pad};border-radius:${radius}px;text-decoration:none;font-weight:bold">${renderChildren(node.children)}</a></div>`
|
|
211
188
|
}
|
|
212
189
|
|
|
213
|
-
case
|
|
214
|
-
const borderColor = sanitizeColor((p.borderColor as string) ??
|
|
190
|
+
case "quote": {
|
|
191
|
+
const borderColor = sanitizeColor((p.borderColor as string) ?? "#ddd")
|
|
215
192
|
return `<blockquote style="margin:0;padding:12px 20px;border-left:4px solid ${borderColor};color:#555">${renderChildren(node.children)}</blockquote>`
|
|
216
193
|
}
|
|
217
194
|
|
|
@@ -225,8 +202,8 @@ type PageMargin = number | [number, number] | [number, number, number, number]
|
|
|
225
202
|
export const htmlRenderer: DocumentRenderer = {
|
|
226
203
|
async render(node: DocNode, options?: RenderOptions): Promise<string> {
|
|
227
204
|
let html = renderNode(node)
|
|
228
|
-
if (options?.direction ===
|
|
229
|
-
html = html.replace(
|
|
205
|
+
if (options?.direction === "rtl") {
|
|
206
|
+
html = html.replace("<body>", '<body dir="rtl" style="direction:rtl">')
|
|
230
207
|
}
|
|
231
208
|
return html
|
|
232
209
|
},
|
|
@@ -1,53 +1,47 @@
|
|
|
1
|
-
import { sanitizeHref, sanitizeImageSrc } from
|
|
2
|
-
import type {
|
|
3
|
-
DocChild,
|
|
4
|
-
DocNode,
|
|
5
|
-
DocumentRenderer,
|
|
6
|
-
RenderOptions,
|
|
7
|
-
TableColumn,
|
|
8
|
-
} from '../types'
|
|
1
|
+
import { sanitizeHref, sanitizeImageSrc } from "../sanitize"
|
|
2
|
+
import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
|
|
9
3
|
|
|
10
4
|
function resolveColumn(col: string | TableColumn): TableColumn {
|
|
11
|
-
return typeof col ===
|
|
5
|
+
return typeof col === "string" ? { header: col } : col
|
|
12
6
|
}
|
|
13
7
|
|
|
14
8
|
function renderChild(child: DocChild): string {
|
|
15
|
-
if (typeof child ===
|
|
9
|
+
if (typeof child === "string") return child
|
|
16
10
|
return renderNode(child)
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
function renderChildren(children: DocChild[]): string {
|
|
20
|
-
return children.map(renderChild).join(
|
|
14
|
+
return children.map(renderChild).join("")
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
function renderInline(children: DocChild[]): string {
|
|
24
|
-
return children.map(renderChild).join(
|
|
18
|
+
return children.map(renderChild).join("")
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
function renderNode(node: DocNode): string {
|
|
28
22
|
const p = node.props
|
|
29
23
|
|
|
30
24
|
switch (node.type) {
|
|
31
|
-
case
|
|
25
|
+
case "document":
|
|
32
26
|
return renderChildren(node.children)
|
|
33
27
|
|
|
34
|
-
case
|
|
28
|
+
case "page":
|
|
35
29
|
return renderChildren(node.children)
|
|
36
30
|
|
|
37
|
-
case
|
|
31
|
+
case "section":
|
|
38
32
|
return `${renderChildren(node.children)}\n`
|
|
39
33
|
|
|
40
|
-
case
|
|
41
|
-
case
|
|
34
|
+
case "row":
|
|
35
|
+
case "column":
|
|
42
36
|
return renderChildren(node.children)
|
|
43
37
|
|
|
44
|
-
case
|
|
38
|
+
case "heading": {
|
|
45
39
|
const level = (p.level as number) ?? 1
|
|
46
|
-
const prefix =
|
|
40
|
+
const prefix = "#".repeat(Math.min(Math.max(level, 1), 6))
|
|
47
41
|
return `${prefix} ${renderInline(node.children)}\n\n`
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
case
|
|
44
|
+
case "text": {
|
|
51
45
|
let text = renderInline(node.children)
|
|
52
46
|
if (p.bold) text = `**${text}**`
|
|
53
47
|
if (p.italic) text = `*${text}*`
|
|
@@ -55,82 +49,78 @@ function renderNode(node: DocNode): string {
|
|
|
55
49
|
return `${text}\n\n`
|
|
56
50
|
}
|
|
57
51
|
|
|
58
|
-
case
|
|
52
|
+
case "link":
|
|
59
53
|
return `[${renderInline(node.children)}](${sanitizeHref(p.href as string)})`
|
|
60
54
|
|
|
61
|
-
case
|
|
62
|
-
const alt = (p.alt as string) ??
|
|
55
|
+
case "image": {
|
|
56
|
+
const alt = (p.alt as string) ?? ""
|
|
63
57
|
let md = `})`
|
|
64
58
|
if (p.caption) md += `\n*${p.caption}*`
|
|
65
59
|
return `${md}\n\n`
|
|
66
60
|
}
|
|
67
61
|
|
|
68
|
-
case
|
|
69
|
-
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
|
|
70
|
-
resolveColumn,
|
|
71
|
-
)
|
|
62
|
+
case "table": {
|
|
63
|
+
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
|
|
72
64
|
const rows = (p.rows ?? []) as (string | number)[][]
|
|
73
65
|
|
|
74
|
-
if (columns.length === 0) return
|
|
66
|
+
if (columns.length === 0) return ""
|
|
75
67
|
|
|
76
68
|
// Header
|
|
77
|
-
const header = `| ${columns.map((c) => c.header).join(
|
|
69
|
+
const header = `| ${columns.map((c) => c.header).join(" | ")} |`
|
|
78
70
|
|
|
79
71
|
// Separator with alignment
|
|
80
72
|
const separator = `| ${columns
|
|
81
73
|
.map((c) => {
|
|
82
|
-
const align = c.align ??
|
|
83
|
-
if (align ===
|
|
84
|
-
if (align ===
|
|
85
|
-
return
|
|
74
|
+
const align = c.align ?? "left"
|
|
75
|
+
if (align === "center") return ":---:"
|
|
76
|
+
if (align === "right") return "---:"
|
|
77
|
+
return "---"
|
|
86
78
|
})
|
|
87
|
-
.join(
|
|
79
|
+
.join(" | ")} |`
|
|
88
80
|
|
|
89
81
|
// Rows
|
|
90
82
|
const body = rows
|
|
91
|
-
.map(
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
.join('\n')
|
|
83
|
+
.map((row) => `| ${row.map((cell) => String(cell ?? "")).join(" | ")} |`)
|
|
84
|
+
.join("\n")
|
|
95
85
|
|
|
96
86
|
let md = `${header}\n${separator}\n${body}\n\n`
|
|
97
87
|
if (p.caption) md = `*${p.caption}*\n\n${md}`
|
|
98
88
|
return md
|
|
99
89
|
}
|
|
100
90
|
|
|
101
|
-
case
|
|
91
|
+
case "list": {
|
|
102
92
|
const ordered = p.ordered as boolean | undefined
|
|
103
93
|
return `${node.children
|
|
104
|
-
.filter((c): c is DocNode => typeof c !==
|
|
94
|
+
.filter((c): c is DocNode => typeof c !== "string")
|
|
105
95
|
.map((item, i) => {
|
|
106
|
-
const prefix = ordered ? `${i + 1}.` :
|
|
96
|
+
const prefix = ordered ? `${i + 1}.` : "-"
|
|
107
97
|
return `${prefix} ${renderInline(item.children)}`
|
|
108
98
|
})
|
|
109
|
-
.join(
|
|
99
|
+
.join("\n")}\n\n`
|
|
110
100
|
}
|
|
111
101
|
|
|
112
|
-
case
|
|
102
|
+
case "list-item":
|
|
113
103
|
return renderInline(node.children)
|
|
114
104
|
|
|
115
|
-
case
|
|
116
|
-
const lang = (p.language as string) ??
|
|
105
|
+
case "code": {
|
|
106
|
+
const lang = (p.language as string) ?? ""
|
|
117
107
|
const content = renderInline(node.children)
|
|
118
108
|
return `\`\`\`${lang}\n${content}\n\`\`\`\n\n`
|
|
119
109
|
}
|
|
120
110
|
|
|
121
|
-
case
|
|
122
|
-
return
|
|
111
|
+
case "divider":
|
|
112
|
+
return "---\n\n"
|
|
123
113
|
|
|
124
|
-
case
|
|
125
|
-
return
|
|
114
|
+
case "page-break":
|
|
115
|
+
return "---\n\n"
|
|
126
116
|
|
|
127
|
-
case
|
|
128
|
-
return
|
|
117
|
+
case "spacer":
|
|
118
|
+
return "\n"
|
|
129
119
|
|
|
130
|
-
case
|
|
120
|
+
case "button":
|
|
131
121
|
return `[${renderInline(node.children)}](${sanitizeHref(p.href as string)})\n\n`
|
|
132
122
|
|
|
133
|
-
case
|
|
123
|
+
case "quote":
|
|
134
124
|
return `> ${renderInline(node.children)}\n\n`
|
|
135
125
|
|
|
136
126
|
default:
|