UNPKG

structure-validation

Version:

A Node.js CLI tool for validating codebase folder and file structure using a clean declarative configuration. Part of the guardz ecosystem for comprehensive TypeScript development.

189 lines 7.16 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileDiscoveryService = void 0; const FileInfo_1 = require("../../domain/entities/FileInfo"); const CacheService_1 = require("../../infrastructure/cache/CacheService"); const fast_glob_1 = __importDefault(require("fast-glob")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); /** * Application service for discovering files in the codebase */ class FileDiscoveryService { constructor(basePath = process.cwd(), configRoot) { this.basePath = basePath; this.configRoot = configRoot; this.cacheService = new CacheService_1.CacheService(); // Clear cache when configRoot is provided to ensure fresh discovery if (configRoot) { this.clearCache(); } } /** * Load skipped files from structure-validation.skip.json */ loadSkippedFiles() { const skipFilePath = path_1.default.join(this.basePath, 'structure-validation.skip.json'); if (!fs_1.default.existsSync(skipFilePath)) { return []; } try { const skipContent = fs_1.default.readFileSync(skipFilePath, 'utf8'); const skippedFiles = JSON.parse(skipContent); return Array.isArray(skippedFiles) ? skippedFiles : []; } catch (error) { console.warn('⚠️ Invalid skip file format, ignoring skipped files.'); return []; } } /** * Filter out skipped files from the file list */ filterSkippedFiles(files) { const skippedFiles = this.loadSkippedFiles(); const skippedSet = new Set(skippedFiles.map(file => path_1.default.resolve(this.basePath, file))); return files.filter(file => !skippedSet.has(file)); } /** * Discover all files in the codebase */ async discoverAllFiles() { const patterns = [ '**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.json', '**/*.md', '**/*.yml', '**/*.yaml' ]; // Use config root if available, otherwise use base path const searchPath = this.configRoot ? path_1.default.join(this.basePath, this.configRoot) : this.basePath; // Try to get from cache first const cacheKey = this.cacheService.getFileDiscoveryKey(searchPath, patterns, this.configRoot); const cachedFiles = this.cacheService.get(cacheKey); let files; if (cachedFiles) { files = cachedFiles; } else { files = await (0, fast_glob_1.default)(patterns, { cwd: searchPath, absolute: true, ignore: [ 'node_modules/**', 'dist/**', 'build/**', '.git/**', 'coverage/**', '*.log' ] }); // Cache the file list for 5 minutes this.cacheService.set(cacheKey, files, 5 * 60 * 1000); } // Filter out skipped files const filteredFiles = this.filterSkippedFiles(files); // Additional filter: only include files that are within the configured root directory const rootFilteredFiles = this.configRoot ? filteredFiles.filter(filePath => { const relativePath = path_1.default.relative(this.basePath, filePath); return relativePath.startsWith(this.configRoot); }) : filteredFiles; return rootFilteredFiles.map(filePath => new FileInfo_1.FileInfo(filePath, this.basePath)); } /** * Discover files from a list of file paths */ async discoverFilesFromPaths(filePaths) { const files = []; for (const filePath of filePaths) { const absolutePath = path_1.default.isAbsolute(filePath) ? filePath : path_1.default.resolve(this.basePath, filePath); if (await this.fileExists(absolutePath)) { files.push(absolutePath); } } // Filter out skipped files const filteredFiles = this.filterSkippedFiles(files); // Additional filter: only include files that are within the configured root directory const rootFilteredFiles = this.configRoot ? filteredFiles.filter(filePath => { const relativePath = path_1.default.relative(this.basePath, filePath); return relativePath.startsWith(this.configRoot); }) : filteredFiles; return rootFilteredFiles.map(filePath => new FileInfo_1.FileInfo(filePath, this.basePath)); } /** * Check if a file exists */ async fileExists(filePath) { try { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const stats = await fs.stat(filePath); return stats.isFile(); } catch { return false; } } /** * Clear file discovery cache */ clearCache() { const patterns = [ '**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.json', '**/*.md', '**/*.yml', '**/*.yaml' ]; this.cacheService.invalidate(this.cacheService.getFileDiscoveryKey(this.basePath, patterns)); } } exports.FileDiscoveryService = FileDiscoveryService; //# sourceMappingURL=FileDiscoveryService.js.map