1/**
2 * Transform a CHANGELOG.md entry body (written for GitHub Releases, which
3 * render HTML) into text suitable for a Discord embed description.
4 *
5 * Discord renders neither HTML entities nor the `<samp>` tag, and only
6 * honors `#`, `##`, and `###` headings. The CHANGELOG entries produced by
7 * `render.ts` / `changelogithub` use:
8 *
9 * - `&nbsp;` sequences to visually indent `###`/`#####` headings and
10 * author/PR separators (`&nbsp;-&nbsp;`)
11 * - `<samp>(hash)</samp>` to render commit-hash links in small caps
12 * - A trailing `#####` "View changes on GitHub" line
13 *
14 * This converts those into plain-text / markdown equivalents.
15 */
16export function toDiscordBody(rawBody: string): string {
17 return (
18 rawBody
19 .replace(/&nbsp;/g, " ")
20 .replace(/<\/?samp>/g, "`")
21 // Collapse the long indent that the GitHub-flavored headings use.
22 .replace(/^(#{1,6})\s+/gm, "$1 ")
23 // Discord only renders #, ##, ### as headings; drop deeper levels so
24 // lines like "##### View changes on GitHub" become plain text.
25 .replace(/^#{4,6}\s+/gm, "")
26 // Strip any other stray HTML tags just in case.
27 .replace(/<\/?[a-z][^>]*>/gi, "")
28 );
29}
30
31/**
32 * Extract the body for a single `## <tag>` entry out of a CHANGELOG.md
33 * string. Stops at the `---` separator that `release-notes.ts` inserts
34 * between entries, or at the next `## ` heading if the separator is
35 * missing. Returns `undefined` if the tag isn't present.
36 */
37export function extractTagBody(
38 changelog: string,
39 tag: string,
40): string | undefined {
41 const heading = `## ${tag}\n`;
42 const start = changelog.indexOf(heading);
43 if (start === -1) return undefined;
44 const after = changelog.slice(start + heading.length);
45 const sepIdx = after.indexOf("\n---\n");
46 const nextHeadingIdx = after.indexOf("\n## ");
47 const end =
48 sepIdx === -1
49 ? nextHeadingIdx === -1
50 ? after.length
51 : nextHeadingIdx
52 : nextHeadingIdx === -1
53 ? sepIdx
54 : Math.min(sepIdx, nextHeadingIdx);
55 return after.slice(0, end).trim();
56}