/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type { GitIgnoreFilter } from '../utils/gitIgnoreParser.js'; import type { QwenIgnoreFilter } from '../utils/qwenIgnoreParser.js'; import { GitIgnoreParser } from '../utils/gitIgnoreParser.js'; import { QwenIgnoreParser } from '../utils/qwenIgnoreParser.js'; import { isGitRepository } from '../utils/gitUtils.js'; import * as path from 'node:path'; export interface FilterFilesOptions { respectGitIgnore?: boolean; respectQwenIgnore?: boolean; } export interface FilterReport { filteredPaths: string[]; gitIgnoredCount: number; qwenIgnoredCount: number; } export class FileDiscoveryService { private gitIgnoreFilter: GitIgnoreFilter | null = null; private qwenIgnoreFilter: QwenIgnoreFilter | null = null; private projectRoot: string; constructor(projectRoot: string) { this.projectRoot = path.resolve(projectRoot); if (isGitRepository(this.projectRoot)) { this.gitIgnoreFilter = new GitIgnoreParser(this.projectRoot); } this.qwenIgnoreFilter = new QwenIgnoreParser(this.projectRoot); } /** * Filters a list of file paths based on git ignore rules */ filterFiles( filePaths: string[], options: FilterFilesOptions = { respectGitIgnore: true, respectQwenIgnore: true, }, ): string[] { return filePaths.filter((filePath) => { if (options.respectGitIgnore && this.shouldGitIgnoreFile(filePath)) { return false; } if (options.respectQwenIgnore && this.shouldQwenIgnoreFile(filePath)) { return false; } return true; }); } /** * Filters a list of file paths based on git ignore rules and returns a report * with counts of ignored files. */ filterFilesWithReport( filePaths: string[], opts: FilterFilesOptions = { respectGitIgnore: true, respectQwenIgnore: true, }, ): FilterReport { const filteredPaths: string[] = []; let gitIgnoredCount = 0; let qwenIgnoredCount = 0; for (const filePath of filePaths) { if (opts.respectGitIgnore && this.shouldGitIgnoreFile(filePath)) { gitIgnoredCount++; continue; } if (opts.respectQwenIgnore && this.shouldQwenIgnoreFile(filePath)) { qwenIgnoredCount++; continue; } filteredPaths.push(filePath); } return { filteredPaths, gitIgnoredCount, qwenIgnoredCount, }; } /** * Checks if a single file should be git-ignored */ shouldGitIgnoreFile(filePath: string): boolean { if (this.gitIgnoreFilter) { return this.gitIgnoreFilter.isIgnored(filePath); } return false; } /** * Checks if a single file should be qwen-ignored */ shouldQwenIgnoreFile(filePath: string): boolean { if (this.qwenIgnoreFilter) { return this.qwenIgnoreFilter.isIgnored(filePath); } return false; } /** * Unified method to check if a file should be ignored based on filtering options */ shouldIgnoreFile( filePath: string, options: FilterFilesOptions = {}, ): boolean { const { respectGitIgnore = true, respectQwenIgnore: respectQwenIgnore = true, } = options; if (respectGitIgnore && this.shouldGitIgnoreFile(filePath)) { return true; } if (respectQwenIgnore && this.shouldQwenIgnoreFile(filePath)) { return true; } return false; } /** * Returns loaded patterns from .qwenignore */ getQwenIgnorePatterns(): string[] { return this.qwenIgnoreFilter?.getPatterns() ?? []; } }