nestjs-d2-diagrams 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/analyzers/class-analyzer.d.ts +13 -0
- package/dist/analyzers/class-analyzer.d.ts.map +1 -0
- package/dist/analyzers/class-analyzer.js +125 -0
- package/dist/analyzers/class-analyzer.js.map +1 -0
- package/dist/analyzers/module-analyzer.d.ts +9 -0
- package/dist/analyzers/module-analyzer.d.ts.map +1 -0
- package/dist/analyzers/module-analyzer.js +78 -0
- package/dist/analyzers/module-analyzer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +90 -0
- package/dist/cli.js.map +1 -0
- package/dist/generators/class-diagram.d.ts +7 -0
- package/dist/generators/class-diagram.d.ts.map +1 -0
- package/dist/generators/class-diagram.js +74 -0
- package/dist/generators/class-diagram.js.map +1 -0
- package/dist/generators/component-diagram.d.ts +6 -0
- package/dist/generators/component-diagram.d.ts.map +1 -0
- package/dist/generators/component-diagram.js +86 -0
- package/dist/generators/component-diagram.js.map +1 -0
- package/dist/interactive.d.ts +5 -0
- package/dist/interactive.d.ts.map +1 -0
- package/dist/interactive.js +62 -0
- package/dist/interactive.js.map +1 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License Copyright (c) 2025 Tomás Yañez
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of
|
|
4
|
+
charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
(including the next paragraph) shall be included in all copies or substantial
|
|
13
|
+
portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
18
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# NestJS D2 Diagrams
|
|
2
|
+
|
|
3
|
+
Generate beautiful C4-style component and class diagrams from your NestJS projects using [D2](https://d2lang.com/).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📦 **Component Diagrams** - Visualize your NestJS modules and their dependencies
|
|
8
|
+
- 🔗 **Class Diagrams** - Map out dependency injection relationships between classes
|
|
9
|
+
- 🎨 **C4 Model Style** - Generates diagrams following C4 architecture diagram conventions
|
|
10
|
+
- 🤝 **Interactive Mode** - Add rich metadata (technology stack, descriptions) to components
|
|
11
|
+
- 🎯 **Universal DI Support** - Detects all constructor injections, not just `@Injectable` classes
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
NPM:
|
|
16
|
+
```bash
|
|
17
|
+
npm install -D nestjs-d2-diagrams
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Yarn:
|
|
21
|
+
```bash
|
|
22
|
+
yarn add -D nestjs-d2-diagrams
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
PNPM:
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add -D nestjs-d2-diagrams
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
Generate both diagrams
|
|
33
|
+
```bash
|
|
34
|
+
npx nest-d2 generate
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Interactive mode (add metadata to components)
|
|
38
|
+
```bash
|
|
39
|
+
npx nest-d2 generate -i
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Generate only component diagram
|
|
43
|
+
```bash
|
|
44
|
+
npx nest-d2 generate --component-only
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Generate only class diagram
|
|
48
|
+
```bash
|
|
49
|
+
npx nest-d2 generate --class-only
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Specify custom paths
|
|
53
|
+
```bash
|
|
54
|
+
npx nest-d2 generate --project ./my-app --output ./docs/diagrams
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Output
|
|
58
|
+
|
|
59
|
+
The tool generates two D2 files:
|
|
60
|
+
- `component-diagram.d2` - Shows modules and their import relationships
|
|
61
|
+
- `class-diagram.d2` - Shows classes and their dependency injection relationships
|
|
62
|
+
|
|
63
|
+
## Rendering Diagrams
|
|
64
|
+
|
|
65
|
+
Use the [D2 CLI](https://github.com/terrastruct/d2) to render your diagrams:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Install D2
|
|
69
|
+
curl -fsSL https://d2lang.com/install.sh | sh -s --
|
|
70
|
+
|
|
71
|
+
# Render to SVG
|
|
72
|
+
d2 diagrams/component-diagram.d2 diagrams/component-diagram.svg
|
|
73
|
+
|
|
74
|
+
# Render with C4 theme (recommended)
|
|
75
|
+
d2 --theme=c4 diagrams/component-diagram.d2 diagrams/component-diagram.svg
|
|
76
|
+
|
|
77
|
+
# Render to PNG
|
|
78
|
+
d2 diagrams/class-diagram.d2 diagrams/class-diagram.png
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Interactive Mode
|
|
82
|
+
|
|
83
|
+
When using interactive mode, you'll be prompted to add metadata to each module:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx nest-d2 generate -i
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Example prompts:**
|
|
90
|
+
```
|
|
91
|
+
Do you want to add metadata (technology, descriptions) to components? › Yes
|
|
92
|
+
Enter default technology for all components: › NestJS
|
|
93
|
+
|
|
94
|
+
--- UserModule ---
|
|
95
|
+
Technology (press Enter for "NestJS"): › [Enter]
|
|
96
|
+
Description (what does this module do?): › Handles user authentication and authorization
|
|
97
|
+
|
|
98
|
+
--- ProductModule ---
|
|
99
|
+
Technology (press Enter for "NestJS"): › [Enter]
|
|
100
|
+
Description (what does this module do?): › Manages product catalog and inventory
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This generates C4-style component diagrams with rich descriptions:
|
|
104
|
+
|
|
105
|
+
```d2
|
|
106
|
+
UserModule: |md
|
|
107
|
+
### UserModule
|
|
108
|
+
---
|
|
109
|
+
**[Component: NestJS]**
|
|
110
|
+
|
|
111
|
+
Handles user authentication and authorization
|
|
112
|
+
| {
|
|
113
|
+
class: [component]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Component Diagram
|
|
118
|
+
|
|
119
|
+
Analyzes `*.module.ts` files and extracts:
|
|
120
|
+
- Module names
|
|
121
|
+
- Imports (dependencies on other modules)
|
|
122
|
+
- Providers (services)
|
|
123
|
+
- Controllers
|
|
124
|
+
- Exports
|
|
125
|
+
|
|
126
|
+
The diagram shows module relationships and, in non-interactive mode, nested providers and controllers.
|
|
127
|
+
|
|
128
|
+
## Class Diagram
|
|
129
|
+
|
|
130
|
+
Analyzes all TypeScript classes and detects:
|
|
131
|
+
- Constructor parameter dependencies (typed injections)
|
|
132
|
+
- `@Inject()` token-based injections
|
|
133
|
+
- Optional dependencies (`?` or `@Optional()`)
|
|
134
|
+
- Classes with `@Injectable()` decorator (highlighted in blue)
|
|
135
|
+
|
|
136
|
+
**Universal DI Detection**: Unlike tools that only look for `@Injectable()` classes, this analyzer detects all constructor injections. This is especially useful for:
|
|
137
|
+
- Use cases in Clean/Onion Architecture
|
|
138
|
+
- Domain services without decorators
|
|
139
|
+
- Any class participating in dependency injection
|
|
140
|
+
|
|
141
|
+
## CLI Options
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
Usage: nest-d2 generate [options]
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
-p, --project <path> Path to NestJS project (default: current directory)
|
|
148
|
+
-o, --output <path> Output directory for diagrams (default: "./diagrams")
|
|
149
|
+
-i, --interactive Enable interactive mode for adding metadata
|
|
150
|
+
--component-only Generate only component diagram
|
|
151
|
+
--class-only Generate only class diagram
|
|
152
|
+
-h, --help Display help for command
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Requirements
|
|
156
|
+
|
|
157
|
+
- Node.js 16+
|
|
158
|
+
- A NestJS project with `tsconfig.json` in the root
|
|
159
|
+
- [D2](https://d2lang.com/) CLI for rendering diagrams (optional, for visualization)
|
|
160
|
+
|
|
161
|
+
## Examples
|
|
162
|
+
|
|
163
|
+
### Component Diagram Output
|
|
164
|
+
```d2
|
|
165
|
+
# NestJS Component Diagram
|
|
166
|
+
|
|
167
|
+
direction: right
|
|
168
|
+
|
|
169
|
+
AppModule: |md
|
|
170
|
+
### AppModule
|
|
171
|
+
---
|
|
172
|
+
**[Component: NestJS]**
|
|
173
|
+
|
|
174
|
+
Main application module
|
|
175
|
+
| {
|
|
176
|
+
class: [component]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
UserModule: |md
|
|
180
|
+
### UserModule
|
|
181
|
+
---
|
|
182
|
+
**[Component: NestJS]**
|
|
183
|
+
|
|
184
|
+
User management and authentication
|
|
185
|
+
| {
|
|
186
|
+
class: [component]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
AppModule -> UserModule: imports
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Class Diagram Output
|
|
193
|
+
```d2
|
|
194
|
+
# NestJS Class Diagram
|
|
195
|
+
|
|
196
|
+
direction: down
|
|
197
|
+
|
|
198
|
+
UserController: UserController {
|
|
199
|
+
shape: class
|
|
200
|
+
# Dependencies
|
|
201
|
+
userService: UserService
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
UserService: UserService {
|
|
205
|
+
shape: class
|
|
206
|
+
style.fill: "#e3f2fd"
|
|
207
|
+
# Dependencies
|
|
208
|
+
userRepository: Repository
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
UserController -> UserService: depends on
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## How It Works
|
|
215
|
+
|
|
216
|
+
1. **TypeScript AST Parsing** - Uses `ts-morph` to parse your TypeScript source files
|
|
217
|
+
2. **Module Analysis** - Extracts `@Module()` decorator metadata from `*.module.ts` files
|
|
218
|
+
3. **Class Analysis** - Analyzes constructor parameters to detect all dependency injections
|
|
219
|
+
4. **D2 Generation** - Converts the analysis into D2 diagram syntax with C4 styling
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|
|
228
|
+
|
|
229
|
+
## Links
|
|
230
|
+
|
|
231
|
+
- [D2 Language](https://d2lang.com/)
|
|
232
|
+
- [C4 Model](https://c4model.com/)
|
|
233
|
+
- [NestJS](https://nestjs.com/)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ClassInfo } from '../types';
|
|
2
|
+
export declare class ClassAnalyzer {
|
|
3
|
+
private project;
|
|
4
|
+
constructor(projectPath: string);
|
|
5
|
+
analyze(): ClassInfo[];
|
|
6
|
+
private analyzeSourceFile;
|
|
7
|
+
private analyzeClass;
|
|
8
|
+
private extractDependencies;
|
|
9
|
+
private extractTypeName;
|
|
10
|
+
private isPrimitiveOrCommon;
|
|
11
|
+
private hasInjectableDecorator;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=class-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/class-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAkB,MAAM,UAAU,CAAC;AAErD,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAU;gBAEb,WAAW,EAAE,MAAM;IAQ/B,OAAO,IAAI,SAAS,EAAE;IAYtB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,mBAAmB;IAsD3B,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,sBAAsB;CAG/B"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClassAnalyzer = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
class ClassAnalyzer {
|
|
6
|
+
constructor(projectPath) {
|
|
7
|
+
this.project = new ts_morph_1.Project({
|
|
8
|
+
tsConfigFilePath: `${projectPath}/tsconfig.json`,
|
|
9
|
+
skipAddingFilesFromTsConfig: true,
|
|
10
|
+
});
|
|
11
|
+
this.project.addSourceFilesAtPaths(`${projectPath}/src/**/*.ts`);
|
|
12
|
+
}
|
|
13
|
+
analyze() {
|
|
14
|
+
const classes = [];
|
|
15
|
+
const sourceFiles = this.project.getSourceFiles();
|
|
16
|
+
for (const sourceFile of sourceFiles) {
|
|
17
|
+
const classInfos = this.analyzeSourceFile(sourceFile);
|
|
18
|
+
classes.push(...classInfos);
|
|
19
|
+
}
|
|
20
|
+
return classes;
|
|
21
|
+
}
|
|
22
|
+
analyzeSourceFile(sourceFile) {
|
|
23
|
+
const classes = [];
|
|
24
|
+
const classDeclarations = sourceFile.getClasses();
|
|
25
|
+
for (const classDeclaration of classDeclarations) {
|
|
26
|
+
const classInfo = this.analyzeClass(classDeclaration, sourceFile);
|
|
27
|
+
if (classInfo) {
|
|
28
|
+
classes.push(classInfo);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return classes;
|
|
32
|
+
}
|
|
33
|
+
analyzeClass(classDeclaration, sourceFile) {
|
|
34
|
+
const name = classDeclaration.getName();
|
|
35
|
+
if (!name)
|
|
36
|
+
return null;
|
|
37
|
+
const dependencies = this.extractDependencies(classDeclaration);
|
|
38
|
+
const isInjectable = this.hasInjectableDecorator(classDeclaration);
|
|
39
|
+
return {
|
|
40
|
+
name,
|
|
41
|
+
filePath: sourceFile.getFilePath(),
|
|
42
|
+
dependencies,
|
|
43
|
+
isInjectable,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
extractDependencies(classDeclaration) {
|
|
47
|
+
const dependencies = [];
|
|
48
|
+
const constructors = classDeclaration.getConstructors();
|
|
49
|
+
if (constructors.length === 0)
|
|
50
|
+
return dependencies;
|
|
51
|
+
const constructor = constructors[0];
|
|
52
|
+
const parameters = constructor.getParameters();
|
|
53
|
+
for (const param of parameters) {
|
|
54
|
+
// Try to get the type from the type annotation first
|
|
55
|
+
const typeNode = param.getTypeNode();
|
|
56
|
+
let typeName;
|
|
57
|
+
if (typeNode) {
|
|
58
|
+
// Get the text directly from the source code (not resolved)
|
|
59
|
+
typeName = typeNode.getText();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Fallback to resolved type
|
|
63
|
+
const type = param.getType();
|
|
64
|
+
typeName = type.getText();
|
|
65
|
+
}
|
|
66
|
+
// Clean up the type name
|
|
67
|
+
typeName = this.extractTypeName(typeName);
|
|
68
|
+
// Skip primitive types and common non-injectable types
|
|
69
|
+
if (this.isPrimitiveOrCommon(typeName))
|
|
70
|
+
continue;
|
|
71
|
+
// Check for @Inject decorator
|
|
72
|
+
const injectDecorator = param.getDecorator('Inject');
|
|
73
|
+
let token;
|
|
74
|
+
if (injectDecorator) {
|
|
75
|
+
const args = injectDecorator.getArguments();
|
|
76
|
+
if (args.length > 0) {
|
|
77
|
+
token = args[0].getText().replace(/['"]/g, '');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check if optional (has ? or @Optional decorator)
|
|
81
|
+
const isOptional = param.hasQuestionToken() || param.getDecorator('Optional') !== undefined;
|
|
82
|
+
dependencies.push({
|
|
83
|
+
name: param.getName(),
|
|
84
|
+
type: typeName,
|
|
85
|
+
isOptional,
|
|
86
|
+
token,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return dependencies;
|
|
90
|
+
}
|
|
91
|
+
extractTypeName(typeText) {
|
|
92
|
+
// Remove import() wrappers if present
|
|
93
|
+
let cleaned = typeText.replace(/import\([^)]+\)\./g, '');
|
|
94
|
+
// Remove array brackets
|
|
95
|
+
cleaned = cleaned.replace(/\[\]/g, '');
|
|
96
|
+
// Remove generics
|
|
97
|
+
cleaned = cleaned.replace(/<[^>]+>/g, '');
|
|
98
|
+
// Handle union types - take the first non-undefined type
|
|
99
|
+
if (cleaned.includes('|')) {
|
|
100
|
+
const types = cleaned.split('|').map(t => t.trim());
|
|
101
|
+
cleaned = types.find(t => t !== 'undefined' && t !== 'null') || types[0];
|
|
102
|
+
}
|
|
103
|
+
// If it still contains a path separator, extract just the last part (class name)
|
|
104
|
+
if (cleaned.includes('/') || cleaned.includes('\\')) {
|
|
105
|
+
const parts = cleaned.split(/[/\\]/);
|
|
106
|
+
cleaned = parts[parts.length - 1];
|
|
107
|
+
// Remove any remaining quotes or special chars
|
|
108
|
+
cleaned = cleaned.replace(/["']/g, '');
|
|
109
|
+
}
|
|
110
|
+
return cleaned.trim();
|
|
111
|
+
}
|
|
112
|
+
isPrimitiveOrCommon(typeText) {
|
|
113
|
+
const primitives = [
|
|
114
|
+
'string', 'number', 'boolean', 'any', 'unknown', 'void', 'never',
|
|
115
|
+
'String', 'Number', 'Boolean', 'undefined', 'null'
|
|
116
|
+
];
|
|
117
|
+
const cleanType = this.extractTypeName(typeText).toLowerCase();
|
|
118
|
+
return primitives.includes(cleanType);
|
|
119
|
+
}
|
|
120
|
+
hasInjectableDecorator(classDeclaration) {
|
|
121
|
+
return classDeclaration.getDecorator('Injectable') !== undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.ClassAnalyzer = ClassAnalyzer;
|
|
125
|
+
//# sourceMappingURL=class-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/class-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ModuleInfo } from '../types';
|
|
2
|
+
export declare class ModuleAnalyzer {
|
|
3
|
+
private project;
|
|
4
|
+
constructor(projectPath: string);
|
|
5
|
+
analyze(): ModuleInfo[];
|
|
6
|
+
private analyzeModuleFile;
|
|
7
|
+
private extractArrayPropertyValues;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=module-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/module-analyzer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAU;gBAEb,WAAW,EAAE,MAAM;IAQ/B,OAAO,IAAI,UAAU,EAAE;IAcvB,OAAO,CAAC,iBAAiB;IA6BzB,OAAO,CAAC,0BAA0B;CAiCnC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModuleAnalyzer = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
class ModuleAnalyzer {
|
|
6
|
+
constructor(projectPath) {
|
|
7
|
+
this.project = new ts_morph_1.Project({
|
|
8
|
+
tsConfigFilePath: `${projectPath}/tsconfig.json`,
|
|
9
|
+
skipAddingFilesFromTsConfig: true,
|
|
10
|
+
});
|
|
11
|
+
this.project.addSourceFilesAtPaths(`${projectPath}/src/**/*.module.ts`);
|
|
12
|
+
}
|
|
13
|
+
analyze() {
|
|
14
|
+
const modules = [];
|
|
15
|
+
const sourceFiles = this.project.getSourceFiles();
|
|
16
|
+
for (const sourceFile of sourceFiles) {
|
|
17
|
+
const moduleInfo = this.analyzeModuleFile(sourceFile);
|
|
18
|
+
if (moduleInfo) {
|
|
19
|
+
modules.push(moduleInfo);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return modules;
|
|
23
|
+
}
|
|
24
|
+
analyzeModuleFile(sourceFile) {
|
|
25
|
+
const classes = sourceFile.getClasses();
|
|
26
|
+
for (const classDeclaration of classes) {
|
|
27
|
+
const decorator = classDeclaration.getDecorator('Module');
|
|
28
|
+
if (!decorator)
|
|
29
|
+
continue;
|
|
30
|
+
const decoratorArgs = decorator.getArguments();
|
|
31
|
+
if (decoratorArgs.length === 0)
|
|
32
|
+
continue;
|
|
33
|
+
const configObject = decoratorArgs[0];
|
|
34
|
+
if (!ts_morph_1.Node.isObjectLiteralExpression(configObject))
|
|
35
|
+
continue;
|
|
36
|
+
const name = classDeclaration.getName() || 'UnknownModule';
|
|
37
|
+
const filePath = sourceFile.getFilePath();
|
|
38
|
+
return {
|
|
39
|
+
name,
|
|
40
|
+
filePath,
|
|
41
|
+
imports: this.extractArrayPropertyValues(configObject, 'imports'),
|
|
42
|
+
providers: this.extractArrayPropertyValues(configObject, 'providers'),
|
|
43
|
+
controllers: this.extractArrayPropertyValues(configObject, 'controllers'),
|
|
44
|
+
exports: this.extractArrayPropertyValues(configObject, 'exports'),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
extractArrayPropertyValues(objectLiteral, propertyName) {
|
|
50
|
+
const property = objectLiteral.getProperty(propertyName);
|
|
51
|
+
if (!property)
|
|
52
|
+
return [];
|
|
53
|
+
const initializer = property.getInitializer?.();
|
|
54
|
+
if (!initializer || !ts_morph_1.Node.isArrayLiteralExpression(initializer))
|
|
55
|
+
return [];
|
|
56
|
+
return initializer.getElements().map((element) => {
|
|
57
|
+
// Handle identifiers (e.g., UserService)
|
|
58
|
+
if (ts_morph_1.Node.isIdentifier(element)) {
|
|
59
|
+
return element.getText();
|
|
60
|
+
}
|
|
61
|
+
// Handle property access (e.g., TypeOrmModule.forRoot())
|
|
62
|
+
if (ts_morph_1.Node.isCallExpression(element)) {
|
|
63
|
+
const expression = element.getExpression();
|
|
64
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(expression)) {
|
|
65
|
+
return expression.getExpression().getText();
|
|
66
|
+
}
|
|
67
|
+
return expression.getText();
|
|
68
|
+
}
|
|
69
|
+
// Handle property access without calls (e.g., SomeModule)
|
|
70
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(element)) {
|
|
71
|
+
return element.getExpression().getText();
|
|
72
|
+
}
|
|
73
|
+
return element.getText();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.ModuleAnalyzer = ModuleAnalyzer;
|
|
78
|
+
//# sourceMappingURL=module-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-analyzer.js","sourceRoot":"","sources":["../../src/analyzers/module-analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const module_analyzer_1 = require("./analyzers/module-analyzer");
|
|
8
|
+
const class_analyzer_1 = require("./analyzers/class-analyzer");
|
|
9
|
+
const component_diagram_1 = require("./generators/component-diagram");
|
|
10
|
+
const class_diagram_1 = require("./generators/class-diagram");
|
|
11
|
+
const interactive_1 = require("./interactive");
|
|
12
|
+
const program = new commander_1.Command();
|
|
13
|
+
program
|
|
14
|
+
.name('nest-d2')
|
|
15
|
+
.description('Generate D2 diagrams from NestJS projects')
|
|
16
|
+
.version('1.0.0');
|
|
17
|
+
program
|
|
18
|
+
.command('generate')
|
|
19
|
+
.description('Generate component and class diagrams')
|
|
20
|
+
.option('-p, --project <path>', 'Path to NestJS project', process.cwd())
|
|
21
|
+
.option('-o, --output <path>', 'Output directory for diagrams', './diagrams')
|
|
22
|
+
.option('--component-only', 'Generate only component diagram')
|
|
23
|
+
.option('--class-only', 'Generate only class diagram')
|
|
24
|
+
.option('-i, --interactive', 'Enable interactive mode for adding metadata')
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
try {
|
|
27
|
+
const projectPath = (0, path_1.resolve)(options.project);
|
|
28
|
+
const outputDir = (0, path_1.resolve)(options.output);
|
|
29
|
+
console.log(`Analyzing NestJS project at: ${projectPath}`);
|
|
30
|
+
// Check if tsconfig.json exists
|
|
31
|
+
if (!(0, fs_1.existsSync)(`${projectPath}/tsconfig.json`)) {
|
|
32
|
+
console.error('Error: tsconfig.json not found in project root');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// Create output directory if it doesn't exist
|
|
36
|
+
if (!(0, fs_1.existsSync)(outputDir)) {
|
|
37
|
+
(0, fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
// Check for interactive mode
|
|
40
|
+
let isInteractive = options.interactive;
|
|
41
|
+
let defaultTechnology = '';
|
|
42
|
+
if (!isInteractive && !options.classOnly) {
|
|
43
|
+
isInteractive = await (0, interactive_1.promptForInteractiveMode)();
|
|
44
|
+
}
|
|
45
|
+
if (isInteractive) {
|
|
46
|
+
defaultTechnology = await (0, interactive_1.promptForDefaultTechnology)();
|
|
47
|
+
}
|
|
48
|
+
// Generate component diagram
|
|
49
|
+
if (!options.classOnly) {
|
|
50
|
+
console.log('\nAnalyzing modules...');
|
|
51
|
+
const moduleAnalyzer = new module_analyzer_1.ModuleAnalyzer(projectPath);
|
|
52
|
+
let modules = moduleAnalyzer.analyze();
|
|
53
|
+
console.log(`Found ${modules.length} modules`);
|
|
54
|
+
// Enrich modules with metadata if interactive
|
|
55
|
+
if (isInteractive) {
|
|
56
|
+
console.log('\n=== Adding metadata to modules ===');
|
|
57
|
+
const enrichedModules = [];
|
|
58
|
+
for (const module of modules) {
|
|
59
|
+
const enriched = await (0, interactive_1.enrichModuleWithMetadata)(module, defaultTechnology);
|
|
60
|
+
enrichedModules.push(enriched);
|
|
61
|
+
}
|
|
62
|
+
modules = enrichedModules;
|
|
63
|
+
}
|
|
64
|
+
const componentGen = new component_diagram_1.ComponentDiagramGenerator();
|
|
65
|
+
const componentD2 = componentGen.generate(modules);
|
|
66
|
+
const componentPath = `${outputDir}/component-diagram.d2`;
|
|
67
|
+
(0, fs_1.writeFileSync)(componentPath, componentD2);
|
|
68
|
+
console.log(`\n✓ Component diagram saved to: ${componentPath}`);
|
|
69
|
+
}
|
|
70
|
+
// Generate class diagram
|
|
71
|
+
if (!options.componentOnly) {
|
|
72
|
+
console.log('\nAnalyzing classes...');
|
|
73
|
+
const classAnalyzer = new class_analyzer_1.ClassAnalyzer(projectPath);
|
|
74
|
+
const classes = classAnalyzer.analyze();
|
|
75
|
+
console.log(`Found ${classes.length} classes`);
|
|
76
|
+
const classGen = new class_diagram_1.ClassDiagramGenerator();
|
|
77
|
+
const classD2 = classGen.generate(classes);
|
|
78
|
+
const classPath = `${outputDir}/class-diagram.d2`;
|
|
79
|
+
(0, fs_1.writeFileSync)(classPath, classD2);
|
|
80
|
+
console.log(`✓ Class diagram saved to: ${classPath}`);
|
|
81
|
+
}
|
|
82
|
+
console.log('\nDone! 🎉');
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error('Error generating diagrams:', error);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
program.parse();
|
|
90
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-diagram.d.ts","sourceRoot":"","sources":["../../src/generators/class-diagram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,qBAAa,qBAAqB;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM;IA0DtC,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,YAAY;CAMrB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClassDiagramGenerator = void 0;
|
|
4
|
+
class ClassDiagramGenerator {
|
|
5
|
+
generate(classes) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push('# NestJS Class Diagram');
|
|
8
|
+
lines.push('');
|
|
9
|
+
lines.push('direction: down');
|
|
10
|
+
lines.push('');
|
|
11
|
+
// Filter to only classes that have dependencies or are depended upon
|
|
12
|
+
const relevantClasses = this.findRelevantClasses(classes);
|
|
13
|
+
// Create nodes for each class
|
|
14
|
+
for (const classInfo of relevantClasses) {
|
|
15
|
+
const className = this.sanitizeName(classInfo.name);
|
|
16
|
+
lines.push(`${className}: ${classInfo.name} {`);
|
|
17
|
+
lines.push(' shape: class');
|
|
18
|
+
// Add a note if it has @Injectable
|
|
19
|
+
if (classInfo.isInjectable) {
|
|
20
|
+
lines.push(' style.fill: "#e3f2fd"');
|
|
21
|
+
}
|
|
22
|
+
// List dependencies as attributes
|
|
23
|
+
if (classInfo.dependencies.length > 0) {
|
|
24
|
+
lines.push(' # Dependencies');
|
|
25
|
+
for (const dep of classInfo.dependencies) {
|
|
26
|
+
const optional = dep.isOptional ? '?' : '';
|
|
27
|
+
const token = dep.token ? ` (@Inject('${dep.token}'))` : '';
|
|
28
|
+
lines.push(` ${dep.name}${optional}: ${dep.type}${token}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
lines.push('}');
|
|
32
|
+
lines.push('');
|
|
33
|
+
}
|
|
34
|
+
// Create edges for dependencies
|
|
35
|
+
for (const classInfo of relevantClasses) {
|
|
36
|
+
const className = this.sanitizeName(classInfo.name);
|
|
37
|
+
for (const dep of classInfo.dependencies) {
|
|
38
|
+
const depClassName = this.sanitizeName(dep.type);
|
|
39
|
+
// Check if the dependency class exists in our analysis
|
|
40
|
+
const exists = relevantClasses.some(c => this.sanitizeName(c.name) === depClassName);
|
|
41
|
+
if (exists) {
|
|
42
|
+
const style = dep.isOptional ? ' {style.stroke-dash: 3}' : '';
|
|
43
|
+
lines.push(`${className} -> ${depClassName}: depends on${style}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
findRelevantClasses(classes) {
|
|
50
|
+
// Find classes that either have dependencies or are referenced as dependencies
|
|
51
|
+
const classNames = new Set(classes.map(c => c.name));
|
|
52
|
+
const referencedTypes = new Set();
|
|
53
|
+
// Collect all referenced types
|
|
54
|
+
for (const classInfo of classes) {
|
|
55
|
+
for (const dep of classInfo.dependencies) {
|
|
56
|
+
referencedTypes.add(dep.type);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Include classes that have dependencies or are referenced
|
|
60
|
+
return classes.filter(classInfo => {
|
|
61
|
+
const hasDependencies = classInfo.dependencies.length > 0;
|
|
62
|
+
const isReferenced = referencedTypes.has(classInfo.name);
|
|
63
|
+
return hasDependencies || isReferenced;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
sanitizeName(name) {
|
|
67
|
+
// Remove special characters and make valid D2 identifier
|
|
68
|
+
return name
|
|
69
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
70
|
+
.replace(/^(\d)/, '_$1'); // D2 identifiers can't start with numbers
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.ClassDiagramGenerator = ClassDiagramGenerator;
|
|
74
|
+
//# sourceMappingURL=class-diagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-diagram.js","sourceRoot":"","sources":["../../src/generators/class-diagram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-diagram.d.ts","sourceRoot":"","sources":["../../src/generators/component-diagram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,qBAAa,yBAAyB;IACpC,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,WAAW,GAAE,OAAe,GAAG,MAAM;IA2FrE,OAAO,CAAC,YAAY;CAMrB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ComponentDiagramGenerator = void 0;
|
|
4
|
+
class ComponentDiagramGenerator {
|
|
5
|
+
generate(modules, showNesting = false) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push('# NestJS Component Diagram');
|
|
8
|
+
lines.push('');
|
|
9
|
+
lines.push('direction: right');
|
|
10
|
+
lines.push('');
|
|
11
|
+
lines.push('classes: {');
|
|
12
|
+
lines.push(' component: {');
|
|
13
|
+
lines.push(' shape: rectangle');
|
|
14
|
+
lines.push(' style.fill: "#87CEEB"');
|
|
15
|
+
lines.push(' style.border-radius: 32');
|
|
16
|
+
lines.push(' }');
|
|
17
|
+
lines.push('}');
|
|
18
|
+
lines.push('');
|
|
19
|
+
// Create nodes for each module
|
|
20
|
+
for (const module of modules) {
|
|
21
|
+
const moduleName = this.sanitizeName(module.name);
|
|
22
|
+
// Build the label in C4 markdown style
|
|
23
|
+
let label = `### ${module.name}`;
|
|
24
|
+
label += `\n ---`;
|
|
25
|
+
if (module.technology) {
|
|
26
|
+
label += `\n **[Component: ${module.technology}]**`;
|
|
27
|
+
}
|
|
28
|
+
if (module.description) {
|
|
29
|
+
label += `\n\n ${module.description}`;
|
|
30
|
+
}
|
|
31
|
+
// Start module block
|
|
32
|
+
lines.push(`${moduleName}: |md`);
|
|
33
|
+
lines.push(` ${label}`);
|
|
34
|
+
lines.push(`| {`);
|
|
35
|
+
lines.push(' class: [component]');
|
|
36
|
+
// Only show nesting if explicitly requested (non-interactive mode)
|
|
37
|
+
if (showNesting) {
|
|
38
|
+
// Add providers as nested elements
|
|
39
|
+
if (module.providers.length > 0) {
|
|
40
|
+
lines.push('');
|
|
41
|
+
lines.push(' providers: Providers {');
|
|
42
|
+
lines.push(' shape: rectangle');
|
|
43
|
+
for (const provider of module.providers) {
|
|
44
|
+
const providerName = this.sanitizeName(provider);
|
|
45
|
+
lines.push(` ${providerName}: ${provider}`);
|
|
46
|
+
}
|
|
47
|
+
lines.push(' }');
|
|
48
|
+
}
|
|
49
|
+
// Add controllers as nested elements
|
|
50
|
+
if (module.controllers.length > 0) {
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push(' controllers: Controllers {');
|
|
53
|
+
lines.push(' shape: rectangle');
|
|
54
|
+
for (const controller of module.controllers) {
|
|
55
|
+
const controllerName = this.sanitizeName(controller);
|
|
56
|
+
lines.push(` ${controllerName}: ${controller}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push(' }');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
lines.push('}');
|
|
62
|
+
lines.push('');
|
|
63
|
+
}
|
|
64
|
+
// Create edges for imports
|
|
65
|
+
for (const module of modules) {
|
|
66
|
+
const moduleName = this.sanitizeName(module.name);
|
|
67
|
+
for (const importedModule of module.imports) {
|
|
68
|
+
const importedModuleName = this.sanitizeName(importedModule);
|
|
69
|
+
// Check if the imported module exists in our analysis
|
|
70
|
+
const exists = modules.some(m => this.sanitizeName(m.name) === importedModuleName);
|
|
71
|
+
if (exists) {
|
|
72
|
+
lines.push(`${moduleName} -> ${importedModuleName}: imports`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
sanitizeName(name) {
|
|
79
|
+
// Remove special characters and make valid D2 identifier
|
|
80
|
+
return name
|
|
81
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
82
|
+
.replace(/^(\d)/, '_$1'); // D2 identifiers can't start with numbers
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.ComponentDiagramGenerator = ComponentDiagramGenerator;
|
|
86
|
+
//# sourceMappingURL=component-diagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-diagram.js","sourceRoot":"","sources":["../../src/generators/component-diagram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ModuleInfo } from './types';
|
|
2
|
+
export declare function promptForInteractiveMode(): Promise<boolean>;
|
|
3
|
+
export declare function promptForDefaultTechnology(): Promise<string>;
|
|
4
|
+
export declare function enrichModuleWithMetadata(module: ModuleInfo, defaultTechnology: string): Promise<ModuleInfo>;
|
|
5
|
+
//# sourceMappingURL=interactive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CASjE;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,MAAM,CAAC,CASlE;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,EAClB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,UAAU,CAAC,CAqCrB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.promptForInteractiveMode = promptForInteractiveMode;
|
|
7
|
+
exports.promptForDefaultTechnology = promptForDefaultTechnology;
|
|
8
|
+
exports.enrichModuleWithMetadata = enrichModuleWithMetadata;
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
10
|
+
async function promptForInteractiveMode() {
|
|
11
|
+
const response = await (0, prompts_1.default)({
|
|
12
|
+
type: 'confirm',
|
|
13
|
+
name: 'interactive',
|
|
14
|
+
message: 'Do you want to add metadata (technology, descriptions) to components?',
|
|
15
|
+
initial: false,
|
|
16
|
+
});
|
|
17
|
+
return response.interactive;
|
|
18
|
+
}
|
|
19
|
+
async function promptForDefaultTechnology() {
|
|
20
|
+
const response = await (0, prompts_1.default)({
|
|
21
|
+
type: 'text',
|
|
22
|
+
name: 'technology',
|
|
23
|
+
message: 'Enter default technology for all components (leave empty to prompt for each):',
|
|
24
|
+
initial: 'NestJS',
|
|
25
|
+
});
|
|
26
|
+
return response.technology || '';
|
|
27
|
+
}
|
|
28
|
+
async function enrichModuleWithMetadata(module, defaultTechnology) {
|
|
29
|
+
console.log(`\n--- ${module.name} ---`);
|
|
30
|
+
const questions = [];
|
|
31
|
+
// Technology question
|
|
32
|
+
if (defaultTechnology) {
|
|
33
|
+
questions.push({
|
|
34
|
+
type: 'text',
|
|
35
|
+
name: 'technology',
|
|
36
|
+
message: `Technology (press Enter for "${defaultTechnology}"):`,
|
|
37
|
+
initial: defaultTechnology,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
questions.push({
|
|
42
|
+
type: 'text',
|
|
43
|
+
name: 'technology',
|
|
44
|
+
message: 'Technology (e.g., NestJS, Spring Boot):',
|
|
45
|
+
initial: 'NestJS',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Description question
|
|
49
|
+
questions.push({
|
|
50
|
+
type: 'text',
|
|
51
|
+
name: 'description',
|
|
52
|
+
message: 'Description (what does this module do?):',
|
|
53
|
+
initial: '',
|
|
54
|
+
});
|
|
55
|
+
const answers = await (0, prompts_1.default)(questions);
|
|
56
|
+
return {
|
|
57
|
+
...module,
|
|
58
|
+
technology: answers.technology || defaultTechnology || 'NestJS',
|
|
59
|
+
description: answers.description || '',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=interactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive.js","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":";;;;;AAGA,4DASC;AAED,gEASC;AAED,4DAwCC;AAjED,sDAA8B;AAGvB,KAAK,UAAU,wBAAwB;IAC5C,MAAM,QAAQ,GAAG,MAAM,IAAA,iBAAO,EAAC;QAC7B,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,uEAAuE;QAChF,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,WAAW,CAAC;AAC9B,CAAC;AAEM,KAAK,UAAU,0BAA0B;IAC9C,MAAM,QAAQ,GAAG,MAAM,IAAA,iBAAO,EAAC;QAC7B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,+EAA+E;QACxF,OAAO,EAAE,QAAQ;KAClB,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC;AACnC,CAAC;AAEM,KAAK,UAAU,wBAAwB,CAC5C,MAAkB,EAClB,iBAAyB;IAEzB,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;IAExC,MAAM,SAAS,GAA2B,EAAE,CAAC;IAE7C,sBAAsB;IACtB,IAAI,iBAAiB,EAAE,CAAC;QACtB,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,gCAAgC,iBAAiB,KAAK;YAC/D,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,yCAAyC;YAClD,OAAO,EAAE,QAAQ;SAClB,CAAC,CAAC;IACL,CAAC;IAED,uBAAuB;IACvB,SAAS,CAAC,IAAI,CAAC;QACb,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,0CAA0C;QACnD,OAAO,EAAE,EAAE;KACZ,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,IAAA,iBAAO,EAAC,SAAS,CAAC,CAAC;IAEzC,OAAO;QACL,GAAG,MAAM;QACT,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,iBAAiB,IAAI,QAAQ;QAC/D,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;KACvC,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface ModuleInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
filePath: string;
|
|
4
|
+
imports: string[];
|
|
5
|
+
providers: string[];
|
|
6
|
+
controllers: string[];
|
|
7
|
+
exports: string[];
|
|
8
|
+
technology?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ClassInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
filePath: string;
|
|
14
|
+
dependencies: DependencyInfo[];
|
|
15
|
+
isInjectable: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface DependencyInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
isOptional: boolean;
|
|
21
|
+
token?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AnalysisResult {
|
|
24
|
+
modules: ModuleInfo[];
|
|
25
|
+
classes: ClassInfo[];
|
|
26
|
+
}
|
|
27
|
+
export interface DiagramOptions {
|
|
28
|
+
outputDir: string;
|
|
29
|
+
projectPath: string;
|
|
30
|
+
includePrivate?: boolean;
|
|
31
|
+
interactive?: boolean;
|
|
32
|
+
defaultTechnology?: string;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nestjs-d2-diagrams",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generate D2 component and class diagrams from NestJS projects",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"nest-d2": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/TSYF/NestJS-D2-Diagrams.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/TSYF/NestJS-D2-Diagrams/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/TSYF/NestJS-D2-Diagrams#readme",
|
|
22
|
+
"author": "Tomás Yañez <tsyf1999@gmail.com>",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"nestjs",
|
|
25
|
+
"d2",
|
|
26
|
+
"diagram",
|
|
27
|
+
"visualization",
|
|
28
|
+
"architecture",
|
|
29
|
+
"c4"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^11.1.0",
|
|
34
|
+
"prompts": "^2.4.2",
|
|
35
|
+
"ts-morph": "^21.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.10.0",
|
|
39
|
+
"@types/prompts": "^2.4.9",
|
|
40
|
+
"typescript": "^5.3.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"dev": "tsc --watch"
|
|
45
|
+
}
|
|
46
|
+
}
|