UNPKG

frontend-standards-checker

Version:

A comprehensive frontend standards validation tool with TypeScript support

165 lines • 6.99 kB
import path from 'path'; // Detect if a file is a Jest (test/spec) function isJestFile(filePath) { if (!filePath || typeof filePath !== 'string') return false; const lowerPath = filePath.toLowerCase(); return (/\.(test|spec)\.[jt]sx?$/.test(lowerPath) || /__tests__/.test(lowerPath) || lowerPath.includes('jest')); } export async function loadAndLogConfig(configLoader, options, logger) { const config = await configLoader.load(options.config); if (options.debug) { logger.debug('Configuration loaded:', JSON.stringify(config, null, 2)); } return config; } export async function analyzeProject(projectAnalyzer, config, logger, options) { const projectInfo = await projectAnalyzer.analyze(config.zones); logger.info(`šŸ“ Project type: ${projectInfo.projectType}`); logger.info(`šŸ—ļø Monorepo: ${projectInfo.isMonorepo ? 'Yes' : 'No'}`); if (options.debug) { logger.debug('Project analysis result:', projectInfo); } return projectInfo; } export async function getChangedFiles(fileScanner, logger, config) { const pipelineMode = config?.pipelineMode || false; if (pipelineMode) { logger.info('šŸ” Using pipeline mode to detect changed files'); } else { logger.info('šŸ” Only checking files staged for commit or recently changed'); } const changedFiles = await fileScanner.getFilesInCommitOrPipeline(pipelineMode); logger.info(`Found ${changedFiles.length} files to check`); return changedFiles; } export function returnEarly(startTime) { return { success: true, totalFiles: 0, totalErrors: 0, totalWarnings: 0, zones: [], summary: { errorsByCategory: {}, errorsByRule: {}, processingTime: Date.now() - startTime, }, }; } export function createSummary(zoneResults, totalFiles, totalErrors, totalWarnings, startTime) { const errorsByCategory = {}; const errorsByRule = {}; zoneResults.forEach((zone) => { zone.errors.forEach((error) => { const categoryKey = error.category ?? 'uncategorized'; errorsByCategory[categoryKey] = (errorsByCategory[categoryKey] ?? 0) + 1; errorsByRule[error.rule] = (errorsByRule[error.rule] ?? 0) + 1; }); }); return { success: totalErrors === 0, totalFiles, totalErrors, totalWarnings, zones: zoneResults, summary: { errorsByCategory, errorsByRule, processingTime: Date.now() - startTime, }, }; } export async function generateReport(reporter, logger, zoneResults, projectInfo, config) { const zoneErrors = {}; zoneResults.forEach((zone) => { zoneErrors[zone.zone] = zone.errors; logger.debug(`šŸ› Zone ${zone.zone}: ${zone.errors.length} errors before reporter`); }); const totalErrorsToReporter = Object.values(zoneErrors).reduce((sum, errors) => sum + errors.length, 0); logger.debug(`šŸ› Total errors being passed to reporter: ${totalErrorsToReporter}`); return await reporter.generate(zoneErrors, projectInfo, config); } export function logSummary(logger, summary, totalFiles, totalErrors, totalWarnings, zoneSummary) { logger.info(`\nšŸŽ‰ Validation completed in ${summary.processingTime}ms`); logger.info(`šŸ“Š Total files: ${totalFiles}`); logger.info(`āŒ Total errors: ${totalErrors}`); logger.info(`āš ļø Total warnings: ${totalWarnings}`); if (zoneSummary) { logger.info(`\n----------------\nRESULTS BY ZONE:\n----------------`); Object.keys(zoneSummary.errorsByZone).forEach((zone) => { logger.info(`\nšŸ“‚ Zone: ${zone}`); logger.info(` Errors: ${zoneSummary.errorsByZone[zone]}`); logger.info(` Warnings: ${zoneSummary.warningsByZone[zone]}`); if (zoneSummary.infosByZone?.[zone] !== undefined) { logger.info(` Info suggestions: ${zoneSummary.infosByZone[zone]}`); } logger.info(` Status: ${(zoneSummary.errorsByZone[zone] ?? 0) > 0 ? 'āŒ FAILED' : 'āœ… PASSED'}`); logger.info('----------------------------------------'); }); } } export function filterChangedFiles(files, changedFiles, rootDir) { return files.filter((file) => { const fileFullPath = file.fullPath ?? path.join(rootDir, file.path); return changedFiles.some((cf) => fileFullPath === cf || (fileFullPath.endsWith(cf) ?? cf.endsWith(file.path))); }); } export async function processZone({ zone, config, changedFiles, hasOnlyZone, options, rootDir, logger, fileScanner, ruleEngine, projectInfo, }) { logger.info(`\nšŸ“‚ Processing zone: ${zone}`); let files = await fileScanner.scanZone(zone, { extensions: config.extensions || ['.js', '.ts', '.jsx', '.tsx'], ignorePatterns: config.ignorePatterns || [], zones: [zone], includePackages: config.zones?.includePackages || false, customZones: config.zones?.customZones || [], }); if ((options.onlyChangedFiles || config.onlyChangedFiles) && !hasOnlyZone && changedFiles.length > 0) { const originalCount = files.length; files = filterChangedFiles(files, changedFiles, rootDir); logger.debug(`Filtered ${originalCount} files to ${files.length} changed files in zone ${zone}`); } if (options.debug) { logger.debug(`šŸ“ Debug: Files found in zone "${zone}":`, files.map((f) => f.path)); } const zoneErrors = []; const validFiles = files.filter((file) => { const isConfigFile = ruleEngine.isConfigurationFile(file.path); if (isConfigFile && options.verbose) { logger.debug(`Skipping configuration file: ${file.path}`); } return !isConfigFile; }); for (const file of validFiles) { if (options.verbose) { logger.info(` šŸ” Validating: ${file.path}`); } const fileErrors = await ruleEngine.validate(file.content, file.path, { filePath: file.path, content: file.content, projectInfo, config, }); zoneErrors.push(...fileErrors); } // Exclude Jest files from error counts const zoneErrorsCount = zoneErrors.filter((e) => e.severity === 'error' && !isJestFile(e.filePath)).length; const zoneWarningsCount = zoneErrors.filter((e) => e.severity === 'warning' && !isJestFile(e.filePath)).length; logger.info(` āœ… Files processed: ${files.length}`); logger.info(` āŒ Errors found: ${zoneErrorsCount}`); logger.info(` āš ļø Warnings found: ${zoneWarningsCount}`); return { zone, filesProcessed: validFiles.length, errors: zoneErrors, errorsCount: zoneErrorsCount, warningsCount: zoneWarningsCount, }; } //# sourceMappingURL=general.helper.js.map