@@ -4,20 +4,19 @@ import MarkdownIt from 'markdown-it';
44import { ContextdocsLinter } from './contextdocs_linter' ;
55import { ContextignoreLinter } from './contextignore_linter' ;
66import { getContextFiles , lintFileIfExists , fileExists , printHeader } from './utils/file_utils' ;
7- import { kebabToCamelCase } from './utils/string_utils' ;
8- import { allowedTopLevelFields , sectionChecks , listTypes , stringTypes , directoryTypes } from './utils/context_structure' ;
7+ import { ContextValidator } from './utils/validator' ;
98
109export class ContextLinter {
1110 private md : MarkdownIt ;
12- private kebabToCamelCache : Map < string , string > ;
1311 private contextdocsLinter : ContextdocsLinter ;
1412 private contextignoreLinter : ContextignoreLinter ;
13+ private contextValidator : ContextValidator ;
1514
1615 constructor ( ) {
1716 this . md = new MarkdownIt ( ) ;
18- this . kebabToCamelCache = new Map ( ) ;
1917 this . contextdocsLinter = new ContextdocsLinter ( ) ;
2018 this . contextignoreLinter = new ContextignoreLinter ( ) ;
19+ this . contextValidator = new ContextValidator ( ) ;
2120 }
2221
2322 public async lintDirectory ( directoryPath : string , packageVersion : string ) : Promise < void > {
@@ -84,70 +83,14 @@ export class ContextLinter {
8483
8584 try {
8685 const frontmatterData = yaml . load ( frontmatter ) as Record < string , unknown > ;
87- this . validateContextData ( frontmatterData , 'markdown' ) ;
86+ this . contextValidator . validateContextData ( frontmatterData , 'markdown' ) ;
8887 } catch ( error ) {
8988 console . error ( ` Error parsing YAML frontmatter: ${ error } ` ) ;
9089 }
9190
9291 this . validateMarkdownContent ( markdownContent ) ;
9392 }
9493
95- private validateContextData ( data : Record < string , unknown > , format : 'markdown' | 'yaml' | 'json' ) : void {
96- const isJson = format === 'json' ;
97- const coveredFields = new Set < string > ( ) ;
98-
99- for ( const [ field , value ] of Object . entries ( data ) ) {
100- const normalizedField = isJson ? kebabToCamelCase ( field , this . kebabToCamelCache ) : field ;
101-
102- if ( allowedTopLevelFields . has ( normalizedField ) ) {
103- coveredFields . add ( normalizedField ) ;
104- this . validateField ( normalizedField , value , isJson ) ;
105- } else {
106- console . warn ( ` Warning: Unexpected top-level field '${ normalizedField } '.` ) ;
107- }
108- }
109-
110- const coveragePercentage = ( coveredFields . size / allowedTopLevelFields . size ) * 100 ;
111- console . log ( ` Context coverage: ${ coveragePercentage . toFixed ( 2 ) } % (${ coveredFields . size } /${ allowedTopLevelFields . size } fields)` ) ;
112-
113- for ( const section of Object . keys ( sectionChecks ) ) {
114- if ( section in data ) {
115- this . validateSectionFields ( section , data [ section ] as Record < string , unknown > , isJson ) ;
116- }
117- }
118- }
119-
120- private validateField ( field : string , value : unknown , isJson : boolean ) : void {
121- if ( listTypes . has ( field ) && ! Array . isArray ( value ) ) {
122- console . error ( ` Error: Field '${ field } ' should be an array.` ) ;
123- } else if ( stringTypes . has ( field ) && typeof value !== 'string' ) {
124- console . error ( ` Error: Field '${ field } ' should be a string.` ) ;
125- } else if ( directoryTypes . has ( field ) ) {
126- // Additional validation for directory types can be added here
127- }
128- }
129-
130- private validateSectionFields ( sectionName : string , data : Record < string , unknown > , isJson : boolean ) : void {
131- const checks = sectionChecks [ sectionName ] ;
132- const coveredFields = new Set < string > ( ) ;
133-
134- if ( checks ) {
135- for ( const [ field , value ] of Object . entries ( data ) ) {
136- const normalizedField = isJson ? kebabToCamelCase ( field , this . kebabToCamelCache ) : field ;
137-
138- if ( checks . has ( normalizedField ) ) {
139- coveredFields . add ( normalizedField ) ;
140- this . validateField ( normalizedField , value , isJson ) ;
141- } else {
142- console . warn ( ` Warning: Unexpected field '${ normalizedField } ' in '${ sectionName } ' section.` ) ;
143- }
144- }
145-
146- const coveragePercentage = ( coveredFields . size / checks . size ) * 100 ;
147- console . log ( ` ${ sectionName } coverage: ${ coveragePercentage . toFixed ( 2 ) } % (${ coveredFields . size } /${ checks . size } fields)` ) ;
148- }
149- }
150-
15194 private validateMarkdownContent ( content : string ) : void {
15295 const tokens = this . md . parse ( content , { } ) ;
15396 let hasTitle = false ;
@@ -180,7 +123,7 @@ export class ContextLinter {
180123
181124 try {
182125 const yamlData = yaml . load ( content ) as Record < string , unknown > ;
183- this . validateContextData ( yamlData , 'yaml' ) ;
126+ this . contextValidator . validateContextData ( yamlData , 'yaml' ) ;
184127 } catch ( error ) {
185128 console . error ( ` Error parsing YAML file: ${ error } ` ) ;
186129 }
@@ -192,7 +135,7 @@ export class ContextLinter {
192135
193136 try {
194137 const jsonData = JSON . parse ( content ) as Record < string , unknown > ;
195- this . validateContextData ( jsonData , 'json' ) ;
138+ this . contextValidator . validateContextData ( jsonData , 'json' ) ;
196139 } catch ( error ) {
197140 console . error ( ` Error parsing JSON file: ${ error } ` ) ;
198141 }
0 commit comments