/** * Channels Code Generator * * Generates TypeScript types and React hooks from Channels OpenAPI schema. * Uses openapi-typescript for robust type generation. */ import openapiTS, { astToString } from 'openapi-typescript' /** * Generate channels TypeScript types using openapi-typescript. */ export async function generateChannelsTypes(schema) { // Generate types using openapi-typescript const ast = await openapiTS(schema) const typesCode = astToString(ast) const lines = [ '// AUTO-GENERATED by mizan - do not edit manually', '// Regenerate with: npm run schemas', '', '// ============================================================================', '// OpenAPI Types (generated by openapi-typescript)', '// ============================================================================', '', typesCode, '', ] // Extract channel metadata from x-mizan-channels extension const channels = schema['x-mizan-channels'] || [] if (channels.length > 0) { lines.push('// ============================================================================') lines.push('// Convenience Type Exports') lines.push('// ============================================================================') lines.push('') for (const channel of channels) { if (channel.hasParams) { lines.push(`export type ${channel.paramsType} = components["schemas"]["${channel.paramsType}"]`) } if (channel.hasReactMessage) { lines.push(`export type ${channel.reactMessageType} = components["schemas"]["${channel.reactMessageType}"]`) } if (channel.hasDjangoMessage) { lines.push(`export type ${channel.djangoMessageType} = components["schemas"]["${channel.djangoMessageType}"]`) } } lines.push('') lines.push('// ============================================================================') lines.push('// Channel Registry') lines.push('// ============================================================================') lines.push('') lines.push('export const CHANNELS = {') for (const channel of channels) { lines.push(` ${channel.name}: {`) lines.push(` name: '${channel.name}',`) lines.push(` pascalName: '${channel.pascalName}',`) lines.push(` hasParams: ${channel.hasParams},`) lines.push(` hasReactMessage: ${channel.hasReactMessage},`) lines.push(` hasDjangoMessage: ${channel.hasDjangoMessage},`) if (channel.hasParams) { lines.push(` paramsType: '${channel.paramsType}',`) } if (channel.hasReactMessage) { lines.push(` reactMessageType: '${channel.reactMessageType}',`) } if (channel.hasDjangoMessage) { lines.push(` djangoMessageType: '${channel.djangoMessageType}',`) } lines.push(` },`) } lines.push('} as const') } else { lines.push('export const CHANNELS = {} as const') } lines.push('') return lines.join('\n') } /** * Generate channel hooks from metadata. */ export function generateChannelsHooks(schema) { const channels = schema['x-mizan-channels'] || [] if (channels.length === 0) { return null } const lines = [ "'use client'", '', '// AUTO-GENERATED by mizan - do not edit manually', '// Regenerate with: npm run schemas', '', "import { useChannel, type ChannelSubscription } from 'mizan/channels'", '', ] // Collect type imports const typeImports = [] for (const channel of channels) { if (channel.hasParams) typeImports.push(channel.paramsType) if (channel.hasReactMessage) typeImports.push(channel.reactMessageType) if (channel.hasDjangoMessage) typeImports.push(channel.djangoMessageType) } if (typeImports.length > 0) { lines.push(`import type { ${typeImports.join(', ')} } from './generated.channels'`) lines.push('') } // Generate hooks for each channel lines.push('// ============================================================================') lines.push('// Channel Hooks') lines.push('// ============================================================================') lines.push('') for (const channel of channels) { const paramsType = channel.hasParams ? channel.paramsType : 'Record' const reactMsgType = channel.hasReactMessage ? channel.reactMessageType : 'never' const djangoMsgType = channel.hasDjangoMessage ? channel.djangoMessageType : 'never' lines.push(`/**`) lines.push(` * Hook for the ${channel.name} channel.`) lines.push(` */`) if (channel.hasParams) { lines.push(`export function use${channel.pascalName}Channel(params: ${paramsType}): ChannelSubscription<${paramsType}, ${djangoMsgType}, ${reactMsgType}> {`) lines.push(` return useChannel('${channel.name}', params)`) } else { lines.push(`export function use${channel.pascalName}Channel(): ChannelSubscription, ${djangoMsgType}, ${reactMsgType}> {`) lines.push(` return useChannel('${channel.name}', {})`) } lines.push('}') lines.push('') } return lines.join('\n') } /** * Generate all channels files. */ export async function generateChannelsFiles(schema) { const types = await generateChannelsTypes(schema) const hooks = generateChannelsHooks(schema) return { types, hooks } }