Skip to content

Commit 69a473c

Browse files
committed
perf: move html entity decoding to server side
1 parent f239c39 commit 69a473c

File tree

7 files changed

+26
-27
lines changed

7 files changed

+26
-27
lines changed

app/components/Package/Playgrounds.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
22
import type { PlaygroundLink } from '#shared/types'
3-
import { decodeHtmlEntities } from '~/utils/formatters'
43
54
const props = defineProps<{
65
links: PlaygroundLink[]
@@ -130,7 +129,7 @@ function focusMenuItem(index: number) {
130129
:class="[getIcon(firstLink.provider), getColor(firstLink.provider), 'w-4 h-4 shrink-0']"
131130
aria-hidden="true"
132131
/>
133-
<span class="truncate text-fg-muted">{{ decodeHtmlEntities(firstLink.label) }}</span>
132+
<span class="truncate text-fg-muted">{{ firstLink.label }}</span>
134133
</a>
135134
</TooltipApp>
136135

@@ -186,7 +185,7 @@ function focusMenuItem(index: number) {
186185
:class="[getIcon(link.provider), getColor(link.provider), 'w-4 h-4 shrink-0']"
187186
aria-hidden="true"
188187
/>
189-
<span class="truncate">{{ decodeHtmlEntities(link.label) }}</span>
188+
<span class="truncate">{{ link.label }}</span>
190189
</a>
191190
</TooltipApp>
192191
</div>

app/components/ReadmeTocDropdown.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script setup lang="ts">
22
import type { TocItem } from '#shared/types/readme'
33
import { onClickOutside, useEventListener } from '@vueuse/core'
4-
import { decodeHtmlEntities } from '~/utils/formatters'
54
65
const props = defineProps<{
76
toc: TocItem[]
@@ -202,7 +201,7 @@ function handleKeydown(event: KeyboardEvent) {
202201
@click="select()"
203202
@mouseenter="highlightedIndex = getIndex(node.id)"
204203
>
205-
<span class="truncate">{{ decodeHtmlEntities(node.text) }}</span>
204+
<span class="truncate">{{ node.text }}</span>
206205
</NuxtLink>
207206

208207
<template v-for="child in node.children" :key="child.id">
@@ -220,7 +219,7 @@ function handleKeydown(event: KeyboardEvent) {
220219
@click="select()"
221220
@mouseenter="highlightedIndex = getIndex(child.id)"
222221
>
223-
<span class="truncate">{{ decodeHtmlEntities(child.text) }}</span>
222+
<span class="truncate">{{ child.text }}</span>
224223
</NuxtLink>
225224

226225
<NuxtLink
@@ -241,7 +240,7 @@ function handleKeydown(event: KeyboardEvent) {
241240
@click="select()"
242241
@mouseenter="highlightedIndex = getIndex(grandchild.id)"
243242
>
244-
<span class="truncate">{{ decodeHtmlEntities(grandchild.text) }}</span>
243+
<span class="truncate">{{ grandchild.text }}</span>
245244
</NuxtLink>
246245
</template>
247246
</template>

app/composables/useMarkdown.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decodeHtmlEntities } from '~/utils/formatters'
1+
import { decodeHtmlEntities } from '#shared/utils/html'
22

33
interface UseMarkdownOptions {
44
text: string

app/utils/formatters.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,3 @@ export function toIsoDateString(date: Date): string {
44
const day = String(date.getUTCDate()).padStart(2, '0')
55
return `${year}-${month}-${day}`
66
}
7-
8-
const htmlEntities: Record<string, string> = {
9-
'&amp;': '&',
10-
'&lt;': '<',
11-
'&gt;': '>',
12-
'&quot;': '"',
13-
'&#39;': "'",
14-
'&apos;': "'",
15-
'&nbsp;': ' ',
16-
}
17-
18-
export function decodeHtmlEntities(text: string): string {
19-
return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match)
20-
}

server/api/registry/readme/[...pkg].get.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default defineCachedEventHandler(
3333
swr: true,
3434
getKey: event => {
3535
const pkg = getRouterParam(event, 'pkg') ?? ''
36-
return `readme:v8:${pkg.replace(/\/+$/, '').trim()}`
36+
return `readme:v9:${pkg.replace(/\/+$/, '').trim()}`
3737
},
3838
},
3939
)

server/utils/readme.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import sanitizeHtml from 'sanitize-html'
33
import { hasProtocol } from 'ufo'
44
import type { ReadmeResponse, TocItem } from '#shared/types/readme'
55
import { convertBlobOrFileToRawUrl, type RepositoryInfo } from '#shared/utils/git-providers'
6-
import { highlightCodeSync } from './shiki'
6+
import { decodeHtmlEntities } from '#shared/utils/html'
77
import { convertToEmoji } from '#shared/utils/emoji'
88

9+
import { highlightCodeSync } from './shiki'
10+
911
/**
1012
* Playground provider configuration
1113
*/
@@ -371,8 +373,8 @@ export async function renderReadmeHtml(
371373
// (e.g., #install, #dependencies, #versions are used by the package page)
372374
const id = `user-content-${uniqueSlug}`
373375

374-
// Collect TOC item with plain text (HTML stripped)
375-
const plainText = text.replace(/<[^>]*>/g, '').trim()
376+
// Collect TOC item with plain text (HTML stripped, entities decoded)
377+
const plainText = decodeHtmlEntities(text.replace(/<[^>]*>/g, '').trim())
376378
if (plainText) {
377379
toc.push({ text: plainText, id, depth })
378380
}
@@ -511,7 +513,7 @@ ${html}
511513
* provide the text of the element. This will automatically be removed, because there
512514
* is an allow list for link attributes.
513515
* */
514-
label: attribs['data-title-intermediate'] || provider.name,
516+
label: decodeHtmlEntities(attribs['data-title-intermediate'] || provider.name),
515517
})
516518
}
517519

shared/utils/html.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const htmlEntities: Record<string, string> = {
2+
'&amp;': '&',
3+
'&lt;': '<',
4+
'&gt;': '>',
5+
'&quot;': '"',
6+
'&#39;': "'",
7+
'&apos;': "'",
8+
'&nbsp;': ' ',
9+
}
10+
11+
export function decodeHtmlEntities(text: string): string {
12+
return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match)
13+
}

0 commit comments

Comments
 (0)