@@ -5,15 +5,18 @@ import fs from "fs/promises";
55import path from "path" ;
66import os from "os" ;
77import { createRequire } from "module" ; // Import createRequire
8+ import prettier from "prettier" ;
9+ import parserTypescript from "prettier/parser-typescript" ;
10+ // import { DocGenerator } from "json-schema-static-docs"; // Removed
811
912// Helper to resolve package paths
10- const require = createRequire ( import . meta. url ) ;
13+ // const require = createRequire(import.meta.url);
1114
1215// Recursive copy function (fs.cp might not be available everywhere or handle nested dirs reliably)
1316async function copyDirRecursive ( src : string , dest : string ) {
1417 await fs . mkdir ( dest , { recursive : true } ) ;
1518 const entries = await fs . readdir ( src , { withFileTypes : true } ) ;
16- for ( let entry of entries ) {
19+ for ( const entry of entries ) {
1720 const srcPath = path . join ( src , entry . name ) ;
1821 const destPath = path . join ( dest , entry . name ) ;
1922 if ( entry . isDirectory ( ) ) {
@@ -25,7 +28,7 @@ async function copyDirRecursive(src: string, dest: string) {
2528}
2629
2730// Helper to create a temporary file
28- async function writeTempSchemaFile ( schema : any ) : Promise < string > {
31+ async function writeTempSchemaFile ( schema : Record < string , unknown > ) : Promise < string > {
2932 const tempDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "schema-" ) ) ;
3033 const tempFilePath = path . join ( tempDir , "schema.json" ) ;
3134 await fs . writeFile ( tempFilePath , JSON . stringify ( schema , null , 2 ) ) ;
@@ -69,14 +72,73 @@ async function cleanupTempDirs(pathsToClean: string[]) {
6972 }
7073}
7174
75+ // Define an interface for the expected structure of the imported library module if possible
76+ // If the exact structure is unknown, we might have to keep `any` casts but minimize them.
77+ interface JsonSchemaStaticDocsOptions {
78+ inputPath : string ;
79+ outputPath : string ;
80+ templatePath : string ;
81+ createIndex : boolean ;
82+ addFrontMatter : boolean ;
83+ // Replace 'any' with 'unknown' for stricter checking
84+ [ key : string ] : unknown ;
85+ }
86+
87+ interface JsonSchemaStaticDocsInstance {
88+ generate : ( ) => Promise < void > ;
89+ // Add other known methods/properties if available
90+ [ key : string ] : unknown ;
91+ }
92+
93+ // Type for the constructor, assuming it's the default export or the module itself
94+ type JsonSchemaStaticDocsConstructor = new (
95+ options : JsonSchemaStaticDocsOptions ,
96+ ) => JsonSchemaStaticDocsInstance ;
97+
98+ // Type for the dynamically imported module
99+ type JsonSchemaStaticDocsModule =
100+ | JsonSchemaStaticDocsConstructor // Case: Module is the constructor
101+ | { default : JsonSchemaStaticDocsConstructor } // Case: Constructor is default export
102+ | { JsonSchemaStaticDocs : JsonSchemaStaticDocsConstructor } // Case: Constructor is named export 'JsonSchemaStaticDocs'
103+ | { DocGenerator : JsonSchemaStaticDocsConstructor } // Case: Constructor is named export 'DocGenerator'
104+ | { [ key : string ] : unknown } ; // Fallback for other structures
105+
106+ // Custom Type Guard for the Constructor
107+ function isDocConstructor ( obj : unknown ) : obj is JsonSchemaStaticDocsConstructor {
108+ return typeof obj === 'function' && obj . prototype !== undefined ;
109+ }
110+
111+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
112+ async function formatMarkdown ( markdown : string ) : Promise < string > {
113+ try {
114+ const result : string = await prettier . format ( markdown , {
115+ parser : "markdown" ,
116+ plugins : [ parserTypescript ] ,
117+ printWidth : 80 ,
118+ proseWrap : "always" ,
119+ } ) ;
120+ return result ;
121+ } catch ( error : unknown ) { // Type error as unknown
122+ console . warn ( "Could not parse error response from generate-schema-doc" ) ;
123+ // Safe access to properties
124+ let statusText = "Unknown status" ;
125+ if ( typeof error === 'object' && error !== null && 'statusText' in error ) {
126+ statusText = String ( ( error as { statusText : unknown } ) . statusText ) ;
127+ }
128+ const errorDetails = `: ${ statusText } ` ; // Use const as errorDetails is not reassigned after this block
129+ const message = error instanceof Error ? error . message : String ( error ) ;
130+ throw new Error ( `Markdown formatting failed${ errorDetails } . Original error: ${ message } ` ) ;
131+ }
132+ }
133+
72134export async function POST ( request : Request ) {
73135 let tempInputPath : string | undefined ;
74136 let tempOutputPath : string | undefined ;
75137 let tempTemplatePath : string | undefined ; // Path for copied templates
76138 const cleanupPaths : string [ ] = [ ] ; // Keep track of all paths to clean
77139
78140 try {
79- const { schema } = await request . json ( ) ;
141+ const { schema } = ( await request . json ( ) ) as { schema : Record < string , unknown > } ; // Added type assertion
80142
81143 if ( ! schema ) {
82144 return NextResponse . json (
@@ -130,25 +192,59 @@ export async function POST(request: Request) {
130192 console . log ( `API: Finished copying templates.` ) ;
131193
132194 // 4. Instantiate and run json-schema-static-docs
133- const Constructor =
134- JsonSchemaStaticDocsLib . default || JsonSchemaStaticDocsLib ;
195+ // Attempt to find the constructor more safely
196+ const LibraryModule : JsonSchemaStaticDocsModule = JsonSchemaStaticDocsLib ;
197+ let Constructor : JsonSchemaStaticDocsConstructor | null = null ;
198+
199+ // Use the type guard first for the direct constructor case
200+ if ( isDocConstructor ( LibraryModule ) ) {
201+ Constructor = LibraryModule ;
202+ // Check if it's an object before checking properties
203+ } else if ( LibraryModule && typeof LibraryModule === 'object' ) {
204+ // Use 'in' operator for safer property checking
205+ if ( 'default' in LibraryModule && LibraryModule . default && isDocConstructor ( LibraryModule . default ) ) {
206+ // Add type assertion
207+ Constructor = LibraryModule . default as JsonSchemaStaticDocsConstructor ;
208+ } else if ( 'JsonSchemaStaticDocs' in LibraryModule && LibraryModule . JsonSchemaStaticDocs && isDocConstructor ( LibraryModule . JsonSchemaStaticDocs ) ) {
209+ // Add type assertion
210+ Constructor = LibraryModule . JsonSchemaStaticDocs as JsonSchemaStaticDocsConstructor ;
211+ } else if ( 'DocGenerator' in LibraryModule && LibraryModule . DocGenerator && isDocConstructor ( LibraryModule . DocGenerator ) ) {
212+ // Add type assertion
213+ Constructor = LibraryModule . DocGenerator as JsonSchemaStaticDocsConstructor ;
214+ }
215+ }
135216
136- const generator = new Constructor ( {
217+ if ( ! Constructor ) {
218+ console . error ( "API: Could not find JsonSchemaStaticDocs constructor in the imported module." , LibraryModule ) ;
219+ throw new Error ( "Failed to load the documentation generator library correctly." ) ;
220+ }
221+
222+ // Instantiate using the found constructor
223+ // Cast options object at point of use due to [key: string]: unknown
224+ const options : JsonSchemaStaticDocsOptions = {
137225 inputPath : tempInputDirectory ,
138226 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- } ) ;
227+ templatePath : tempTemplatePath ,
228+ createIndex : false ,
229+ addFrontMatter : false ,
230+ } ;
231+ const generator : JsonSchemaStaticDocsInstance = new Constructor ( options ) ;
146232
147233 console . log (
148234 `API: Running generator. Input: ${ tempInputDirectory } , Output: ${ tempOutputPath } , Templates: ${ tempTemplatePath } ` ,
149235 ) ;
150- await generator . generate ( ) ;
151- console . log ( "API: Generator finished." ) ;
236+ // Wrap generate call in try-catch
237+ try {
238+ // Call generate method (type safety from interface)
239+ await generator . generate ( ) ;
240+ console . log ( "API: Generator finished." ) ;
241+ } catch ( genError : unknown ) { // Ensure genError is unknown
242+ console . error ( "API: Error during generator.generate():" , genError ) ;
243+ // Use type guard for message
244+ const message = genError instanceof Error ? genError . message : "Generator failed" ;
245+
246+ throw new Error ( `Schema documentation generation failed: ${ message } ` ) ;
247+ }
152248
153249 // 5. Read the generated Markdown file
154250 const markdown = await readGeneratedMarkdown (
@@ -161,16 +257,18 @@ export async function POST(request: Request) {
161257
162258 // 7. Return the Markdown content
163259 return NextResponse . json ( { markdown } ) ;
164- } catch ( error : any ) {
260+ } catch ( error : unknown ) { // Changed to unknown
165261 console . error ( "API Error generating schema doc:" , error ) ;
166262
167263 // Ensure cleanup happens even on error
168264 await cleanupTempDirs ( cleanupPaths ) ;
169265
266+ // Add type guard before accessing message
267+ const details = error instanceof Error ? error . message : "Unknown error" ;
170268 return NextResponse . json (
171269 {
172270 error : "Failed to generate schema documentation" ,
173- details : error . message ,
271+ details : details , // Use guarded details
174272 } ,
175273 { status : 500 } ,
176274 ) ;
0 commit comments