#!/usr/bin/env bun /** * OpenAPI Types Build Script * * This script generates JSON schemas for all OpenAPI specification versions * and performs the complete build process for the package. */ import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { rimrafSync } from "rimraf"; import { createGenerator } from "ts-json-schema-generator"; // Configuration for schema generation const generatorConfig = { path: "", tsconfig: "./tsconfig.json", expose: "export" as const, jsDoc: "extended" as const, markdownDescription: true, fullDescription: true, additionalProperties: false, strictTuples: false, topRef: true, sortProps: true, skipTypeCheck: false, encodeRefs: true, minify: false, }; // OpenAPI versions to process const versions = ["2.0", "3.0", "3.1", "3.2"] as const; interface BuildResult { version: string; type: string; success: boolean; error?: string; outputPath?: string; } const schemasToGenerate = { "2.0": ["Schema", "Parameter", "Response", "PathItem"], "3.0": [ "Schema", "Response", "Parameter", "Example", "RequestBody", "Header", "SecurityScheme", "Link", "Callback", ], "3.1": [ "Schema", "Response", "Parameter", "Example", "RequestBody", "Header", "SecurityScheme", "Link", "Callback", "PathItem", ], "3.2": [ "Schema", "Response", "Parameter", "Example", "RequestBody", "Header", "SecurityScheme", "Link", "Callback", "PathItem", "MediaType", ], }; async function generateAllSchemasForVersion( version: string ): Promise { const results: BuildResult[] = []; const outputDir = resolve(`./schemas/${version}`); rimrafSync(outputDir); // Ensure output directory exists mkdirSync(`${outputDir}/main`, { recursive: true }); mkdirSync(`${outputDir}/components`, { recursive: true }); console.log(`šŸ“¦ Processing OpenAPI ${version}...`); try { // Get the main spec file const specFile = resolve(`./${version}/spec.ts`); if (!existsSync(specFile)) { console.warn(`āš ļø Spec file not found: ${specFile}`); return results; } // Create generator with version-specific config const config = { ...generatorConfig, path: specFile, }; const generator = createGenerator(config); // Generate schema for ALL exported types at once try { console.log( `šŸ” Generating schemas for all exported types in ${version}...` ); const schema = generator.createSchema(); // No type parameter = all types if (schema?.definitions) { const definitions = schema.definitions; const definitionNames = Object.keys(definitions); console.log( `šŸ“‹ Found ${ definitionNames.length } exported types: ${definitionNames.join(", ")}` ); // Generate main specification schema (if it exists) if (definitions.Specification) { const specSchema = { $schema: "http://json-schema.org/draft-07/schema#", ...(definitions.Specification as any), definitions: definitions, }; const outputPath = `${outputDir}/main/specification.json`; writeFileSync(outputPath, JSON.stringify(specSchema, null, 2)); results.push({ version, type: "Specification", success: true, outputPath, }); console.log(`āœ… Generated main schema for ${version}/Specification`); } // Generate individual component schemas for (const [typeName, typeSchema] of Object.entries(definitions)) { if (typeName === "Specification") continue; // Skip main spec, already handled if ( !schemasToGenerate[ version as keyof typeof schemasToGenerate ].includes(typeName) ) continue; // Skip types that are not in the schemasToGenerate object try { const componentSchema = { $schema: "http://json-schema.org/draft-07/schema#", ...(typeSchema as any), definitions: definitions, // Include all definitions for references }; const outputPath = `${outputDir}/components/${typeName.toLowerCase()}.json`; writeFileSync(outputPath, JSON.stringify(componentSchema, null, 2)); results.push({ version, type: typeName, success: true, outputPath, }); console.log( `āœ… Generated component schema for ${version}/${typeName}` ); } catch (error) { results.push({ version, type: typeName, success: false, error: error instanceof Error ? error.message : String(error), }); console.error( `āŒ Failed to generate schema for ${version}/${typeName}:`, error ); } } } else { results.push({ version, type: "all", success: false, error: "No definitions found in generated schema", }); console.error(`āŒ No definitions found for ${version}`); } } catch (error) { results.push({ version, type: "all", success: false, error: error instanceof Error ? error.message : String(error), }); console.error(`āŒ Failed to generate schemas for ${version}:`, error); } } catch (error) { console.error(`āŒ Failed to process version ${version}:`, error); // @ts-expect-error console.log(error.diagnostic); results.push({ version, type: "all", success: false, error: error instanceof Error ? error.message : String(error), }); } return results; } async function generateIndexFiles(): Promise { console.log("šŸ“ Generating index.ts files..."); // Generate index.ts for each version for (const version of versions) { const outputDir = resolve(`./schemas/${version}`); const indexPath = `${outputDir}/index.ts`; // Check if main specification exists const hasMainSpec = existsSync(`${outputDir}/main/specification.json`); let indexContent = `/** * OpenAPI ${version} JSON Schemas * * This file exports all available JSON schemas for OpenAPI ${version} specification. * These schemas can be used to validate OpenAPI ${version} documents and components. */ `; // Add main specification export if it exists if (hasMainSpec) { indexContent += `export { default as specification } from "./main/specification.json";\n\n`; } // Add component exports - dynamically discover all component files indexContent += `// Component schemas\n`; const componentsDir = `${outputDir}/components`; let componentFiles: string[] = []; if (existsSync(componentsDir)) { componentFiles = readdirSync(componentsDir) .filter((file) => file.endsWith(".json")) .map((file) => file.replace(".json", "")); for (const component of componentFiles) { indexContent += `export { default as ${component} } from "./components/${component}.json";\n`; } } // Add imports and schemas object indexContent += `\n// Import all schemas for internal use\n`; if (hasMainSpec) { indexContent += `import specification from "./main/specification.json";\n`; } for (const component of componentFiles) { indexContent += `import ${component} from "./components/${component}.json";\n`; } indexContent += `\n// Re-export all schemas as a single object for convenience\nexport const schemas = {\n`; if (hasMainSpec) { indexContent += ` specification,\n`; } for (const component of componentFiles) { indexContent += ` ${component},\n`; } indexContent += `} as const;\n\n`; writeFileSync(indexPath, indexContent); console.log(`āœ… Generated index.ts for ${version}`); } // Generate main schemas index.ts const mainIndexPath = resolve(`./schemas/index.ts`); const mainIndexContent = `/** * OpenAPI JSON Schemas * * This file exports all available JSON schemas for all OpenAPI specification versions. * These schemas can be used to validate OpenAPI documents and components. */ // Export schemas for each version export * as schemas2_0 from "./2.0"; export * as schemas3_0 from "./3.0"; export * as schemas3_1 from "./3.1"; export * as schemas3_2 from "./3.2"; import { schemas as schemas2_0 } from "./2.0"; import { schemas as schemas3_0 } from "./3.0"; import { schemas as schemas3_1 } from "./3.1"; import { schemas as schemas3_2 } from "./3.2"; // Export all schemas in a single object organized by version export const allSchemas = { "2.0": schemas2_0, "3.0": schemas3_0, "3.1": schemas3_1, "3.2": schemas3_2, } as const; `; writeFileSync(mainIndexPath, mainIndexContent); console.log("āœ… Generated main schemas index.ts"); } async function main() { console.log("šŸš€ Starting OpenAPI Types build process...\n"); const allResults: BuildResult[] = []; // Generate schemas for each version for (const version of versions) { const results = await generateAllSchemasForVersion(version); allResults.push(...results); console.log(`āœ… Completed OpenAPI ${version}\n`); } // Generate index files await generateIndexFiles(); // Summary const successful = allResults.filter((r) => r.success); const failed = allResults.filter((r) => !r.success); console.log("šŸ“Š Build Summary:"); console.log(`āœ… Successful: ${successful.length}`); console.log(`āŒ Failed: ${failed.length}`); if (failed.length > 0) { console.log("\nāŒ Failed generations:"); failed.forEach((result) => { console.log(` - ${result.version}/${result.type}: ${result.error}`); }); } console.log("\nšŸŽ‰ Build complete!"); console.log("šŸ“ Generated schemas are available in the 'schemas/' directory"); console.log("šŸ“– See schemas/README.md for usage instructions"); } // Run the build script main().catch(console.error);