@notjustcoders/ioc-arise
Version:
Arise type-safe IoC containers from your code. Zero overhead, zero coupling.
90 lines • 4.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectAnalyzer = void 0;
const class_analyzer_1 = require("./class-analyzer");
const file_discovery_1 = require("./file-discovery");
const index_js_1 = require("../errors/index.js");
class ProjectAnalyzer {
constructor(options) {
this.fileDiscovery = new file_discovery_1.FileDiscovery(options.sourceDir, options.excludePatterns);
this.classAnalyzer = new class_analyzer_1.ClassAnalyzer(options.sourceDir, options.interfacePattern);
}
async analyzeProject() {
const tsFiles = await this.fileDiscovery.findTypeScriptFiles();
// Analyze all files in a single batch operation for better performance
const allClasses = await this.classAnalyzer.analyzeFiles(tsFiles);
// Validate that no interface is implemented by multiple classes
this.validateUniqueInterfaceImplementations(allClasses);
// Validate that no abstract class is extended by multiple classes
this.validateUniqueAbstractClassExtensions(allClasses);
return allClasses;
}
validateUniqueInterfaceImplementations(classes) {
const interfaceToClassMap = new Map();
// Group classes by their interface names
for (const classInfo of classes) {
if (classInfo.interfaceName) {
if (!interfaceToClassMap.has(classInfo.interfaceName)) {
interfaceToClassMap.set(classInfo.interfaceName, []);
}
interfaceToClassMap.get(classInfo.interfaceName).push(classInfo);
}
}
// Check for duplicate implementations
const duplicateInterfaces = [];
for (const [interfaceName, implementingClasses] of interfaceToClassMap) {
if (implementingClasses.length > 1) {
duplicateInterfaces.push(interfaceName);
const error = index_js_1.ErrorFactory.duplicateInterfaceImplementation(interfaceName, implementingClasses.map(c => c.name));
console.error(`❌ ${index_js_1.ErrorUtils.formatForConsole(error)}`);
for (const classInfo of implementingClasses) {
console.error(` • ${classInfo.name} (${classInfo.filePath})`);
}
}
}
if (duplicateInterfaces.length > 0) {
const allClassNames = [];
for (const [interfaceName, implementingClasses] of interfaceToClassMap) {
if (implementingClasses.length > 1) {
allClassNames.push(...implementingClasses.map(c => c.name));
}
}
throw index_js_1.ErrorFactory.duplicateInterfaceImplementation(duplicateInterfaces.join(', '), allClassNames);
}
}
validateUniqueAbstractClassExtensions(classes) {
const abstractClassToClassMap = new Map();
// Group classes by their abstract class names
for (const classInfo of classes) {
if (classInfo.abstractClassName) {
if (!abstractClassToClassMap.has(classInfo.abstractClassName)) {
abstractClassToClassMap.set(classInfo.abstractClassName, []);
}
abstractClassToClassMap.get(classInfo.abstractClassName).push(classInfo);
}
}
// Check for duplicate extensions
const duplicateAbstractClasses = [];
for (const [abstractClassName, extendingClasses] of abstractClassToClassMap) {
if (extendingClasses.length > 1) {
duplicateAbstractClasses.push(abstractClassName);
const error = index_js_1.ErrorFactory.duplicateAbstractClassExtension(abstractClassName, extendingClasses.map(c => c.name));
console.error(`❌ Multiple classes extending abstract class: ${abstractClassName}`);
for (const classInfo of extendingClasses) {
console.error(` • ${classInfo.name} (${classInfo.filePath})`);
}
}
}
if (duplicateAbstractClasses.length > 0) {
const allClassNames = [];
for (const [abstractClassName, extendingClasses] of abstractClassToClassMap) {
if (extendingClasses.length > 1) {
allClassNames.push(...extendingClasses.map(c => c.name));
}
}
throw index_js_1.ErrorFactory.duplicateAbstractClassExtension(duplicateAbstractClasses.join(', '), allClassNames);
}
}
}
exports.ProjectAnalyzer = ProjectAnalyzer;
//# sourceMappingURL=project-analyzer.js.map