import { Documentation, DocumentationOptions, FieldInfo, OpenAPISpec, SchemaAnalysis } from "../../../../types/extension.type"; import { SchemaInterface } from "../../../../types/SchemaValidator.type"; import { SchemaAnalyzer } from "./SchemaAnalyzer"; /** * Main documentation generator */ export class DocumentationGenerator { constructor( private schema: SchemaInterface, private options: DocumentationOptions ) {} generate(): Documentation { const analyzer = new SchemaAnalyzer(this.schema); const analysis = analyzer.analyze(); return { markdown: this.generateMarkdown(analysis), html: this.generateHTML(analysis), openapi: this.generateOpenAPI(analysis), json: this.generateJSON(analysis), examples: this.generateExamples(analysis) }; } private generateMarkdown(analysis: SchemaAnalysis): string { const { title = "Schema Documentation", description = "" } = this.options; let markdown = `# ${title}\n\n`; if (description) { markdown += `${description}\n\n`; } markdown += "## Schema Structure\n\n"; markdown += this.generateFieldTable(analysis.fields); if (this.options.examples) { markdown += "\n## Examples\n\n"; markdown += this.generateExampleMarkdown(analysis); } return markdown; } private generateHTML(analysis: SchemaAnalysis): string { const { title = "Schema Documentation" } = this.options; return ` ${title}

${title}

${this.generateFieldHTML(analysis.fields)} ${this.options.examples ? this.generateExampleHTML(analysis) : ''} `; } private generateOpenAPI(analysis: SchemaAnalysis): OpenAPISpec { return { openapi: "3.0.0", info: { title: this.options.title || "API Documentation", version: "1.0.0" }, components: { schemas: { [this.options.title || "Schema"]: this.convertToOpenAPISchema(analysis) } } }; } private generateJSON(analysis: SchemaAnalysis): any { return { schema: this.schema, analysis, metadata: { generatedAt: new Date().toISOString(), version: "1.0.0" } }; } private generateExamples(analysis: SchemaAnalysis): any[] { return [ this.generateValidExample(analysis), this.generateInvalidExample(analysis) ]; } private generateFieldTable(fields: FieldInfo[]): string { let table = "| Field | Type | Required | Description |\n"; table += "|-------|------|----------|-------------|\n"; fields.forEach(field => { table += `| ${field.name} | \`${field.type}\` | ${field.required ? 'Yes' : 'No'} | ${field.description || ''} |\n`; }); return table; } private generateFieldHTML(fields: FieldInfo[]): string { return fields.map(field => `
${field.name}
${field.type}
${field.description || ''}
`).join(''); } private generateExampleMarkdown(analysis: SchemaAnalysis): string { const example = this.generateValidExample(analysis); return `\`\`\`json\n${JSON.stringify(example, null, 2)}\n\`\`\``; } private generateExampleHTML(analysis: SchemaAnalysis): string { const example = this.generateValidExample(analysis); return `
${JSON.stringify(example, null, 2)}
`; } private generateValidExample(analysis: SchemaAnalysis): any { const example: any = {}; analysis.fields.forEach(field => { if (field.required || Math.random() > 0.3) { // Include some optional fields example[field.name] = this.generateExampleValue(field.type); } }); return example; } private generateInvalidExample(analysis: SchemaAnalysis): any { const example = this.generateValidExample(analysis); // Introduce some invalid data if (example.email) example.email = "invalid-email"; if (example.age) example.age = -5; return example; } private generateExampleValue(type: string): any { if (type === "email") return "user@example.com"; if (type === "uuid") return "550e8400-e29b-41d4-a716-446655440000"; if (type === "url") return "https://example.com"; if (type.startsWith("string")) return "Example string"; if (type.includes("number") || type === "positive" || type === "int") return 42; if (type === "boolean") return true; if (type === "date") return new Date().toISOString(); if (type.includes("[]")) return ["item1", "item2"]; return "example"; } private convertToOpenAPISchema(analysis: SchemaAnalysis): any { const properties: any = {}; const required: string[] = []; analysis.fields.forEach(field => { properties[field.name] = this.convertFieldToOpenAPI(field); if (field.required) { required.push(field.name); } }); return { type: "object", properties, required }; } private convertFieldToOpenAPI(field: FieldInfo): any { const type = field.type; if (type === "email") return { type: "string", format: "email" }; if (type === "uuid") return { type: "string", format: "uuid" }; if (type === "url") return { type: "string", format: "uri" }; if (type.startsWith("string")) return { type: "string" }; if (type.includes("number") || type === "positive" || type === "int") return { type: "number" }; if (type === "boolean") return { type: "boolean" }; if (type === "date") return { type: "string", format: "date-time" }; if (type.includes("[]")) return { type: "array", items: { type: "string" } }; return { type: "string" }; } }