@angular-modernizer/api 0.1.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 +507 -0
- package/dist/analysis/complexity-metrics-calculator.d.ts +99 -0
- package/dist/analysis/complexity-metrics-calculator.d.ts.map +1 -0
- package/dist/analysis/complexity-metrics-calculator.js +195 -0
- package/dist/analysis/complexity-metrics-calculator.js.map +1 -0
- package/dist/analysis/dependency-analyzer.d.ts +227 -0
- package/dist/analysis/dependency-analyzer.d.ts.map +1 -0
- package/dist/analysis/dependency-analyzer.js +451 -0
- package/dist/analysis/dependency-analyzer.js.map +1 -0
- package/dist/analysis/external-template-loader.d.ts +44 -0
- package/dist/analysis/external-template-loader.d.ts.map +1 -0
- package/dist/analysis/external-template-loader.js +69 -0
- package/dist/analysis/external-template-loader.js.map +1 -0
- package/dist/analysis/selector-mapper.d.ts +77 -0
- package/dist/analysis/selector-mapper.d.ts.map +1 -0
- package/dist/analysis/selector-mapper.js +146 -0
- package/dist/analysis/selector-mapper.js.map +1 -0
- package/dist/analysis/service-detector.d.ts +164 -0
- package/dist/analysis/service-detector.d.ts.map +1 -0
- package/dist/analysis/service-detector.js +231 -0
- package/dist/analysis/service-detector.js.map +1 -0
- package/dist/analysis/service-pattern-recognizer.d.ts +247 -0
- package/dist/analysis/service-pattern-recognizer.d.ts.map +1 -0
- package/dist/analysis/service-pattern-recognizer.js +444 -0
- package/dist/analysis/service-pattern-recognizer.js.map +1 -0
- package/dist/analysis/symbol-locator.d.ts +81 -0
- package/dist/analysis/symbol-locator.d.ts.map +1 -0
- package/dist/analysis/symbol-locator.js +128 -0
- package/dist/analysis/symbol-locator.js.map +1 -0
- package/dist/analysis/template-analyzer-usage.d.ts +144 -0
- package/dist/analysis/template-analyzer-usage.d.ts.map +1 -0
- package/dist/analysis/template-analyzer-usage.js +474 -0
- package/dist/analysis/template-analyzer-usage.js.map +1 -0
- package/dist/analysis/template-analyzer.d.ts +80 -0
- package/dist/analysis/template-analyzer.d.ts.map +1 -0
- package/dist/analysis/template-analyzer.js +146 -0
- package/dist/analysis/template-analyzer.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/investigation/angular-symbol-finder.d.ts +76 -0
- package/dist/investigation/angular-symbol-finder.d.ts.map +1 -0
- package/dist/investigation/angular-symbol-finder.js +199 -0
- package/dist/investigation/angular-symbol-finder.js.map +1 -0
- package/dist/investigation/call-graph-builder.d.ts +85 -0
- package/dist/investigation/call-graph-builder.d.ts.map +1 -0
- package/dist/investigation/call-graph-builder.js +196 -0
- package/dist/investigation/call-graph-builder.js.map +1 -0
- package/dist/investigation/codebase-searcher.d.ts +99 -0
- package/dist/investigation/codebase-searcher.d.ts.map +1 -0
- package/dist/investigation/codebase-searcher.js +201 -0
- package/dist/investigation/codebase-searcher.js.map +1 -0
- package/dist/investigation/stack-trace-parser.d.ts +70 -0
- package/dist/investigation/stack-trace-parser.d.ts.map +1 -0
- package/dist/investigation/stack-trace-parser.js +117 -0
- package/dist/investigation/stack-trace-parser.js.map +1 -0
- package/dist/investigation/type-resolver.d.ts +59 -0
- package/dist/investigation/type-resolver.d.ts.map +1 -0
- package/dist/investigation/type-resolver.js +111 -0
- package/dist/investigation/type-resolver.js.map +1 -0
- package/dist/investigation/usage-finder.d.ts +69 -0
- package/dist/investigation/usage-finder.d.ts.map +1 -0
- package/dist/investigation/usage-finder.js +132 -0
- package/dist/investigation/usage-finder.js.map +1 -0
- package/dist/metrics/complexity-metrics.d.ts +196 -0
- package/dist/metrics/complexity-metrics.d.ts.map +1 -0
- package/dist/metrics/complexity-metrics.js +445 -0
- package/dist/metrics/complexity-metrics.js.map +1 -0
- package/dist/public-api.d.ts +77 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +50 -0
- package/dist/public-api.js.map +1 -0
- package/dist/transformation/import-manager.d.ts +276 -0
- package/dist/transformation/import-manager.d.ts.map +1 -0
- package/dist/transformation/import-manager.js +488 -0
- package/dist/transformation/import-manager.js.map +1 -0
- package/dist/transformation/ng-morph-adapter.d.ts +111 -0
- package/dist/transformation/ng-morph-adapter.d.ts.map +1 -0
- package/dist/transformation/ng-morph-adapter.js +179 -0
- package/dist/transformation/ng-morph-adapter.js.map +1 -0
- package/dist/transformation/ngmodule-manager.d.ts +145 -0
- package/dist/transformation/ngmodule-manager.d.ts.map +1 -0
- package/dist/transformation/ngmodule-manager.js +356 -0
- package/dist/transformation/ngmodule-manager.js.map +1 -0
- package/package.json +62 -0
- package/src/analysis/complexity-metrics-calculator.ts +253 -0
- package/src/analysis/dependency-analyzer.ts +720 -0
- package/src/analysis/external-template-loader.ts +79 -0
- package/src/analysis/selector-mapper.ts +186 -0
- package/src/analysis/service-detector.ts +280 -0
- package/src/analysis/service-pattern-recognizer.ts +545 -0
- package/src/analysis/symbol-locator.ts +170 -0
- package/src/analysis/template-analyzer-usage.ts +614 -0
- package/src/analysis/template-analyzer.ts +208 -0
- package/src/index.ts +51 -0
- package/src/investigation/angular-symbol-finder.ts +263 -0
- package/src/investigation/call-graph-builder.ts +298 -0
- package/src/investigation/codebase-searcher.ts +286 -0
- package/src/investigation/stack-trace-parser.ts +166 -0
- package/src/investigation/type-resolver.ts +144 -0
- package/src/investigation/usage-finder.ts +198 -0
- package/src/metrics/complexity-metrics.ts +617 -0
- package/src/public-api.ts +119 -0
- package/src/transformation/import-manager.ts +626 -0
- package/src/transformation/ng-morph-adapter.ts +252 -0
- package/src/transformation/ngmodule-manager.ts +450 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christian Weiss<CWOIDA>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# @angular-modernizer/api
|
|
2
|
+
|
|
3
|
+
Analysis and transformation utilities used by plugins. Plugins must use this API and never manipulate the AST directly.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@angular-modernizer/api` package provides high-level, reusable tools that plugins use to analyze and transform Angular codebases. This package contains the core business logic that powers the entire platform.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
The API follows the "API-Driven Plugin" pattern:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Incorrect: plugin implements low-level AST logic
|
|
15
|
+
class BadPlugin {
|
|
16
|
+
analyze(sourceFile: SourceFile) {
|
|
17
|
+
// 100 lines of AST manipulation code
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Correct: plugin uses API tools
|
|
22
|
+
class GoodPlugin {
|
|
23
|
+
analyze(context: AnalysisContext) {
|
|
24
|
+
const usages = context.api.analysis.templateAnalyzer.analyze(template);
|
|
25
|
+
const deps = context.api.analysis.dependencyAnalyzer.analyzeDependencies(sourceFile);
|
|
26
|
+
// Focus on business logic, not implementation details
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Core Components
|
|
32
|
+
|
|
33
|
+
### PublicApi
|
|
34
|
+
|
|
35
|
+
The `PublicApi` is the single entry point for all analysis and transformation tools:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { createPublicApi } from '@angular-modernizer/api';
|
|
39
|
+
|
|
40
|
+
const api = createPublicApi();
|
|
41
|
+
|
|
42
|
+
const templateUsages = api.analysis.templateAnalyzer.analyze(template);
|
|
43
|
+
const dependencies = api.analysis.dependencyAnalyzer.analyzeDependencies(sourceFile);
|
|
44
|
+
const imports = api.transformation.importManager.addImport(
|
|
45
|
+
sourceFile,
|
|
46
|
+
'Inject',
|
|
47
|
+
'@angular/core',
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Analysis Tools
|
|
52
|
+
|
|
53
|
+
#### TemplateAnalyzer
|
|
54
|
+
|
|
55
|
+
Purpose: extract all template usages.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
interface TemplateAnalyzer {
|
|
59
|
+
analyze(template: string): TemplateUsage[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface TemplateUsage {
|
|
63
|
+
element: string;
|
|
64
|
+
selector: string;
|
|
65
|
+
attributes: string[];
|
|
66
|
+
inputs: string[];
|
|
67
|
+
outputs: string[];
|
|
68
|
+
content: string;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Design: extracts all usages from templates without filtering or heuristics. Provides raw data for downstream decision-making.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const api = createPublicApi();
|
|
78
|
+
const usages = api.analysis.templateAnalyzer.analyze(`
|
|
79
|
+
<app-user [user]="currentUser" (save)="onSave()">
|
|
80
|
+
<div *ngIf="loading">Loading...</div>
|
|
81
|
+
<input [(ngModel)]="name">
|
|
82
|
+
</app-user>
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
// Result:
|
|
86
|
+
// - app-user component with [user] input and (save) output
|
|
87
|
+
// - *ngIf directive usage
|
|
88
|
+
// - ngModel directive usage
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### DependencyAnalyzer
|
|
92
|
+
|
|
93
|
+
Purpose: build comprehensive symbol maps.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
interface DependencyAnalyzer {
|
|
97
|
+
analyzeDependencies(sourceFile: SourceFile): DependencyMap;
|
|
98
|
+
findNgModules(project: Project): NgModuleInfo[];
|
|
99
|
+
resolveSelector(selector: string, project: Project): SymbolInfo | null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface DependencyMap {
|
|
103
|
+
components: Map<string, ComponentInfo>;
|
|
104
|
+
directives: Map<string, DirectiveInfo>;
|
|
105
|
+
pipes: Map<string, PipeInfo>;
|
|
106
|
+
modules: Map<string, NgModuleInfo>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface ComponentInfo {
|
|
110
|
+
className: string;
|
|
111
|
+
selector: string;
|
|
112
|
+
inputs: PropertyInfo[];
|
|
113
|
+
outputs: PropertyInfo[];
|
|
114
|
+
module?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface PropertyInfo {
|
|
118
|
+
name: string;
|
|
119
|
+
alias?: string;
|
|
120
|
+
type?: string;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Key features:
|
|
125
|
+
- Maps selectors to class names
|
|
126
|
+
- Handles aliased @Input/@Output bindings
|
|
127
|
+
- Resolves module declarations
|
|
128
|
+
- Builds complete dependency graphs
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const api = createPublicApi();
|
|
134
|
+
const sourceFile = project.addSourceFileAtPath('user.module.ts');
|
|
135
|
+
const dependencies = api.analysis.dependencyAnalyzer.analyzeDependencies(sourceFile);
|
|
136
|
+
|
|
137
|
+
// Result: complete symbol map
|
|
138
|
+
// - UserComponent: selector 'app-user', inputs: ['user'], outputs: ['save']
|
|
139
|
+
// - UserModule: declarations: [UserComponent], exports: [UserComponent]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Also supports advanced analysis:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const analyzer = new DependencyAnalyzer();
|
|
146
|
+
|
|
147
|
+
// Build comprehensive symbol map
|
|
148
|
+
const symbolMap = analyzer.buildSymbolMap(moduleFile, project);
|
|
149
|
+
console.log(symbolMap.available); // All available symbols in scope
|
|
150
|
+
|
|
151
|
+
// Detect circular dependencies
|
|
152
|
+
const cycles = analyzer.detectCircularDependencies(project);
|
|
153
|
+
|
|
154
|
+
// Get transitive dependencies
|
|
155
|
+
const transitiveDeps = analyzer.getTransitiveDependencies(sourceFile, project);
|
|
156
|
+
|
|
157
|
+
// Extract symbol information
|
|
158
|
+
const symbolInfo = analyzer.extractSymbolInfo(sourceFile, 'MyComponent');
|
|
159
|
+
console.log(symbolInfo?.inputs); // ['input1', 'customAlias']
|
|
160
|
+
console.log(symbolInfo?.outputs); // ['output1']
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### ComplexityMetricsCalculator
|
|
164
|
+
|
|
165
|
+
Purpose: calculate code complexity and maintainability metrics.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
interface ComplexityMetricsCalculator {
|
|
169
|
+
calculateForFile(sourceFile: SourceFile): FileMetrics;
|
|
170
|
+
calculateForProject(project: Project): ProjectMetrics;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface FileMetrics {
|
|
174
|
+
linesOfCode: number;
|
|
175
|
+
cyclomaticComplexity: number;
|
|
176
|
+
maintainabilityIndex: number;
|
|
177
|
+
halsteadMetrics: HalsteadMetrics;
|
|
178
|
+
cognitiveComplexity: number;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface ProjectMetrics {
|
|
182
|
+
totalFiles: number;
|
|
183
|
+
averageComplexity: number;
|
|
184
|
+
complexityDistribution: ComplexityDistribution;
|
|
185
|
+
maintainabilityScore: number;
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const calculator = new ComplexityMetricsCalculator();
|
|
193
|
+
|
|
194
|
+
const score = calculator.calculateOverallComplexity(componentClass);
|
|
195
|
+
const cyclomatic = calculator.calculateCyclomaticComplexity(method);
|
|
196
|
+
const loc = calculator.calculateLOC(sourceFile);
|
|
197
|
+
const angularMetrics = calculator.calculateAngularSpecificMetrics(classDecl);
|
|
198
|
+
console.log(angularMetrics.lifecycleHooks);
|
|
199
|
+
console.log(angularMetrics.inputs);
|
|
200
|
+
console.log(angularMetrics.outputs);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Transformation Tools
|
|
204
|
+
|
|
205
|
+
#### ImportManager
|
|
206
|
+
|
|
207
|
+
Purpose: safely manage TypeScript import statements.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
interface ImportManager {
|
|
211
|
+
addImport(sourceFile: SourceFile, symbol: string, module: string): boolean;
|
|
212
|
+
removeImport(sourceFile: SourceFile, symbol: string): boolean;
|
|
213
|
+
organizeImports(sourceFile: SourceFile): void;
|
|
214
|
+
hasImport(sourceFile: SourceFile, symbol: string, module?: string): boolean;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Key features:
|
|
219
|
+
- Prevents duplicate imports
|
|
220
|
+
- Handles default vs named imports
|
|
221
|
+
- Organizes import statements
|
|
222
|
+
- Resolves import conflicts
|
|
223
|
+
|
|
224
|
+
Example:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const importManager = new ImportManager();
|
|
228
|
+
|
|
229
|
+
importManager.addNamedImport(sourceFile, 'OnInit', '@angular/core');
|
|
230
|
+
importManager.addNamedImport(sourceFile, 'Directive', '@angular/core', 'NgDirective');
|
|
231
|
+
|
|
232
|
+
importManager.removeNamedImport(sourceFile, 'OnInit', '@angular/core');
|
|
233
|
+
|
|
234
|
+
const hasImport = importManager.hasImport(sourceFile, 'Component', '@angular/core');
|
|
235
|
+
const alias = importManager.getImportAlias(sourceFile, 'Component', '@angular/core');
|
|
236
|
+
|
|
237
|
+
importManager.mergeImports(sourceFile);
|
|
238
|
+
importManager.cleanupEmptyImports(sourceFile);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Performance Characteristics
|
|
242
|
+
|
|
243
|
+
- ComplexityMetricsCalculator: <100ms for large components
|
|
244
|
+
- ImportManager: <50ms for 20+ import operations
|
|
245
|
+
- DependencyAnalyzer: <200ms for comprehensive analysis
|
|
246
|
+
- Memory usage: <50MB increase during repeated operations
|
|
247
|
+
|
|
248
|
+
## Usage Examples
|
|
249
|
+
|
|
250
|
+
### Standalone Migration Workflow
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { createPublicApi } from '@angular-modernizer/api';
|
|
254
|
+
import { ContextFactory } from '@angular-modernizer/plugin-system';
|
|
255
|
+
|
|
256
|
+
const api = createPublicApi();
|
|
257
|
+
|
|
258
|
+
// 1. Analyze template dependencies
|
|
259
|
+
const template = `<div *ngIf="loading" [user]="user" (save)="onSave()"></div>`;
|
|
260
|
+
const templateUsages = api.analysis.templateAnalyzer.analyze(template);
|
|
261
|
+
|
|
262
|
+
// 2. Get component dependencies
|
|
263
|
+
const sourceFile = project.addSourceFileAtPath('user.component.ts');
|
|
264
|
+
const dependencies = api.analysis.dependencyAnalyzer.analyzeDependencies(sourceFile);
|
|
265
|
+
|
|
266
|
+
// 3. Transform component to standalone
|
|
267
|
+
const context = ContextFactory.createTransformContext({
|
|
268
|
+
sourceFile,
|
|
269
|
+
project,
|
|
270
|
+
api,
|
|
271
|
+
config: {},
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// The plugin uses the API to:
|
|
275
|
+
// - Add standalone: true to @Component decorator
|
|
276
|
+
// - Build imports array from templateUsages + dependencies
|
|
277
|
+
// - Add necessary import statements
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### SOLID Principle Analysis
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const api = createPublicApi();
|
|
284
|
+
|
|
285
|
+
function findDirectInstantiations(sourceFile: SourceFile): Violation[] {
|
|
286
|
+
const violations: Violation[] = [];
|
|
287
|
+
|
|
288
|
+
sourceFile.forEachDescendant((node) => {
|
|
289
|
+
if (ts.isNewExpression(node)) {
|
|
290
|
+
const className = node.expression.getText();
|
|
291
|
+
|
|
292
|
+
if (className.endsWith('Service')) {
|
|
293
|
+
violations.push({
|
|
294
|
+
type: 'direct-instantiation',
|
|
295
|
+
location: getLocation(node),
|
|
296
|
+
message: `Direct instantiation of ${className} violates Dependency Inversion Principle`,
|
|
297
|
+
suggestion: 'Use dependency injection instead',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return violations;
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Complexity Analysis
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const api = createPublicApi();
|
|
311
|
+
|
|
312
|
+
const projectMetrics = api.analysis.complexityMetricsCalculator.calculateForProject(project);
|
|
313
|
+
|
|
314
|
+
const complexFiles = projectMetrics.fileMetrics.filter(
|
|
315
|
+
(metrics) =>
|
|
316
|
+
metrics.cyclomaticComplexity > 10 || metrics.maintainabilityIndex < 50,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
complexFiles.forEach((file) => {
|
|
320
|
+
console.log(`${file.filePath}:`);
|
|
321
|
+
console.log(` - Complexity: ${file.cyclomaticComplexity}`);
|
|
322
|
+
console.log(` - Maintainability: ${file.maintainabilityIndex}`);
|
|
323
|
+
console.log(' -> Consider breaking down into smaller functions');
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## API Design Principles
|
|
328
|
+
|
|
329
|
+
### Stateless and Pure
|
|
330
|
+
|
|
331
|
+
All API methods are stateless and side-effect free:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
const result1 = api.analysis.templateAnalyzer.analyze(template);
|
|
335
|
+
const result2 = api.analysis.templateAnalyzer.analyze(template);
|
|
336
|
+
assert.deepEqual(result1, result2); // Always true
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Composable
|
|
340
|
+
|
|
341
|
+
Tools work together seamlessly:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
const templateUsages = api.analysis.templateAnalyzer.analyze(template);
|
|
345
|
+
const dependencies = api.analysis.dependencyAnalyzer.analyzeDependencies(sourceFile);
|
|
346
|
+
const complexity = api.analysis.complexityMetricsCalculator.calculateForFile(sourceFile);
|
|
347
|
+
|
|
348
|
+
const transformationPlan = createTransformationPlan({
|
|
349
|
+
templateUsages,
|
|
350
|
+
dependencies,
|
|
351
|
+
complexity,
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Error-Resilient
|
|
356
|
+
|
|
357
|
+
APIs handle edge cases gracefully:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// Handles malformed templates
|
|
361
|
+
const usages = api.analysis.templateAnalyzer.analyze('<invalid html>>>');
|
|
362
|
+
// Returns empty array or best-effort parsing
|
|
363
|
+
|
|
364
|
+
// Handles missing files
|
|
365
|
+
const dependencies = api.analysis.dependencyAnalyzer.analyzeDependencies(missingFile);
|
|
366
|
+
// Returns empty map, no crash
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Performance-Conscious
|
|
370
|
+
|
|
371
|
+
Efficient algorithms for large codebases:
|
|
372
|
+
- Lazy evaluation where possible
|
|
373
|
+
- Caching of expensive operations
|
|
374
|
+
- Streaming processing for large files
|
|
375
|
+
- Incremental analysis support
|
|
376
|
+
|
|
377
|
+
## Testing
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
pnpm test --filter @angular-modernizer/api
|
|
381
|
+
pnpm test --filter @angular-modernizer/api --coverage
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Test categories:
|
|
385
|
+
- Unit tests: individual tool functionality
|
|
386
|
+
- Integration tests: tool composition and workflows
|
|
387
|
+
- Performance tests: large codebase handling
|
|
388
|
+
- Edge case tests: error conditions and malformed input
|
|
389
|
+
|
|
390
|
+
Testing patterns:
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
import { createPublicApi } from '@angular-modernizer/api';
|
|
394
|
+
import { Project } from 'ts-morph';
|
|
395
|
+
|
|
396
|
+
describe('TemplateAnalyzer', () => {
|
|
397
|
+
let api: PublicApi;
|
|
398
|
+
let project: Project;
|
|
399
|
+
|
|
400
|
+
beforeEach(() => {
|
|
401
|
+
api = createPublicApi();
|
|
402
|
+
project = new Project();
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should extract component usages', () => {
|
|
406
|
+
const template = '<app-user [user]="user"></app-user>';
|
|
407
|
+
const usages = api.analysis.templateAnalyzer.analyze(template);
|
|
408
|
+
|
|
409
|
+
expect(usages).toContainEqual({
|
|
410
|
+
element: 'app-user',
|
|
411
|
+
selector: 'app-user',
|
|
412
|
+
inputs: ['user'],
|
|
413
|
+
attributes: [],
|
|
414
|
+
outputs: [],
|
|
415
|
+
content: '',
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Package Structure
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
packages/api/
|
|
425
|
+
├── src/
|
|
426
|
+
│ ├── analysis/
|
|
427
|
+
│ │ ├── template-analyzer.ts
|
|
428
|
+
│ │ ├── dependency-analyzer.ts
|
|
429
|
+
│ │ └── complexity-metrics-calculator.ts
|
|
430
|
+
│ ├── transformation/
|
|
431
|
+
│ │ └── import-manager.ts
|
|
432
|
+
│ ├── index.ts
|
|
433
|
+
│ └── types.ts
|
|
434
|
+
├── __tests__/
|
|
435
|
+
│ ├── analysis/
|
|
436
|
+
│ │ ├── template-analyzer.test.ts
|
|
437
|
+
│ │ ├── dependency-analyzer.test.ts
|
|
438
|
+
│ │ └── complexity-metrics-calculator.test.ts
|
|
439
|
+
│ ├── transformation/
|
|
440
|
+
│ │ └── import-manager.test.ts
|
|
441
|
+
│ └── integration.test.ts
|
|
442
|
+
├── package.json
|
|
443
|
+
├── tsconfig.json
|
|
444
|
+
├── jest.config.js
|
|
445
|
+
└── README.md
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Dependencies
|
|
449
|
+
|
|
450
|
+
- **ts-morph:** TypeScript AST manipulation
|
|
451
|
+
- **typescript:** TypeScript compiler API
|
|
452
|
+
- **@angular-modernizer/core:** Kernel and infrastructure (peer dependency)
|
|
453
|
+
|
|
454
|
+
## Contributing
|
|
455
|
+
|
|
456
|
+
When adding new API tools:
|
|
457
|
+
|
|
458
|
+
1. Follow the PublicApi pattern - add to the unified interface
|
|
459
|
+
2. Write comprehensive tests (unit, integration, and edge cases)
|
|
460
|
+
3. Document with examples and update this README
|
|
461
|
+
4. Maintain backward compatibility
|
|
462
|
+
5. Optimize for large codebases
|
|
463
|
+
|
|
464
|
+
### Adding a New Analysis Tool
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// 1. Create the tool interface
|
|
468
|
+
export interface NewAnalyzer {
|
|
469
|
+
analyze(input: InputType): OutputType;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// 2. Implement the tool
|
|
473
|
+
export class NewAnalyzerImpl implements NewAnalyzer {
|
|
474
|
+
analyze(input: InputType): OutputType {
|
|
475
|
+
// Implementation
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 3. Add to PublicApi interface
|
|
480
|
+
interface PublicApi {
|
|
481
|
+
analysis: {
|
|
482
|
+
templateAnalyzer: TemplateAnalyzer;
|
|
483
|
+
dependencyAnalyzer: DependencyAnalyzer;
|
|
484
|
+
newAnalyzer: NewAnalyzer;
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// 4. Update factory function
|
|
489
|
+
export function createPublicApi(): PublicApi {
|
|
490
|
+
return {
|
|
491
|
+
analysis: {
|
|
492
|
+
templateAnalyzer: new TemplateAnalyzerImpl(),
|
|
493
|
+
dependencyAnalyzer: new DependencyAnalyzerImpl(),
|
|
494
|
+
newAnalyzer: new NewAnalyzerImpl(),
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## See Also
|
|
501
|
+
|
|
502
|
+
- [packages/core/README.md](../core/README.md) - Kernel infrastructure
|
|
503
|
+
- [packages/plugin-system/README.md](../plugin-system/README.md) - Plugin contracts
|
|
504
|
+
|
|
505
|
+
## License
|
|
506
|
+
|
|
507
|
+
MIT License - See LICENSE file for details
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @angular-modernizer/api - Complexity Metrics Calculator
|
|
3
|
+
*
|
|
4
|
+
* Calculates code complexity and maintainability metrics for Angular applications.
|
|
5
|
+
* Provides quantitative measures to prioritize refactoring efforts.
|
|
6
|
+
*/
|
|
7
|
+
import type { SourceFile, ClassDeclaration } from 'ts-morph';
|
|
8
|
+
/**
|
|
9
|
+
* Angular-specific metrics for components and directives.
|
|
10
|
+
*/
|
|
11
|
+
export interface AngularMetrics {
|
|
12
|
+
/**
|
|
13
|
+
* Number of lifecycle hooks (ngOnInit, ngOnDestroy, etc.).
|
|
14
|
+
*/
|
|
15
|
+
lifecycleHooks: number;
|
|
16
|
+
/**
|
|
17
|
+
* Number of @Input properties.
|
|
18
|
+
*/
|
|
19
|
+
inputs: number;
|
|
20
|
+
/**
|
|
21
|
+
* Number of @Output properties.
|
|
22
|
+
*/
|
|
23
|
+
outputs: number;
|
|
24
|
+
/**
|
|
25
|
+
* Number of Angular decorators (@Component, @Directive, etc.).
|
|
26
|
+
*/
|
|
27
|
+
decorators: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Complexity Metrics Calculator - analyzes code complexity and maintainability.
|
|
31
|
+
*/
|
|
32
|
+
export declare class ComplexityMetricsCalculator {
|
|
33
|
+
/**
|
|
34
|
+
* Calculate overall complexity score for a class (0-100 scale).
|
|
35
|
+
* Custom weighted heuristic combining established metrics (McCabe's Cyclomatic Complexity,
|
|
36
|
+
* LOC, method count from CK metrics) with Angular-specific indicators.
|
|
37
|
+
*
|
|
38
|
+
* Combines multiple metrics:
|
|
39
|
+
* - Cyclomatic complexity
|
|
40
|
+
* - Lines of code
|
|
41
|
+
* - Method count
|
|
42
|
+
* - Angular-specific complexity
|
|
43
|
+
*
|
|
44
|
+
* @param classDecl - The class declaration to analyze
|
|
45
|
+
* @returns Complexity score (0-100)
|
|
46
|
+
*/
|
|
47
|
+
calculateOverallComplexity(classDecl: ClassDeclaration): number;
|
|
48
|
+
/**
|
|
49
|
+
* Calculate cyclomatic complexity for a class.
|
|
50
|
+
*
|
|
51
|
+
* Measures the number of linearly independent paths through the code.
|
|
52
|
+
* Higher values indicate more complex control flow.
|
|
53
|
+
*
|
|
54
|
+
* @param classDecl - The class declaration
|
|
55
|
+
* @returns Cyclomatic complexity score
|
|
56
|
+
*/
|
|
57
|
+
calculateCyclomaticComplexity(classDecl: ClassDeclaration): number;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate cyclomatic complexity for a single method.
|
|
60
|
+
* Implements McCabe's algorithm by counting decision points:
|
|
61
|
+
* if/else if, loops, switch cases, ternary operators, logical operators.
|
|
62
|
+
*/
|
|
63
|
+
private calculateMethodComplexity;
|
|
64
|
+
/**
|
|
65
|
+
* Calculate lines of code for a source file.
|
|
66
|
+
*
|
|
67
|
+
* Excludes comments and whitespace for meaningful LOC count.
|
|
68
|
+
*
|
|
69
|
+
* @param sourceFile - The source file
|
|
70
|
+
* @returns Number of lines of code
|
|
71
|
+
*/
|
|
72
|
+
calculateLOC(sourceFile: SourceFile): number;
|
|
73
|
+
/**
|
|
74
|
+
* Calculate the number of methods in a class.
|
|
75
|
+
*
|
|
76
|
+
* @param classDecl - The class declaration
|
|
77
|
+
* @returns Number of methods
|
|
78
|
+
*/
|
|
79
|
+
calculateMethodCount(classDecl: ClassDeclaration): number;
|
|
80
|
+
/**
|
|
81
|
+
* Calculate dependency count for a source file.
|
|
82
|
+
*
|
|
83
|
+
* Counts unique external dependencies (imports from node_modules).
|
|
84
|
+
*
|
|
85
|
+
* @param sourceFile - The source file
|
|
86
|
+
* @returns Number of unique dependencies
|
|
87
|
+
*/
|
|
88
|
+
calculateDependencyCount(sourceFile: SourceFile): number;
|
|
89
|
+
/**
|
|
90
|
+
* Calculate Angular-specific metrics for a class.
|
|
91
|
+
*
|
|
92
|
+
* Analyzes Angular decorators and patterns.
|
|
93
|
+
*
|
|
94
|
+
* @param classDecl - The class declaration
|
|
95
|
+
* @returns Angular-specific metrics
|
|
96
|
+
*/
|
|
97
|
+
calculateAngularSpecificMetrics(classDecl: ClassDeclaration): AngularMetrics;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=complexity-metrics-calculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complexity-metrics-calculator.d.ts","sourceRoot":"","sources":["../../src/analysis/complexity-metrics-calculator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAqB,MAAM,UAAU,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,2BAA2B;IACtC;;;;;;;;;;;;;OAaG;IACH,0BAA0B,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM;IAyB/D;;;;;;;;OAQG;IACH,6BAA6B,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM;IAWlE;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;IAuBjC;;;;;;;OAOG;IACH,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM;IAqB5C;;;;;OAKG;IACH,oBAAoB,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM;IAIzD;;;;;;;OAOG;IACH,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM;IAkBxD;;;;;;;OAOG;IACH,+BAA+B,CAAC,SAAS,EAAE,gBAAgB,GAAG,cAAc;CAsD7E"}
|