@@ -128,17 +128,19 @@ async function resolveRefs(obj: any, sourceDir: string): Promise<any> {
128128 customTypePath = [ ...refTokens , title ] . join ( "." ) ;
129129 }
130130
131- // Create a simplified reference object
132- return {
133- type : "object" ,
134- title,
135- properties : { } ,
136- additionalProperties : false ,
137- // Add metadata for TypeScript generator
138- // These will be processed by json-schema-to-typescript
139- description : `Reference to ${ customTypePath } ` ,
140- tsType : customTypePath . split ( "." ) . pop ( ) ,
141- } ;
131+ // For external references, just return a simple type reference
132+ // This prevents inlining the entire interface definition
133+ if ( title ) {
134+ return {
135+ type : "object" ,
136+ title : title ,
137+ // Mark as external reference to prevent inlining
138+ "x-external-ref" : true ,
139+ } ;
140+ }
141+
142+ // Fallback: return the resolved object if no title
143+ return refObj || { } ;
142144 } catch ( error ) {
143145 console . error ( `Error resolving reference ${ ref } :` , error ) ;
144146 return { } ;
@@ -198,8 +200,14 @@ async function convertSchemaToTypeScript(
198200 const outputDir = path . dirname ( outputPath ) ;
199201 await mkdir ( outputDir , { recursive : true } ) ;
200202
203+ // Post-process to fix external references
204+ const processedTypeScript = await postProcessTypeScript (
205+ typeScript ,
206+ outputPath
207+ ) ;
208+
201209 // Write the TypeScript interface to file
202- await writeFile ( outputPath , typeScript ) ;
210+ await writeFile ( outputPath , processedTypeScript ) ;
203211 console . log ( `Generated: ${ outputPath } ` ) ;
204212 } catch ( error ) {
205213 console . error ( `Error converting schema ${ schemaPath } :` , error ) ;
@@ -274,6 +282,76 @@ function createBarrelFiles(dir: string): void {
274282 }
275283}
276284
285+ /**
286+ * Post-process generated TypeScript to fix external references
287+ */
288+ async function postProcessTypeScript (
289+ typeScript : string ,
290+ outputPath : string
291+ ) : Promise < string > {
292+ let processed = typeScript ;
293+
294+ // Calculate the correct import path for each interface based on current output path
295+ const getImportPath = ( interfaceName : string ) : string => {
296+ const outputDir = path . dirname ( outputPath ) ;
297+ const modelsGenerated = path . resolve ( __dirname , "../src/models/generated" ) ;
298+
299+ // Define the actual locations of each interface
300+ const interfaceLocations : Record < string , string > = {
301+ BlockBase : path . join ( modelsGenerated , "block/base" ) ,
302+ Block : path . join ( modelsGenerated , "block" ) ,
303+ RichText : path . join ( modelsGenerated , "block/types/rich_text" ) ,
304+ File : path . join ( modelsGenerated , "file" ) ,
305+ ColumnBlock : path . join ( modelsGenerated , "block/types/column" ) ,
306+ TableRowBlock : path . join ( modelsGenerated , "block/types/table_row" ) ,
307+ FileExternal : path . join ( modelsGenerated , "file/external" ) ,
308+ RichTextText : path . join ( modelsGenerated , "block/types/rich_text/text" ) ,
309+ } ;
310+
311+ const targetPath = interfaceLocations [ interfaceName ] ;
312+ if ( ! targetPath ) return "" ;
313+
314+ const relativePath = path . relative ( outputDir , targetPath ) ;
315+ return relativePath . startsWith ( "." )
316+ ? relativePath . replace ( / \\ / g, "/" )
317+ : `./${ relativePath . replace ( / \\ / g, "/" ) } ` ;
318+ } ;
319+
320+ // Find empty interface definitions and replace with imports
321+ const emptyInterfaceRegex = / e x p o r t i n t e r f a c e ( \w + ) \{ \} \s * / g;
322+ const imports : string [ ] = [ ] ;
323+
324+ processed = processed . replace ( emptyInterfaceRegex , ( match , interfaceName ) => {
325+ const importPath = getImportPath ( interfaceName ) ;
326+ if ( importPath ) {
327+ imports . push ( `import type { ${ interfaceName } } from '${ importPath } ';` ) ;
328+ return "" ; // Remove the empty interface
329+ }
330+ return match ; // Keep if not in our external interfaces map
331+ } ) ;
332+
333+ // Add imports at the top if any were found
334+ if ( imports . length > 0 ) {
335+ processed = imports . join ( "\n" ) + "\n\n" + processed ;
336+ }
337+
338+ // Fix Block type to extend BlockBase if this is the block.ts file
339+ if ( outputPath . endsWith ( "block/block.ts" ) ) {
340+ // Add import for BlockBase if not already present
341+ if ( ! processed . includes ( "import type { BlockBase }" ) ) {
342+ processed = `import type { BlockBase } from './base';\n\n` + processed ;
343+ }
344+
345+ // Replace the Block type definition to extend BlockBase
346+ processed = processed . replace (
347+ / e x p o r t t y p e B l o c k = \{ [ ^ } ] * \} & \{ / ,
348+ "export type Block = BlockBase & {"
349+ ) ;
350+ }
351+
352+ return processed ;
353+ }
354+
277355/**
278356 * Extract enum values from a JSON schema file
279357 */
0 commit comments