UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

283 lines (282 loc) 10.5 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SpecialFileCoverageScanner = void 0; const path = __importStar(require("path")); const fs = __importStar(require("fs")); const utils_1 = require("../utils"); /** * Scans Next.js app directory for special files and builds coverage analysis */ class SpecialFileCoverageScanner { constructor(appDirectory) { this.appDirectory = appDirectory; this.validExtensions = [".js", ".jsx", ".ts", ".tsx"]; } /** * Scans a route path for all special files coverage */ scanRouteSpecialFiles(routePath) { const routeSegments = (0, utils_1.parseRoutePath)(routePath); return { layout: this.scanLayoutFiles(routeSegments), template: this.scanTemplateFile(routeSegments), error: this.scanErrorFile(routeSegments), loading: this.scanLoadingFile(routeSegments), notFound: this.scanNotFoundFile(routeSegments), }; } /** * Gets all special files for a specific route segment */ getSegmentSpecialFiles(routeSegment) { const segmentPath = path.join(this.appDirectory, routeSegment); return { layout: this.scanLayoutInDirectory(segmentPath, routeSegment), template: this.scanSpecialFileInDirectory(segmentPath, "template", routeSegment), error: this.scanSpecialFileInDirectory(segmentPath, "error", routeSegment), loading: this.scanSpecialFileInDirectory(segmentPath, "loading", routeSegment), notFound: this.scanSpecialFileInDirectory(segmentPath, "not-found", routeSegment), }; } /** * Checks if a specific special file exists in a directory */ hasSpecialFile(directory, fileName) { for (const ext of this.validExtensions) { const filePath = path.join(directory, `${fileName}${ext}`); if (fs.existsSync(filePath)) { return true; } } return false; } /** * Gets the full path of a special file if it exists */ getSpecialFilePath(directory, fileName) { for (const ext of this.validExtensions) { const filePath = path.join(directory, `${fileName}${ext}`); if (fs.existsSync(filePath)) { return filePath; } } return null; } /** * Scans for all layout files in the route hierarchy */ scanLayoutFiles(routeSegments) { const layouts = []; // Start from app root and traverse down to the route let currentPath = this.appDirectory; // Check root layout const rootLayoutPath = this.getSpecialFilePath(currentPath, "layout"); layouts.push({ exists: rootLayoutPath !== null, filePath: rootLayoutPath || undefined, routeSegment: "", }); // Check each segment for layout files for (const segment of routeSegments) { currentPath = path.join(currentPath, segment); const layoutPath = this.getSpecialFilePath(currentPath, "layout"); layouts.push({ exists: layoutPath !== null, filePath: layoutPath || undefined, routeSegment: segment, }); } return layouts; } /** * Scans for template file - only the closest one matters */ scanTemplateFile(routeSegments) { // Check from the deepest route segment up to root for (let i = routeSegments.length - 1; i >= 0; i--) { const segmentPath = path.join(this.appDirectory, ...routeSegments.slice(0, i + 1)); const templatePath = this.getSpecialFilePath(segmentPath, "template"); if (templatePath) { return { exists: true, filePath: templatePath, routeSegment: routeSegments[i], }; } } // Check root const rootTemplatePath = this.getSpecialFilePath(this.appDirectory, "template"); return { exists: rootTemplatePath !== null, filePath: rootTemplatePath || undefined, routeSegment: "", }; } /** * Scans for error file - only the closest one matters */ scanErrorFile(routeSegments) { // Check from the deepest route segment up to root for (let i = routeSegments.length - 1; i >= 0; i--) { const segmentPath = path.join(this.appDirectory, ...routeSegments.slice(0, i + 1)); const errorPath = this.getSpecialFilePath(segmentPath, "error"); if (errorPath) { return { exists: true, filePath: errorPath, routeSegment: routeSegments[i], }; } } // Check root const rootErrorPath = this.getSpecialFilePath(this.appDirectory, "error"); return { exists: rootErrorPath !== null, filePath: rootErrorPath || undefined, routeSegment: "", }; } /** * Scans for loading file - only the closest one matters */ scanLoadingFile(routeSegments) { // Check from the deepest route segment up to root for (let i = routeSegments.length - 1; i >= 0; i--) { const segmentPath = path.join(this.appDirectory, ...routeSegments.slice(0, i + 1)); const loadingPath = this.getSpecialFilePath(segmentPath, "loading"); if (loadingPath) { return { exists: true, filePath: loadingPath, routeSegment: routeSegments[i], }; } } // Check root const rootLoadingPath = this.getSpecialFilePath(this.appDirectory, "loading"); return { exists: rootLoadingPath !== null, filePath: rootLoadingPath || undefined, routeSegment: "", }; } /** * Scans for not-found file - only the closest one matters */ scanNotFoundFile(routeSegments) { // Check from the deepest route segment up to root for (let i = routeSegments.length - 1; i >= 0; i--) { const segmentPath = path.join(this.appDirectory, ...routeSegments.slice(0, i + 1)); const notFoundPath = this.getSpecialFilePath(segmentPath, "not-found"); if (notFoundPath) { return { exists: true, filePath: notFoundPath, routeSegment: routeSegments[i], }; } } // Check root const rootNotFoundPath = this.getSpecialFilePath(this.appDirectory, "not-found"); return { exists: rootNotFoundPath !== null, filePath: rootNotFoundPath || undefined, routeSegment: "", }; } /** * Scans for layout files in a specific directory */ scanLayoutInDirectory(directory, routeSegment) { const layoutPath = this.getSpecialFilePath(directory, "layout"); return [ { exists: layoutPath !== null, filePath: layoutPath || undefined, routeSegment, }, ]; } /** * Scans for a specific special file in a directory */ scanSpecialFileInDirectory(directory, fileName, routeSegment) { const filePath = this.getSpecialFilePath(directory, fileName); return { exists: filePath !== null, filePath: filePath || undefined, routeSegment, }; } /** * Gets coverage summary for a route */ getCoverageSummary(coverage) { const files = [ { name: "layout", info: coverage.layout }, { name: "template", info: coverage.template }, { name: "error", info: coverage.error }, { name: "loading", info: coverage.loading }, { name: "not-found", info: coverage.notFound }, ]; let totalFiles = 0; let existingFiles = 0; const missingFiles = []; for (const file of files) { if (file.name === "layout" && Array.isArray(file.info)) { // Handle layout array for (const layout of file.info) { totalFiles++; if (layout.exists) { existingFiles++; } else { missingFiles.push(`${file.name} (${layout.routeSegment || "root"})`); } } } else if (file.info && typeof file.info === "object" && !Array.isArray(file.info)) { // Handle single file info totalFiles++; if (file.info.exists) { existingFiles++; } else { missingFiles.push(file.name); } } } return { totalFiles, existingFiles, missingFiles, coveragePercentage: totalFiles > 0 ? (existingFiles / totalFiles) * 100 : 0, }; } } exports.SpecialFileCoverageScanner = SpecialFileCoverageScanner;