import { Injectable, Inject, forwardRef } from '@nestjs/common'; import { execFile } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs/promises'; import * as path from 'path'; // eslint-disable-next-line @typescript-eslint/no-require-imports const AsyncApiGenerator = require('@asyncapi/generator'); import { ApisService } from '../apis/apis.service'; const execFileAsync = promisify(execFile); /* Ajoute node_modules/.bin au PATH pour trouver redocly en local */ const localBin = path.join(process.cwd(), 'node_modules', '.bin'); const execEnv = { ...process.env, PATH: `${localBin}${path.delimiter}${process.env.PATH ?? ''}`, }; @Injectable() export class DocsGenerationService { private readonly outputDir = process.env.DOCS_OUTPUT_DIR ?? './docs-output'; constructor( @Inject(forwardRef(() => ApisService)) private readonly apisService: ApisService, ) {} async generate(entryId: string): Promise { const entry = await this.apisService.findOneOrThrow(entryId); const entryDir = path.join(this.outputDir, entryId); await fs.mkdir(entryDir, { recursive: true }); const yamlPath = path.join(entryDir, 'input.yaml'); await fs.writeFile(yamlPath, entry.yamlContent, 'utf-8'); try { if (entry.type === 'ASYNCAPI') { await this.generateAsyncApi(yamlPath, entryDir); } else { await this.generateOpenApi(yamlPath, entryDir); } const htmlPath = path.join(entryDir, 'index.html'); await this.apisService.updateInternal(entryId, { htmlPath, status: 'GENERATED' }); } catch (error) { await this.apisService.updateInternal(entryId, { status: 'ERROR', errorMessage: error instanceof Error ? error.message : String(error), }); } } private async generateAsyncApi(yamlPath: string, outputDir: string): Promise { /* API programmatique — aucun appel réseau, template résolu localement */ const templatePath = path.join(process.cwd(), 'node_modules', '@asyncapi', 'html-template'); const generator = new AsyncApiGenerator(templatePath, outputDir, { forceWrite: true }); await generator.generateFromFile(yamlPath); } private async generateOpenApi(yamlPath: string, outputDir: string): Promise { await execFileAsync('redocly', [ 'build-docs', yamlPath, '--output', path.join(outputDir, 'index.html'), ], { shell: true, env: execEnv }); } }