nestjs-module-lint
v0.11.0
Published
A linter for NestJS modules to detect unused imports and analyze module dependencies
Maintainers
Readme
NestJS Module Lint
A powerful command-line tool for analyzing NestJS modules to identify unused module imports in @Module() decorators. Detects when modules are imported but their exports are never used by the module's providers or controllers. Built with Go and tree-sitter for fast and accurate TypeScript parsing.
🚀 Features
- Fast Analysis: Built with Go and tree-sitter for high-performance TypeScript parsing
- Unused Module Detection: Identifies modules in
@Module()imports arrays that aren't actually used - Multiple Output Formats: Support for both text and JSON output
- Recursive Directory Scanning: Analyze entire project directories or individual files
- CI/CD Integration: Perfect for automated code quality checks
- Cross-Platform: Works on macOS, Linux, and Windows
📦 Installation
NPM/Yarn (Recommended)
Install as a development dependency:
npm install --save-dev nestjs-module-lintOr with Yarn:
yarn add --dev nestjs-module-lintFor global installation:
npm install -g nestjs-module-lintGo Install (Alternative)
If you have Go 1.21+ installed:
go install github.com/evanrichards/nestjs-module-lint@latestManual Build
git clone https://github.com/evanrichards/nestjs-module-lint.git
cd nestjs-module-lint
go build -o nestjs-module-lint .🔧 Usage
Basic Usage
Analyze a single module file:
npx nestjs-module-lint import-lint src/app/app.module.tsAnalyze an entire directory recursively:
npx nestjs-module-lint import-lint src/Output Formats
Text Output (Default):
npx nestjs-module-lint import-lint src/app/app.module.tsJSON Output:
npx nestjs-module-lint import-lint --json src/app/app.module.tsAuto-Fix Unused Imports
Preview Changes:
npx nestjs-module-lint import-lint src/app/app.module.tsAutomatically Fix:
npx nestjs-module-lint import-lint --fix src/app/app.module.tsThe --fix flag will:
- Remove unused import statements from the top of files
- Clean up the
imports: [...]arrays in@Module()decorators - Preserve formatting and handle both inline and multiline arrays
- Support all import types: named, default, and aliased imports
Command Options
nestjs-module-lint import-lint [flags] <path>
Output Flags:
--json Output in JSON format
--text Output in text format (default)
Fix Flags:
--fix Automatically remove unused imports
CI/CD Flags:
--check Check mode with pass/fail output (good for CI)
--exit-zero Exit with code 0 even when issues are found
--quiet Suppress output (useful with --exit-zero)
Other:
-h, --help help for import-lint📋 Prerequisites
- Node.js: Version 14.0 or higher
- TypeScript Project: Must have a
tsconfig.jsonfile in your project root - NestJS: Compatible with NestJS projects using standard module patterns
📖 How It Works
The tool analyzes your NestJS modules by:
- Parsing TypeScript: Uses tree-sitter to build an Abstract Syntax Tree (AST) of your TypeScript files
- Module Analysis: Identifies
@Module()decorators and extracts their imports, providers, controllers, and exports arrays - Inheritance Analysis: Detects class inheritance patterns and traces dependencies through base classes
- Dependency Tracking: For each module in the imports array, checks if any of its exports are used by the current module's providers or controllers (including inherited dependencies)
- Unused Detection: Reports modules in the imports array whose exports are never actually used
Example Analysis
Given this NestJS module:
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { AuthModule } from '../auth/auth.module';
import { EmailModule } from '../email/email.module';
import { LoggingModule } from '../logging/logging.module';
@Module({
imports: [
AuthModule, // Used: UsersService uses AuthService from AuthModule
EmailModule, // UNUSED: No provider uses EmailService from EmailModule
LoggingModule, // UNUSED: No provider uses LoggingService from LoggingModule
],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}If UsersService only injects AuthService but never uses exports from EmailModule or LoggingModule, the tool will report:
Module: UsersModule
Path: src/users/users.module.ts
Unnecessary Imports:
EmailModule
LoggingModule
Total number of modules with unused imports: 1Inheritance-Aware Analysis
The tool automatically detects dependencies through inheritance chains. For example:
// base.service.ts
import { Injectable } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Injectable()
export class BaseService {
constructor(private db: DatabaseService) {}
}
// user.service.ts
import { Injectable } from '@nestjs/common';
import { BaseService } from './base.service';
@Injectable()
export class UserService extends BaseService {
// No explicit constructor - inherits DatabaseService dependency
}
// user.module.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { DatabaseModule } from './database.module';
@Module({
imports: [DatabaseModule], // NOT flagged as unused due to inheritance
providers: [UserService],
})
export class UserModule {}The tool recognizes that UserService inherits from BaseService, which requires DatabaseService, so DatabaseModule is correctly identified as needed.
🚫 Ignore Comments
You can disable linting for specific files or individual imports using special comments:
File-Level Ignores
Skip analysis for an entire file by adding a comment at the top:
// nestjs-module-lint-disable-file
import { Module } from '@nestjs/common';
import { LegacyModuleA } from './legacy-a.module';
import { LegacyModuleB } from './legacy-b.module';
@Module({
imports: [LegacyModuleA, LegacyModuleB], // Neither will be flagged
providers: [],
})
export class LegacyModule {}Line-Level Ignores
Skip specific imports by adding a comment on the same line:
import { Module } from '@nestjs/common';
import { RequiredModule } from './required.module';
import { LegacyModule } from './legacy.module';
import { OptionalModule } from './optional.module';
@Module({
imports: [
RequiredModule,
LegacyModule, // nestjs-module-lint-disable-line
OptionalModule, // nestjs-module-lint-disable-line
],
providers: [],
})
export class MyModule {}In this example, LegacyModule and OptionalModule won't be flagged as unused, even if they're not actually used by the module's providers or controllers.
Note: The ignore comment must be on the same line as the import for line-level ignores to work.
🔄 Re-Export Pattern Detection
The tool intelligently handles NestJS "barrel" or "aggregator" modules that import and re-export other modules:
Barrel Module Pattern
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { ProductsModule } from './products/products.module';
import { OrdersModule } from './orders/orders.module';
@Module({
imports: [
UsersModule,
ProductsModule,
OrdersModule,
],
exports: [
UsersModule, // Re-exported - won't be flagged as unused
ProductsModule, // Re-exported - won't be flagged as unused
OrdersModule, // Re-exported - won't be flagged as unused
],
})
export class ApiModule {}In this example, none of the imported modules will be flagged as unused because they're all re-exported. The tool recognizes this common NestJS pattern where modules are imported solely to be re-exported.
Partial Re-Export Pattern
@Module({
imports: [
PublicModule, // Re-exported - won't be flagged
InternalModule, // Used by providers - won't be flagged
UnusedModule, // Not used or re-exported - WILL be flagged
],
providers: [SomeService], // Assume SomeService uses InternalModule
exports: [
PublicModule, // Re-exported for external use
// InternalModule not re-exported (internal use only)
],
})
export class MixedModule {}The tool correctly identifies:
- ✅
PublicModule: Safe (re-exported) - ✅
InternalModule: Safe (used by providers) - ❌
UnusedModule: Flagged as unused
🔄 Integration
Package.json Scripts
Add to your package.json:
{
"scripts": {
"lint:modules": "nestjs-module-lint import-lint src/",
"lint:modules:fix": "nestjs-module-lint import-lint --fix src/",
"lint:modules:json": "nestjs-module-lint import-lint --json src/"
}
}CI/CD Integration
The tool is designed with CI/CD best practices in mind:
Exit Codes:
0- No unused imports found (or--exit-zeroflag used)1- Unused imports found (fails CI/CD pipeline)2- Execution error (invalid path, parsing errors, etc.)
CI/CD Modes:
# Standard mode (exit 1 if issues found)
nestjs-module-lint import-lint src/
# Check mode (clear pass/fail for CI)
nestjs-module-lint import-lint --check src/
# Report mode (never fail CI, just report)
nestjs-module-lint import-lint --exit-zero --quiet src/GitHub Actions:
name: Module Lint
on: [push, pull_request]
jobs:
module-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npx nestjs-module-lint import-lint --check src/Pre-commit Hook:
{
"husky": {
"hooks": {
"pre-commit": "npx nestjs-module-lint import-lint --check src/"
}
}
}🛠️ Development
This project includes a comprehensive Makefile for easy development and CI/CD integration.
Quick Start
git clone https://github.com/evanrichards/nestjs-module-lint.git
cd nestjs-module-lint
make help # See all available commands
make build # Build the binary
make test # Run testsAvailable Make Targets
| Target | Description |
|--------|-------------|
| make build | Build the binary to bin/nestjs-module-lint |
| make test | Run all tests with verbose output |
| make bench | Run benchmarks on core packages |
| make lint | Run golangci-lint for code quality |
| make fmt | Format all Go code |
| make clean | Remove build artifacts and clear cache |
| make install | Install binary to $GOPATH/bin |
| make run | Build and run with test.ts |
| make run-json | Build and run with JSON output |
| make check | Build and run in check mode (CI-friendly) |
| make help | Show all available targets |
Manual Build
go mod download
go build -o bin/nestjs-module-lint .Quality Assurance
The project uses automated CI/CD with comprehensive testing:
- Multi-OS Testing: Ubuntu, Windows, macOS
- Multi-Go Version: Go 1.21, 1.22
- Automated Linting: golangci-lint with latest rules
- Benchmark Testing: Performance regression detection
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Run the development workflow:
make fmt # Format code make lint # Check code quality make test # Run tests make bench # Run benchmarks - Commit your changes:
git commit -am 'Add my feature' - Push to the branch:
git push origin feature/my-feature - Submit a pull request
The CI pipeline will automatically run tests across multiple platforms and Go versions.
📊 Output Examples
Text Output
Module: AppModule
Path: src/app/app.module.ts
Unnecessary Imports:
EmailModule
LoggingModule
Module: UsersModule
Path: src/users/users.module.ts
Unnecessary Imports:
NotificationModule
Total number of modules with unused imports: 2JSON Output
[
{
"module_name": "AppModule",
"path": "src/app/app.module.ts",
"unnecessary_imports": ["EmailModule", "LoggingModule"]
},
{
"module_name": "UsersModule",
"path": "src/users/users.module.ts",
"unnecessary_imports": ["NotificationModule"]
}
]🗺️ Features & Roadmap
✅ Current Features
- Import Analysis: Detect unused module imports in
@Module()decorators - Re-Export Pattern Detection: Smart handling of modules that import and re-export other modules (barrel/aggregator pattern)
- Inheritance-Aware Analysis: Automatically detects dependencies through class inheritance chains
- Auto-Fix Capability: Automatically remove unused imports with
--fixflag - Ignore Comments: File-level and line-level ignore functionality
- Multiple Output Formats: Text and JSON output support
- CI/CD Integration: Standardized exit codes and check modes
- Cross-Platform: Works on macOS, Linux, Windows
- TypeScript Path Mapping: Full support for tsconfig.json paths
- Performance Optimized: Built with Go and tree-sitter for speed
🚧 Planned Features
Object Provider Pattern Analysis
- Advanced Provider Detection: Recognize classes used in object-based provider definitions
// These patterns should be detected as "used": @Module({ providers: [ { provide: 'SERVICE_TOKEN', useClass: MyService, // MyService should be considered used }, { provide: MyInterface, useClass: MyImplementation, // MyImplementation should be considered used }, { provide: 'CONFIG', useValue: myConfigObject, // myConfigObject should be considered used }, { provide: 'FACTORY', useFactory: createService, // createService should be considered used inject: [DatabaseService], // DatabaseService should be considered used } ], }) export class AppModule {}- Provider Object Parsing: Detect
useClass,useValue,useFactorypatterns - Inject Array Analysis: Recognize dependencies in
injectarrays for factories - Token Recognition: Handle both string tokens and class tokens in
providefield
- Provider Object Parsing: Detect
Export Analysis
export-lintCommand: Find unused exports in NestJS modules# Find exports that are never imported by other modules nestjs-module-lint export-lint src/ # Combined import + export analysis nestjs-module-lint lint src/
Project-Level Configuration
- Configuration File:
.nestjs-module-lint.jsonornestjs-module-lint.config.jsfor project-wide settings{ "ignoreModules": ["LegacyModule", "ThirdPartyModule"], "ignoreDirectories": ["src/legacy/**", "src/external/**"], "ignoreSubdomains": ["@company/legacy", "@deprecated/*"], "rules": { "allowUnusedInTests": true, "strictMode": false }, "exclude": ["**/*.spec.ts", "**/*.test.ts"] }- Module Allowlisting: Never error on specific modules by name
- Directory Exclusion: Skip entire subdirectories from analysis
- Subdomain Ignoring: Ignore imports from specific npm scopes or patterns
- Rule Customization: Fine-tune linting behavior per project
- Test File Handling: Special rules for test files and mocks
Advanced Analysis
- Dependency Graph: Visualize module dependencies
- Circular Dependency Detection: Find circular imports between modules
- Dead Code Analysis: Find modules that are never imported anywhere
- Module Health Score: Overall module dependency health metrics
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🐛 Issues & Support
If you encounter any issues or have questions:
🏷️ Changelog
See Releases for version history and changes.
