Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions app/components/Package/Playgrounds.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import type { PlaygroundLink } from '#shared/types'
import { decodeHtmlEntities } from '~/utils/formatters'

const props = defineProps<{
links: PlaygroundLink[]
Expand Down Expand Up @@ -130,7 +129,7 @@ function focusMenuItem(index: number) {
:class="[getIcon(firstLink.provider), getColor(firstLink.provider), 'w-4 h-4 shrink-0']"
aria-hidden="true"
/>
<span class="truncate text-fg-muted">{{ decodeHtmlEntities(firstLink.label) }}</span>
<span class="truncate text-fg-muted">{{ firstLink.label }}</span>
</a>
</TooltipApp>

Expand Down Expand Up @@ -186,7 +185,7 @@ function focusMenuItem(index: number) {
:class="[getIcon(link.provider), getColor(link.provider), 'w-4 h-4 shrink-0']"
aria-hidden="true"
/>
<span class="truncate">{{ decodeHtmlEntities(link.label) }}</span>
<span class="truncate">{{ link.label }}</span>
</a>
</TooltipApp>
</div>
Expand Down
7 changes: 3 additions & 4 deletions app/components/ReadmeTocDropdown.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import type { TocItem } from '#shared/types/readme'
import { onClickOutside, useEventListener } from '@vueuse/core'
import { decodeHtmlEntities } from '~/utils/formatters'

const props = defineProps<{
toc: TocItem[]
Expand Down Expand Up @@ -202,7 +201,7 @@ function handleKeydown(event: KeyboardEvent) {
@click="select()"
@mouseenter="highlightedIndex = getIndex(node.id)"
>
<span class="truncate">{{ decodeHtmlEntities(node.text) }}</span>
<span class="truncate">{{ node.text }}</span>
</NuxtLink>

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

<NuxtLink
Expand All @@ -241,7 +240,7 @@ function handleKeydown(event: KeyboardEvent) {
@click="select()"
@mouseenter="highlightedIndex = getIndex(grandchild.id)"
>
<span class="truncate">{{ decodeHtmlEntities(grandchild.text) }}</span>
<span class="truncate">{{ grandchild.text }}</span>
</NuxtLink>
</template>
</template>
Expand Down
2 changes: 1 addition & 1 deletion app/composables/useMarkdown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { decodeHtmlEntities } from '~/utils/formatters'
import { decodeHtmlEntities } from '#shared/utils/html'

interface UseMarkdownOptions {
text: string
Expand Down
14 changes: 0 additions & 14 deletions app/utils/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,3 @@ export function toIsoDateString(date: Date): string {
const day = String(date.getUTCDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}

const htmlEntities: Record<string, string> = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
'&apos;': "'",
'&nbsp;': ' ',
}

export function decodeHtmlEntities(text: string): string {
return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match)
}
2 changes: 1 addition & 1 deletion server/api/registry/readme/[...pkg].get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineCachedEventHandler(
swr: true,
getKey: event => {
const pkg = getRouterParam(event, 'pkg') ?? ''
return `readme:v8:${pkg.replace(/\/+$/, '').trim()}`
return `readme:v9:${pkg.replace(/\/+$/, '').trim()}`
},
},
)
10 changes: 6 additions & 4 deletions server/utils/readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import { hasProtocol } from 'ufo'
import type { ReadmeResponse, TocItem } from '#shared/types/readme'
import { convertBlobOrFileToRawUrl, type RepositoryInfo } from '#shared/utils/git-providers'
import { highlightCodeSync } from './shiki'
import { decodeHtmlEntities } from '#shared/utils/html'
import { convertToEmoji } from '#shared/utils/emoji'

import { highlightCodeSync } from './shiki'

/**
* Playground provider configuration
*/
Expand Down Expand Up @@ -371,8 +373,8 @@
// (e.g., #install, #dependencies, #versions are used by the package page)
const id = `user-content-${uniqueSlug}`

// Collect TOC item with plain text (HTML stripped)
const plainText = text.replace(/<[^>]*>/g, '').trim()
// Collect TOC item with plain text (HTML stripped, entities decoded)
const plainText = decodeHtmlEntities(text.replace(/<[^>]*>/g, '').trim())
Comment thread Fixed
if (plainText) {
toc.push({ text: plainText, id, depth })
}
Expand Down Expand Up @@ -511,7 +513,7 @@
* provide the text of the element. This will automatically be removed, because there
* is an allow list for link attributes.
* */
label: attribs['data-title-intermediate'] || provider.name,
label: decodeHtmlEntities(attribs['data-title-intermediate'] || provider.name),
})
}

Expand Down
13 changes: 13 additions & 0 deletions shared/utils/html.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const htmlEntities: Record<string, string> = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
'&apos;': "'",
'&nbsp;': ' ',
}

export function decodeHtmlEntities(text: string): string {
return text.replace(/&(?:amp|lt|gt|quot|apos|nbsp|#39);/g, match => htmlEntities[match] || match)
}
Loading