Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A NestJS Module for generating a NestJS Applications Module Dependency Graph.

License

NotificationsYou must be signed in to change notification settings

jmcdo29/nestjs-spelunker

Repository files navigation

Description

This module does a bit of a dive through the provided module and reads through the dependency tree from the point of entry given. It will find what a moduleimports,provides, hascontrollers for, andexports and will recursively search through the dependency tree until all modules have been scanned. Forproviders if there is a custom provider, the Spelunker will do its best to determine if Nest is to use a value, a class/standard, or a factory, and if a factory, what value is to be injected.

Installation

Pretty straightforward installation:

npm i nestjs-spelunkeryarn add nestjs-spelunkerpnpm i nestjs-spelunker

Exploration Mode

Exploration Usage

Much like theSwaggerModule, theSpelunkerModule is not a module that you register within Nest's DI system, but rather use after the DI system has done all of the heavy lifting. Simple usage of the Spelunker could be like:

// ...import{SpelunkerModule}from'nestjs-spelunker';asyncfunctionbootstrap(){constapp=awaitNestFactory.create(AppModule);// const app = await NestFactory.createApplicationContext(AppModule);console.log(SpelunkerModule.explore(app));// ...}// ...

TheSpelunkerModule will not get in the way of application bootstrapping, and will still allow for the server to listen.

Excluding modules

SpelunkerModule.explore(app,{// A list of regexes or predicate functions to apply over modules that will be ignoredignoreImports:[/^TypeOrmModule/i,(moduleName)=>moduleName.endsWith('something'),],})

Exploration Sample Output

Given the following source code

Sample code
// main.tsimport*asutilfrom'util'import{NestFactory}from'@nestjs/core'import{SpelunkerModule}from'nestjs-spelunker'import{AppModule}from'./app.module'asyncfunctionbootstrap(){constapp=awaitNestFactory.createApplicationContext(AppModule,{logger:false})console.log(util.inspect(SpelunkerModule.explore(app),{depth:Infinity,colors:true}))}bootstrap();// src/app.module.tsimport{Module,Injectable,Controller}from'@nestjs/common'@Controller('hamsters')exportclassHamstersController{}@Injectable()exportclassHamstersService{}@Module({controllers:[HamstersController],providers:[HamstersService],})exportclassHamstersModule{}@Controller('dogs')exportclassDogsController{}exportclassDogsService{}@Module({controllers:[DogsController],providers:[{provide:DogsService,inject:['someString'],useFactory:(str:string)=>newDogsService(),},{provide:'someString',useValue:'my string',},],exports:[DogsService],})exportclassDogsModule{}@Controller('cats')exportclassCatsController{}@Injectable()exportclassCatsService{}@Module({controllers:[CatsController],providers:[CatsService],})exportclassCatsModule{}exportclassAnimalsService{}@Controller('animals')exportclassAnimalsController{}@Module({imports:[CatsModule,DogsModule,HamstersModule],controllers:[AnimalsController],providers:[{provide:AnimalsService,useValue:newAnimalsService(),}],exports:[DogsModule],})exportclassAnimalsModule{}@Module({imports:[AnimalsModule],})exportclassAppModule{}

it outputs this:

[{name:'AppModule',imports:['AnimalsModule'],providers:{},controllers:[],exports:[]},{name:'AnimalsModule',imports:['CatsModule','DogsModule','HamstersModule'],providers:{AnimalsService:{method:'value'}},controllers:['AnimalsController'],exports:['DogsModule']},{name:'CatsModule',imports:[],providers:{CatsService:{method:'standard'}},controllers:['CatsController'],exports:[]},{name:'DogsModule',imports:[],providers:{DogsService:{method:'factory',injections:['someString']},someString:{method:'value'}},controllers:['DogsController'],exports:['DogsService']},{name:'HamstersModule',imports:[],providers:{HamstersService:{method:'standard'}},controllers:['HamstersController'],exports:[]}]

In this example,AppModule importsAnimalsModule, andAnimalsModule importsCatsModule,DogsModule, andHamstersModule and each of those has its own set ofproviders andcontrollers.

Graph Mode

Sometimes you want to visualize the module inter-dependencies so you can better reason about them. TheSpelunkerModule has agraph method that builds on the output of theexplore method by generating a doubly-linked graph where each node represents a module and each edge a link to that module's dependencies or dependents. ThegetEdges method can traverse this graph from from the root (or any given) node, recursively following dependencies and returning a flat array of edges. These edges can be easily mapped to inputs for graphing tools, such asMermaid.

Graphing Usage

Assume you have the sample output of the aboveexplore section in a variable called tree. The following code will generate the list of edges suitable for pasting into aMermaid graph.

consttree=SpelunkerModule.explore(app);constroot=SpelunkerModule.graph(tree);constedges=SpelunkerModule.findGraphEdges(root);console.log('graph LR');constmermaidEdges=edges.map(({ from, to})=>`${from.module.name}-->${to.module.name}`,);console.log(mermaidEdges.join('\n'));
graph LR  AppModule-->AnimalsModule  AnimalsModule-->CatsModule  AnimalsModule-->DogsModule  AnimalsModule-->HamstersModule
Loading

The edges can certainly be transformed into formats more suitable for other visualization tools. And the graph can be traversed with other strategies.

Debug Mode

Every now again again you may find yourself running into problems where Nest can't resolve a provider's dependencies. TheSpelunkerModule has adebug method that's meant to help out with this kind of situation.

Debug Usage

Assume you have aDogsModule with the following information:

@Module({controller:[DogsController],exports:[DogsService],providers:[{provide:'someString',useValue:'something',},{provide:DogsService,inject:['someString'],useFactory:(someStringInjection:string)=>{returnnewDogsService(someStringInjection),},}]})exportclassDogsModule{}

Now theSpelunkerModule.debug() method can be used anywhere with theDogsModule to get the dependency tree of theDogsModule including what the controller depends on, what imports are made, and what providers exist and their token dependencies.

asyncfunctionbootstrap(){constdogsDeps=awaitSpelunkerModule.debug(DogsModule);constapp=awaitNestFactory.create(AppModule);awaitapp.listen(3000);}

Because this method does not require theINestApplicationContext it can be usedbefore theNestFactory allowing you to have insight into what is being seen as the injection values and what's needed for the module to run.

Debug Sample Output

The output of thedebug() method is an array of metadata, imports, controllers, exports, and providers. TheDogsModule from above would look like this:

[{name:'DogsModule',imports:[],providers:[{name:'someString',dependencies:[],type:'value',},{name:'DogsService',dependencies:['someString'],type:'factory',},],controllers:[{name:'DogsController',dependencies:['DogsService'],},],exports:[{name:'DogsService',type:'provider',},],},];

Debug Messages

If you are using thedebug method and happen to have an invalid circular, theSpelunkerModule will write message to the log about the possibility of an unmarked circular dependency, meaning a missingforwardRef and the output will have***** in place of theimports where there's a problem reading the imported module.

Caution

This package is in early development, and any bugs found or improvements that can be thought of would be amazingly helpful. You canlog a bug here, and you can reach out to me on Discord atPerfectOrphan#6003.

About

A NestJS Module for generating a NestJS Applications Module Dependency Graph.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp