@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
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @angular-modernizer/api - Service Pattern Recognizer
|
|
3
|
+
*
|
|
4
|
+
* Centralized service for recognizing and classifying Angular service patterns.
|
|
5
|
+
* Uses ng-morph for Angular-specific detection and ts-morph for fallback analysis.
|
|
6
|
+
*
|
|
7
|
+
* MIGRATION NOTE: Created as part of ng-morph integration Phase 4.
|
|
8
|
+
* - Centralizes service pattern recognition across orchestrators
|
|
9
|
+
* - Uses ng-morph for Angular services (`` decorated classes)
|
|
10
|
+
* - Falls back to ts-morph for complex edge cases
|
|
11
|
+
* - Replaces scattered service suffix checks in multiple orchestrators
|
|
12
|
+
* - Expected code reduction: ~40% in orchestrators using this service
|
|
13
|
+
*/
|
|
14
|
+
import type { SourceFile, ClassDeclaration } from 'ts-morph';
|
|
15
|
+
import type { NgMorphAdapter } from '../transformation/ng-morph-adapter.js';
|
|
16
|
+
/**
|
|
17
|
+
* Service pattern types that can be recognized.
|
|
18
|
+
*/
|
|
19
|
+
export declare enum ServicePattern {
|
|
20
|
+
/** Angular service decorated with `` */
|
|
21
|
+
INJECTABLE = "injectable",
|
|
22
|
+
/** Service class with 'Service' suffix */
|
|
23
|
+
SERVICE = "service",
|
|
24
|
+
/** Repository pattern class with 'Repository' suffix */
|
|
25
|
+
REPOSITORY = "repository",
|
|
26
|
+
/** Provider class with 'Provider' suffix */
|
|
27
|
+
PROVIDER = "provider",
|
|
28
|
+
/** Facade pattern class with 'Facade' suffix */
|
|
29
|
+
FACADE = "facade",
|
|
30
|
+
/** Store class (state management) with 'Store' suffix */
|
|
31
|
+
STORE = "store",
|
|
32
|
+
/** Helper class with 'Helper' suffix */
|
|
33
|
+
HELPER = "helper",
|
|
34
|
+
/** Utility class with 'Util' or 'Utils' suffix */
|
|
35
|
+
UTILITY = "utility",
|
|
36
|
+
/** Unknown pattern - not recognized as service */
|
|
37
|
+
UNKNOWN = "unknown"
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Service classification result.
|
|
41
|
+
*/
|
|
42
|
+
export interface ServiceClassification {
|
|
43
|
+
/** The service pattern type */
|
|
44
|
+
pattern: ServicePattern;
|
|
45
|
+
/** Whether this is an Angular `` service */
|
|
46
|
+
isInjectable: boolean;
|
|
47
|
+
/** Class name */
|
|
48
|
+
className: string;
|
|
49
|
+
/** File path */
|
|
50
|
+
filePath: string;
|
|
51
|
+
/** Whether this is a state management service */
|
|
52
|
+
isStateful: boolean;
|
|
53
|
+
/** Confidence level (0-1) for the classification */
|
|
54
|
+
confidence: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Service detection result with confidence scoring.
|
|
58
|
+
*/
|
|
59
|
+
export interface ServiceDetectionResult {
|
|
60
|
+
/** Whether this is a service */
|
|
61
|
+
isService: boolean;
|
|
62
|
+
/** Confidence level (0-1) for the detection */
|
|
63
|
+
confidence: number;
|
|
64
|
+
/** Detection method used */
|
|
65
|
+
detectionMethod: 'decorator' | 'filename' | 'classname' | 'combined' | 'none';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Service Pattern Recognizer - centralized service pattern detection.
|
|
69
|
+
*
|
|
70
|
+
* HYBRID APPROACH:
|
|
71
|
+
* - Uses ng-morph to detect `` decorated classes
|
|
72
|
+
* - Falls back to ts-morph for suffix-based pattern matching
|
|
73
|
+
* - Provides confidence scores for classifications
|
|
74
|
+
*
|
|
75
|
+
* This replaces scattered service detection logic across orchestrators.
|
|
76
|
+
*/
|
|
77
|
+
export declare class ServicePatternRecognizer {
|
|
78
|
+
private readonly ngMorphAdapter?;
|
|
79
|
+
/**
|
|
80
|
+
* Constructor for ServicePatternRecognizer.
|
|
81
|
+
*
|
|
82
|
+
* @param ngMorphAdapter - Optional ng-morph adapter for Angular-specific operations
|
|
83
|
+
*
|
|
84
|
+
* BACKWARD COMPATIBILITY: Constructor is optional - if not provided, falls back to pure ts-morph.
|
|
85
|
+
*/
|
|
86
|
+
constructor(ngMorphAdapter?: NgMorphAdapter);
|
|
87
|
+
/**
|
|
88
|
+
* Classify a source file as a service pattern.
|
|
89
|
+
*
|
|
90
|
+
* Uses ng-morph to detect ``, then analyzes class structure
|
|
91
|
+
* and naming to determine the specific service pattern.
|
|
92
|
+
*
|
|
93
|
+
* @param sourceFile - The source file to analyze
|
|
94
|
+
* @returns Service classification result
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const classification = recognizer.classifyService(sourceFile);
|
|
99
|
+
* if (classification.pattern === ServicePattern.INJECTABLE) {
|
|
100
|
+
* console.info('This is an Angular service');
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
classifyService(sourceFile: SourceFile): ServiceClassification;
|
|
105
|
+
/**
|
|
106
|
+
* Check if a source file contains a service class.
|
|
107
|
+
*
|
|
108
|
+
* Quick check without full classification.
|
|
109
|
+
*
|
|
110
|
+
* @param sourceFile - The source file to check
|
|
111
|
+
* @returns True if the file contains a service class
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* if (recognizer.isServiceFile(sourceFile)) {
|
|
116
|
+
* // Perform service-specific analysis
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
isServiceFile(sourceFile: SourceFile): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Get all service patterns in the source file.
|
|
123
|
+
*
|
|
124
|
+
* Some files may contain multiple service classes.
|
|
125
|
+
*
|
|
126
|
+
* @param sourceFile - The source file to analyze
|
|
127
|
+
* @returns Array of service classifications
|
|
128
|
+
*/
|
|
129
|
+
getAllServicePatterns(sourceFile: SourceFile): ServiceClassification[];
|
|
130
|
+
/**
|
|
131
|
+
* Detect if a class is a service with confidence scoring.
|
|
132
|
+
*
|
|
133
|
+
* This method provides a more detailed analysis than isService(),
|
|
134
|
+
* including confidence levels and detection methodology.
|
|
135
|
+
*
|
|
136
|
+
* @param classDecl - The class declaration to analyze
|
|
137
|
+
* @returns Service detection result with confidence and method
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const result = recognizer.detectService(classDecl);
|
|
142
|
+
* if (result.isService && result.confidence > 0.8) {
|
|
143
|
+
* console.info(`Detected service via ${result.detectionMethod}`);
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
detectService(classDecl: ClassDeclaration): ServiceDetectionResult;
|
|
148
|
+
/**
|
|
149
|
+
* Check if a class declaration has the `` decorator.
|
|
150
|
+
*
|
|
151
|
+
* Uses ng-morph adapter if available for enhanced detection,
|
|
152
|
+
* falls back to legacy ts-morph detection otherwise.
|
|
153
|
+
*
|
|
154
|
+
* @param classDecl - The class declaration to check
|
|
155
|
+
* @returns True if the class has `` decorator
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* if (recognizer.hasInjectableDecorator(classDecl)) {
|
|
160
|
+
* console.info('This is an Angular service');
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
hasInjectableDecorator(classDecl: ClassDeclaration): boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Legacy method to check for `` decorator using ts-morph.
|
|
167
|
+
*
|
|
168
|
+
* This is the original detection logic, kept for backward compatibility
|
|
169
|
+
* and as a fallback when ng-morph is not available.
|
|
170
|
+
*
|
|
171
|
+
* @param classDecl - The class declaration to check
|
|
172
|
+
* @returns True if the class has `` decorator
|
|
173
|
+
*/
|
|
174
|
+
private legacyHasInjectableDecorator;
|
|
175
|
+
/**
|
|
176
|
+
* Simple check if a class is a service.
|
|
177
|
+
*
|
|
178
|
+
* This is a convenience method that wraps detectService()
|
|
179
|
+
* for backward compatibility with existing code.
|
|
180
|
+
*
|
|
181
|
+
* @param classDecl - The class declaration to check
|
|
182
|
+
* @returns True if the class is detected as a service
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* if (recognizer.isService(classDecl)) {
|
|
187
|
+
* // Handle service class
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
isService(classDecl: ClassDeclaration): boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Detect `` service using ng-morph.
|
|
194
|
+
*
|
|
195
|
+
* IMPLEMENTATION NOTE: ng-morph doesn't have a dedicated API for finding
|
|
196
|
+
* `` services. We use ts-morph to search for the `` decorator
|
|
197
|
+
* directly, which provides HIGH CONFIDENCE (1.0) classification.
|
|
198
|
+
*
|
|
199
|
+
* @param filePath - File path to check
|
|
200
|
+
* @returns Class declaration if found, undefined otherwise
|
|
201
|
+
*/
|
|
202
|
+
private detectInjectableUsingNgMorph;
|
|
203
|
+
/**
|
|
204
|
+
* Get all `` classes using ng-morph.
|
|
205
|
+
*
|
|
206
|
+
* @param filePath - File path to check
|
|
207
|
+
* @returns Array of class declarations
|
|
208
|
+
*/
|
|
209
|
+
private getAllInjectablesUsingNgMorph;
|
|
210
|
+
/**
|
|
211
|
+
* Classify an `` service.
|
|
212
|
+
*
|
|
213
|
+
* @param classDecl - The class declaration
|
|
214
|
+
* @param filePath - The file path
|
|
215
|
+
* @returns Service classification
|
|
216
|
+
*/
|
|
217
|
+
private classifyInjectableService;
|
|
218
|
+
/**
|
|
219
|
+
* Classify service using ts-morph (fallback).
|
|
220
|
+
*
|
|
221
|
+
* @param sourceFile - The source file
|
|
222
|
+
* @returns Service classification
|
|
223
|
+
*/
|
|
224
|
+
private classifyUsingTsMorph;
|
|
225
|
+
/**
|
|
226
|
+
* Detect service pattern from class name.
|
|
227
|
+
*
|
|
228
|
+
* @param className - The class name to analyze
|
|
229
|
+
* @returns Service pattern
|
|
230
|
+
*/
|
|
231
|
+
private detectPatternFromName;
|
|
232
|
+
/**
|
|
233
|
+
* Check if a pattern represents stateful services.
|
|
234
|
+
*
|
|
235
|
+
* @param pattern - The service pattern
|
|
236
|
+
* @returns True if stateful
|
|
237
|
+
*/
|
|
238
|
+
private isStatefulPattern;
|
|
239
|
+
/**
|
|
240
|
+
* Check if a class is stateful (has state properties).
|
|
241
|
+
*
|
|
242
|
+
* @param classDecl - The class declaration
|
|
243
|
+
* @returns True if stateful
|
|
244
|
+
*/
|
|
245
|
+
private isStatefulClass;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=service-pattern-recognizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-pattern-recognizer.d.ts","sourceRoot":"","sources":["../../src/analysis/service-pattern-recognizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E;;GAEG;AACH,oBAAY,cAAc;IACxB,wCAAwC;IACxC,UAAU,eAAe;IAEzB,0CAA0C;IAC1C,OAAO,YAAY;IAEnB,wDAAwD;IACxD,UAAU,eAAe;IAEzB,4CAA4C;IAC5C,QAAQ,aAAa;IAErB,gDAAgD;IAChD,MAAM,WAAW;IAEjB,yDAAyD;IACzD,KAAK,UAAU;IAEf,wCAAwC;IACxC,MAAM,WAAW;IAEjB,kDAAkD;IAClD,OAAO,YAAY;IAEnB,kDAAkD;IAClD,OAAO,YAAY;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,OAAO,EAAE,cAAc,CAAC;IAExB,4CAA4C;IAC5C,YAAY,EAAE,OAAO,CAAC;IAEtB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAElB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAC;IAEpB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gCAAgC;IAChC,SAAS,EAAE,OAAO,CAAC;IAEnB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IAEnB,4BAA4B;IAC5B,eAAe,EAAE,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;CAC/E;AAED;;;;;;;;;GASG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IAEjD;;;;;;OAMG;gBACS,cAAc,CAAC,EAAE,cAAc;IAI3C;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,qBAAqB;IAe9D;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO;IAK9C;;;;;;;OAOG;IACH,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,qBAAqB,EAAE;IA2CtE;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,SAAS,EAAE,gBAAgB,GAAG,sBAAsB;IA0ClE;;;;;;;;;;;;;;;OAeG;IACH,sBAAsB,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO;IAkB5D;;;;;;;;OAQG;IACH,OAAO,CAAC,4BAA4B;IAIpC;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO;IAI/C;;;;;;;;;OASG;IACH,OAAO,CAAC,4BAA4B;IA6BpC;;;;;OAKG;IACH,OAAO,CAAC,6BAA6B;IAyBrC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAiC5B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA4B7B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAiBxB"}
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @angular-modernizer/api - Service Pattern Recognizer
|
|
3
|
+
*
|
|
4
|
+
* Centralized service for recognizing and classifying Angular service patterns.
|
|
5
|
+
* Uses ng-morph for Angular-specific detection and ts-morph for fallback analysis.
|
|
6
|
+
*
|
|
7
|
+
* MIGRATION NOTE: Created as part of ng-morph integration Phase 4.
|
|
8
|
+
* - Centralizes service pattern recognition across orchestrators
|
|
9
|
+
* - Uses ng-morph for Angular services (`` decorated classes)
|
|
10
|
+
* - Falls back to ts-morph for complex edge cases
|
|
11
|
+
* - Replaces scattered service suffix checks in multiple orchestrators
|
|
12
|
+
* - Expected code reduction: ~40% in orchestrators using this service
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Service pattern types that can be recognized.
|
|
16
|
+
*/
|
|
17
|
+
export var ServicePattern;
|
|
18
|
+
(function (ServicePattern) {
|
|
19
|
+
/** Angular service decorated with `` */
|
|
20
|
+
ServicePattern["INJECTABLE"] = "injectable";
|
|
21
|
+
/** Service class with 'Service' suffix */
|
|
22
|
+
ServicePattern["SERVICE"] = "service";
|
|
23
|
+
/** Repository pattern class with 'Repository' suffix */
|
|
24
|
+
ServicePattern["REPOSITORY"] = "repository";
|
|
25
|
+
/** Provider class with 'Provider' suffix */
|
|
26
|
+
ServicePattern["PROVIDER"] = "provider";
|
|
27
|
+
/** Facade pattern class with 'Facade' suffix */
|
|
28
|
+
ServicePattern["FACADE"] = "facade";
|
|
29
|
+
/** Store class (state management) with 'Store' suffix */
|
|
30
|
+
ServicePattern["STORE"] = "store";
|
|
31
|
+
/** Helper class with 'Helper' suffix */
|
|
32
|
+
ServicePattern["HELPER"] = "helper";
|
|
33
|
+
/** Utility class with 'Util' or 'Utils' suffix */
|
|
34
|
+
ServicePattern["UTILITY"] = "utility";
|
|
35
|
+
/** Unknown pattern - not recognized as service */
|
|
36
|
+
ServicePattern["UNKNOWN"] = "unknown";
|
|
37
|
+
})(ServicePattern || (ServicePattern = {}));
|
|
38
|
+
/**
|
|
39
|
+
* Service Pattern Recognizer - centralized service pattern detection.
|
|
40
|
+
*
|
|
41
|
+
* HYBRID APPROACH:
|
|
42
|
+
* - Uses ng-morph to detect `` decorated classes
|
|
43
|
+
* - Falls back to ts-morph for suffix-based pattern matching
|
|
44
|
+
* - Provides confidence scores for classifications
|
|
45
|
+
*
|
|
46
|
+
* This replaces scattered service detection logic across orchestrators.
|
|
47
|
+
*/
|
|
48
|
+
export class ServicePatternRecognizer {
|
|
49
|
+
ngMorphAdapter;
|
|
50
|
+
/**
|
|
51
|
+
* Constructor for ServicePatternRecognizer.
|
|
52
|
+
*
|
|
53
|
+
* @param ngMorphAdapter - Optional ng-morph adapter for Angular-specific operations
|
|
54
|
+
*
|
|
55
|
+
* BACKWARD COMPATIBILITY: Constructor is optional - if not provided, falls back to pure ts-morph.
|
|
56
|
+
*/
|
|
57
|
+
constructor(ngMorphAdapter) {
|
|
58
|
+
this.ngMorphAdapter = ngMorphAdapter;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Classify a source file as a service pattern.
|
|
62
|
+
*
|
|
63
|
+
* Uses ng-morph to detect ``, then analyzes class structure
|
|
64
|
+
* and naming to determine the specific service pattern.
|
|
65
|
+
*
|
|
66
|
+
* @param sourceFile - The source file to analyze
|
|
67
|
+
* @returns Service classification result
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const classification = recognizer.classifyService(sourceFile);
|
|
72
|
+
* if (classification.pattern === ServicePattern.INJECTABLE) {
|
|
73
|
+
* console.info('This is an Angular service');
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
classifyService(sourceFile) {
|
|
78
|
+
const filePath = sourceFile.getFilePath();
|
|
79
|
+
// Try ng-morph first for Angular services
|
|
80
|
+
if (this.ngMorphAdapter) {
|
|
81
|
+
const injectableClass = this.detectInjectableUsingNgMorph(filePath);
|
|
82
|
+
if (injectableClass) {
|
|
83
|
+
return this.classifyInjectableService(injectableClass, filePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Fall back to ts-morph for suffix-based detection
|
|
87
|
+
return this.classifyUsingTsMorph(sourceFile);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if a source file contains a service class.
|
|
91
|
+
*
|
|
92
|
+
* Quick check without full classification.
|
|
93
|
+
*
|
|
94
|
+
* @param sourceFile - The source file to check
|
|
95
|
+
* @returns True if the file contains a service class
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* if (recognizer.isServiceFile(sourceFile)) {
|
|
100
|
+
* // Perform service-specific analysis
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
isServiceFile(sourceFile) {
|
|
105
|
+
const classification = this.classifyService(sourceFile);
|
|
106
|
+
return classification.pattern !== ServicePattern.UNKNOWN;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get all service patterns in the source file.
|
|
110
|
+
*
|
|
111
|
+
* Some files may contain multiple service classes.
|
|
112
|
+
*
|
|
113
|
+
* @param sourceFile - The source file to analyze
|
|
114
|
+
* @returns Array of service classifications
|
|
115
|
+
*/
|
|
116
|
+
getAllServicePatterns(sourceFile) {
|
|
117
|
+
const filePath = sourceFile.getFilePath();
|
|
118
|
+
const results = [];
|
|
119
|
+
// Try ng-morph for all @Injectable classes
|
|
120
|
+
if (this.ngMorphAdapter) {
|
|
121
|
+
const injectables = this.getAllInjectablesUsingNgMorph(filePath);
|
|
122
|
+
results.push(...injectables.map((cls) => this.classifyInjectableService(cls, filePath)));
|
|
123
|
+
}
|
|
124
|
+
// Also check for non-@Injectable service classes
|
|
125
|
+
const classes = sourceFile.getClasses();
|
|
126
|
+
for (const cls of classes) {
|
|
127
|
+
const className = cls.getName();
|
|
128
|
+
if (!className) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// Skip if already classified as @Injectable
|
|
132
|
+
if (results.some((r) => r.className === className)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const pattern = this.detectPatternFromName(className);
|
|
136
|
+
if (pattern !== ServicePattern.UNKNOWN) {
|
|
137
|
+
results.push({
|
|
138
|
+
pattern,
|
|
139
|
+
isInjectable: false,
|
|
140
|
+
className,
|
|
141
|
+
filePath,
|
|
142
|
+
isStateful: this.isStatefulPattern(pattern),
|
|
143
|
+
confidence: 0.7, // Lower confidence for suffix-based detection
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Detect if a class is a service with confidence scoring.
|
|
151
|
+
*
|
|
152
|
+
* This method provides a more detailed analysis than isService(),
|
|
153
|
+
* including confidence levels and detection methodology.
|
|
154
|
+
*
|
|
155
|
+
* @param classDecl - The class declaration to analyze
|
|
156
|
+
* @returns Service detection result with confidence and method
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* const result = recognizer.detectService(classDecl);
|
|
161
|
+
* if (result.isService && result.confidence > 0.8) {
|
|
162
|
+
* console.info(`Detected service via ${result.detectionMethod}`);
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
detectService(classDecl) {
|
|
167
|
+
// Priority 1: Check for @Injectable decorator (highest confidence)
|
|
168
|
+
if (this.hasInjectableDecorator(classDecl)) {
|
|
169
|
+
return {
|
|
170
|
+
isService: true,
|
|
171
|
+
confidence: 1.0,
|
|
172
|
+
detectionMethod: 'decorator',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// Priority 2: Check class name pattern
|
|
176
|
+
const className = classDecl.getName();
|
|
177
|
+
if (className) {
|
|
178
|
+
const pattern = this.detectPatternFromName(className);
|
|
179
|
+
if (pattern !== ServicePattern.UNKNOWN) {
|
|
180
|
+
return {
|
|
181
|
+
isService: true,
|
|
182
|
+
confidence: 0.7,
|
|
183
|
+
detectionMethod: 'classname',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Priority 3: Check file name pattern
|
|
188
|
+
const sourceFile = classDecl.getSourceFile();
|
|
189
|
+
const filePath = sourceFile.getFilePath().toLowerCase();
|
|
190
|
+
if (filePath.includes('.service.') || filePath.endsWith('service.ts')) {
|
|
191
|
+
return {
|
|
192
|
+
isService: true,
|
|
193
|
+
confidence: 0.6,
|
|
194
|
+
detectionMethod: 'filename',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Not detected as service
|
|
198
|
+
return {
|
|
199
|
+
isService: false,
|
|
200
|
+
confidence: 0.0,
|
|
201
|
+
detectionMethod: 'none',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Check if a class declaration has the `` decorator.
|
|
206
|
+
*
|
|
207
|
+
* Uses ng-morph adapter if available for enhanced detection,
|
|
208
|
+
* falls back to legacy ts-morph detection otherwise.
|
|
209
|
+
*
|
|
210
|
+
* @param classDecl - The class declaration to check
|
|
211
|
+
* @returns True if the class has `` decorator
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* if (recognizer.hasInjectableDecorator(classDecl)) {
|
|
216
|
+
* console.info('This is an Angular service');
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
hasInjectableDecorator(classDecl) {
|
|
221
|
+
// Try ng-morph approach if adapter is available
|
|
222
|
+
if (this.ngMorphAdapter) {
|
|
223
|
+
try {
|
|
224
|
+
const filePath = classDecl.getSourceFile().getFilePath();
|
|
225
|
+
const ngMorphClass = this.detectInjectableUsingNgMorph(filePath);
|
|
226
|
+
if (ngMorphClass && ngMorphClass.getName() === classDecl.getName()) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Fall through to legacy detection
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Fall back to legacy ts-morph detection
|
|
235
|
+
return this.legacyHasInjectableDecorator(classDecl);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Legacy method to check for `` decorator using ts-morph.
|
|
239
|
+
*
|
|
240
|
+
* This is the original detection logic, kept for backward compatibility
|
|
241
|
+
* and as a fallback when ng-morph is not available.
|
|
242
|
+
*
|
|
243
|
+
* @param classDecl - The class declaration to check
|
|
244
|
+
* @returns True if the class has `` decorator
|
|
245
|
+
*/
|
|
246
|
+
legacyHasInjectableDecorator(classDecl) {
|
|
247
|
+
return classDecl.getDecorator('Injectable') !== undefined;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Simple check if a class is a service.
|
|
251
|
+
*
|
|
252
|
+
* This is a convenience method that wraps detectService()
|
|
253
|
+
* for backward compatibility with existing code.
|
|
254
|
+
*
|
|
255
|
+
* @param classDecl - The class declaration to check
|
|
256
|
+
* @returns True if the class is detected as a service
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* if (recognizer.isService(classDecl)) {
|
|
261
|
+
* // Handle service class
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
isService(classDecl) {
|
|
266
|
+
return this.detectService(classDecl).isService;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Detect `` service using ng-morph.
|
|
270
|
+
*
|
|
271
|
+
* IMPLEMENTATION NOTE: ng-morph doesn't have a dedicated API for finding
|
|
272
|
+
* `` services. We use ts-morph to search for the `` decorator
|
|
273
|
+
* directly, which provides HIGH CONFIDENCE (1.0) classification.
|
|
274
|
+
*
|
|
275
|
+
* @param filePath - File path to check
|
|
276
|
+
* @returns Class declaration if found, undefined otherwise
|
|
277
|
+
*/
|
|
278
|
+
detectInjectableUsingNgMorph(filePath) {
|
|
279
|
+
try {
|
|
280
|
+
// Get the source file from ts-morph project
|
|
281
|
+
const project = this.ngMorphAdapter?.getProject();
|
|
282
|
+
const sourceFile = project?.getSourceFile(filePath);
|
|
283
|
+
if (!sourceFile) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
// Find the first class with @Injectable decorator
|
|
287
|
+
// Note: ng-morph (as of v4.8.4) doesn't provide a specialized API
|
|
288
|
+
// for services, so we use ts-morph's decorator detection
|
|
289
|
+
for (const cls of sourceFile.getClasses()) {
|
|
290
|
+
const hasInjectableDecorator = cls.getDecorator('Injectable') !== undefined;
|
|
291
|
+
if (hasInjectableDecorator) {
|
|
292
|
+
return cls;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Get all `` classes using ng-morph.
|
|
303
|
+
*
|
|
304
|
+
* @param filePath - File path to check
|
|
305
|
+
* @returns Array of class declarations
|
|
306
|
+
*/
|
|
307
|
+
getAllInjectablesUsingNgMorph(filePath) {
|
|
308
|
+
try {
|
|
309
|
+
const classes = [];
|
|
310
|
+
const project = this.ngMorphAdapter?.getProject();
|
|
311
|
+
const sourceFile = project?.getSourceFile(filePath);
|
|
312
|
+
if (!sourceFile) {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
// Check all classes in the file
|
|
316
|
+
for (const cls of sourceFile.getClasses()) {
|
|
317
|
+
const hasInjectableDecorator = cls.getDecorator('Injectable') !== undefined;
|
|
318
|
+
if (hasInjectableDecorator) {
|
|
319
|
+
classes.push(cls);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return classes;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Classify an `` service.
|
|
330
|
+
*
|
|
331
|
+
* @param classDecl - The class declaration
|
|
332
|
+
* @param filePath - The file path
|
|
333
|
+
* @returns Service classification
|
|
334
|
+
*/
|
|
335
|
+
classifyInjectableService(classDecl, filePath) {
|
|
336
|
+
const className = classDecl.getName() ?? 'Unknown';
|
|
337
|
+
const pattern = this.detectPatternFromName(className);
|
|
338
|
+
return {
|
|
339
|
+
pattern: pattern === ServicePattern.UNKNOWN
|
|
340
|
+
? ServicePattern.INJECTABLE
|
|
341
|
+
: pattern,
|
|
342
|
+
isInjectable: true,
|
|
343
|
+
className,
|
|
344
|
+
filePath,
|
|
345
|
+
isStateful: this.isStatefulClass(classDecl),
|
|
346
|
+
confidence: 1.0, // High confidence for @Injectable
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Classify service using ts-morph (fallback).
|
|
351
|
+
*
|
|
352
|
+
* @param sourceFile - The source file
|
|
353
|
+
* @returns Service classification
|
|
354
|
+
*/
|
|
355
|
+
classifyUsingTsMorph(sourceFile) {
|
|
356
|
+
const filePath = sourceFile.getFilePath();
|
|
357
|
+
const classes = sourceFile.getClasses();
|
|
358
|
+
for (const cls of classes) {
|
|
359
|
+
const className = cls.getName();
|
|
360
|
+
if (!className) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const pattern = this.detectPatternFromName(className);
|
|
364
|
+
if (pattern !== ServicePattern.UNKNOWN) {
|
|
365
|
+
return {
|
|
366
|
+
pattern,
|
|
367
|
+
isInjectable: false,
|
|
368
|
+
className,
|
|
369
|
+
filePath,
|
|
370
|
+
isStateful: this.isStatefulClass(cls),
|
|
371
|
+
confidence: 0.7, // Lower confidence for suffix-based
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
pattern: ServicePattern.UNKNOWN,
|
|
377
|
+
isInjectable: false,
|
|
378
|
+
className: 'Unknown',
|
|
379
|
+
filePath,
|
|
380
|
+
isStateful: false,
|
|
381
|
+
confidence: 0.0,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Detect service pattern from class name.
|
|
386
|
+
*
|
|
387
|
+
* @param className - The class name to analyze
|
|
388
|
+
* @returns Service pattern
|
|
389
|
+
*/
|
|
390
|
+
detectPatternFromName(className) {
|
|
391
|
+
const lowerName = className.toLowerCase();
|
|
392
|
+
if (lowerName.endsWith('service')) {
|
|
393
|
+
return ServicePattern.SERVICE;
|
|
394
|
+
}
|
|
395
|
+
if (lowerName.endsWith('repository')) {
|
|
396
|
+
return ServicePattern.REPOSITORY;
|
|
397
|
+
}
|
|
398
|
+
if (lowerName.endsWith('provider')) {
|
|
399
|
+
return ServicePattern.PROVIDER;
|
|
400
|
+
}
|
|
401
|
+
if (lowerName.endsWith('facade')) {
|
|
402
|
+
return ServicePattern.FACADE;
|
|
403
|
+
}
|
|
404
|
+
if (lowerName.endsWith('store')) {
|
|
405
|
+
return ServicePattern.STORE;
|
|
406
|
+
}
|
|
407
|
+
if (lowerName.endsWith('helper')) {
|
|
408
|
+
return ServicePattern.HELPER;
|
|
409
|
+
}
|
|
410
|
+
if (lowerName.endsWith('util') || lowerName.endsWith('utils')) {
|
|
411
|
+
return ServicePattern.UTILITY;
|
|
412
|
+
}
|
|
413
|
+
return ServicePattern.UNKNOWN;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Check if a pattern represents stateful services.
|
|
417
|
+
*
|
|
418
|
+
* @param pattern - The service pattern
|
|
419
|
+
* @returns True if stateful
|
|
420
|
+
*/
|
|
421
|
+
isStatefulPattern(pattern) {
|
|
422
|
+
return (pattern === ServicePattern.STORE || pattern === ServicePattern.FACADE);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Check if a class is stateful (has state properties).
|
|
426
|
+
*
|
|
427
|
+
* @param classDecl - The class declaration
|
|
428
|
+
* @returns True if stateful
|
|
429
|
+
*/
|
|
430
|
+
isStatefulClass(classDecl) {
|
|
431
|
+
const properties = classDecl.getProperties();
|
|
432
|
+
// Check for state-related property names
|
|
433
|
+
for (const prop of properties) {
|
|
434
|
+
const propName = prop.getName().toLowerCase();
|
|
435
|
+
if (propName.includes('state') ||
|
|
436
|
+
propName.includes('store') ||
|
|
437
|
+
propName.includes('cache')) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=service-pattern-recognizer.js.map
|