MCPcopy
hub / github.com/npmx-dev/npmx.dev / renderReadmeHtml

Function renderReadmeHtml

server/utils/readme.ts:475–786  ·  view source on GitHub ↗
(
  content: string,
  packageName: string,
  repoInfo?: RepositoryInfo,
)

Source from the content-addressed store, hash-verified

473}
474
475export async function renderReadmeHtml(
476 content: string,
477 packageName: string,
478 repoInfo?: RepositoryInfo,
479): Promise<ReadmeResponse> {
480 if (!content) return { html: '', playgroundLinks: [], toc: [] }
481
482 // Parse and strip YAML frontmatter, render as table if present
483 let markdownBody = content
484 let frontmatterHtml = ''
485 try {
486 const { data, content: body } = matter(content)
487 if (data && Object.keys(data).length > 0) {
488 frontmatterHtml = renderFrontmatterTable(data)
489 markdownBody = body
490 }
491 } catch {
492 // If frontmatter parsing fails, render the full content as-is
493 }
494
495 const shiki = await getShikiHighlighter()
496 const renderer = new marked.Renderer()
497
498 // Collect playground links during parsing
499 const collectedLinks: PlaygroundLink[] = []
500 const seenUrls = new Set<string>()
501
502 // Collect table of contents items during parsing
503 const toc: TocItem[] = []
504
505 // Track used heading slugs to handle duplicates (GitHub-style: foo, foo-1, foo-2)
506 const usedSlugs = new Map<string, number>()
507
508 // Track heading hierarchy to ensure sequential order for accessibility
509 // Page h1 = package name, h2 = "Readme" section heading
510 // So README starts at h3, and we ensure no levels are skipped
511 // Visual styling preserved via data-level attribute (original depth)
512 let lastSemanticLevel = 2 // Start after h2 (the "Readme" section heading)
513
514 // Shared heading processing for both markdown and HTML headings
515 function processHeading(
516 depth: number,
517 displayHtml: string,
518 plainText: string,
519 slugSource: string,
520 preservedAttrs = '',
521 ) {
522 const semanticLevel = calculateSemanticDepth(depth, lastSemanticLevel)
523 lastSemanticLevel = semanticLevel
524
525 let slug = slugify(slugSource)
526 if (!slug) slug = 'heading'
527
528 const count = usedSlugs.get(slug) ?? 0
529 usedSlugs.set(slug, count + 1)
530 const uniqueSlug = count === 0 ? slug : `${slug}-${count}`
531 const id = toUserContentId(uniqueSlug)
532

Callers 3

readme.spec.tsFile · 0.85
[...pkg].get.tsFile · 0.85
[...pkg].get.tsFile · 0.85

Calls 15

decodeHtmlEntitiesFunction · 0.90
stripHtmlTagsFunction · 0.90
highlightCodeSyncFunction · 0.90
escapeHtmlFunction · 0.90
convertToEmojiFunction · 0.90
renderFrontmatterTableFunction · 0.85
getShikiHighlighterFunction · 0.85
getHeadingPlainTextFunction · 0.85
getHeadingSlugSourceFunction · 0.85
processHeadingFunction · 0.85
extractHeadingAttrsFunction · 0.85
processLinkFunction · 0.85

Tested by

no test coverage detected