@samiyev/guardian
Version:
Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W
150 lines • 5.57 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AggregatePathAnalyzer = void 0;
const detectorPatterns_1 = require("../constants/detectorPatterns");
const paths_1 = require("../constants/paths");
/**
* Analyzes file paths and imports to extract aggregate information
*
* Handles path normalization, aggregate extraction, and entity name detection
* for aggregate boundary validation.
*/
class AggregatePathAnalyzer {
folderRegistry;
constructor(folderRegistry) {
this.folderRegistry = folderRegistry;
}
/**
* Extracts the aggregate name from a file path
*
* Handles patterns like:
* - domain/aggregates/order/Order.ts → 'order'
* - domain/order/Order.ts → 'order'
* - domain/entities/order/Order.ts → 'order'
*/
extractAggregateFromPath(filePath) {
const normalizedPath = this.normalizePath(filePath);
const segments = this.getPathSegmentsAfterDomain(normalizedPath);
if (!segments || segments.length < 2) {
return undefined;
}
return this.findAggregateInSegments(segments);
}
/**
* Extracts the aggregate name from an import path
*/
extractAggregateFromImport(importPath) {
const normalizedPath = importPath.replace(paths_1.IMPORT_PATTERNS.QUOTE, "").toLowerCase();
const segments = normalizedPath.split("/").filter((seg) => seg !== ".." && seg !== ".");
if (segments.length === 0) {
return undefined;
}
return this.findAggregateInImportSegments(segments);
}
/**
* Extracts the entity name from an import path
*/
extractEntityName(importPath) {
const normalizedPath = importPath.replace(paths_1.IMPORT_PATTERNS.QUOTE, "");
const segments = normalizedPath.split("/");
const lastSegment = segments[segments.length - 1];
if (lastSegment) {
return lastSegment.replace(/\.(ts|js)$/, "");
}
return undefined;
}
/**
* Normalizes a file path for consistent processing
*/
normalizePath(filePath) {
return filePath.toLowerCase().replace(/\\/g, "/");
}
/**
* Gets path segments after the 'domain' folder
*/
getPathSegmentsAfterDomain(normalizedPath) {
const domainMatch = /(?:^|\/)(domain)\//.exec(normalizedPath);
if (!domainMatch) {
return undefined;
}
const domainEndIndex = domainMatch.index + domainMatch[0].length;
const pathAfterDomain = normalizedPath.substring(domainEndIndex);
return pathAfterDomain.split("/").filter(Boolean);
}
/**
* Finds aggregate name in path segments after domain folder
*/
findAggregateInSegments(segments) {
if (this.folderRegistry.isEntityFolder(segments[0])) {
return this.extractFromEntityFolder(segments);
}
const aggregate = segments[0];
if (this.folderRegistry.isNonAggregateFolder(aggregate)) {
return undefined;
}
return aggregate;
}
/**
* Extracts aggregate from entity folder structure
*/
extractFromEntityFolder(segments) {
if (segments.length < 3) {
return undefined;
}
const aggregate = segments[1];
if (this.folderRegistry.isNonAggregateFolder(aggregate)) {
return undefined;
}
return aggregate;
}
/**
* Finds aggregate in import path segments
*/
findAggregateInImportSegments(segments) {
const aggregateFromDomainFolder = this.findAggregateAfterDomainFolder(segments);
if (aggregateFromDomainFolder) {
return aggregateFromDomainFolder;
}
return this.findAggregateFromSecondLastSegment(segments);
}
/**
* Finds aggregate after 'domain' or 'aggregates' folder in import
*/
findAggregateAfterDomainFolder(segments) {
for (let i = 0; i < segments.length; i++) {
const isDomainOrAggregatesFolder = segments[i] === detectorPatterns_1.DDD_FOLDER_NAMES.DOMAIN ||
segments[i] === detectorPatterns_1.DDD_FOLDER_NAMES.AGGREGATES;
if (!isDomainOrAggregatesFolder) {
continue;
}
if (i + 1 >= segments.length) {
continue;
}
const nextSegment = segments[i + 1];
const isEntityOrAggregateFolder = this.folderRegistry.isEntityFolder(nextSegment) ||
nextSegment === detectorPatterns_1.DDD_FOLDER_NAMES.AGGREGATES;
if (isEntityOrAggregateFolder) {
return i + 2 < segments.length ? segments[i + 2] : undefined;
}
return nextSegment;
}
return undefined;
}
/**
* Extracts aggregate from second-to-last segment if applicable
*/
findAggregateFromSecondLastSegment(segments) {
if (segments.length >= 2) {
const secondLastSegment = segments[segments.length - 2];
if (!this.folderRegistry.isEntityFolder(secondLastSegment) &&
!this.folderRegistry.isValueObjectFolder(secondLastSegment) &&
!this.folderRegistry.isAllowedFolder(secondLastSegment) &&
secondLastSegment !== detectorPatterns_1.DDD_FOLDER_NAMES.DOMAIN) {
return secondLastSegment;
}
}
return undefined;
}
}
exports.AggregatePathAnalyzer = AggregatePathAnalyzer;
//# sourceMappingURL=AggregatePathAnalyzer.js.map