forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1/**
2 * Signature Formatting
3 *
4 * Functions for formatting TypeScript signatures, parameters, and types.
5 *
6 * @module server/utils/docs/format
7 */
8
9import type { DenoDocNode, FunctionParam, TsType } from '#shared/types/deno-doc'
10import { cleanSymbolName, stripAnsi } from './text'
11
12/**
13 * Generate a TypeScript signature string for a node.
14 */
15export function getNodeSignature(node: DenoDocNode): string | null {
16 const name = cleanSymbolName(node.name)
17
18 switch (node.kind) {
19 case 'function': {
20 const typeParams = node.functionDef?.typeParams?.map(t => t.name).join(', ')
21 const typeParamsStr = typeParams ? `<${typeParams}>` : ''
22 const params = node.functionDef?.params?.map(p => formatParam(p)).join(', ') || ''
23 const ret = formatType(node.functionDef?.returnType) || 'void'
24 const asyncStr = node.functionDef?.isAsync ? 'async ' : ''
25 return `${asyncStr}function ${name}${typeParamsStr}(${params}): ${ret}`
26 }
27 case 'class': {
28 const ext = node.classDef?.extends ? ` extends ${formatType(node.classDef.extends)}` : ''
29 const impl = node.classDef?.implements?.map(t => formatType(t)).join(', ')
30 const implStr = impl ? ` implements ${impl}` : ''
31 const abstractStr = node.classDef?.isAbstract ? 'abstract ' : ''
32 return `${abstractStr}class ${name}${ext}${implStr}`
33 }
34 case 'interface': {
35 const typeParams = node.interfaceDef?.typeParams?.map(t => t.name).join(', ')
36 const typeParamsStr = typeParams ? `<${typeParams}>` : ''
37 const ext = node.interfaceDef?.extends?.map(t => formatType(t)).join(', ')
38 const extStr = ext ? ` extends ${ext}` : ''
39 return `interface ${name}${typeParamsStr}${extStr}`
40 }
41 case 'typeAlias': {
42 const typeParams = node.typeAliasDef?.typeParams?.map(t => t.name).join(', ')
43 const typeParamsStr = typeParams ? `<${typeParams}>` : ''
44 const type = formatType(node.typeAliasDef?.tsType) || 'unknown'
45 return `type ${name}${typeParamsStr} = ${type}`
46 }
47 case 'variable': {
48 const keyword = node.variableDef?.kind === 'const' ? 'const' : 'let'
49 const type = formatType(node.variableDef?.tsType) || 'unknown'
50 return `${keyword} ${name}: ${type}`
51 }
52 case 'enum': {
53 return `enum ${name}`
54 }
55 default:
56 return null
57 }
58}
59
60/**
61 * Format a function parameter.
62 */
63export function formatParam(param: FunctionParam): string {
64 const optional = param.optional ? '?' : ''
65 const type = formatType(param.tsType)
66 return type ? `${param.name}${optional}: ${type}` : `${param.name}${optional}`
67}
68
69/**
70 * Format a TypeScript type.
71 */
72export function formatType(type?: TsType): string {
73 if (!type) return ''
74
75 // Strip ANSI codes from repr (deno doc may include terminal colors since it's built for that)
76 if (type.repr) return stripAnsi(type.repr)
77
78 if (type.kind === 'keyword' && type.keyword) {
79 return type.keyword
80 }
81
82 if (type.kind === 'typeRef' && type.typeRef) {
83 const params = type.typeRef.typeParams?.map(t => formatType(t)).join(', ')
84 return params ? `${type.typeRef.typeName}<${params}>` : type.typeRef.typeName
85 }
86
87 if (type.kind === 'array' && type.array) {
88 return `${formatType(type.array)}[]`
89 }
90
91 if (type.kind === 'union' && type.union) {
92 return type.union.map(t => formatType(t)).join(' | ')
93 }
94
95 return type.repr ? stripAnsi(type.repr) : 'unknown'
96}