mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-02-12 15:52:47 +00:00
1262 lines
43 KiB
TypeScript
1262 lines
43 KiB
TypeScript
/**
|
|
* API Documentation Generator
|
|
* Parses TypeScript source files using the TypeScript Compiler API
|
|
* and generates markdown documentation from types, interfaces, functions, and classes.
|
|
*/
|
|
|
|
import * as ts from 'typescript';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname } from 'path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
interface DocEntry {
|
|
name: string;
|
|
kind: 'interface' | 'type' | 'function' | 'class' | 'method' | 'property' | 'variable';
|
|
description?: string;
|
|
signature?: string;
|
|
parameters?: Array<{ name: string; type: string; description?: string; optional?: boolean; defaultValue?: string }>;
|
|
returnType?: string;
|
|
returnDescription?: string;
|
|
properties?: DocEntry[];
|
|
methods?: DocEntry[];
|
|
examples?: string[];
|
|
tags?: Record<string, string>;
|
|
exported?: boolean;
|
|
}
|
|
|
|
interface ModuleDoc {
|
|
name: string;
|
|
description?: string;
|
|
exports: DocEntry[];
|
|
}
|
|
|
|
function getJSDocComment(node: ts.Node, sourceFile: ts.SourceFile): { description?: string; tags: Record<string, string>; examples: string[] } {
|
|
const tags: Record<string, string> = {};
|
|
const examples: string[] = [];
|
|
let description: string | undefined;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TypeScript AST doesn't expose jsDoc in types
|
|
const jsDocNodes = (node as any).jsDoc as ts.JSDoc[] | undefined;
|
|
if (jsDocNodes && jsDocNodes.length > 0) {
|
|
const jsDoc = jsDocNodes[0];
|
|
|
|
if (jsDoc.comment) {
|
|
description = typeof jsDoc.comment === 'string'
|
|
? jsDoc.comment
|
|
: jsDoc.comment.map(c => c.text || '').join('');
|
|
}
|
|
|
|
if (jsDoc.tags) {
|
|
for (const tag of jsDoc.tags) {
|
|
const tagName = tag.tagName.text;
|
|
let tagComment = '';
|
|
|
|
if (tag.comment) {
|
|
tagComment = typeof tag.comment === 'string'
|
|
? tag.comment
|
|
: tag.comment.map(c => c.text || '').join('');
|
|
}
|
|
|
|
if (tagName === 'example') {
|
|
examples.push(tagComment);
|
|
} else if (tagName === 'param' && ts.isJSDocParameterTag(tag)) {
|
|
const paramName = tag.name.getText(sourceFile);
|
|
tags[`param:${paramName}`] = tagComment;
|
|
} else if (tagName === 'returns' || tagName === 'return') {
|
|
tags['returns'] = tagComment;
|
|
} else {
|
|
tags[tagName] = tagComment;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { description, tags, examples };
|
|
}
|
|
|
|
function getTypeString(type: ts.TypeNode | undefined, sourceFile: ts.SourceFile): string {
|
|
if (!type) return 'any';
|
|
return type.getText(sourceFile);
|
|
}
|
|
|
|
function parseFunction(node: ts.FunctionDeclaration | ts.MethodDeclaration | ts.ArrowFunction, sourceFile: ts.SourceFile, checker: ts.TypeChecker): DocEntry {
|
|
const { description, tags, examples } = getJSDocComment(node, sourceFile);
|
|
|
|
const name = ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)
|
|
? node.name?.getText(sourceFile) || 'anonymous'
|
|
: 'anonymous';
|
|
|
|
const parameters: Array<{ name: string; type: string; description?: string; optional?: boolean; defaultValue?: string; isRest?: boolean }> = [];
|
|
|
|
for (const param of node.parameters) {
|
|
const paramName = param.name.getText(sourceFile);
|
|
const paramType = getTypeString(param.type, sourceFile);
|
|
const optional = !!param.questionToken || !!param.initializer;
|
|
const defaultValue = param.initializer?.getText(sourceFile);
|
|
const paramDescription = tags[`param:${paramName}`];
|
|
const isRest = !!param.dotDotDotToken;
|
|
|
|
parameters.push({
|
|
name: paramName,
|
|
type: paramType,
|
|
description: paramDescription,
|
|
optional,
|
|
defaultValue,
|
|
isRest,
|
|
});
|
|
}
|
|
|
|
const returnType = getTypeString(node.type, sourceFile);
|
|
|
|
// Build signature - handle rest parameters with ... prefix
|
|
const paramSignature = parameters
|
|
.map(p => `${p.isRest ? '...' : ''}${p.name}${p.optional ? '?' : ''}: ${p.type}`)
|
|
.join(', ');
|
|
const signature = `${name}(${paramSignature}): ${returnType}`;
|
|
|
|
return {
|
|
name,
|
|
kind: ts.isMethodDeclaration(node) ? 'method' : 'function',
|
|
description,
|
|
signature,
|
|
parameters,
|
|
returnType,
|
|
returnDescription: tags['returns'],
|
|
examples,
|
|
tags,
|
|
};
|
|
}
|
|
|
|
function parseInterface(node: ts.InterfaceDeclaration, sourceFile: ts.SourceFile, checker: ts.TypeChecker): DocEntry {
|
|
const { description, tags, examples } = getJSDocComment(node, sourceFile);
|
|
const name = node.name.getText(sourceFile);
|
|
|
|
const properties: DocEntry[] = [];
|
|
|
|
for (const member of node.members) {
|
|
if (ts.isPropertySignature(member)) {
|
|
const propName = member.name.getText(sourceFile);
|
|
const propType = getTypeString(member.type, sourceFile);
|
|
const optional = !!member.questionToken;
|
|
const { description: propDesc } = getJSDocComment(member, sourceFile);
|
|
|
|
properties.push({
|
|
name: propName,
|
|
kind: 'property',
|
|
description: propDesc,
|
|
signature: `${propName}${optional ? '?' : ''}: ${propType}`,
|
|
});
|
|
} else if (ts.isMethodSignature(member)) {
|
|
const methodName = member.name.getText(sourceFile);
|
|
const params = member.parameters.map(p => {
|
|
const pName = p.name.getText(sourceFile);
|
|
const pType = getTypeString(p.type, sourceFile);
|
|
const pOptional = !!p.questionToken;
|
|
return `${pName}${pOptional ? '?' : ''}: ${pType}`;
|
|
}).join(', ');
|
|
const returnType = getTypeString(member.type, sourceFile);
|
|
const { description: methodDesc } = getJSDocComment(member, sourceFile);
|
|
|
|
properties.push({
|
|
name: methodName,
|
|
kind: 'method',
|
|
description: methodDesc,
|
|
signature: `${methodName}(${params}): ${returnType}`,
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
name,
|
|
kind: 'interface',
|
|
description,
|
|
properties,
|
|
examples,
|
|
tags,
|
|
};
|
|
}
|
|
|
|
function parseTypeAlias(node: ts.TypeAliasDeclaration, sourceFile: ts.SourceFile, checker: ts.TypeChecker): DocEntry {
|
|
const { description, tags, examples } = getJSDocComment(node, sourceFile);
|
|
const name = node.name.getText(sourceFile);
|
|
const typeText = node.type.getText(sourceFile);
|
|
|
|
return {
|
|
name,
|
|
kind: 'type',
|
|
description,
|
|
signature: `type ${name} = ${typeText}`,
|
|
examples,
|
|
tags,
|
|
};
|
|
}
|
|
|
|
function parseClass(node: ts.ClassDeclaration, sourceFile: ts.SourceFile, checker: ts.TypeChecker): DocEntry {
|
|
const { description, tags, examples } = getJSDocComment(node, sourceFile);
|
|
const name = node.name?.getText(sourceFile) || 'AnonymousClass';
|
|
|
|
const methods: DocEntry[] = [];
|
|
const properties: DocEntry[] = [];
|
|
|
|
for (const member of node.members) {
|
|
// Skip private members
|
|
const modifiers = ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined;
|
|
const isPrivate = modifiers?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword);
|
|
if (isPrivate) continue;
|
|
|
|
if (ts.isMethodDeclaration(member)) {
|
|
const methodDoc = parseFunction(member, sourceFile, checker);
|
|
// Only include public methods (not starting with _)
|
|
if (!methodDoc.name.startsWith('_')) {
|
|
methods.push(methodDoc);
|
|
}
|
|
} else if (ts.isPropertyDeclaration(member)) {
|
|
const propName = member.name.getText(sourceFile);
|
|
if (!propName.startsWith('_')) {
|
|
const propType = getTypeString(member.type, sourceFile);
|
|
const { description: propDesc } = getJSDocComment(member, sourceFile);
|
|
properties.push({
|
|
name: propName,
|
|
kind: 'property',
|
|
description: propDesc,
|
|
signature: `${propName}: ${propType}`,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
name,
|
|
kind: 'class',
|
|
description,
|
|
methods,
|
|
properties,
|
|
examples,
|
|
tags,
|
|
};
|
|
}
|
|
|
|
function parseVariableStatement(node: ts.VariableStatement, sourceFile: ts.SourceFile, checker: ts.TypeChecker): DocEntry[] {
|
|
const { description, tags, examples } = getJSDocComment(node, sourceFile);
|
|
const entries: DocEntry[] = [];
|
|
|
|
for (const decl of node.declarationList.declarations) {
|
|
const name = decl.name.getText(sourceFile);
|
|
let signature = name;
|
|
|
|
if (decl.type) {
|
|
signature += `: ${decl.type.getText(sourceFile)}`;
|
|
} else if (decl.initializer) {
|
|
// Try to infer from initializer
|
|
const initText = decl.initializer.getText(sourceFile);
|
|
if (initText.length < 100) {
|
|
signature += ` = ${initText}`;
|
|
}
|
|
}
|
|
|
|
// Parse object literal properties (methods) for objects like `templates`
|
|
const methods: DocEntry[] = [];
|
|
if (decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
|
|
for (const prop of decl.initializer.properties) {
|
|
if (ts.isPropertyAssignment(prop) && prop.name) {
|
|
const propName = prop.name.getText(sourceFile);
|
|
const { description: propDesc, tags: propTags, examples: propExamples } = getJSDocComment(prop, sourceFile);
|
|
|
|
// Handle arrow functions and function expressions
|
|
if (ts.isArrowFunction(prop.initializer) || ts.isFunctionExpression(prop.initializer)) {
|
|
const fn = prop.initializer;
|
|
const params: Array<{ name: string; type: string; description?: string; optional?: boolean; defaultValue?: string }> = [];
|
|
|
|
for (const param of fn.parameters) {
|
|
const paramName = param.name.getText(sourceFile);
|
|
const paramType = getTypeString(param.type, sourceFile);
|
|
const optional = !!param.questionToken || !!param.initializer;
|
|
const defaultValue = param.initializer?.getText(sourceFile);
|
|
|
|
params.push({
|
|
name: paramName,
|
|
type: paramType,
|
|
description: propTags[`param:${paramName}`],
|
|
optional,
|
|
defaultValue,
|
|
});
|
|
}
|
|
|
|
const returnType = getTypeString(fn.type, sourceFile);
|
|
const paramSignature = params
|
|
.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type}`)
|
|
.join(', ');
|
|
const methodSignature = `${propName}(${paramSignature}): ${returnType}`;
|
|
|
|
methods.push({
|
|
name: propName,
|
|
kind: 'method',
|
|
description: propDesc,
|
|
signature: methodSignature,
|
|
parameters: params,
|
|
returnType,
|
|
examples: propExamples,
|
|
tags: propTags,
|
|
});
|
|
}
|
|
} else if (ts.isMethodDeclaration(prop) && prop.name) {
|
|
const methodDoc = parseFunction(prop, sourceFile, checker);
|
|
methods.push(methodDoc);
|
|
}
|
|
}
|
|
}
|
|
|
|
entries.push({
|
|
name,
|
|
kind: 'variable',
|
|
description,
|
|
signature,
|
|
examples,
|
|
tags,
|
|
methods: methods.length > 0 ? methods : undefined,
|
|
});
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
function parseSourceFile(filePath: string, program: ts.Program): ModuleDoc {
|
|
const sourceFile = program.getSourceFile(filePath);
|
|
if (!sourceFile) {
|
|
throw new Error(`Could not find source file: ${filePath}`);
|
|
}
|
|
|
|
const checker = program.getTypeChecker();
|
|
const exports: DocEntry[] = [];
|
|
|
|
// Get module-level JSDoc if any
|
|
let moduleDescription: string | undefined;
|
|
const firstStatement = sourceFile.statements[0];
|
|
if (firstStatement) {
|
|
const { description } = getJSDocComment(firstStatement, sourceFile);
|
|
if (description && description.includes('@module')) {
|
|
moduleDescription = description;
|
|
}
|
|
}
|
|
|
|
// Check for leading comment as module description
|
|
const fullText = sourceFile.getFullText();
|
|
const leadingCommentMatch = fullText.match(/^\/\*\*\s*\n([^*]|\*(?!\/))*\*\//);
|
|
if (leadingCommentMatch) {
|
|
const comment = leadingCommentMatch[0];
|
|
const lines = comment.split('\n').slice(1, -1).map(l => l.replace(/^\s*\*\s?/, ''));
|
|
moduleDescription = lines.join('\n').trim();
|
|
}
|
|
|
|
function visit(node: ts.Node) {
|
|
// Check if node is exported
|
|
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
const isExported = modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
|
|
if (ts.isFunctionDeclaration(node) && isExported && node.name) {
|
|
const doc = parseFunction(node, sourceFile!, checker);
|
|
doc.exported = true;
|
|
exports.push(doc);
|
|
} else if (ts.isInterfaceDeclaration(node) && isExported) {
|
|
const doc = parseInterface(node, sourceFile!, checker);
|
|
doc.exported = true;
|
|
exports.push(doc);
|
|
} else if (ts.isTypeAliasDeclaration(node) && isExported) {
|
|
const doc = parseTypeAlias(node, sourceFile!, checker);
|
|
doc.exported = true;
|
|
exports.push(doc);
|
|
} else if (ts.isClassDeclaration(node) && isExported && node.name) {
|
|
const doc = parseClass(node, sourceFile!, checker);
|
|
doc.exported = true;
|
|
exports.push(doc);
|
|
} else if (ts.isVariableStatement(node) && isExported) {
|
|
const docs = parseVariableStatement(node, sourceFile!, checker);
|
|
docs.forEach(d => {
|
|
d.exported = true;
|
|
exports.push(d);
|
|
});
|
|
}
|
|
|
|
ts.forEachChild(node, visit);
|
|
}
|
|
|
|
visit(sourceFile);
|
|
|
|
const moduleName = path.basename(filePath, '.ts');
|
|
return {
|
|
name: moduleName,
|
|
description: moduleDescription,
|
|
exports,
|
|
};
|
|
}
|
|
|
|
function generateMarkdown(modules: ModuleDoc[]): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push('# API Reference\n');
|
|
lines.push('> Auto-generated from TypeScript source files\n');
|
|
|
|
// Table of contents
|
|
lines.push('## Table of Contents\n');
|
|
for (const mod of modules) {
|
|
lines.push(`- [${mod.name}](#${mod.name.toLowerCase()})`);
|
|
for (const exp of mod.exports) {
|
|
if (exp.kind === 'function' || exp.kind === 'class') {
|
|
lines.push(` - [${exp.name}](#${exp.name.toLowerCase()})`);
|
|
}
|
|
}
|
|
}
|
|
lines.push('');
|
|
|
|
// Module documentation
|
|
for (const mod of modules) {
|
|
lines.push(`---\n`);
|
|
lines.push(`## ${mod.name}\n`);
|
|
|
|
if (mod.description) {
|
|
lines.push(`${mod.description}\n`);
|
|
}
|
|
|
|
// Group by kind
|
|
const interfaces = mod.exports.filter(e => e.kind === 'interface');
|
|
const types = mod.exports.filter(e => e.kind === 'type');
|
|
const functions = mod.exports.filter(e => e.kind === 'function');
|
|
const classes = mod.exports.filter(e => e.kind === 'class');
|
|
const variables = mod.exports.filter(e => e.kind === 'variable');
|
|
|
|
// Types
|
|
if (types.length > 0) {
|
|
lines.push(`### Types\n`);
|
|
for (const t of types) {
|
|
lines.push(`#### \`${t.name}\`\n`);
|
|
if (t.description) {
|
|
lines.push(`${t.description}\n`);
|
|
}
|
|
lines.push('```typescript');
|
|
lines.push(t.signature || '');
|
|
lines.push('```\n');
|
|
}
|
|
}
|
|
|
|
// Interfaces
|
|
if (interfaces.length > 0) {
|
|
lines.push(`### Interfaces\n`);
|
|
for (const iface of interfaces) {
|
|
lines.push(`#### \`${iface.name}\`\n`);
|
|
if (iface.description) {
|
|
lines.push(`${iface.description}\n`);
|
|
}
|
|
if (iface.properties && iface.properties.length > 0) {
|
|
lines.push('| Property | Type | Description |');
|
|
lines.push('|----------|------|-------------|');
|
|
for (const prop of iface.properties) {
|
|
const sig = prop.signature || prop.name;
|
|
const typeMatch = sig.match(/:\s*(.+)$/);
|
|
const type = typeMatch ? typeMatch[1] : 'unknown';
|
|
lines.push(`| \`${prop.name}\` | \`${type}\` | ${prop.description || '-'} |`);
|
|
}
|
|
lines.push('');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Classes
|
|
if (classes.length > 0) {
|
|
lines.push(`### Classes\n`);
|
|
for (const cls of classes) {
|
|
lines.push(`#### \`${cls.name}\`\n`);
|
|
if (cls.description) {
|
|
lines.push(`${cls.description}\n`);
|
|
}
|
|
|
|
if (cls.methods && cls.methods.length > 0) {
|
|
lines.push('**Methods:**\n');
|
|
lines.push('| Method | Description |');
|
|
lines.push('|--------|-------------|');
|
|
for (const method of cls.methods) {
|
|
const sig = method.signature?.replace(/\n/g, ' ') || method.name;
|
|
lines.push(`| \`${sig}\` | ${method.description || '-'} |`);
|
|
}
|
|
lines.push('');
|
|
|
|
// Detailed method docs
|
|
for (const method of cls.methods) {
|
|
lines.push(`##### \`${method.name}()\`\n`);
|
|
if (method.description) {
|
|
lines.push(`${method.description}\n`);
|
|
}
|
|
if (method.signature) {
|
|
lines.push('```typescript');
|
|
lines.push(method.signature);
|
|
lines.push('```\n');
|
|
}
|
|
if (method.parameters && method.parameters.length > 0) {
|
|
lines.push('**Parameters:**\n');
|
|
for (const param of method.parameters) {
|
|
const opt = param.optional ? ' (optional)' : '';
|
|
const def = param.defaultValue ? ` = \`${param.defaultValue}\`` : '';
|
|
lines.push(`- \`${param.name}\`: \`${param.type}\`${opt}${def}${param.description ? ` - ${param.description}` : ''}`);
|
|
}
|
|
lines.push('');
|
|
}
|
|
if (method.returnType && method.returnType !== 'void') {
|
|
lines.push(`**Returns:** \`${method.returnType}\`${method.returnDescription ? ` - ${method.returnDescription}` : ''}\n`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Functions
|
|
if (functions.length > 0) {
|
|
lines.push(`### Functions\n`);
|
|
for (const fn of functions) {
|
|
lines.push(`#### \`${fn.name}()\`\n`);
|
|
if (fn.description) {
|
|
lines.push(`${fn.description}\n`);
|
|
}
|
|
if (fn.signature) {
|
|
lines.push('```typescript');
|
|
lines.push(fn.signature);
|
|
lines.push('```\n');
|
|
}
|
|
if (fn.parameters && fn.parameters.length > 0) {
|
|
lines.push('**Parameters:**\n');
|
|
for (const param of fn.parameters) {
|
|
const opt = param.optional ? ' (optional)' : '';
|
|
const def = param.defaultValue ? ` = \`${param.defaultValue}\`` : '';
|
|
lines.push(`- \`${param.name}\`: \`${param.type}\`${opt}${def}${param.description ? ` - ${param.description}` : ''}`);
|
|
}
|
|
lines.push('');
|
|
}
|
|
if (fn.returnType && fn.returnType !== 'void') {
|
|
lines.push(`**Returns:** \`${fn.returnType}\`${fn.returnDescription ? ` - ${fn.returnDescription}` : ''}\n`);
|
|
}
|
|
if (fn.examples && fn.examples.length > 0) {
|
|
lines.push('**Example:**\n');
|
|
for (const example of fn.examples) {
|
|
lines.push('```typescript');
|
|
lines.push(example.trim());
|
|
lines.push('```\n');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Variables/Constants
|
|
if (variables.length > 0) {
|
|
lines.push(`### Constants\n`);
|
|
for (const v of variables) {
|
|
lines.push(`#### \`${v.name}\`\n`);
|
|
if (v.description) {
|
|
lines.push(`${v.description}\n`);
|
|
}
|
|
if (v.signature) {
|
|
lines.push('```typescript');
|
|
lines.push(v.signature);
|
|
lines.push('```\n');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// Generate TypeScript file for IDE sidebar
|
|
function generateSidebarTS(modules: ModuleDoc[]): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push('/**');
|
|
lines.push(' * Auto-generated API documentation for IDE sidebar');
|
|
lines.push(' * Generated from TypeScript source files via reflection');
|
|
lines.push(' * DO NOT EDIT MANUALLY - run `npm run docs:generate` to regenerate');
|
|
lines.push(' */');
|
|
lines.push('');
|
|
lines.push('export interface ApiSection {');
|
|
lines.push(' name: string;');
|
|
lines.push(' items: ApiItem[];');
|
|
lines.push('}');
|
|
lines.push('');
|
|
lines.push('export interface ApiItem {');
|
|
lines.push(' name: string;');
|
|
lines.push(' type: "function" | "class" | "type" | "interface" | "const" | "method";');
|
|
lines.push(' signature?: string;');
|
|
lines.push(' description?: string;');
|
|
lines.push(' example?: string;');
|
|
lines.push(' returns?: string;');
|
|
lines.push(' params?: { name: string; type: string; description?: string }[];');
|
|
lines.push('}');
|
|
lines.push('');
|
|
lines.push('export const API_DOCS: ApiSection[] = [');
|
|
|
|
// Group modules by category for better organization
|
|
const moduleGroups: Record<string, ModuleDoc[]> = {
|
|
'Text Prompts': [],
|
|
'Chat Prompts': [],
|
|
'Image Prompts': [],
|
|
'Video Prompts': [],
|
|
'Audio Prompts': [],
|
|
'Variables': [],
|
|
'Similarity': [],
|
|
'Quality': [],
|
|
'Parser': [],
|
|
};
|
|
|
|
for (const mod of modules) {
|
|
if (mod.name.includes('variables')) {
|
|
moduleGroups['Variables'].push(mod);
|
|
} else if (mod.name.includes('similarity')) {
|
|
moduleGroups['Similarity'].push(mod);
|
|
} else if (mod.name.includes('quality')) {
|
|
moduleGroups['Quality'].push(mod);
|
|
} else if (mod.name.includes('parser')) {
|
|
moduleGroups['Parser'].push(mod);
|
|
} else if (mod.name.includes('chat')) {
|
|
moduleGroups['Chat Prompts'].push(mod);
|
|
} else if (mod.name.includes('media') || mod.name.includes('image')) {
|
|
moduleGroups['Image Prompts'].push(mod);
|
|
} else if (mod.name.includes('video')) {
|
|
moduleGroups['Video Prompts'].push(mod);
|
|
} else if (mod.name.includes('audio')) {
|
|
moduleGroups['Audio Prompts'].push(mod);
|
|
} else if (mod.name.includes('builder')) {
|
|
moduleGroups['Text Prompts'].push(mod);
|
|
}
|
|
}
|
|
|
|
// Process each group
|
|
for (const [groupName, groupModules] of Object.entries(moduleGroups)) {
|
|
if (groupModules.length === 0) continue;
|
|
|
|
const items: string[] = [];
|
|
|
|
for (const mod of groupModules) {
|
|
// Add functions
|
|
const functions = mod.exports.filter(e => e.kind === 'function');
|
|
for (const fn of functions) {
|
|
const params = fn.parameters?.map(p => ({
|
|
name: p.name,
|
|
type: p.type,
|
|
description: p.description || undefined,
|
|
})) || [];
|
|
|
|
items.push(` {
|
|
name: "${fn.name}()",
|
|
type: "function",
|
|
signature: ${JSON.stringify(fn.signature || fn.name)},
|
|
description: ${JSON.stringify(fn.description || '')},
|
|
returns: ${JSON.stringify(fn.returnType || '')},
|
|
params: ${JSON.stringify(params)},
|
|
}`);
|
|
}
|
|
|
|
// Add classes with their methods
|
|
const classes = mod.exports.filter(e => e.kind === 'class');
|
|
for (const cls of classes) {
|
|
// Add the class itself
|
|
items.push(` {
|
|
name: "${cls.name}",
|
|
type: "class",
|
|
description: ${JSON.stringify(cls.description || '')},
|
|
}`);
|
|
|
|
// Add class methods
|
|
if (cls.methods) {
|
|
for (const method of cls.methods) {
|
|
const params = method.parameters?.map(p => ({
|
|
name: p.name,
|
|
type: p.type,
|
|
description: p.description || undefined,
|
|
})) || [];
|
|
|
|
items.push(` {
|
|
name: ".${method.name}()",
|
|
type: "method",
|
|
signature: ${JSON.stringify(method.signature || method.name)},
|
|
description: ${JSON.stringify(method.description || '')},
|
|
returns: ${JSON.stringify(method.returnType || '')},
|
|
params: ${JSON.stringify(params)},
|
|
}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add interfaces
|
|
const interfaces = mod.exports.filter(e => e.kind === 'interface');
|
|
for (const iface of interfaces) {
|
|
items.push(` {
|
|
name: "${iface.name}",
|
|
type: "interface",
|
|
description: ${JSON.stringify(iface.description || '')},
|
|
}`);
|
|
}
|
|
|
|
// Add types
|
|
const types = mod.exports.filter(e => e.kind === 'type');
|
|
for (const t of types) {
|
|
items.push(` {
|
|
name: "${t.name}",
|
|
type: "type",
|
|
signature: ${JSON.stringify(t.signature || '')},
|
|
description: ${JSON.stringify(t.description || '')},
|
|
}`);
|
|
}
|
|
|
|
// Add constants/variables
|
|
const vars = mod.exports.filter(e => e.kind === 'variable');
|
|
for (const v of vars) {
|
|
items.push(` {
|
|
name: "${v.name}",
|
|
type: "const",
|
|
signature: ${JSON.stringify(v.signature || '')},
|
|
description: ${JSON.stringify(v.description || '')},
|
|
}`);
|
|
|
|
// Add methods from object literals (like templates)
|
|
if (v.methods) {
|
|
for (const method of v.methods) {
|
|
const params = method.parameters?.map(p => ({
|
|
name: p.name,
|
|
type: p.type,
|
|
description: p.description || undefined,
|
|
})) || [];
|
|
|
|
items.push(` {
|
|
name: "${v.name}.${method.name}()",
|
|
type: "method",
|
|
signature: ${JSON.stringify(method.signature || method.name)},
|
|
description: ${JSON.stringify(method.description || '')},
|
|
returns: ${JSON.stringify(method.returnType || '')},
|
|
params: ${JSON.stringify(params)},
|
|
}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (items.length > 0) {
|
|
lines.push(` {`);
|
|
lines.push(` name: "${groupName}",`);
|
|
lines.push(` items: [`);
|
|
lines.push(items.join(',\n'));
|
|
lines.push(` ],`);
|
|
lines.push(` },`);
|
|
}
|
|
}
|
|
|
|
lines.push('];');
|
|
lines.push('');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// Generate TypeScript declaration file content for Monaco editor
|
|
function generateTypeDefinitions(modules: ModuleDoc[]): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push('/**');
|
|
lines.push(' * Auto-generated type definitions for prompts.chat');
|
|
lines.push(' * Generated from TypeScript source files via reflection');
|
|
lines.push(' * DO NOT EDIT MANUALLY - run `npm run docs:generate` to regenerate');
|
|
lines.push(' */');
|
|
lines.push('');
|
|
lines.push("export const TYPE_DEFINITIONS = `");
|
|
lines.push("declare module 'prompts.chat' {");
|
|
|
|
// Group by category
|
|
const builderModule = modules.find(m => m.name === 'builder/index');
|
|
const chatModule = modules.find(m => m.name === 'builder/chat');
|
|
const mediaModule = modules.find(m => m.name === 'builder/media');
|
|
const videoModule = modules.find(m => m.name === 'builder/video');
|
|
const audioModule = modules.find(m => m.name === 'builder/audio');
|
|
const variablesModule = modules.find(m => m.name === 'variables/index');
|
|
const similarityModule = modules.find(m => m.name === 'similarity/index');
|
|
const qualityModule = modules.find(m => m.name === 'quality/index');
|
|
const parserModule = modules.find(m => m.name === 'parser/index');
|
|
|
|
// Helper to generate interface/type declarations
|
|
const generateInterfaceDecl = (entry: DocEntry): string => {
|
|
if (entry.kind === 'interface' && entry.properties) {
|
|
const props = entry.properties.map(p => {
|
|
const sig = p.signature || `${p.name}: unknown`;
|
|
return ` ${sig};`;
|
|
}).join('\n');
|
|
return ` export interface ${entry.name} {\n${props}\n }`;
|
|
}
|
|
return '';
|
|
};
|
|
|
|
const generateTypeDecl = (entry: DocEntry): string => {
|
|
if (entry.kind === 'type' && entry.signature) {
|
|
return ` export ${entry.signature};`;
|
|
}
|
|
return '';
|
|
};
|
|
|
|
const generateClassDecl = (entry: DocEntry): string => {
|
|
if (entry.kind === 'class' && entry.methods) {
|
|
const methods = entry.methods.map(m => {
|
|
const sig = m.signature || `${m.name}(): unknown`;
|
|
return ` ${sig};`;
|
|
}).join('\n');
|
|
return ` export class ${entry.name} {\n${methods}\n }`;
|
|
}
|
|
return '';
|
|
};
|
|
|
|
const generateFunctionDecl = (entry: DocEntry): string => {
|
|
if (entry.kind === 'function' && entry.signature) {
|
|
return ` export function ${entry.signature};`;
|
|
}
|
|
return '';
|
|
};
|
|
|
|
// Track already added declarations to avoid duplicates
|
|
const addedTypes = new Set<string>();
|
|
const addedInterfaces = new Set<string>();
|
|
const addedClasses = new Set<string>();
|
|
const addedFunctions = new Set<string>();
|
|
|
|
// Process each module
|
|
const processModule = (mod: ModuleDoc | undefined, sectionComment: string) => {
|
|
if (!mod) return;
|
|
|
|
lines.push('');
|
|
lines.push(` // ${sectionComment}`);
|
|
|
|
// Types first (skip duplicates)
|
|
for (const entry of mod.exports.filter(e => e.kind === 'type')) {
|
|
if (addedTypes.has(entry.name)) continue;
|
|
const decl = generateTypeDecl(entry);
|
|
if (decl) {
|
|
lines.push(decl);
|
|
addedTypes.add(entry.name);
|
|
}
|
|
}
|
|
|
|
// Interfaces (skip duplicates)
|
|
for (const entry of mod.exports.filter(e => e.kind === 'interface')) {
|
|
if (addedInterfaces.has(entry.name)) continue;
|
|
const decl = generateInterfaceDecl(entry);
|
|
if (decl) {
|
|
lines.push(decl);
|
|
addedInterfaces.add(entry.name);
|
|
}
|
|
}
|
|
|
|
// Classes (skip duplicates)
|
|
for (const entry of mod.exports.filter(e => e.kind === 'class')) {
|
|
if (addedClasses.has(entry.name)) continue;
|
|
const decl = generateClassDecl(entry);
|
|
if (decl) {
|
|
lines.push(decl);
|
|
addedClasses.add(entry.name);
|
|
}
|
|
}
|
|
|
|
// Functions (skip duplicates)
|
|
for (const entry of mod.exports.filter(e => e.kind === 'function')) {
|
|
if (addedFunctions.has(entry.name)) continue;
|
|
const decl = generateFunctionDecl(entry);
|
|
if (decl) {
|
|
lines.push(decl);
|
|
addedFunctions.add(entry.name);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Process modules in order - audio first since it has the complete type definitions
|
|
processModule(builderModule, 'BUILDER TYPES');
|
|
processModule(chatModule, 'CHAT BUILDER TYPES');
|
|
processModule(audioModule, 'AUDIO BUILDER TYPES');
|
|
processModule(videoModule, 'VIDEO BUILDER TYPES');
|
|
processModule(mediaModule, 'IMAGE BUILDER TYPES');
|
|
|
|
// Generate templates namespace from object literal methods
|
|
if (builderModule) {
|
|
const templatesVar = builderModule.exports.find(e => e.kind === 'variable' && e.name === 'templates');
|
|
if (templatesVar && templatesVar.methods && templatesVar.methods.length > 0) {
|
|
lines.push('');
|
|
lines.push(' // TEMPLATES - Pre-built prompt templates');
|
|
lines.push(' export const templates: {');
|
|
for (const method of templatesVar.methods) {
|
|
const sig = method.signature || `${method.name}(): PromptBuilder`;
|
|
// Extract just the function signature part
|
|
const funcMatch = sig.match(/^(\w+)\((.*?)\):\s*(.+)$/);
|
|
if (funcMatch) {
|
|
const [, name, params, returnType] = funcMatch;
|
|
// Default to PromptBuilder for templates if return type is 'any'
|
|
const actualReturnType = returnType === 'any' ? 'PromptBuilder' : returnType;
|
|
lines.push(` ${name}: (${params}) => ${actualReturnType};`);
|
|
}
|
|
}
|
|
lines.push(' };');
|
|
}
|
|
}
|
|
|
|
// Utility namespaces
|
|
if (variablesModule) {
|
|
lines.push('');
|
|
lines.push(' // UTILITY MODULES');
|
|
lines.push(' export namespace variables {');
|
|
for (const entry of variablesModule.exports.filter(e => e.kind === 'function')) {
|
|
if (entry.signature) {
|
|
lines.push(` export function ${entry.signature};`);
|
|
}
|
|
}
|
|
lines.push(' }');
|
|
}
|
|
|
|
if (similarityModule) {
|
|
lines.push(' export namespace similarity {');
|
|
for (const entry of similarityModule.exports.filter(e => e.kind === 'function')) {
|
|
if (entry.signature) {
|
|
lines.push(` export function ${entry.signature};`);
|
|
}
|
|
}
|
|
lines.push(' }');
|
|
}
|
|
|
|
if (qualityModule) {
|
|
lines.push(' export namespace quality {');
|
|
for (const entry of qualityModule.exports.filter(e => e.kind === 'function')) {
|
|
if (entry.signature) {
|
|
lines.push(` export function ${entry.signature};`);
|
|
}
|
|
}
|
|
lines.push(' }');
|
|
}
|
|
|
|
if (parserModule) {
|
|
lines.push(' export namespace parser {');
|
|
for (const entry of parserModule.exports.filter(e => e.kind === 'function')) {
|
|
if (entry.signature) {
|
|
lines.push(` export function ${entry.signature};`);
|
|
}
|
|
}
|
|
lines.push(' }');
|
|
}
|
|
|
|
lines.push('}');
|
|
lines.push('`;');
|
|
lines.push('');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// Extract string literal options from type aliases and method parameters
|
|
function extractStringLiteralOptions(program: ts.Program, files: string[]): { methodOptions: Record<string, string[]>; typeAliases: Record<string, string[]> } {
|
|
// Phase 1: Collect all type aliases and interface properties
|
|
const typeAliases: Record<string, string[]> = {};
|
|
const interfaceProps: Record<string, Record<string, string[]>> = {}; // InterfaceName -> { propName -> literals }
|
|
|
|
function extractLiteralsFromType(type: ts.TypeNode, sourceFile: ts.SourceFile): string[] {
|
|
const literals: string[] = [];
|
|
|
|
if (ts.isUnionTypeNode(type)) {
|
|
for (const member of type.types) {
|
|
if (ts.isLiteralTypeNode(member) && member.literal && ts.isStringLiteral(member.literal)) {
|
|
literals.push(member.literal.text);
|
|
} else if (ts.isUnionTypeNode(member)) {
|
|
literals.push(...extractLiteralsFromType(member, sourceFile));
|
|
}
|
|
}
|
|
} else if (ts.isLiteralTypeNode(type) && type.literal && ts.isStringLiteral(type.literal)) {
|
|
literals.push(type.literal.text);
|
|
}
|
|
|
|
return literals;
|
|
}
|
|
|
|
// First pass: collect type aliases and interface properties
|
|
for (const filePath of files) {
|
|
const sourceFile = program.getSourceFile(filePath);
|
|
if (!sourceFile) continue;
|
|
const sf = sourceFile; // Capture for closure
|
|
|
|
function collectTypes(node: ts.Node) {
|
|
if (ts.isTypeAliasDeclaration(node)) {
|
|
const typeName = node.name.getText(sf);
|
|
const literals = extractLiteralsFromType(node.type, sf);
|
|
if (literals.length > 0) {
|
|
typeAliases[typeName] = literals;
|
|
}
|
|
}
|
|
|
|
if (ts.isInterfaceDeclaration(node)) {
|
|
const interfaceName = node.name.getText(sf);
|
|
interfaceProps[interfaceName] = interfaceProps[interfaceName] || {};
|
|
|
|
for (const member of node.members) {
|
|
if (ts.isPropertySignature(member) && member.name && member.type) {
|
|
const propName = member.name.getText(sf);
|
|
const literals = extractLiteralsFromType(member.type, sf);
|
|
if (literals.length > 0) {
|
|
interfaceProps[interfaceName][propName] = literals;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ts.forEachChild(node, collectTypes);
|
|
}
|
|
|
|
collectTypes(sf);
|
|
}
|
|
|
|
// Phase 2: Extract method options by resolving parameter types
|
|
const methodOptions: Record<string, string[]> = {};
|
|
|
|
for (const filePath of files) {
|
|
const sourceFile = program.getSourceFile(filePath);
|
|
if (!sourceFile) continue;
|
|
const sf = sourceFile; // Capture for closure
|
|
|
|
function extractMethods(node: ts.Node) {
|
|
if (ts.isClassDeclaration(node)) {
|
|
for (const member of node.members) {
|
|
if (ts.isMethodDeclaration(member) && member.name) {
|
|
const methodName = member.name.getText(sf);
|
|
|
|
for (const param of member.parameters) {
|
|
if (!param.type) continue;
|
|
|
|
let literals: string[] = [];
|
|
|
|
// Direct literal union in parameter
|
|
literals = extractLiteralsFromType(param.type, sf);
|
|
|
|
// Type reference (e.g., MusicGenre)
|
|
if (literals.length === 0 && ts.isTypeReferenceNode(param.type)) {
|
|
const refName = param.type.typeName.getText(sf);
|
|
if (typeAliases[refName]) {
|
|
literals = typeAliases[refName];
|
|
}
|
|
}
|
|
|
|
// Indexed access type (e.g., AudioTempo['feel'])
|
|
if (literals.length === 0 && ts.isIndexedAccessTypeNode(param.type)) {
|
|
const objectType = param.type.objectType;
|
|
const indexType = param.type.indexType;
|
|
|
|
if (ts.isTypeReferenceNode(objectType) && ts.isLiteralTypeNode(indexType)) {
|
|
const interfaceName = objectType.typeName.getText(sf);
|
|
if (indexType.literal && ts.isStringLiteral(indexType.literal)) {
|
|
const propName = indexType.literal.text;
|
|
if (interfaceProps[interfaceName]?.[propName]) {
|
|
literals = interfaceProps[interfaceName][propName];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Union containing type references (e.g., MusicGenre | AudioGenre)
|
|
if (literals.length === 0 && ts.isUnionTypeNode(param.type)) {
|
|
for (const member of param.type.types) {
|
|
if (ts.isTypeReferenceNode(member)) {
|
|
const refName = member.typeName.getText(sf);
|
|
if (typeAliases[refName]) {
|
|
literals.push(...typeAliases[refName]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (literals.length > 0) {
|
|
// Deduplicate
|
|
const uniqueLiterals = [...new Set(literals)];
|
|
if (!methodOptions[methodName] || methodOptions[methodName].length < uniqueLiterals.length) {
|
|
methodOptions[methodName] = uniqueLiterals;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ts.forEachChild(node, extractMethods);
|
|
}
|
|
|
|
extractMethods(sourceFile);
|
|
}
|
|
|
|
return { methodOptions, typeAliases };
|
|
}
|
|
|
|
// Generate type options mapping (TypeName -> valid values) for error messages
|
|
function generateTypeOptions(typeAliases: Record<string, string[]>): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push('');
|
|
lines.push('// Type name to valid options mapping for enhanced error messages');
|
|
lines.push('export const TYPE_OPTIONS: Record<string, string[]> = {');
|
|
|
|
// Sort keys for consistent output
|
|
const sortedKeys = Object.keys(typeAliases).sort();
|
|
|
|
for (const key of sortedKeys) {
|
|
if (typeAliases[key].length > 0) {
|
|
lines.push(` ${JSON.stringify(key)}: ${JSON.stringify(typeAliases[key])},`);
|
|
}
|
|
}
|
|
|
|
lines.push('};');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// Generate method options file for Monaco autocomplete
|
|
function generateMethodOptions(options: Record<string, string[]>): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push('/**');
|
|
lines.push(' * Auto-generated method options for Monaco autocomplete');
|
|
lines.push(' * Generated from TypeScript source files via reflection');
|
|
lines.push(' * DO NOT EDIT MANUALLY - run `npm run docs:generate` to regenerate');
|
|
lines.push(' */');
|
|
lines.push('');
|
|
lines.push('export const METHOD_OPTIONS: Record<string, string[]> = {');
|
|
|
|
// Collect all method names with their values, avoiding duplicates
|
|
const methodMap: Record<string, string[]> = {};
|
|
|
|
// Common method name aliases (type name -> method name)
|
|
const methodAliases: Record<string, string[]> = {
|
|
musicGenre: ['genre'],
|
|
personaTone: ['tone'],
|
|
personaExpertise: ['expertise'],
|
|
outputLength: ['length'],
|
|
outputStyle: ['style'],
|
|
shotType: ['shot'],
|
|
cameraAngle: ['angle'],
|
|
cameraMovement: ['movement'],
|
|
lensType: ['lens'],
|
|
lightingType: ['lighting', 'lightingType'],
|
|
focusType: ['focus'],
|
|
bokehStyle: ['bokeh'],
|
|
filterType: ['filter'],
|
|
colorPalette: ['palette'],
|
|
artStyle: ['medium', 'look', 'artStyle'],
|
|
videoPacing: ['pacing'],
|
|
videoTransition: ['transition'],
|
|
vocalStyle: ['vocalStyle'],
|
|
vocalLanguage: ['language'],
|
|
weatherLighting: ['weather'],
|
|
sensorFormat: ['sensor'],
|
|
songSection: ['section'],
|
|
};
|
|
|
|
for (const [key, values] of Object.entries(options)) {
|
|
if (values.length > 0) {
|
|
// Convert type names to likely method names (camelCase, remove 'Type' suffix)
|
|
const methodName = key.charAt(0).toLowerCase() + key.slice(1).replace(/Type$/, '');
|
|
|
|
// Keep the longer list if there's a conflict
|
|
if (!methodMap[methodName] || methodMap[methodName].length < values.length) {
|
|
methodMap[methodName] = values;
|
|
}
|
|
|
|
// Also add aliases
|
|
const aliases = methodAliases[methodName] || [];
|
|
for (const alias of aliases) {
|
|
if (!methodMap[alias] || methodMap[alias].length < values.length) {
|
|
methodMap[alias] = values;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort keys for consistent output
|
|
const sortedKeys = Object.keys(methodMap).sort();
|
|
|
|
for (const key of sortedKeys) {
|
|
lines.push(` ${JSON.stringify(key)}: ${JSON.stringify(methodMap[key])},`);
|
|
}
|
|
|
|
lines.push('};');
|
|
lines.push('');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
// Main execution
|
|
const srcDir = path.join(__dirname, '../src');
|
|
const outputPath = path.join(__dirname, '../API.md');
|
|
const sidebarOutputPath = path.resolve(__dirname, '../../../src/data/api-docs.ts');
|
|
const typeDefsOutputPath = path.resolve(__dirname, '../../../src/data/type-definitions.ts');
|
|
const methodOptionsOutputPath = path.resolve(__dirname, '../../../src/data/method-options.ts');
|
|
|
|
// Files to parse
|
|
const filesToParse = [
|
|
path.join(srcDir, 'variables/index.ts'),
|
|
path.join(srcDir, 'similarity/index.ts'),
|
|
path.join(srcDir, 'quality/index.ts'),
|
|
path.join(srcDir, 'parser/index.ts'),
|
|
path.join(srcDir, 'builder/index.ts'),
|
|
path.join(srcDir, 'builder/chat.ts'),
|
|
path.join(srcDir, 'builder/media.ts'),
|
|
path.join(srcDir, 'builder/video.ts'),
|
|
path.join(srcDir, 'builder/audio.ts'),
|
|
];
|
|
|
|
// Filter to existing files
|
|
const existingFiles = filesToParse.filter(f => fs.existsSync(f));
|
|
|
|
// Create TypeScript program
|
|
const program = ts.createProgram(existingFiles, {
|
|
target: ts.ScriptTarget.ESNext,
|
|
module: ts.ModuleKind.ESNext,
|
|
strict: true,
|
|
esModuleInterop: true,
|
|
skipLibCheck: true,
|
|
});
|
|
|
|
// Parse each file
|
|
const modules: ModuleDoc[] = [];
|
|
for (const file of existingFiles) {
|
|
try {
|
|
const mod = parseSourceFile(file, program);
|
|
if (mod.exports.length > 0) {
|
|
// Use relative path as module name for clarity
|
|
mod.name = path.relative(srcDir, file).replace(/\.ts$/, '').replace(/\\/g, '/');
|
|
modules.push(mod);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error parsing ${file}:`, error);
|
|
}
|
|
}
|
|
|
|
// Generate and write markdown
|
|
const markdown = generateMarkdown(modules);
|
|
fs.writeFileSync(outputPath, markdown, 'utf-8');
|
|
|
|
// Generate and write TypeScript sidebar file
|
|
const sidebarTS = generateSidebarTS(modules);
|
|
// Ensure the data directory exists
|
|
const dataDir = path.dirname(sidebarOutputPath);
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
fs.writeFileSync(sidebarOutputPath, sidebarTS, 'utf-8');
|
|
|
|
// Generate and write TYPE_DEFINITIONS file
|
|
const typeDefs = generateTypeDefinitions(modules);
|
|
fs.writeFileSync(typeDefsOutputPath, typeDefs, 'utf-8');
|
|
|
|
// Extract string literal options and generate method options file
|
|
const { methodOptions, typeAliases } = extractStringLiteralOptions(program, existingFiles);
|
|
const methodOptionsContent = generateMethodOptions(methodOptions) + generateTypeOptions(typeAliases);
|
|
fs.writeFileSync(methodOptionsOutputPath, methodOptionsContent, 'utf-8');
|
|
|
|
console.log(`✅ Generated API documentation: ${outputPath}`);
|
|
console.log(`✅ Generated sidebar TypeScript: ${sidebarOutputPath}`);
|
|
console.log(`✅ Generated type definitions: ${typeDefsOutputPath}`);
|
|
console.log(`✅ Generated method options: ${methodOptionsOutputPath}`);
|
|
console.log(` Parsed ${modules.length} modules with ${modules.reduce((acc, m) => acc + m.exports.length, 0)} exports`);
|
|
console.log(` Extracted ${Object.keys(methodOptions).length} method options and ${Object.keys(typeAliases).length} type aliases`);
|