| 1 | import type { PullRequestBodySegment } from "@t3tools/client-runtime/pull-request-markdown"; |
| 2 | import type { MarkdownNode } from "../../lib/markdownDocument"; |
| 3 | |
| 4 | /** Read media from the parsed document so code examples never become attachments. */ |
| 5 | export function pullRequestDescriptionContent( |
| 6 | document: MarkdownNode, |
| 7 | media: readonly PullRequestBodySegment[] = [], |
| 8 | ) { |
| 9 | const attachments = new Map< |
| 10 | string, |
| 11 | { readonly uri: string; readonly caption: string; readonly kind: "image" | "video" | "unknown" } |
| 12 | >(); |
| 13 | const text: string[] = []; |
| 14 | const visit = (node: MarkdownNode) => { |
| 15 | if (node.type === "image") { |
| 16 | if (node.url && /^https?:\/\//i.test(node.url) && !attachments.has(node.url)) { |
| 17 | attachments.set(node.url, { |
| 18 | uri: node.url, |
| 19 | caption: node.alt || node.title || "Image", |
| 20 | kind: "image", |
| 21 | }); |
| 22 | } |
| 23 | return; |
| 24 | } |
| 25 | if (node.type === "html") return; |
| 26 | if ("value" in node) text.push(node.value); |
| 27 | if ("children" in node) node.children.forEach(visit); |
| 28 | if (["paragraph", "heading", "listItem", "code"].includes(node.type)) text.push("\n"); |
| 29 | if (node.type === "break") text.push(" "); |
| 30 | }; |
| 31 | visit(document); |
| 32 | for (const segment of media) { |
| 33 | if (segment.kind === "attachment" && !attachments.has(segment.url)) { |
| 34 | const name = new URL(segment.url).pathname.split("/").pop(); |
| 35 | attachments.set(segment.url, { |
| 36 | uri: segment.url, |
| 37 | caption: segment.media === "video" ? "Video" : name || "Image", |
| 38 | kind: segment.media, |
| 39 | }); |
| 40 | } |
| 41 | } |
| 42 | return { preview: text.join("").trim(), attachments: [...attachments.values()] }; |
| 43 | } |