@@ -4,6 +4,8 @@ import * as JsonSchemaStaticDocsLib from "json-schema-static-docs"; // Use names
44import fs from "fs/promises" ;
55import path from "path" ;
66import os from "os" ;
7+ import prettier from "prettier" ;
8+ import parserTypescript from "prettier/parser-typescript" ;
79// import { DocGenerator } from "json-schema-static-docs"; // Removed
810
911// Helper to resolve package paths
@@ -69,14 +71,73 @@ async function cleanupTempDirs(pathsToClean: string[]) {
6971 }
7072}
7173
74+ // Define an interface for the expected structure of the imported library module if possible
75+ // If the exact structure is unknown, we might have to keep `any` casts but minimize them.
76+ interface JsonSchemaStaticDocsOptions {
77+ inputPath : string ;
78+ outputPath : string ;
79+ templatePath : string ;
80+ createIndex : boolean ;
81+ addFrontMatter : boolean ;
82+ // Replace 'any' with 'unknown' for stricter checking
83+ [ key : string ] : unknown ;
84+ }
85+
86+ interface JsonSchemaStaticDocsInstance {
87+ generate : ( ) => Promise < void > ;
88+ // Add other known methods/properties if available
89+ [ key : string ] : unknown ;
90+ }
91+
92+ // Type for the constructor, assuming it's the default export or the module itself
93+ type JsonSchemaStaticDocsConstructor = new (
94+ options : JsonSchemaStaticDocsOptions ,
95+ ) => JsonSchemaStaticDocsInstance ;
96+
97+ // Type for the dynamically imported module
98+ type JsonSchemaStaticDocsModule =
99+ | JsonSchemaStaticDocsConstructor // Case: Module is the constructor
100+ | { default : JsonSchemaStaticDocsConstructor } // Case: Constructor is default export
101+ | { JsonSchemaStaticDocs : JsonSchemaStaticDocsConstructor } // Case: Constructor is named export 'JsonSchemaStaticDocs'
102+ | { DocGenerator : JsonSchemaStaticDocsConstructor } // Case: Constructor is named export 'DocGenerator'
103+ | { [ key : string ] : unknown } ; // Fallback for other structures
104+
105+ // Custom Type Guard for the Constructor
106+ function isDocConstructor ( obj : unknown ) : obj is JsonSchemaStaticDocsConstructor {
107+ return typeof obj === 'function' && obj . prototype !== undefined ;
108+ }
109+
110+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
111+ async function formatMarkdown ( markdown : string ) : Promise < string > {
112+ try {
113+ const result : string = await prettier . format ( markdown , {
114+ parser : "markdown" ,
115+ plugins : [ parserTypescript ] ,
116+ printWidth : 80 ,
117+ proseWrap : "always" ,
118+ } ) ;
119+ return result ;
120+ } catch ( error : unknown ) { // Type error as unknown
121+ console . warn ( "Could not parse error response from generate-schema-doc" ) ;
122+ // Safe access to properties
123+ let statusText = "Unknown status" ;
124+ if ( typeof error === 'object' && error !== null && 'statusText' in error ) {
125+ statusText = String ( ( error as { statusText : unknown } ) . statusText ) ;
126+ }
127+ const errorDetails = `: ${ statusText } ` ; // Use const as errorDetails is not reassigned after this block
128+ const message = error instanceof Error ? error . message : String ( error ) ;
129+ throw new Error ( `Markdown formatting failed${ errorDetails } . Original error: ${ message } ` ) ;
130+ }
131+ }
132+
72133export async function POST ( request : Request ) {
73134 let tempInputPath : string | undefined ;
74135 let tempOutputPath : string | undefined ;
75136 let tempTemplatePath : string | undefined ; // Path for copied templates
76137 const cleanupPaths : string [ ] = [ ] ; // Keep track of all paths to clean
77138
78139 try {
79- const { schema } = await request . json ( ) ;
140+ const { schema } = ( await request . json ( ) ) as { schema : Record < string , unknown > } ; // Added type assertion
80141
81142 if ( ! schema ) {
82143 return NextResponse . json (
@@ -130,25 +191,52 @@ export async function POST(request: Request) {
130191 console . log ( `API: Finished copying templates.` ) ;
131192
132193 // 4. Instantiate and run json-schema-static-docs
133- const Constructor =
134- JsonSchemaStaticDocsLib . default || JsonSchemaStaticDocsLib ;
194+ // Attempt to find the constructor more safely
195+ const LibraryModule : JsonSchemaStaticDocsModule = JsonSchemaStaticDocsLib ;
196+ let Constructor : JsonSchemaStaticDocsConstructor | null = null ;
197+
198+ // Use the type guard
199+ if ( isDocConstructor ( LibraryModule ) ) {
200+ Constructor = LibraryModule ;
201+ } else if ( LibraryModule && typeof LibraryModule === 'object' && LibraryModule . default && isDocConstructor ( LibraryModule . default ) ) {
202+ Constructor = LibraryModule . default ;
203+ } else if ( LibraryModule && typeof LibraryModule === 'object' && LibraryModule . JsonSchemaStaticDocs && isDocConstructor ( LibraryModule . JsonSchemaStaticDocs ) ) {
204+ Constructor = LibraryModule . JsonSchemaStaticDocs ;
205+ } else if ( LibraryModule && typeof LibraryModule === 'object' && LibraryModule . DocGenerator && isDocConstructor ( LibraryModule . DocGenerator ) ) {
206+ Constructor = LibraryModule . DocGenerator ;
207+ }
135208
136- const generator = new Constructor ( {
209+ if ( ! Constructor ) {
210+ console . error ( "API: Could not find JsonSchemaStaticDocs constructor in the imported module." , LibraryModule ) ;
211+ throw new Error ( "Failed to load the documentation generator library correctly." ) ;
212+ }
213+
214+ // Instantiate using the found constructor
215+ // Cast options object at point of use due to [key: string]: unknown
216+ const options : JsonSchemaStaticDocsOptions = {
137217 inputPath : tempInputDirectory ,
138218 outputPath : tempOutputPath ,
139- templatePath : tempTemplatePath , // <-- Use the path with copied templates
140- createIndex : false , // We only want the single doc
141- addFrontMatter : false , // No frontmatter needed
142- // Add any other options needed for styling/generation
143- // ajvOptions: { allowUnionTypes: true }, // Example ajv option
144- // enableMetaEnum: true, // Example meta enum option
145- } ) ;
219+ templatePath : tempTemplatePath ,
220+ createIndex : false ,
221+ addFrontMatter : false ,
222+ } ;
223+ const generator : JsonSchemaStaticDocsInstance = new Constructor ( options ) ;
146224
147225 console . log (
148226 `API: Running generator. Input: ${ tempInputDirectory } , Output: ${ tempOutputPath } , Templates: ${ tempTemplatePath } ` ,
149227 ) ;
150- await generator . generate ( ) ;
151- console . log ( "API: Generator finished." ) ;
228+ // Wrap generate call in try-catch
229+ try {
230+ // Call generate method (type safety from interface)
231+ await generator . generate ( ) ;
232+ console . log ( "API: Generator finished." ) ;
233+ } catch ( genError : unknown ) { // Ensure genError is unknown
234+ console . error ( "API: Error during generator.generate():" , genError ) ;
235+ // Use type guard for message
236+ const message = genError instanceof Error ? genError . message : "Generator failed" ;
237+
238+ throw new Error ( `Schema documentation generation failed: ${ message } ` ) ;
239+ }
152240
153241 // 5. Read the generated Markdown file
154242 const markdown = await readGeneratedMarkdown (
@@ -161,16 +249,18 @@ export async function POST(request: Request) {
161249
162250 // 7. Return the Markdown content
163251 return NextResponse . json ( { markdown } ) ;
164- } catch ( error : any ) {
252+ } catch ( error : unknown ) { // Changed to unknown
165253 console . error ( "API Error generating schema doc:" , error ) ;
166254
167255 // Ensure cleanup happens even on error
168256 await cleanupTempDirs ( cleanupPaths ) ;
169257
258+ // Add type guard before accessing message
259+ const details = error instanceof Error ? error . message : "Unknown error" ;
170260 return NextResponse . json (
171261 {
172262 error : "Failed to generate schema documentation" ,
173- details : error . message ,
263+ details : details , // Use guarded details
174264 } ,
175265 { status : 500 } ,
176266 ) ;
0 commit comments