diff --git a/src/core/parser.ts b/src/core/parser.ts index 3bbb69c87c3..58124a6262e 100644 --- a/src/core/parser.ts +++ b/src/core/parser.ts @@ -12,6 +12,22 @@ import { red, yellow, green, cyan } from 'chalk'; import type { Diagnostic } from '@asyncapi/parser/cjs'; import type Command from './base'; import type { Specification } from './models/SpecificationFile'; +import { promises } from 'fs'; +import path from 'path'; + +const { writeFile } = promises; + +type DiagnosticsFormat = 'stylish' | 'json' | 'junit' | 'html' | 'text' | 'teamcity' | 'pretty'; + +const formatExtensions: Record = { + stylish: '.txt', + json: '.json', + junit: '.xml', + html: '.html', + text: '.txt', + teamcity: '.txt', + pretty: '.txt', +}; export type SeverityKind = 'error' | 'warn' | 'info' | 'hint'; @@ -56,6 +72,10 @@ export function validationFlags({ logDiagnostics = true }: ValidationFlagsOption options: ['error', 'warn', 'info', 'hint'] as const, default: 'error', })(), + 'output': Flags.string({ + description: 'The output file name. Omitting this flag the result will be printed in the console.', + char: 'o' + }) }; } @@ -63,6 +83,7 @@ export interface ValidateOptions { 'log-diagnostics'?: boolean; 'diagnostics-format'?: `${OutputFormat}`; 'fail-severity'?: SeverityKind; + 'output'?: string; } export async function validate(command: Command, specFile: Specification, options: ValidateOptions = {}) { @@ -86,14 +107,24 @@ function logDiagnostics(diagnostics: Diagnostic[], command: Command, specFile: S if (hasFailSeverity(diagnostics, failSeverity)) { if (logDiagnostics) { command.logToStderr(`\n${sourceString} and/or referenced documents have governance issues.`); - command.logToStderr(formatOutput(diagnostics, diagnosticsFormat, failSeverity)); + const diagnosticsOutput = formatOutput(diagnostics, diagnosticsFormat, failSeverity); + if (options.output) { + writeValidationDiagnostic(options.output, command, diagnosticsFormat, diagnosticsOutput); + } else { + command.log(diagnosticsOutput); + } } return ValidationStatus.INVALID; } if (logDiagnostics) { command.log(`\n${sourceString} is valid but has (itself and/or referenced documents) governance issues.`); - command.log(formatOutput(diagnostics, diagnosticsFormat, failSeverity)); + const diagnosticsOutput = formatOutput(diagnostics, diagnosticsFormat, failSeverity); + if (options.output) { + writeValidationDiagnostic(options.output, command, diagnosticsFormat, diagnosticsOutput); + } else { + command.log(diagnosticsOutput); + } } } else if (logDiagnostics) { command.log(`\n${sourceString} is valid! ${sourceString} and referenced documents don't have governance issues.`); @@ -145,3 +176,18 @@ function hasFailSeverity(diagnostics: Diagnostic[], failSeverity: SeverityKind) const diagnosticSeverity = getDiagnosticSeverity(failSeverity); return diagnostics.some(diagnostic => diagnostic.severity <= diagnosticSeverity); } + +async function writeValidationDiagnostic(outputPath: string, command: Command, format: DiagnosticsFormat, formatOutput: string){ + const expectedExtension = formatExtensions[format]; + const actualExtension = path.extname(outputPath); + + // Validate file extension against diagnostics format + if (expectedExtension && (actualExtension !== expectedExtension)) { + command.logToStderr(`Invalid file extension for format "${format}". Expected extension: "${expectedExtension}"`); + } else { + await writeFile(path.resolve(process.cwd(), outputPath), formatOutput, { + encoding: 'utf-8', + }).catch(err => console.log(err)); + } + +}