Skip to content

Commit 8249570

Browse files
committed
fix: fix the recursion and logging
1 parent 5d44aa5 commit 8249570

File tree

3 files changed

+95
-75
lines changed

3 files changed

+95
-75
lines changed

linters/typescript/src/context_linter.ts

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -133,34 +133,32 @@ export class ContextLinter {
133133
*/
134134
private async handleContextFilesRecursively(directoryPath: string): Promise<boolean> {
135135
let isValid = true;
136-
const contextFiles = await getContextFiles(directoryPath);
136+
const contextFiles = await getContextFiles(directoryPath, (filePath, relativeTo) => this.contextignoreLinter.isIgnored(filePath, relativeTo));
137137

138138
for (const filePath of contextFiles) {
139-
if (!this.contextignoreLinter.isIgnored(filePath, directoryPath)) {
140-
const fullPath = path.join(directoryPath, filePath);
141-
const fileContent = await fs.promises.readFile(fullPath, 'utf-8');
142-
const fileExtension = path.extname(fullPath);
143-
let result: ValidationResult;
144-
145-
switch (fileExtension) {
146-
case '.md':
147-
result = await this.lintMarkdownFile(fileContent, fullPath);
148-
break;
149-
case '.yaml':
150-
case '.yml':
151-
result = await this.lintYamlFile(fileContent, fullPath);
152-
break;
153-
case '.json':
154-
result = await this.lintJsonFile(fileContent, fullPath);
155-
break;
156-
default:
157-
this.log(LogLevel.WARN, `Unsupported file extension: ${fileExtension}`);
158-
continue;
159-
}
160-
161-
this.printValidationResult(result, fullPath);
162-
isValid = isValid && result.isValid;
139+
const fullPath = path.join(directoryPath, filePath);
140+
const fileContent = await fs.promises.readFile(fullPath, 'utf-8');
141+
const fileExtension = path.extname(fullPath);
142+
let result: ValidationResult;
143+
144+
switch (fileExtension) {
145+
case '.md':
146+
result = await this.lintMarkdownFile(fileContent, fullPath);
147+
break;
148+
case '.yaml':
149+
case '.yml':
150+
result = await this.lintYamlFile(fileContent, fullPath);
151+
break;
152+
case '.json':
153+
result = await this.lintJsonFile(fileContent, fullPath);
154+
break;
155+
default:
156+
this.log(LogLevel.WARN, `Unsupported file extension: ${fileExtension}`);
157+
continue;
163158
}
159+
160+
this.printValidationResult(result, fullPath);
161+
isValid = isValid && result.isValid;
164162
}
165163

166164
// Recursively process subdirectories
@@ -180,11 +178,13 @@ export class ContextLinter {
180178
* @param directoryPath The path of the directory to check for ignored files
181179
*/
182180
private logIgnoredFiles(directoryPath: string): void {
183-
const ignoredFiles = this.contextignoreLinter.getIgnoredFiles(directoryPath);
184-
if (ignoredFiles.length > 0) {
185-
this.log(LogLevel.INFO, '\nIgnored files:');
186-
for (const file of ignoredFiles) {
187-
this.log(LogLevel.INFO, ` ${this.normalizePath(file)}`);
181+
if (this.logLevel === LogLevel.DEBUG) {
182+
const ignoredFiles = this.contextignoreLinter.getIgnoredFiles(directoryPath);
183+
if (ignoredFiles.length > 0) {
184+
this.log(LogLevel.DEBUG, '\nIgnored files:');
185+
for (const file of ignoredFiles) {
186+
this.log(LogLevel.DEBUG, ` ${this.normalizePath(file)}`);
187+
}
188188
}
189189
}
190190
}
@@ -262,7 +262,7 @@ export class ContextLinter {
262262
* @returns A ValidationResult object
263263
*/
264264
private async lintYamlFile(content: string, filePath: string): Promise<ValidationResult> {
265-
this.log(LogLevel.INFO, ' - Validating YAML structure');
265+
this.log(LogLevel.DEBUG, ' - Validating YAML structure');
266266

267267
try {
268268
const yamlData = this.parseYaml(content);
@@ -291,7 +291,7 @@ export class ContextLinter {
291291
* @returns A ValidationResult object
292292
*/
293293
private async lintJsonFile(content: string, filePath: string): Promise<ValidationResult> {
294-
this.log(LogLevel.INFO, ' - Validating JSON structure');
294+
this.log(LogLevel.DEBUG, ' - Validating JSON structure');
295295

296296
try {
297297
const jsonData = JSON.parse(content) as Record<string, unknown>;
@@ -322,10 +322,23 @@ export class ContextLinter {
322322
const relativePath = this.normalizePath(path.relative(process.cwd(), filePath));
323323
this.log(LogLevel.INFO, `Linting file: ${relativePath}`);
324324

325-
this.log(LogLevel.INFO, `main context: ${result.coveragePercentage.toFixed(2)}% (${result.coveredFields}/${result.totalFields} top level fields)`);
325+
// Display main context coverage information at INFO level
326+
this.log(LogLevel.INFO, `Main context: ${result.coveragePercentage.toFixed(2)}% (${result.coveredFields}/${result.totalFields} top level fields)`);
326327

327-
for (const [sectionName, sectionResult] of Object.entries(result.sections)) {
328-
this.log(LogLevel.INFO, `|- ${sectionName}: ${sectionResult.coveragePercentage.toFixed(2)}% (${sectionResult.coveredFields}/${sectionResult.totalFields} fields)`);
328+
// Display section names at INFO level
329+
const sectionNames = Object.keys(result.sections);
330+
if (sectionNames.length > 0) {
331+
this.log(LogLevel.INFO, 'Sections:');
332+
sectionNames.forEach(sectionName => {
333+
this.log(LogLevel.INFO, ` - ${sectionName}`);
334+
});
335+
}
336+
337+
// Display detailed section coverage information at DEBUG level
338+
if (this.logLevel === LogLevel.DEBUG) {
339+
for (const [sectionName, sectionResult] of Object.entries(result.sections)) {
340+
this.log(LogLevel.DEBUG, `|- ${sectionName}: ${sectionResult.coveragePercentage.toFixed(2)}% (${sectionResult.coveredFields}/${sectionResult.totalFields} fields)`);
341+
}
329342
}
330343

331344
if (!result.isValid) {

linters/typescript/src/contextignore_linter.ts

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,21 @@ export class ContextignoreLinter {
1313
// Cache of ignore instances for each directory
1414
private ignoreCache: Map<string, ReturnType<typeof ignore>>;
1515
private logLevel: LogLevel;
16+
private errorMessages: string[];
1617

1718
constructor(logLevel: LogLevel = LogLevel.INFO) {
1819
this.criticalPatterns = new Set(['.context.md', '.context.yaml', '.context.json', '.contextdocs.md', '.contextignore']);
1920
this.ignoreCache = new Map();
2021
this.logLevel = logLevel;
22+
this.errorMessages = [];
2123
}
2224

2325
private log(level: LogLevel, message: string): void {
2426
if (level <= this.logLevel) {
2527
switch (level) {
2628
case LogLevel.ERROR:
2729
console.error(message);
30+
this.errorMessages.push(message);
2831
break;
2932
case LogLevel.WARN:
3033
console.warn(message);
@@ -37,18 +40,34 @@ export class ContextignoreLinter {
3740
}
3841
}
3942

43+
/**
44+
* Get the collected error messages
45+
* @returns An array of error messages
46+
*/
47+
public getErrorMessages(): string[] {
48+
return this.errorMessages;
49+
}
50+
51+
/**
52+
* Clear the collected error messages
53+
*/
54+
public clearErrorMessages(): void {
55+
this.errorMessages = [];
56+
}
57+
4058
/**
4159
* Lint a .contextignore file
4260
* @param content The content of the .contextignore file
4361
* @param filePath The path of the .contextignore file
4462
* @returns A boolean indicating whether the file is valid
4563
*/
4664
public async lintContextignoreFile(content: string, filePath: string): Promise<boolean> {
65+
this.clearErrorMessages();
4766
try {
4867
const relativePath = path.relative(process.cwd(), filePath);
49-
this.log(LogLevel.INFO, `\nLinting file: ${relativePath}`);
50-
this.log(LogLevel.INFO, '- Validating .contextignore format');
51-
this.log(LogLevel.INFO, '- Checking for valid ignore patterns');
68+
this.log(LogLevel.DEBUG, `\nLinting file: ${relativePath}`);
69+
this.log(LogLevel.DEBUG, '- Validating .contextignore format');
70+
this.log(LogLevel.DEBUG, '- Checking for valid ignore patterns');
5271

5372
const lines = content.split('\n').map(line => line.trim()).filter(line => line !== '' && !line.startsWith('#'));
5473
const patterns = new Set<string>();
@@ -78,7 +97,7 @@ export class ContextignoreLinter {
7897
this.log(LogLevel.WARN, '⚠️ File has validation warnings');
7998
}
8099

81-
this.log(LogLevel.INFO, ''); // Add a blank line for better readability
100+
this.log(LogLevel.DEBUG, ''); // Add a blank line for better readability
82101
return isValid;
83102
} catch (error) {
84103
this.log(LogLevel.ERROR, `Error linting .contextignore file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
@@ -150,11 +169,14 @@ export class ContextignoreLinter {
150169
const ig = this.ignoreCache.get(currentDir);
151170
if (ig) {
152171
const relativeFilePath = path.relative(currentDir, filePath);
153-
return ig.ignores(relativeFilePath);
172+
const ignored = ig.ignores(relativeFilePath);
173+
this.log(LogLevel.DEBUG, `File ${filePath} is ${ignored ? 'ignored' : 'not ignored'}`);
174+
return ignored;
154175
}
155176
currentDir = path.dirname(currentDir);
156177
}
157178

179+
this.log(LogLevel.DEBUG, `File ${filePath} is not ignored (no .contextignore found)`);
158180
return false;
159181
} catch (error) {
160182
this.log(LogLevel.ERROR, `Error checking if file ${filePath} is ignored: ${error instanceof Error ? error.message : String(error)}`);
@@ -181,41 +203,25 @@ export class ContextignoreLinter {
181203
return [];
182204
}
183205

184-
// Get all files in the directory
185-
const allFiles = this.getAllFiles(directoryPath);
206+
const ignoredFiles: string[] = [];
207+
const walk = (dir: string) => {
208+
const dirents = fs.readdirSync(dir, { withFileTypes: true });
209+
for (const dirent of dirents) {
210+
const res = path.join(dir, dirent.name);
211+
const relativePath = path.relative(directoryPath, res);
212+
if (ig.ignores(relativePath)) {
213+
ignoredFiles.push(res);
214+
} else if (dirent.isDirectory()) {
215+
walk(res);
216+
}
217+
}
218+
};
186219

187-
// Filter the files using the ignore patterns
188-
return allFiles.filter(file => {
189-
const relativePath = path.relative(directoryPath, file);
190-
return ig.ignores(relativePath);
191-
});
220+
walk(directoryPath);
221+
return ignoredFiles;
192222
} catch (error) {
193223
this.log(LogLevel.ERROR, `Error getting ignored files for directory ${directoryPath}: ${error instanceof Error ? error.message : String(error)}`);
194224
return [];
195225
}
196226
}
197-
198-
/**
199-
* Get all files in a directory recursively
200-
* @param directoryPath The path of the directory to check
201-
* @returns An array of file paths
202-
*/
203-
private getAllFiles(directoryPath: string): string[] {
204-
const files: string[] = [];
205-
206-
const walk = (dir: string) => {
207-
const dirents = fs.readdirSync(dir, { withFileTypes: true });
208-
for (const dirent of dirents) {
209-
const res = path.join(dir, dirent.name);
210-
if (dirent.isDirectory()) {
211-
walk(res);
212-
} else {
213-
files.push(res);
214-
}
215-
}
216-
};
217-
218-
walk(directoryPath);
219-
return files;
220-
}
221227
}

linters/typescript/src/utils/file_utils.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import * as fs from 'fs/promises';
22
import * as path from 'path';
33

4-
export async function getContextFiles(directoryPath: string): Promise<string[]> {
4+
export async function getContextFiles(directoryPath: string, isIgnored: (filePath: string, relativeTo: string) => boolean): Promise<string[]> {
55
const files = await fs.readdir(directoryPath);
66
return files.filter(file =>
7-
file.endsWith('.context.md') ||
8-
file.endsWith('.context.yaml') ||
9-
file.endsWith('.context.json')
7+
(file.endsWith('.context.md') ||
8+
file.endsWith('.context.yaml') ||
9+
file.endsWith('.context.json')) &&
10+
!isIgnored(file, directoryPath)
1011
);
1112
}
1213

0 commit comments

Comments
 (0)