sicua
Version:
A tool for analyzing project structure and dependencies
283 lines (282 loc) • 10.5 kB
JavaScript
"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;