#!/usr/bin/env tsx import fs from 'fs/promises'; import path from 'path'; async function generateIndividualDTOs() { const openapiPath = path.join(__dirname, '../apps/api/openapi.json'); const outputDir = path.join(__dirname, '../apps/website/lib/types/generated'); console.log('🔄 Generating individual DTO files from OpenAPI spec...'); try { // Check if OpenAPI spec exists await fs.access(openapiPath); } catch { console.error(`❌ OpenAPI spec not found at: ${openapiPath}`); console.error('Run "npm run api:generate-spec" first'); process.exit(1); } try { // Read the OpenAPI spec const specContent = await fs.readFile(openapiPath, 'utf-8'); const spec = JSON.parse(specContent); // Ensure output directory exists await fs.mkdir(outputDir, { recursive: true }); // Extract schemas from the spec const schemas = spec.components?.schemas || {}; console.log(`📝 Found ${Object.keys(schemas).length} schemas to generate`); // Generate individual files for each schema for (const [schemaName, schema] of Object.entries(schemas)) { if (typeof schema === 'object' && schema !== null) { const fileName = `${schemaName}.ts`; const filePath = path.join(outputDir, fileName); // Convert OpenAPI schema to TypeScript interface const tsInterface = generateTypeScriptInterface(schemaName, schema); await fs.writeFile(filePath, tsInterface); console.log(`✅ Generated ${fileName}`); } } console.log(`🎉 Generated ${Object.keys(schemas).length} DTO files in ${outputDir}`); } catch (error) { console.error('❌ Failed to generate DTOs:', error); process.exit(1); } } function generateTypeScriptInterface(name: string, schema: any): string { const properties = schema.properties || {}; const required = schema.required || []; let interfaceContent = `/** * Auto-generated DTO from OpenAPI spec * This file is generated by scripts/generate-api-types.ts * Do not edit manually - regenerate using: npm run api:sync-types */ export interface ${name} {\n`; for (const [propName, propSchema] of Object.entries(properties)) { const isRequired = required.includes(propName); const optionalMark = isRequired ? '' : '?'; const type = openApiTypeToTypeScript(propSchema); interfaceContent += ` ${propName}${optionalMark}: ${type};\n`; } interfaceContent += '}\n'; return interfaceContent; } function openApiTypeToTypeScript(schema: any): string { if (schema.$ref) { // Handle references const refName = schema.$ref.split('/').pop(); return refName; } if (schema.type === 'array') { const itemType = openApiTypeToTypeScript(schema.items); return `${itemType}[]`; } if (schema.type === 'object') { // For complex objects, we'll use a generic Record for now return 'Record'; } switch (schema.type) { case 'string': if (schema.format === 'date-time') { return 'string'; // Keep as string for now, could be Date } return 'string'; case 'number': case 'integer': return 'number'; case 'boolean': return 'boolean'; default: return 'any'; } } generateIndividualDTOs().catch(console.error);