mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-27 09:08:30 +00:00
4b633d8cd2
The release notes in Admin read ## as the version and ### as a section, and printed a #### line as plain text. A release that heads each feature inside a section with #### (Tours, TREK Studio under Added) now shows it as a feature heading between the section and its text, on desktop and on the phone.
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
/** One block of a release body: a heading, a paragraph, or a run of list items. */
|
|
export type ReleaseNoteBlock = { kind: 'h3' | 'h4' | 'h5' | 'p'; text: string } | { kind: 'ul'; items: string[] };
|
|
|
|
/** Escapes the characters that would let release text break out of the HTML it is placed in. */
|
|
export function escapeReleaseHtml(str: string): string {
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
}
|
|
|
|
/**
|
|
* Splits a GitHub release body into the blocks both admin shells render: `## `, `### ` and
|
|
* `#### ` headings (the version, a section such as Added, a feature inside it), `- ` or `* ` list items gathered into one list until anything else breaks the
|
|
* run, and every other non-empty line as a paragraph. Blank lines only end a list. The
|
|
* text stays raw; each shell formats it inline itself.
|
|
*/
|
|
export function parseReleaseNotes(body: string): ReleaseNoteBlock[] {
|
|
const blocks: ReleaseNoteBlock[] = [];
|
|
let listItems: string[] = [];
|
|
|
|
const flushList = () => {
|
|
if (listItems.length > 0) {
|
|
blocks.push({ kind: 'ul', items: listItems });
|
|
listItems = [];
|
|
}
|
|
};
|
|
|
|
for (const line of body.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed) {
|
|
flushList();
|
|
continue;
|
|
}
|
|
|
|
if (trimmed.startsWith('#### ')) {
|
|
flushList();
|
|
blocks.push({ kind: 'h5', text: trimmed.slice(5) });
|
|
} else if (trimmed.startsWith('### ')) {
|
|
flushList();
|
|
blocks.push({ kind: 'h4', text: trimmed.slice(4) });
|
|
} else if (trimmed.startsWith('## ')) {
|
|
flushList();
|
|
blocks.push({ kind: 'h3', text: trimmed.slice(3) });
|
|
} else if (/^[-*] /.test(trimmed)) {
|
|
listItems.push(trimmed.slice(2));
|
|
} else {
|
|
flushList();
|
|
blocks.push({ kind: 'p', text: trimmed });
|
|
}
|
|
}
|
|
flushList();
|
|
return blocks;
|
|
}
|