/** * @bfra.me/doc-sync/generators/api-reference-generator - API documentation table generation */ import type {ExportedFunction, ExportedType, PackageAPI} from '../types' export function generateAPIReference(api: PackageAPI): string { const sections: string[] = [] if (api.functions.length > 0) { sections.push('### Functions') sections.push('') sections.push(generateFunctionsTable(api.functions)) sections.push('') sections.push(generateFunctionDetails(api.functions)) } if (api.types.length > 0) { sections.push('### Types') sections.push('') sections.push(generateTypesTable(api.types)) sections.push('') sections.push(generateTypeDetails(api.types)) } return sections.join('\n') } function generateFunctionsTable(functions: readonly ExportedFunction[]): string { const lines: string[] = [] lines.push('| Function | Description |') lines.push('| -------- | ----------- |') for (const fn of functions) { const name = formatFunctionName(fn) const description = fn.jsdoc?.description ?? '' const firstLine = getFirstLine(description) lines.push(`| ${name} | ${escapeTableCell(firstLine)} |`) } return lines.join('\n') } function generateFunctionDetails(functions: readonly ExportedFunction[]): string { const sections: string[] = [] for (const fn of functions) { sections.push(generateFunctionDetail(fn)) } return sections.join('\n\n') } function generateFunctionDetail(fn: ExportedFunction): string { const lines: string[] = [] lines.push(`#### \`${fn.name}\``) lines.push('') if (fn.jsdoc?.description !== undefined) { lines.push(fn.jsdoc.description) lines.push('') } lines.push('```typescript') lines.push(fn.signature) lines.push('```') if (fn.parameters.length > 0) { lines.push('') lines.push('**Parameters:**') lines.push('') lines.push('| Name | Type | Description |') lines.push('| ---- | ---- | ----------- |') for (const param of fn.parameters) { const paramDoc = fn.jsdoc?.params?.find(p => p.name === param.name) const description = paramDoc?.description ?? '' const optional = param.optional ? ' (optional)' : '' lines.push( `| \`${param.name}\`${optional} | \`${escapeCode(param.type)}\` | ${escapeTableCell(description)} |`, ) } } if (fn.returnType !== 'void') { lines.push('') lines.push(`**Returns:** \`${escapeCode(fn.returnType)}\``) if (fn.jsdoc?.returns !== undefined) { lines.push('') lines.push(fn.jsdoc.returns) } } if (fn.jsdoc?.deprecated !== undefined) { lines.push('') lines.push(`:::caution[Deprecated]`) lines.push(fn.jsdoc.deprecated) lines.push(':::') } if (fn.jsdoc?.since !== undefined) { lines.push('') lines.push(`*Since: ${fn.jsdoc.since}*`) } return lines.join('\n') } function generateTypesTable(types: readonly ExportedType[]): string { const lines: string[] = [] lines.push('| Type | Kind | Description |') lines.push('| ---- | ---- | ----------- |') for (const type of types) { const name = `[\`${type.name}\`](#${type.name.toLowerCase()})` const kind = type.kind const description = type.jsdoc?.description ?? '' const firstLine = getFirstLine(description) lines.push(`| ${name} | ${kind} | ${escapeTableCell(firstLine)} |`) } return lines.join('\n') } function generateTypeDetails(types: readonly ExportedType[]): string { const sections: string[] = [] for (const type of types) { sections.push(generateTypeDetail(type)) } return sections.join('\n\n') } function generateTypeDetail(type: ExportedType): string { const lines: string[] = [] lines.push(`#### \`${type.name}\``) lines.push('') if (type.jsdoc?.description !== undefined) { lines.push(type.jsdoc.description) lines.push('') } lines.push('```typescript') lines.push(type.definition) lines.push('```') if (type.typeParameters !== undefined && type.typeParameters.length > 0) { lines.push('') lines.push(`**Type Parameters:** ${type.typeParameters.map(t => `\`${t}\``).join(', ')}`) } if (type.jsdoc?.deprecated !== undefined) { lines.push('') lines.push(`:::caution[Deprecated]`) lines.push(type.jsdoc.deprecated) lines.push(':::') } if (type.jsdoc?.since !== undefined) { lines.push('') lines.push(`*Since: ${type.jsdoc.since}*`) } return lines.join('\n') } function formatFunctionName(fn: ExportedFunction): string { const name = `[\`${fn.name}\`](#${fn.name.toLowerCase()})` const badges: string[] = [] if (fn.isAsync) { badges.push('') } if (fn.isGenerator) { badges.push('') } if (fn.isDefault) { badges.push('') } return badges.length > 0 ? `${name} ${badges.join(' ')}` : name } function getFirstLine(text: string): string { const firstLine = text.split('\n')[0]?.trim() ?? '' if (firstLine.length > 100) { const truncated = firstLine.slice(0, 97) const lastSpace = truncated.lastIndexOf(' ') if (lastSpace > 50) { return `${truncated.slice(0, lastSpace)}...` } return `${truncated}...` } return firstLine } function escapeTableCell(content: string): string { return content.replaceAll('|', String.raw`\|`).replaceAll('\n', ' ') } function escapeCode(code: string): string { return code.replaceAll('`', '\\`') } export function generateAPICompact(api: PackageAPI): string { const lines: string[] = [] if (api.functions.length > 0) { lines.push('**Functions:**') for (const fn of api.functions) { lines.push(`- \`${fn.name}(${formatParameterList(fn)})\` → \`${fn.returnType}\``) } } if (api.types.length > 0) { if (lines.length > 0) lines.push('') lines.push('**Types:**') for (const type of api.types) { lines.push(`- \`${type.name}\` (${type.kind})`) } } return lines.join('\n') } function formatParameterList(fn: ExportedFunction): string { return fn.parameters.map(p => (p.optional ? `${p.name}?` : p.name)).join(', ') } export function generateCategoryReference( functions: readonly ExportedFunction[], types: readonly ExportedType[], categoryName: string, ): string { const sections: string[] = [] sections.push(`### ${categoryName}`) sections.push('') if (functions.length > 0) { sections.push('#### Functions') sections.push('') sections.push(generateFunctionsTable(functions)) sections.push('') sections.push(generateFunctionDetails(functions)) } if (types.length > 0) { sections.push('#### Types') sections.push('') sections.push(generateTypesTable(types)) sections.push('') sections.push(generateTypeDetails(types)) } return sections.join('\n') }