diff --git a/app/data/markdown-renderer.ts b/app/data/markdown-renderer.ts index 8668f74..91c4c60 100644 --- a/app/data/markdown-renderer.ts +++ b/app/data/markdown-renderer.ts @@ -5,6 +5,7 @@ export const makeMD = (opts: { tailwindLists: boolean; md?: MarkdownIt; inlineCodeSmall?: boolean; + levelShift?: number; }) => { const md = opts.md || @@ -21,15 +22,19 @@ export const makeMD = (opts: { // Headings const headingSizes = { - '1': 'text-xl font-bold mb-3', - '2': 'text-xl font-semibold mb-3', - '3': 'text-xl font-semibold mb-3', - '4': 'text-xl font-semibold mb-3', - '5': 'text-lg font-medium mb-2', - '6': 'text-base font-medium mb-1', + '1': 'text-xl font-bold my-3', + '2': 'text-xl font-semibold my-3', + '3': 'text-lg font-semibold my-3', + '4': 'text-lg font-semibold my-3', + '5': 'text-base font-medium my-2', + '6': 'text-base font-medium my-1', } as const; md.renderer.rules.heading_open = (tokens, idx) => { - const level = tokens[idx].tag.slice(1) as '1' | '2' | '3' | '4' | '5' | '6'; + let level = tokens[idx].tag.slice(1) as '1' | '2' | '3' | '4' | '5' | '6'; + if (opts.levelShift) { + const newLevel = Math.min(Math.max(parseInt(level) + opts.levelShift, 1), 6); + level = newLevel.toString() as '1' | '2' | '3' | '4' | '5' | '6'; + } return ``; }; diff --git a/app/data/markdown.test.ts b/app/data/markdown.test.ts index 7340c09..409b687 100644 --- a/app/data/markdown.test.ts +++ b/app/data/markdown.test.ts @@ -8,14 +8,14 @@ import { describe('renderMarkdownSafely', () => { test('renders headers correctly', () => { expect(renderMarkdownSafely('# Foo')).toMatchInlineSnapshot(` - "

Foo

+ "

Foo

" `); }); test('renders absurd headers correctly', () => { expect(renderMarkdownSafely('###### Foo')).toMatchInlineSnapshot(` - "
Foo
+ "
Foo
" `); }); @@ -210,4 +210,38 @@ describe('renderGroupedReleaseNotes', () => { } `); }); + + test('renders version grouped release notes correctly (Windows newlines)', () => { + const releaseNotes = [ + { + version: 'v1.0.0', + content: '## New Feature\r\n\r\n* Added new feature', + }, + { + version: 'v1.1.0', + content: '## New Feature\r\n\r\n* Added another feature', + }, + ]; + + expect(renderGroupedReleaseNotes(releaseNotes)).toMatchInlineSnapshot(` + { + "New Feature": [ + { + "content": " + ", + "version": "v1.1.0", + }, + { + "content": " + ", + "version": "v1.0.0", + }, + ], + } + `); + }); }); diff --git a/app/data/markdown.ts b/app/data/markdown.ts index 655eb0e..5c85baf 100644 --- a/app/data/markdown.ts +++ b/app/data/markdown.ts @@ -13,7 +13,7 @@ const titleMdIt = new MarkdownIt('zero', { }); titleMdIt.inline.ruler.enable(['backticks']); const listMD = makeMD({ tailwindLists: true, inlineCodeSmall: true }); -const noListMD = makeMD({ tailwindLists: false, inlineCodeSmall: true }); +const noListMD = makeMD({ tailwindLists: false, inlineCodeSmall: true, levelShift: 2 }); const titleMD = makeMD({ tailwindLists: false, md: titleMdIt, @@ -50,7 +50,7 @@ export const renderGroupedReleaseNotes = (versions: { version: string; content: groups[key] = []; } for (const { version, content } of versions) { - const headers = content.split(/^## ([A-Za-z ]+)\n/gm).slice(1); + const headers = content.split(/^## ([A-Za-z ]+)(?:\r\n|\n)/gm).slice(1); for (let i = 0; i < headers.length; i += 2) { const groupName = headers[i]; const groupContent = headers[i + 1]; diff --git a/app/routes/release/compare.tsx b/app/routes/release/compare.tsx index fdd949b..1f4fa7e 100644 --- a/app/routes/release/compare.tsx +++ b/app/routes/release/compare.tsx @@ -72,22 +72,21 @@ export const loader = async (args: LoaderFunctionArgs) => { return redirect('/release'); } - const grouped = renderGroupedReleaseNotes( - versionsForNotes.map((version, i) => { - let releaseNotes = githubReleaseNotes[i]!; - const parsed = semverParse(version); - if (parsed?.prerelease.length) { - releaseNotes = releaseNotes?.split(new RegExp(`@${escapeRegExp(version)}\`?.`))[1]; - } - releaseNotes = - releaseNotes?.replace(/# Release Notes for [^\r\n]+(?:(?:\n)|(?:\r\n))/i, '') || - 'Missing...'; - return { - version, - content: releaseNotes, - }; - }), - ); + const rawGroupedNotes = versionsForNotes.map((version, i) => { + let releaseNotes = githubReleaseNotes[i]!; + const parsed = semverParse(version); + if (parsed?.prerelease.length) { + releaseNotes = releaseNotes?.split(new RegExp(`@${escapeRegExp(version)}\`?.`))[1]; + } + releaseNotes = + releaseNotes?.replace(/# Release Notes for [^\r\n]+(?:(?:\n)|(?:\r\n))/i, '') || 'Missing...'; + return { + version, + content: releaseNotes, + }; + }); + + const grouped = renderGroupedReleaseNotes(rawGroupedNotes); args.context.cacheControl = 'private, max-age=300';