@pulzar/core
Version:
Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support
146 lines • 4.9 kB
JavaScript
export class ModuleRegistry {
modules = new Map();
resolved = false;
register(module) {
if (this.resolved) {
throw new Error("Cannot register modules after resolution");
}
if (this.modules.has(module.name)) {
throw new Error(`Module '${module.name}' is already registered`);
}
this.modules.set(module.name, module);
}
getModule(name) {
return this.modules.get(name);
}
getAllModules() {
return Array.from(this.modules.values());
}
hasModule(name) {
return this.modules.has(name);
}
resolve() {
if (this.resolved) {
throw new Error("Module registry already resolved");
}
const dependencies = new Map();
const exports = new Map();
// Build dependency graph
for (const [name, module] of this.modules.entries()) {
dependencies.set(name, module.imports);
exports.set(name, module.exports);
}
// Validate dependencies
this.validateDependencies(dependencies);
// Sort modules topologically
const sortedModules = this.topologicalSort(dependencies);
this.resolved = true;
return {
modules: this.modules,
dependencies,
exports,
};
}
validateDependencies(dependencies) {
const errors = [];
// Check for missing imports
for (const [moduleName, imports] of dependencies.entries()) {
for (const importedModule of imports) {
if (!this.modules.has(importedModule)) {
errors.push(`Module '${moduleName}' imports unknown module '${importedModule}'`);
}
}
}
// Check for circular dependencies
const visited = new Set();
const visiting = new Set();
for (const moduleName of this.modules.keys()) {
if (this.hasCircularDependency(moduleName, dependencies, visited, visiting)) {
errors.push(`Circular dependency detected involving module '${moduleName}'`);
}
}
if (errors.length > 0) {
throw new Error(`Module validation failed:\n${errors.join("\n")}`);
}
}
hasCircularDependency(moduleName, dependencies, visited, visiting) {
if (visiting.has(moduleName)) {
return true;
}
if (visited.has(moduleName)) {
return false;
}
visiting.add(moduleName);
const deps = dependencies.get(moduleName) || [];
for (const dep of deps) {
if (this.hasCircularDependency(dep, dependencies, visited, visiting)) {
return true;
}
}
visiting.delete(moduleName);
visited.add(moduleName);
return false;
}
topologicalSort(dependencies) {
const visited = new Set();
const result = [];
const visit = (moduleName) => {
if (visited.has(moduleName)) {
return;
}
visited.add(moduleName);
const deps = dependencies.get(moduleName) || [];
for (const dep of deps) {
visit(dep);
}
result.push(moduleName);
};
for (const moduleName of this.modules.keys()) {
visit(moduleName);
}
return result;
}
getExportedProviders(moduleName) {
const module = this.modules.get(moduleName);
return module ? module.exports : [];
}
resolveImport(moduleName, providerToken) {
const module = this.modules.get(moduleName);
if (!module) {
return false;
}
// Check if provider is directly exported
if (module.exports.includes(providerToken)) {
return true;
}
// Check re-exports from imported modules
for (const importedModuleName of module.imports) {
const importedModule = this.modules.get(importedModuleName);
if (importedModule && importedModule.exports.includes(providerToken)) {
return true;
}
}
return false;
}
getModuleNamespace(moduleName) {
const module = this.modules.get(moduleName);
return module?.namespace;
}
clear() {
this.modules.clear();
this.resolved = false;
}
getStats() {
return {
totalModules: this.modules.size,
resolved: this.resolved,
moduleNames: Array.from(this.modules.keys()),
totalExports: Array.from(this.modules.values()).reduce((total, module) => total + module.exports.length, 0),
};
}
}
export function createModuleRegistry() {
return new ModuleRegistry();
}
export default ModuleRegistry;
//# sourceMappingURL=module-registry.js.map