@@ -12,11 +12,29 @@ import { red, yellow, green, cyan } from 'chalk';
12
12
import type { Diagnostic } from '@asyncapi/parser/cjs' ;
13
13
import type Command from './base' ;
14
14
import type { Specification } from './models/SpecificationFile' ;
15
+ import { promises } from 'fs' ;
16
+ import path from 'path' ;
17
+
18
+ type DiagnosticsFormat = 'stylish' | 'json' | 'junit' | 'html' | 'text' | 'teamcity' | 'pretty' ;
15
19
16
20
export type SeverityKind = 'error' | 'warn' | 'info' | 'hint' ;
17
21
18
22
export { convertToOldAPI } ;
19
23
24
+ const { writeFile } = promises ;
25
+
26
+ const formatExtensions : Record < DiagnosticsFormat , string > = {
27
+ stylish : '.txt' ,
28
+ json : '.json' ,
29
+ junit : '.xml' ,
30
+ html : '.html' ,
31
+ text : '.txt' ,
32
+ teamcity : '.txt' ,
33
+ pretty : '.txt' ,
34
+ } ;
35
+
36
+ const validFormats = [ 'stylish' , 'json' , 'junit' , 'html' , 'text' , 'teamcity' , 'pretty' ] ;
37
+
20
38
const parser = new Parser ( {
21
39
__unstable : {
22
40
resolver : {
@@ -56,13 +74,18 @@ export function validationFlags({ logDiagnostics = true }: ValidationFlagsOption
56
74
options : [ 'error' , 'warn' , 'info' , 'hint' ] as const ,
57
75
default : 'error' ,
58
76
} ) ( ) ,
77
+ output : Flags . string ( {
78
+ description : 'The output file name. Omitting this flag the result will be printed in the console.' ,
79
+ char : 'o'
80
+ } )
59
81
} ;
60
82
}
61
83
62
84
export interface ValidateOptions {
63
85
'log-diagnostics' ?: boolean ;
64
86
'diagnostics-format' ?: `${OutputFormat } `;
65
87
'fail-severity' ?: SeverityKind ;
88
+ 'output' ?: string ;
66
89
}
67
90
68
91
export async function validate ( command : Command , specFile : Specification , options : ValidateOptions = { } ) {
@@ -76,30 +99,56 @@ export async function parse(command: Command, specFile: Specification, options:
76
99
return { document, diagnostics, status } ;
77
100
}
78
101
79
- function logDiagnostics ( diagnostics : Diagnostic [ ] , command : Command , specFile : Specification , options : ValidateOptions = { } ) : 'valid' | 'invalid' {
102
+ function logDiagnostics (
103
+ diagnostics : Diagnostic [ ] ,
104
+ command : Command ,
105
+ specFile : Specification ,
106
+ options : ValidateOptions = { }
107
+ ) : 'valid' | 'invalid' {
80
108
const logDiagnostics = options [ 'log-diagnostics' ] ;
81
109
const failSeverity = options [ 'fail-severity' ] ?? 'error' ;
82
110
const diagnosticsFormat = options [ 'diagnostics-format' ] ?? 'stylish' ;
83
-
84
111
const sourceString = specFile . toSourceString ( ) ;
85
- if ( diagnostics . length ) {
86
- if ( hasFailSeverity ( diagnostics , failSeverity ) ) {
87
- if ( logDiagnostics ) {
88
- command . logToStderr ( `\n${ sourceString } and/or referenced documents have governance issues.` ) ;
89
- command . logToStderr ( formatOutput ( diagnostics , diagnosticsFormat , failSeverity ) ) ;
90
- }
91
- return ValidationStatus . INVALID ;
92
- }
93
112
94
- if ( logDiagnostics ) {
95
- command . log ( `\n${ sourceString } is valid but has (itself and/or referenced documents) governance issues.` ) ;
96
- command . log ( formatOutput ( diagnostics , diagnosticsFormat , failSeverity ) ) ;
97
- }
98
- } else if ( logDiagnostics ) {
113
+ const hasIssues = diagnostics . length > 0 ;
114
+ const isFailSeverity = hasIssues && hasFailSeverity ( diagnostics , failSeverity ) ;
115
+
116
+ if ( logDiagnostics ) {
117
+ logGovernanceMessage ( command , sourceString , hasIssues , isFailSeverity ) ;
118
+ outputDiagnostics ( command , diagnostics , diagnosticsFormat , failSeverity , options ) ;
119
+ }
120
+
121
+ return isFailSeverity ? ValidationStatus . INVALID : ValidationStatus . VALID ;
122
+ }
123
+
124
+ function logGovernanceMessage (
125
+ command : Command ,
126
+ sourceString : string ,
127
+ hasIssues : boolean ,
128
+ isFailSeverity : boolean
129
+ ) {
130
+ if ( ! hasIssues ) {
99
131
command . log ( `\n${ sourceString } is valid! ${ sourceString } and referenced documents don't have governance issues.` ) ;
132
+ } else if ( isFailSeverity ) {
133
+ command . logToStderr ( `\n${ sourceString } and/or referenced documents have governance issues.` ) ;
134
+ } else {
135
+ command . log ( `\n${ sourceString } is valid but has (itself and/or referenced documents) governance issues.` ) ;
100
136
}
137
+ }
101
138
102
- return ValidationStatus . VALID ;
139
+ function outputDiagnostics (
140
+ command : Command ,
141
+ diagnostics : Diagnostic [ ] ,
142
+ diagnosticsFormat : DiagnosticsFormat ,
143
+ failSeverity : SeverityKind ,
144
+ options : ValidateOptions
145
+ ) {
146
+ const diagnosticsOutput = formatOutput ( diagnostics , diagnosticsFormat , failSeverity ) ;
147
+ if ( options . output ) {
148
+ writeValidationDiagnostic ( options . output , command , diagnosticsFormat , diagnosticsOutput ) ;
149
+ } else {
150
+ command . log ( diagnosticsOutput ) ;
151
+ }
103
152
}
104
153
105
154
export function formatOutput ( diagnostics : Diagnostic [ ] , format : `${OutputFormat } `, failSeverity : SeverityKind ) {
@@ -145,3 +194,22 @@ function hasFailSeverity(diagnostics: Diagnostic[], failSeverity: SeverityKind)
145
194
const diagnosticSeverity = getDiagnosticSeverity ( failSeverity ) ;
146
195
return diagnostics . some ( diagnostic => diagnostic . severity <= diagnosticSeverity ) ;
147
196
}
197
+
198
+ async function writeValidationDiagnostic ( outputPath : string , command : Command , format : DiagnosticsFormat , formatOutput : string ) {
199
+ if ( ! validFormats . includes ( format ) ) {
200
+ command . logToStderr ( `Invalid diagnostics format: "${ format } "` ) ;
201
+ return ;
202
+ }
203
+
204
+ const expectedExtension = formatExtensions [ format as keyof typeof formatExtensions ] ;
205
+ const actualExtension = path . extname ( outputPath ) ;
206
+
207
+ // Validate file extension against diagnostics format
208
+ if ( expectedExtension && ( actualExtension !== expectedExtension ) ) {
209
+ command . logToStderr ( `Invalid file extension for format "${ format } ". Expected extension: "${ expectedExtension } "` ) ;
210
+ } else {
211
+ await writeFile ( path . resolve ( process . cwd ( ) , outputPath ) , formatOutput , {
212
+ encoding : 'utf-8' ,
213
+ } ) . catch ( err => console . log ( err ) ) ;
214
+ }
215
+ }
0 commit comments