@@ -8,6 +8,13 @@ import { ContextignoreLinter } from './contextignore_linter';
88import { getContextFiles , lintFileIfExists , fileExists , printHeader } from './utils/file_utils' ;
99import { ContextValidator , ValidationResult , SectionValidationResult } from './utils/validator' ;
1010
11+ export enum LogLevel {
12+ ERROR ,
13+ WARN ,
14+ INFO ,
15+ DEBUG
16+ }
17+
1118/**
1219 * ContextLinter class handles the linting of .context files (md, yaml, json)
1320 * and coordinates the use of ContextignoreLinter and ContextdocsLinter.
@@ -17,12 +24,31 @@ export class ContextLinter {
1724 private contextdocsLinter : ContextdocsLinter ;
1825 private contextignoreLinter : ContextignoreLinter ;
1926 private contextValidator : ContextValidator ;
27+ private logLevel : LogLevel ;
2028
21- constructor ( ) {
29+ constructor ( logLevel : LogLevel = LogLevel . INFO ) {
2230 this . md = new MarkdownIt ( ) ;
23- this . contextdocsLinter = new ContextdocsLinter ( ) ;
24- this . contextignoreLinter = new ContextignoreLinter ( ) ;
25- this . contextValidator = new ContextValidator ( ) ;
31+ this . contextdocsLinter = new ContextdocsLinter ( logLevel ) ;
32+ this . contextignoreLinter = new ContextignoreLinter ( logLevel ) ;
33+ this . contextValidator = new ContextValidator ( logLevel ) ;
34+ this . logLevel = logLevel ;
35+ }
36+
37+ private log ( level : LogLevel , message : string ) : void {
38+ if ( level <= this . logLevel ) {
39+ switch ( level ) {
40+ case LogLevel . ERROR :
41+ console . error ( message ) ;
42+ break ;
43+ case LogLevel . WARN :
44+ console . warn ( message ) ;
45+ break ;
46+ case LogLevel . INFO :
47+ case LogLevel . DEBUG :
48+ console . log ( message ) ;
49+ break ;
50+ }
51+ }
2652 }
2753
2854 /**
@@ -54,18 +80,26 @@ export class ContextLinter {
5480 // Log ignored files
5581 this . logIgnoredFiles ( directoryPath ) ;
5682
57- // Clear ignore cache after processing the directory
58- this . contextignoreLinter . clearCache ( ) ;
83+ // Clear all caches after processing the directory
84+ this . clearAllCaches ( ) ;
5985
60- console . log ( 'Linting completed.' ) ;
86+ this . log ( LogLevel . INFO , 'Linting completed.' ) ;
6187
6288 return isValid ;
6389 } catch ( error ) {
64- console . error ( `Error linting directory: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
90+ this . log ( LogLevel . ERROR , `Error linting directory: ${ error instanceof Error ? error . message : String ( error ) } ` ) ;
6591 return false ;
6692 }
6793 }
6894
95+ /**
96+ * Clear all caches from linter components
97+ */
98+ private clearAllCaches ( ) : void {
99+ this . contextignoreLinter . clearCache ( ) ;
100+ this . contextValidator . clearCache ( ) ;
101+ }
102+
69103 /**
70104 * Initialize ignore patterns from .contextignore file
71105 * @param directoryPath The path of the directory containing .contextignore
@@ -120,7 +154,7 @@ export class ContextLinter {
120154 result = await this . lintJsonFile ( fileContent , fullPath ) ;
121155 break ;
122156 default :
123- console . warn ( `Unsupported file extension: ${ fileExtension } ` ) ;
157+ this . log ( LogLevel . WARN , `Unsupported file extension: ${ fileExtension } ` ) ;
124158 continue ;
125159 }
126160
@@ -148,9 +182,9 @@ export class ContextLinter {
148182 private logIgnoredFiles ( directoryPath : string ) : void {
149183 const ignoredFiles = this . contextignoreLinter . getIgnoredFiles ( directoryPath ) ;
150184 if ( ignoredFiles . length > 0 ) {
151- console . log ( '\nIgnored files:' ) ;
185+ this . log ( LogLevel . INFO , '\nIgnored files:' ) ;
152186 for ( const file of ignoredFiles ) {
153- console . log ( ` ${ this . normalizePath ( file ) } ` ) ;
187+ this . log ( LogLevel . INFO , ` ${ this . normalizePath ( file ) } ` ) ;
154188 }
155189 }
156190 }
@@ -172,7 +206,7 @@ export class ContextLinter {
172206 isValid : validationResult . isValid && markdownValid
173207 } ;
174208 } catch ( error ) {
175- console . error ( ` Error parsing Markdown file: ${ error } ` ) ;
209+ this . log ( LogLevel . ERROR , ` Error parsing Markdown file: ${ error } ` ) ;
176210 return {
177211 isValid : false ,
178212 coveragePercentage : 0 ,
@@ -202,19 +236,19 @@ export class ContextLinter {
202236 if ( token . type === 'link_open' ) {
203237 const hrefToken = tokens [ tokens . indexOf ( token ) + 1 ] ;
204238 if ( hrefToken . type !== 'text' || ! hrefToken . content . startsWith ( 'http' ) ) {
205- console . error ( ' Warning: Link may be improperly formatted or using relative path.' ) ;
239+ this . log ( LogLevel . WARN , ' Warning: Link may be improperly formatted or using relative path.' ) ;
206240 isValid = false ;
207241 }
208242 }
209243
210244 if ( token . type === 'fence' && ! token . info ) {
211- console . error ( ' Warning: Code block is missing language specification.' ) ;
245+ this . log ( LogLevel . WARN , ' Warning: Code block is missing language specification.' ) ;
212246 isValid = false ;
213247 }
214248 }
215249
216250 if ( ! hasTitle ) {
217- console . error ( ' Error: Markdown content should start with a title (H1 heading).' ) ;
251+ this . log ( LogLevel . ERROR , ' Error: Markdown content should start with a title (H1 heading).' ) ;
218252 isValid = false ;
219253 }
220254
@@ -228,16 +262,16 @@ export class ContextLinter {
228262 * @returns A ValidationResult object
229263 */
230264 private async lintYamlFile ( content : string , filePath : string ) : Promise < ValidationResult > {
231- console . log ( ' - Validating YAML structure' ) ;
265+ this . log ( LogLevel . INFO , ' - Validating YAML structure' ) ;
232266
233267 try {
234268 const yamlData = this . parseYaml ( content ) ;
235269 return this . contextValidator . validateContextData ( yamlData , 'yaml' ) ;
236270 } catch ( error ) {
237271 if ( error instanceof yaml . YAMLException ) {
238- console . error ( ` Error parsing YAML file: ${ this . formatYamlError ( error ) } ` ) ;
272+ this . log ( LogLevel . ERROR , ` Error parsing YAML file: ${ this . formatYamlError ( error ) } ` ) ;
239273 } else {
240- console . error ( ` Error parsing YAML file: ${ error } ` ) ;
274+ this . log ( LogLevel . ERROR , ` Error parsing YAML file: ${ error } ` ) ;
241275 }
242276 return {
243277 isValid : false ,
@@ -257,16 +291,16 @@ export class ContextLinter {
257291 * @returns A ValidationResult object
258292 */
259293 private async lintJsonFile ( content : string , filePath : string ) : Promise < ValidationResult > {
260- console . log ( ' - Validating JSON structure' ) ;
294+ this . log ( LogLevel . INFO , ' - Validating JSON structure' ) ;
261295
262296 try {
263297 const jsonData = JSON . parse ( content ) as Record < string , unknown > ;
264298 return this . contextValidator . validateContextData ( jsonData , 'json' ) ;
265299 } catch ( error ) {
266300 if ( error instanceof SyntaxError ) {
267- console . error ( ` Error parsing JSON file: ${ this . formatJsonError ( error , content ) } ` ) ;
301+ this . log ( LogLevel . ERROR , ` Error parsing JSON file: ${ this . formatJsonError ( error , content ) } ` ) ;
268302 } else {
269- console . error ( ` Error parsing JSON file: ${ error } ` ) ;
303+ this . log ( LogLevel . ERROR , ` Error parsing JSON file: ${ error } ` ) ;
270304 }
271305 return {
272306 isValid : false ,
@@ -286,19 +320,19 @@ export class ContextLinter {
286320 */
287321 private printValidationResult ( result : ValidationResult , filePath : string ) : void {
288322 const relativePath = this . normalizePath ( path . relative ( process . cwd ( ) , filePath ) ) ;
289- console . log ( `Linting file: ${ relativePath } ` ) ;
323+ this . log ( LogLevel . INFO , `Linting file: ${ relativePath } ` ) ;
290324
291- console . log ( `main context: ${ result . coveragePercentage . toFixed ( 2 ) } % (${ result . coveredFields } /${ result . totalFields } top level fields)` ) ;
325+ this . log ( LogLevel . INFO , `main context: ${ result . coveragePercentage . toFixed ( 2 ) } % (${ result . coveredFields } /${ result . totalFields } top level fields)` ) ;
292326
293327 for ( const [ sectionName , sectionResult ] of Object . entries ( result . sections ) ) {
294- console . log ( `|- ${ sectionName } : ${ sectionResult . coveragePercentage . toFixed ( 2 ) } % (${ sectionResult . coveredFields } /${ sectionResult . totalFields } fields)` ) ;
328+ this . log ( LogLevel . INFO , `|- ${ sectionName } : ${ sectionResult . coveragePercentage . toFixed ( 2 ) } % (${ sectionResult . coveredFields } /${ sectionResult . totalFields } fields)` ) ;
295329 }
296330
297331 if ( ! result . isValid ) {
298- console . warn ( `⚠️ File has coverage warnings` ) ;
332+ this . log ( LogLevel . WARN , `⚠️ File has coverage warnings` ) ;
299333 }
300334
301- console . log ( '' ) ; // Add a blank line for better readability
335+ this . log ( LogLevel . INFO , '' ) ; // Add a blank line for better readability
302336 }
303337
304338 private parseYaml ( content : string ) : Record < string , unknown > {
0 commit comments