Skip to content

Commit b345df1

Browse files
committed
markdown is now being rendered (settings need to be added still)
1 parent f8b32d4 commit b345df1

File tree

8 files changed

+125
-17
lines changed

8 files changed

+125
-17
lines changed

app/components/Changelog/Card.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ const { release } = defineProps<{
77
</script>
88
<template>
99
<section class="border border-border rounded-lg p-4 sm:p-6">
10-
<h1 class="text-1xl sm:text-2xl font-medium min-w-0 break-words py-2">
10+
<h2 class="text-1xl sm:text-2xl font-medium min-w-0 break-words py-2">
1111
{{ release.title }}
12-
</h1>
13-
<Readme v-if="release.html" :html="release.html.trim()"></Readme>
12+
</h2>
13+
<Readme v-if="release.html" :html="release.html"></Readme>
1414
</section>
1515
</template>
1616

app/components/Changelog/Releases.vue

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,5 @@ const { data: releases } = useFetch<ReleaseData[]>(
88
<template>
99
<div class="flex flex-col gap-2 py-3" v-if="releases">
1010
<ChangelogCard v-for="release of releases" :release :key="release.id" />
11-
12-
<!-- <ChangelogCard />
13-
<ChangelogCard /> -->
1411
</div>
1512
</template>

app/pages/package-changes/[...path].vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ const { data: changelog } = usePackageChangelog(packageName, version)
7070
<header class="border-b border-border bg-bg sticky top-14 z-20">
7171
<div class="container pt-4 pb-3">
7272
<div class="flex items-center gap-2 mb-3 flex-wrap min-w-0">
73-
<NuxtLink
74-
v-if="packageName"
75-
:to="packageRoute(packageName, version)"
73+
<h1
7674
class="font-mono text-lg sm:text-xl font-semibold text-fg hover:text-fg-muted transition-colors truncate"
7775
>
78-
{{ packageName }}
79-
</NuxtLink>
76+
<NuxtLink v-if="packageName" :to="packageRoute(packageName, version)">
77+
{{ packageName }}
78+
</NuxtLink>
79+
</h1>
8080

8181
<VersionSelector
8282
v-if="version && pkg?.versions && pkg?.['dist-tags']"

server/api/changelog/releases/[provider]/[owner]/[repo].ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { ProviderId } from '~~/shared/utils/git-providers'
22
import type { ReleaseData } from '~~/shared/types/changelog'
3-
import { GithubReleaseCollectionSchama } from '~~/shared/schemas/changelog/release'
43
import { ERROR_CHANGELOG_RELEASES_FAILED, THROW_INCOMPLETE_PARAM } from '~~/shared/utils/constants'
4+
import { GithubReleaseCollectionSchama } from '~~/shared/schemas/changelog/release'
55
import { parse } from 'valibot'
6+
import { changelogRenderer, sanitizeRawHTML } from '~~/server/utils/changelog/markdown'
67

78
export default defineCachedEventHandler(async event => {
89
const provider = getRouterParam(event, 'provider')
@@ -43,11 +44,13 @@ async function getReleasesFromGithub(owner: string, repo: string) {
4344

4445
const { releases } = parse(GithubReleaseCollectionSchama, data)
4546

47+
const render = await changelogRenderer()
48+
4649
return releases.map(
4750
r =>
4851
({
4952
id: r.id,
50-
html: r.html,
53+
html: r.markdown ? sanitizeRawHTML(render(r.markdown) as string) : null,
5154
title: r.name,
5255
draft: r.draft,
5356
prerelease: r.prerelease,

server/utils/changelog/markdown.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { marked } from 'marked'
2+
import { ALLOWED_ATTR, ALLOWED_TAGS } from '../readme'
3+
import sanitizeHtml from 'sanitize-html'
4+
5+
export async function changelogRenderer() {
6+
const renderer = new marked.Renderer()
7+
8+
// settings will need to be added still
9+
10+
return (markdown: string) =>
11+
marked.parse(markdown, {
12+
renderer,
13+
})
14+
}
15+
16+
export function sanitizeRawHTML(rawHtml: string) {
17+
return sanitizeHtml(rawHtml, {
18+
allowedTags: ALLOWED_TAGS,
19+
allowedAttributes: ALLOWED_ATTR,
20+
allowedSchemes: ['http', 'https', 'mailto'],
21+
// Transform img src URLs (GitHub blob → raw, relative → GitHub raw)
22+
transformTags: {
23+
h1: (_, attribs) => {
24+
return { tagName: 'h3', attribs: { ...attribs, 'data-level': '1' } }
25+
},
26+
h2: (_, attribs) => {
27+
return { tagName: 'h4', attribs: { ...attribs, 'data-level': '2' } }
28+
},
29+
h3: (_, attribs) => {
30+
if (attribs['data-level']) return { tagName: 'h3', attribs: attribs }
31+
return { tagName: 'h5', attribs: { ...attribs, 'data-level': '3' } }
32+
},
33+
h4: (_, attribs) => {
34+
if (attribs['data-level']) return { tagName: 'h4', attribs: attribs }
35+
return { tagName: 'h6', attribs: { ...attribs, 'data-level': '4' } }
36+
},
37+
h5: (_, attribs) => {
38+
if (attribs['data-level']) return { tagName: 'h5', attribs: attribs }
39+
return { tagName: 'h6', attribs: { ...attribs, 'data-level': '5' } }
40+
},
41+
h6: (_, attribs) => {
42+
if (attribs['data-level']) return { tagName: 'h6', attribs: attribs }
43+
return { tagName: 'h6', attribs: { ...attribs, 'data-level': '6' } }
44+
},
45+
// img: (tagName, attribs) => {
46+
// if (attribs.src) {
47+
// attribs.src = resolveImageUrl(attribs.src, packageName, repoInfo)
48+
// }
49+
// return { tagName, attribs }
50+
// },
51+
// source: (tagName, attribs) => {
52+
// if (attribs.src) {
53+
// attribs.src = resolveImageUrl(attribs.src, packageName, repoInfo)
54+
// }
55+
// if (attribs.srcset) {
56+
// attribs.srcset = attribs.srcset
57+
// .split(',')
58+
// .map(entry => {
59+
// const parts = entry.trim().split(/\s+/)
60+
// const url = parts[0]
61+
// if (!url) return entry.trim()
62+
// const descriptor = parts[1]
63+
// const resolvedUrl = resolveImageUrl(url, packageName, repoInfo)
64+
// return descriptor ? `${resolvedUrl} ${descriptor}` : resolvedUrl
65+
// })
66+
// .join(', ')
67+
// }
68+
// return { tagName, attribs }
69+
// },
70+
// a: (tagName, attribs) => {
71+
// if (!attribs.href) {
72+
// return { tagName, attribs }
73+
// }
74+
75+
// const resolvedHref = resolveUrl(attribs.href, packageName, repoInfo)
76+
77+
// const provider = matchPlaygroundProvider(resolvedHref)
78+
// if (provider && !seenUrls.has(resolvedHref)) {
79+
// seenUrls.add(resolvedHref)
80+
81+
// collectedLinks.push({
82+
// url: resolvedHref,
83+
// provider: provider.id,
84+
// providerName: provider.name,
85+
// /**
86+
// * We need to set some data attribute before hand because `transformTags` doesn't
87+
// * provide the text of the element. This will automatically be removed, because there
88+
// * is an allow list for link attributes.
89+
// * */
90+
// label: attribs['data-title-intermediate'] || provider.name,
91+
// })
92+
// }
93+
94+
// // Add security attributes for external links
95+
// if (resolvedHref && hasProtocol(resolvedHref, { acceptRelative: true })) {
96+
// attribs.rel = 'nofollow noreferrer noopener'
97+
// attribs.target = '_blank'
98+
// }
99+
// attribs.href = resolvedHref
100+
// return { tagName, attribs }
101+
// },
102+
// div: prefixId,
103+
// p: prefixId,
104+
// span: prefixId,
105+
// section: prefixId,
106+
// article: prefixId,
107+
},
108+
})
109+
}

server/utils/readme.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function matchPlaygroundProvider(url: string): PlaygroundProvider | null {
110110

111111
// allow h1-h6, but replace h1-h2 later since we shift README headings down by 2 levels
112112
// (page h1 = package name, h2 = "Readme" section, so README h1 → h3)
113-
const ALLOWED_TAGS = [
113+
export const ALLOWED_TAGS = [
114114
'h1',
115115
'h2',
116116
'h3',
@@ -151,7 +151,7 @@ const ALLOWED_TAGS = [
151151
'button',
152152
]
153153

154-
const ALLOWED_ATTR: Record<string, string[]> = {
154+
export const ALLOWED_ATTR: Record<string, string[]> = {
155155
'*': ['id'], // Allow id on all tags
156156
'a': ['href', 'title', 'target', 'rel'],
157157
'img': ['src', 'alt', 'title', 'width', 'height', 'align'],

shared/schemas/changelog/release.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export const GithubReleaseSchama = v.object({
55
name: v.string(),
66
draft: v.boolean(),
77
prerelease: v.boolean(),
8-
html: v.string(),
98
markdown: v.nullable(v.string()), // can be null if no descroption was made
109
publishedAt: v.pipe(v.string(), v.isoTimestamp()),
1110
})

shared/types/changelog.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export type ChangelogInfo = ChangelogReleaseInfo | ChangelogMarkdownInfo
1919

2020
export interface ReleaseData {
2121
title: string // example "v1.x.x",
22-
html: string
22+
html: string | null
2323
prerelease?: boolean
2424
draft?: boolean
2525
id: string | number

0 commit comments

Comments
 (0)