@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +507 -0
  3. package/dist/analysis/complexity-metrics-calculator.d.ts +99 -0
  4. package/dist/analysis/complexity-metrics-calculator.d.ts.map +1 -0
  5. package/dist/analysis/complexity-metrics-calculator.js +195 -0
  6. package/dist/analysis/complexity-metrics-calculator.js.map +1 -0
  7. package/dist/analysis/dependency-analyzer.d.ts +227 -0
  8. package/dist/analysis/dependency-analyzer.d.ts.map +1 -0
  9. package/dist/analysis/dependency-analyzer.js +451 -0
  10. package/dist/analysis/dependency-analyzer.js.map +1 -0
  11. package/dist/analysis/external-template-loader.d.ts +44 -0
  12. package/dist/analysis/external-template-loader.d.ts.map +1 -0
  13. package/dist/analysis/external-template-loader.js +69 -0
  14. package/dist/analysis/external-template-loader.js.map +1 -0
  15. package/dist/analysis/selector-mapper.d.ts +77 -0
  16. package/dist/analysis/selector-mapper.d.ts.map +1 -0
  17. package/dist/analysis/selector-mapper.js +146 -0
  18. package/dist/analysis/selector-mapper.js.map +1 -0
  19. package/dist/analysis/service-detector.d.ts +164 -0
  20. package/dist/analysis/service-detector.d.ts.map +1 -0
  21. package/dist/analysis/service-detector.js +231 -0
  22. package/dist/analysis/service-detector.js.map +1 -0
  23. package/dist/analysis/service-pattern-recognizer.d.ts +247 -0
  24. package/dist/analysis/service-pattern-recognizer.d.ts.map +1 -0
  25. package/dist/analysis/service-pattern-recognizer.js +444 -0
  26. package/dist/analysis/service-pattern-recognizer.js.map +1 -0
  27. package/dist/analysis/symbol-locator.d.ts +81 -0
  28. package/dist/analysis/symbol-locator.d.ts.map +1 -0
  29. package/dist/analysis/symbol-locator.js +128 -0
  30. package/dist/analysis/symbol-locator.js.map +1 -0
  31. package/dist/analysis/template-analyzer-usage.d.ts +144 -0
  32. package/dist/analysis/template-analyzer-usage.d.ts.map +1 -0
  33. package/dist/analysis/template-analyzer-usage.js +474 -0
  34. package/dist/analysis/template-analyzer-usage.js.map +1 -0
  35. package/dist/analysis/template-analyzer.d.ts +80 -0
  36. package/dist/analysis/template-analyzer.d.ts.map +1 -0
  37. package/dist/analysis/template-analyzer.js +146 -0
  38. package/dist/analysis/template-analyzer.js.map +1 -0
  39. package/dist/index.d.ts +23 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +22 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/investigation/angular-symbol-finder.d.ts +76 -0
  44. package/dist/investigation/angular-symbol-finder.d.ts.map +1 -0
  45. package/dist/investigation/angular-symbol-finder.js +199 -0
  46. package/dist/investigation/angular-symbol-finder.js.map +1 -0
  47. package/dist/investigation/call-graph-builder.d.ts +85 -0
  48. package/dist/investigation/call-graph-builder.d.ts.map +1 -0
  49. package/dist/investigation/call-graph-builder.js +196 -0
  50. package/dist/investigation/call-graph-builder.js.map +1 -0
  51. package/dist/investigation/codebase-searcher.d.ts +99 -0
  52. package/dist/investigation/codebase-searcher.d.ts.map +1 -0
  53. package/dist/investigation/codebase-searcher.js +201 -0
  54. package/dist/investigation/codebase-searcher.js.map +1 -0
  55. package/dist/investigation/stack-trace-parser.d.ts +70 -0
  56. package/dist/investigation/stack-trace-parser.d.ts.map +1 -0
  57. package/dist/investigation/stack-trace-parser.js +117 -0
  58. package/dist/investigation/stack-trace-parser.js.map +1 -0
  59. package/dist/investigation/type-resolver.d.ts +59 -0
  60. package/dist/investigation/type-resolver.d.ts.map +1 -0
  61. package/dist/investigation/type-resolver.js +111 -0
  62. package/dist/investigation/type-resolver.js.map +1 -0
  63. package/dist/investigation/usage-finder.d.ts +69 -0
  64. package/dist/investigation/usage-finder.d.ts.map +1 -0
  65. package/dist/investigation/usage-finder.js +132 -0
  66. package/dist/investigation/usage-finder.js.map +1 -0
  67. package/dist/metrics/complexity-metrics.d.ts +196 -0
  68. package/dist/metrics/complexity-metrics.d.ts.map +1 -0
  69. package/dist/metrics/complexity-metrics.js +445 -0
  70. package/dist/metrics/complexity-metrics.js.map +1 -0
  71. package/dist/public-api.d.ts +77 -0
  72. package/dist/public-api.d.ts.map +1 -0
  73. package/dist/public-api.js +50 -0
  74. package/dist/public-api.js.map +1 -0
  75. package/dist/transformation/import-manager.d.ts +276 -0
  76. package/dist/transformation/import-manager.d.ts.map +1 -0
  77. package/dist/transformation/import-manager.js +488 -0
  78. package/dist/transformation/import-manager.js.map +1 -0
  79. package/dist/transformation/ng-morph-adapter.d.ts +111 -0
  80. package/dist/transformation/ng-morph-adapter.d.ts.map +1 -0
  81. package/dist/transformation/ng-morph-adapter.js +179 -0
  82. package/dist/transformation/ng-morph-adapter.js.map +1 -0
  83. package/dist/transformation/ngmodule-manager.d.ts +145 -0
  84. package/dist/transformation/ngmodule-manager.d.ts.map +1 -0
  85. package/dist/transformation/ngmodule-manager.js +356 -0
  86. package/dist/transformation/ngmodule-manager.js.map +1 -0
  87. package/package.json +62 -0
  88. package/src/analysis/complexity-metrics-calculator.ts +253 -0
  89. package/src/analysis/dependency-analyzer.ts +720 -0
  90. package/src/analysis/external-template-loader.ts +79 -0
  91. package/src/analysis/selector-mapper.ts +186 -0
  92. package/src/analysis/service-detector.ts +280 -0
  93. package/src/analysis/service-pattern-recognizer.ts +545 -0
  94. package/src/analysis/symbol-locator.ts +170 -0
  95. package/src/analysis/template-analyzer-usage.ts +614 -0
  96. package/src/analysis/template-analyzer.ts +208 -0
  97. package/src/index.ts +51 -0
  98. package/src/investigation/angular-symbol-finder.ts +263 -0
  99. package/src/investigation/call-graph-builder.ts +298 -0
  100. package/src/investigation/codebase-searcher.ts +286 -0
  101. package/src/investigation/stack-trace-parser.ts +166 -0
  102. package/src/investigation/type-resolver.ts +144 -0
  103. package/src/investigation/usage-finder.ts +198 -0
  104. package/src/metrics/complexity-metrics.ts +617 -0
  105. package/src/public-api.ts +119 -0
  106. package/src/transformation/import-manager.ts +626 -0
  107. package/src/transformation/ng-morph-adapter.ts +252 -0
  108. 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