[READ-ONLY] a fast, modern browser for the npm registry
at main 114 lines 3.0 kB view raw
1/** 2 * Node Processing 3 * 4 * Functions for processing deno doc output: flattening namespaces, 5 * merging overloads, and building symbol lookups. 6 * 7 * @module server/utils/docs/processing 8 */ 9 10import type { DenoDocNode } from '#shared/types/deno-doc' 11import type { MergedSymbol, SymbolLookup } from './types' 12import { cleanSymbolName, createSymbolId } from './text' 13 14/** 15 * Flatten namespace elements into top-level nodes for easier display. 16 * Also filters out import/reference nodes that aren't useful for docs. 17 */ 18export function flattenNamespaces(nodes: DenoDocNode[]): DenoDocNode[] { 19 const result: DenoDocNode[] = [] 20 21 for (const node of nodes) { 22 // Skip internal nodes 23 if (node.kind === 'import' || node.kind === 'reference') { 24 continue 25 } 26 27 result.push(node) 28 29 // Inline namespace members with qualified names 30 if (node.kind === 'namespace' && node.namespaceDef?.elements) { 31 for (const element of node.namespaceDef.elements) { 32 result.push({ 33 ...element, 34 name: `${node.name}.${element.name}`, 35 }) 36 } 37 } 38 } 39 40 return result 41} 42 43/** 44 * Build a lookup table mapping symbol names to their HTML anchor IDs. 45 * Used for {@link} cross-references. 46 */ 47export function buildSymbolLookup(nodes: DenoDocNode[]): SymbolLookup { 48 const lookup = new Map<string, string>() 49 50 for (const node of nodes) { 51 const cleanName = cleanSymbolName(node.name) 52 const id = createSymbolId(node.kind, cleanName) 53 lookup.set(cleanName, id) 54 } 55 56 return lookup 57} 58 59/** 60 * Merge function/method overloads into single entries. 61 * 62 * TypeScript packages often export many overloads for the same function 63 * (e.g., React's `h` has 23 overloads). This groups them together. 64 */ 65export function mergeOverloads(nodes: DenoDocNode[]): MergedSymbol[] { 66 const byKey = new Map<string, DenoDocNode[]>() 67 68 for (const node of nodes) { 69 const cleanName = cleanSymbolName(node.name) 70 const key = `${node.kind}:${cleanName}` 71 const existing = byKey.get(key) 72 if (existing) { 73 existing.push(node) 74 } else { 75 byKey.set(key, [node]) 76 } 77 } 78 79 const result: MergedSymbol[] = [] 80 81 for (const [, groupedNodes] of byKey) { 82 const first = groupedNodes[0] 83 if (!first) continue // Should never happen, but defensive programming etc 84 85 // Use JSDoc from the best-documented overload 86 const withDoc = groupedNodes.find(n => n.jsDoc?.doc) ?? first 87 88 result.push({ 89 name: cleanSymbolName(first.name), 90 kind: first.kind, 91 nodes: groupedNodes, 92 jsDoc: withDoc.jsDoc, 93 }) 94 } 95 96 // Sort alphabetically 97 result.sort((a, b) => a.name.localeCompare(b.name)) 98 99 return result 100} 101 102/** 103 * Group merged symbols by their kind (function, class, etc.) 104 */ 105export function groupMergedByKind(symbols: MergedSymbol[]): Record<string, MergedSymbol[]> { 106 const grouped: Record<string, MergedSymbol[]> = {} 107 108 for (const sym of symbols) { 109 const kindGroup = (grouped[sym.kind] ??= []) 110 kindGroup.push(sym) 111 } 112 113 return grouped 114}