1- <script setup lang="ts">
21import { decodeHtmlEntities } from '~/utils/formatters'
32
4- const props = defineProps < {
3+ interface UseMarkdownOptions {
54 text : string
65 /** When true, renders link text without the anchor tag (useful when inside another link) */
76 plain ?: boolean
87 /** Package name to strip from the beginning of the description (if present) */
98 packageName ?: string
10- }>()
9+ }
10+
11+ /** @public */
12+ export function useMarkdown ( options : MaybeRefOrGetter < UseMarkdownOptions > ) {
13+ return computed ( ( ) => parseMarkdown ( toValue ( options ) ) )
14+ }
1115
1216// Strip markdown image badges from text
1317function stripMarkdownImages ( text : string ) : string {
@@ -22,7 +26,7 @@ function stripMarkdownImages(text: string): string {
2226}
2327
2428// Strip HTML tags and escape remaining HTML to prevent XSS
25- function stripAndEscapeHtml(text : string ): string {
29+ function stripAndEscapeHtml ( text : string , packageName ?: string ) : string {
2630 // First decode any HTML entities in the input
2731 let stripped = decodeHtmlEntities ( text )
2832
@@ -33,13 +37,13 @@ function stripAndEscapeHtml(text: string): string {
3337 // Only match tags that start with a letter or / (to avoid matching things like "a < b > c")
3438 stripped = stripped . replace ( / < \/ ? [ a - z ] [ ^ > ] * > / gi, '' )
3539
36- if (props . packageName ) {
40+ if ( packageName ) {
3741 // Trim first to handle leading/trailing whitespace from stripped HTML
3842 stripped = stripped . trim ( )
3943 // Collapse multiple whitespace into single space
4044 stripped = stripped . replace ( / \s + / g, ' ' )
4145 // Escape special regex characters in package name
42- const escapedName = props . packageName .replace (/ [. *+?^${}()|[\]\\ ] / g , ' \\ $&' )
46+ const escapedName = packageName . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' )
4347 // Match package name at the start, optionally followed by: space, dash, colon, hyphen, or just space
4448 const namePattern = new RegExp ( `^${ escapedName } \\s*[-:—]?\\s*` , 'i' )
4549 stripped = stripped . replace ( namePattern , '' ) . trim ( )
@@ -55,11 +59,11 @@ function stripAndEscapeHtml(text: string): string {
5559}
5660
5761// Parse simple inline markdown to HTML
58- function parseMarkdown(text : string ): string {
62+ function parseMarkdown ( { text, packageName , plain } : UseMarkdownOptions ) : string {
5963 if ( ! text ) return ''
6064
6165 // First strip HTML tags and escape remaining HTML
62- let html = stripAndEscapeHtml (text )
66+ let html = stripAndEscapeHtml ( text , packageName )
6367
6468 // Bold: **text** or __text__
6569 html = html . replace ( / \* \* ( .+ ?) \* \* / g, '<strong>$1</strong>' )
@@ -78,7 +82,7 @@ function parseMarkdown(text: string): string {
7882 // Links: [text](url) - only allow https, mailto
7983 html = html . replace ( / \[ ( [ ^ \] ] + ) \] \( ( [ ^ ) ] + ) \) / g, ( _match , text , url ) => {
8084 // In plain mode, just render the link text without the anchor
81- if (props . plain ) {
85+ if ( plain ) {
8286 return text
8387 }
8488 const decodedUrl = url . replace ( / & a m p ; / g, '&' )
@@ -94,11 +98,3 @@ function parseMarkdown(text: string): string {
9498
9599 return html
96100}
97-
98- const html = computed (() => parseMarkdown (props .text ))
99- </script >
100-
101- <template >
102- <!-- eslint-disable-next-line vue/no-v-html -->
103- <span v-html =" html" />
104- </template >
0 commit comments