apisurf
Version:
Analyze API surface changes between npm package versions to catch breaking changes
198 lines (197 loc) • 7.41 kB
JavaScript
/**
* Formats inspect results as Markdown.
*/
export function formatInspectMarkdownOutput(result, verbose = false) {
const lines = [];
// Header
lines.push(`# API Surface Report: ${result.packageName}@${result.version}`);
lines.push('');
if (result.repositoryUrl) {
lines.push(`**Repository:** ${result.repositoryUrl}`);
lines.push('');
}
// Summary
lines.push('## Summary');
lines.push('');
lines.push(result.summary);
// Errors
if (result.errors && result.errors.length > 0) {
lines.push('');
lines.push('### ⚠️ Errors');
lines.push('');
result.errors.forEach(error => {
lines.push(`- ${error}`);
});
}
if (!result.success) {
return lines.join('\n');
}
// Table of Contents
if (result.apiSurfaces.size > 1) {
lines.push('');
lines.push('## Entry Points');
lines.push('');
for (const [entryPoint] of result.apiSurfaces) {
const displayName = entryPoint === 'main' ? 'Main Export' : entryPoint;
const anchor = entryPoint.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
lines.push(`- [${displayName}](#${anchor})`);
}
}
// API Surfaces
for (const [entryPoint, surface] of result.apiSurfaces) {
lines.push('');
const displayName = entryPoint === 'main' ? 'Main Export' : `Export: ${entryPoint}`;
lines.push(`## ${displayName}`);
lines.push('');
// Statistics
const stats = [];
if (surface.defaultExport)
stats.push('Default export');
if (surface.namedExports.size > 0)
stats.push(`${surface.namedExports.size} named exports`);
if (surface.typeOnlyExports.size > 0)
stats.push(`${surface.typeOnlyExports.size} type exports`);
if (surface.starExports.length > 0)
stats.push(`${surface.starExports.length} re-exports`);
if (stats.length > 0) {
lines.push(`**Stats:** ${stats.join(', ')}`);
lines.push('');
}
// Default export
if (surface.defaultExport) {
lines.push('### Default Export');
lines.push('');
lines.push('This module has a default export.');
lines.push('');
}
// Named exports
if (surface.namedExports.size > 0) {
lines.push('### Named Exports');
lines.push('');
if (verbose && surface.typeDefinitions) {
// Detailed view with type information
const exports = Array.from(surface.namedExports).sort();
exports.forEach(name => {
const typeDef = surface.typeDefinitions?.get(name);
if (typeDef) {
lines.push(`#### \`${name}\` (${typeDef.kind})`);
lines.push('');
lines.push(formatTypeDefinitionMarkdown(typeDef));
lines.push('');
}
else {
lines.push(`- \`${name}\``);
}
});
}
else {
// Simple list view
lines.push('| Export | Type |');
lines.push('|--------|------|');
const exports = Array.from(surface.namedExports).sort();
exports.forEach(name => {
const typeDef = surface.typeDefinitions?.get(name);
const type = typeDef ? typeDef.kind : 'unknown';
lines.push(`| \`${name}\` | ${type} |`);
});
lines.push('');
}
}
// Type-only exports
if (surface.typeOnlyExports.size > 0) {
lines.push('### Type Exports');
lines.push('');
if (verbose && surface.typeDefinitions) {
// Detailed view
const typeExports = Array.from(surface.typeOnlyExports).sort();
typeExports.forEach(name => {
const typeDef = surface.typeDefinitions?.get(name);
if (typeDef) {
lines.push(`#### \`${name}\` (${typeDef.kind})`);
lines.push('');
lines.push(formatTypeDefinitionMarkdown(typeDef));
lines.push('');
}
else {
lines.push(`- \`${name}\``);
}
});
}
else {
// Simple list view
lines.push('| Type | Kind |');
lines.push('|------|------|');
const typeExports = Array.from(surface.typeOnlyExports).sort();
typeExports.forEach(name => {
const typeDef = surface.typeDefinitions?.get(name);
const kind = typeDef ? typeDef.kind : 'unknown';
lines.push(`| \`${name}\` | ${kind} |`);
});
lines.push('');
}
}
// Star exports
if (surface.starExports.length > 0) {
lines.push('### Re-exports');
lines.push('');
lines.push('This module re-exports from the following modules:');
lines.push('');
surface.starExports.forEach(module => {
lines.push(`- \`${module}\``);
});
lines.push('');
}
}
return lines.join('\n');
}
/**
* Formats a type definition for Markdown output.
*/
function formatTypeDefinitionMarkdown(def) {
const lines = [];
if (def.signature) {
lines.push('```typescript');
lines.push(def.signature);
lines.push('```');
}
// Additional details based on kind
switch (def.kind) {
case 'interface':
case 'class':
if (def.properties && def.properties.size > 0) {
lines.push('');
lines.push('**Properties:**');
lines.push('');
lines.push('| Property | Type |');
lines.push('|----------|------|');
const props = Array.from(def.properties.entries()).sort((a, b) => a[0].localeCompare(b[0]));
props.forEach(([name, type]) => {
// Escape pipe characters in type strings
const escapedType = type.replace(/\|/g, '\\|');
lines.push(`| \`${name}\` | \`${escapedType}\` |`);
});
}
break;
case 'enum':
if (def.members && def.members.length > 0) {
lines.push('');
lines.push('**Members:**');
lines.push('');
def.members.forEach(member => {
lines.push(`- \`${member}\``);
});
}
break;
case 'function':
if (def.parameters && def.returnType) {
lines.push('');
lines.push('**Signature:**');
lines.push('');
lines.push('```typescript');
lines.push(`(${def.parameters.join(', ')}) => ${def.returnType}`);
lines.push('```');
}
break;
}
return lines.join('\n');
}