-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(module): adds a debug method to print out modules and deps
`SpelunkerModule.debug()` is now an available method that can take in a module. This function will output a JSON with a module's imports, providers, provider type, dependencies, controllers, controller deps, and exports along with the export types.
- Loading branch information
Showing
12 changed files
with
423 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { Type } from '@nestjs/common'; | ||
import { MODULE_METADATA } from '@nestjs/common/constants'; | ||
import { | ||
DebuggedTree, | ||
DebuggedProvider, | ||
ProviderType, | ||
CustomProvider, | ||
DebuggedExports, | ||
} from './spelunker.interface'; | ||
|
||
export class DebugModule { | ||
static debug(app: Type<any>): DebuggedTree[] { | ||
const debuggedTree: DebuggedTree[] = []; | ||
const imports: string[] = []; | ||
const providers: (DebuggedProvider & { type: ProviderType })[] = []; | ||
const controllers: DebuggedProvider[] = []; | ||
const exports: DebuggedExports[] = []; | ||
for (const key of Reflect.getMetadataKeys(app)) { | ||
switch (key) { | ||
case MODULE_METADATA.IMPORTS: | ||
const baseImports = DebugModule.getImports(app); | ||
for (const imp of baseImports) { | ||
debuggedTree.push(...DebugModule.debug(imp)); | ||
} | ||
imports.push(...baseImports.map((imp) => imp.name)); | ||
break; | ||
case MODULE_METADATA.PROVIDERS: | ||
providers.push(...DebugModule.getProviders(app)); | ||
break; | ||
case MODULE_METADATA.CONTROLLERS: | ||
const baseControllers = DebugModule.getController(app); | ||
const debuggedControllers = []; | ||
for (const controller of baseControllers) { | ||
debuggedControllers.push({ | ||
name: controller.name, | ||
dependencies: DebugModule.getDependencies(controller), | ||
}); | ||
} | ||
controllers.push(...debuggedControllers); | ||
break; | ||
case MODULE_METADATA.EXPORTS: | ||
const baseExports = DebugModule.getExports(app); | ||
exports.push( | ||
...baseExports.map((exp) => ({ | ||
name: exp.name, | ||
type: DebugModule.exportType(exp), | ||
})), | ||
); | ||
break; | ||
} | ||
} | ||
debuggedTree.push({ | ||
name: app.name, | ||
imports, | ||
providers, | ||
controllers, | ||
exports, | ||
}); | ||
return debuggedTree; | ||
} | ||
|
||
private static getImports(app: Type<any>): Array<Type<any>> { | ||
return Reflect.getMetadata(MODULE_METADATA.IMPORTS, app); | ||
} | ||
|
||
private static getController(app: Type<any>): Array<Type<any>> { | ||
return Reflect.getMetadata(MODULE_METADATA.CONTROLLERS, app); | ||
} | ||
|
||
private static getProviders( | ||
app: Type<any>, | ||
): (DebuggedProvider & { type: ProviderType })[] { | ||
const baseProviders = Reflect.getMetadata(MODULE_METADATA.PROVIDERS, app); | ||
const debuggedProviders: (DebuggedProvider & { | ||
type: ProviderType; | ||
})[] = []; | ||
for (const provider of baseProviders) { | ||
let dependencies: () => any[]; | ||
// regular providers | ||
if (!DebugModule.isCustomProvider(provider)) { | ||
debuggedProviders.push({ | ||
name: provider.name, | ||
dependencies: DebugModule.getDependencies(provider), | ||
type: 'class', | ||
}); | ||
// custom providers | ||
} else { | ||
// set provide defaults | ||
const newProvider: DebuggedProvider & { | ||
type: ProviderType; | ||
} = { | ||
name: DebugModule.getProviderName(provider.provide), | ||
dependencies: [], | ||
type: 'class', | ||
}; | ||
if (provider.useValue) { | ||
newProvider.type = 'value'; | ||
dependencies = () => []; | ||
} else if (provider.useFactory) { | ||
newProvider.type = 'factory'; | ||
dependencies = () => provider.inject.map(DebugModule.getProviderName); | ||
} else { | ||
newProvider.type = 'class'; | ||
dependencies = () => | ||
DebugModule.getDependencies( | ||
provider.useClass || provider.useExisting, | ||
); | ||
} | ||
newProvider.dependencies = dependencies(); | ||
debuggedProviders.push(newProvider); | ||
} | ||
} | ||
return debuggedProviders; | ||
} | ||
|
||
private static getExports(app: Type<any>): Array<Type<any>> { | ||
return Reflect.getMetadata(MODULE_METADATA.EXPORTS, app); | ||
} | ||
|
||
private static getDependencies(classObj: Type<any>): Array<string> { | ||
const retDeps = []; | ||
const typedDeps = | ||
(Reflect.getMetadata('design:paramtypes', classObj) as Array< | ||
Type<any> | ||
>) || []; | ||
for (const dep of typedDeps) { | ||
retDeps.push(dep.name); | ||
} | ||
const selfDeps = | ||
(Reflect.getMetadata('self:paramtypes', classObj) as [ | ||
{ index: number; param: string }, | ||
]) || []; | ||
for (const selfDep of selfDeps) { | ||
retDeps[selfDep.index] = selfDep.param; | ||
} | ||
return retDeps; | ||
} | ||
|
||
private static getProviderName( | ||
provider: string | symbol | Type<any>, | ||
): string { | ||
return typeof provider === 'function' ? provider.name : provider.toString(); | ||
} | ||
|
||
private static isCustomProvider( | ||
provider: CustomProvider | Type<any>, | ||
): provider is CustomProvider { | ||
return (provider as any).provide; | ||
} | ||
|
||
private static exportType(classObj: Type<any>): 'module' | 'provider' { | ||
let isModule = false; | ||
for (const key of Object.keys(MODULE_METADATA)) { | ||
if (Reflect.getMetadata(MODULE_METADATA[key], classObj)) { | ||
isModule = true; | ||
} | ||
} | ||
return isModule ? 'module' : 'provider'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { INestApplicationContext, HttpModule } from '@nestjs/common'; | ||
import { NestContainer } from '@nestjs/core'; | ||
import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module'; | ||
import { Module as NestModule } from '@nestjs/core/injector/module'; | ||
import { SpelunkedTree } from './spelunker.interface'; | ||
|
||
export class ExplorationModule { | ||
static explore(app: INestApplicationContext): SpelunkedTree[] { | ||
const dependencyMap = []; | ||
const modulesArray = Array.from( | ||
((app as any).container as NestContainer).getModules().values(), | ||
); | ||
modulesArray | ||
.filter( | ||
(module) => | ||
module.metatype.name !== InternalCoreModule.name && | ||
module.metatype.name !== HttpModule.name, | ||
) | ||
.forEach((module) => { | ||
dependencyMap.push({ | ||
name: module.metatype.name, | ||
imports: this.getImports(module), | ||
providers: this.getProviders(module), | ||
controllers: this.getControllers(module), | ||
exports: this.getExports(module), | ||
}); | ||
}); | ||
return dependencyMap; | ||
} | ||
|
||
private static getImports(module: NestModule): string[] { | ||
return Array.from(module.imports) | ||
.filter((module) => module.metatype.name !== InternalCoreModule.name) | ||
.map((module) => module.metatype.name); | ||
} | ||
|
||
private static getProviders(module: NestModule): any { | ||
const providerList = {}; | ||
const providerNames = Array.from(module.providers.keys()).filter( | ||
(provider) => | ||
provider !== module.metatype.name && | ||
provider !== 'ModuleRef' && | ||
provider !== 'ApplicationConfig', | ||
); | ||
providerNames.map((prov) => { | ||
const provider = module.providers.get(prov); | ||
const metatype = provider.metatype; | ||
const name = (metatype && metatype.name) || 'useValue'; | ||
let provided = {}; | ||
switch (name) { | ||
case 'useValue': | ||
provided = { | ||
method: 'value', | ||
}; | ||
break; | ||
case 'useClass': | ||
provided = { | ||
method: 'class', | ||
}; | ||
break; | ||
case 'useFactory': | ||
provided = { | ||
method: 'factory', | ||
injections: provider.inject, | ||
}; | ||
break; | ||
default: | ||
provided = { | ||
method: 'standard', | ||
}; | ||
} | ||
providerList[prov] = provided; | ||
}); | ||
return providerList; | ||
} | ||
|
||
private static getControllers(module: NestModule): string[] { | ||
return Array.from(module.controllers.values()).map( | ||
(controller) => controller.metatype.name, | ||
); | ||
} | ||
|
||
private static getExports(module: NestModule): string[] { | ||
return Array.from(module.exports).map((exportValue) => | ||
exportValue.toString(), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,14 @@ | ||
import { INestApplicationContext, HttpModule } from '@nestjs/common'; | ||
import { NestContainer } from '@nestjs/core'; | ||
import { InternalCoreModule } from '@nestjs/core/injector/internal-core-module'; | ||
import { Module as NestModule } from '@nestjs/core/injector/module'; | ||
import { SpelunkedTree } from './spelunker.interface'; | ||
import { INestApplicationContext, Type } from '@nestjs/common'; | ||
import { DebugModule } from './debug.module'; | ||
import { ExplorationModule } from './exploration.module'; | ||
import { SpelunkedTree, DebuggedTree } from './spelunker.interface'; | ||
|
||
export class SpelunkerModule { | ||
static explore(app: INestApplicationContext): SpelunkedTree[] { | ||
const dependencyMap = []; | ||
const modulesArray = Array.from( | ||
((app as any).container as NestContainer).getModules().values(), | ||
); | ||
modulesArray | ||
.filter( | ||
(module) => | ||
module.metatype.name !== InternalCoreModule.name && | ||
module.metatype.name !== HttpModule.name, | ||
) | ||
.forEach((module) => { | ||
dependencyMap.push({ | ||
name: module.metatype.name, | ||
imports: this.getImports(module), | ||
providers: this.getProviders(module), | ||
controllers: this.getControllers(module), | ||
exports: this.getExports(module), | ||
}); | ||
}); | ||
return dependencyMap; | ||
return ExplorationModule.explore(app); | ||
} | ||
|
||
private static getImports(module: NestModule): string[] { | ||
return Array.from(module.imports) | ||
.filter((module) => module.metatype.name !== InternalCoreModule.name) | ||
.map((module) => module.metatype.name); | ||
} | ||
|
||
private static getProviders(module: NestModule): any { | ||
const providerList = {}; | ||
const providerNames = Array.from(module.providers.keys()).filter( | ||
(provider) => | ||
provider !== module.metatype.name && | ||
provider !== 'ModuleRef' && | ||
provider !== 'ApplicationConfig', | ||
); | ||
providerNames.map((prov) => { | ||
const provider = module.providers.get(prov); | ||
const metatype = provider.metatype; | ||
const name = (metatype && metatype.name) || 'useValue'; | ||
let provided = {}; | ||
switch (name) { | ||
case 'useValue': | ||
provided = { | ||
method: 'value', | ||
}; | ||
break; | ||
case 'useClass': | ||
provided = { | ||
method: 'class', | ||
}; | ||
break; | ||
case 'useFactory': | ||
provided = { | ||
method: 'factory', | ||
injections: provider.inject, | ||
}; | ||
break; | ||
default: | ||
provided = { | ||
method: 'standard', | ||
}; | ||
} | ||
providerList[prov] = provided; | ||
}); | ||
return providerList; | ||
} | ||
|
||
private static getControllers(module: NestModule): string[] { | ||
return Array.from(module.controllers.values()).map( | ||
(controller) => controller.metatype.name, | ||
); | ||
} | ||
|
||
private static getExports(module: NestModule): string[] { | ||
return Array.from(module.exports).map((exportValue) => | ||
exportValue.toString(), | ||
); | ||
static debug(mod: Type<any>): DebuggedTree[] { | ||
return DebugModule.debug(mod); | ||
} | ||
} |
Oops, something went wrong.