113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
#!/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<string, any>';
|
|
}
|
|
|
|
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); |