ts2famix 2.1.0-beta.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.eslintrc.json +24 -24
  2. package/LICENSE +24 -24
  3. package/README.md +78 -78
  4. package/dist/analyze.js +20 -10
  5. package/dist/analyze_functions/process_functions.js +109 -69
  6. package/dist/famix_functions/EntityDictionary.js +691 -606
  7. package/dist/famix_functions/helpers_creation.js +35 -19
  8. package/dist/fqn.js +351 -18
  9. package/dist/lib/famix/famix_JSON_exporter.js +1 -1
  10. package/dist/lib/famix/famix_base_element.js +1 -1
  11. package/dist/lib/famix/famix_repository.js +31 -12
  12. package/dist/lib/famix/index.js +18 -8
  13. package/dist/lib/famix/model/famix/access.js +1 -1
  14. package/dist/lib/famix/model/famix/accessor.js +1 -1
  15. package/dist/lib/famix/model/famix/alias.js +1 -1
  16. package/dist/lib/famix/model/famix/arrow_function.js +1 -1
  17. package/dist/lib/famix/model/famix/behavioral_entity.js +1 -1
  18. package/dist/lib/famix/model/famix/class.js +1 -1
  19. package/dist/lib/famix/model/famix/comment.js +1 -1
  20. package/dist/lib/famix/model/famix/concretisation.js +1 -1
  21. package/dist/lib/famix/model/famix/container_entity.js +1 -1
  22. package/dist/lib/famix/model/famix/decorator.js +1 -1
  23. package/dist/lib/famix/model/famix/entity.js +1 -1
  24. package/dist/lib/famix/model/famix/enum.js +1 -1
  25. package/dist/lib/famix/model/famix/enum_value.js +1 -1
  26. package/dist/lib/famix/model/famix/function.js +1 -1
  27. package/dist/lib/famix/model/famix/import_clause.js +1 -1
  28. package/dist/lib/famix/model/famix/index.js +1 -1
  29. package/dist/lib/famix/model/famix/indexed_file_anchor.js +1 -1
  30. package/dist/lib/famix/model/famix/inheritance.js +1 -1
  31. package/dist/lib/famix/model/famix/interface.js +1 -1
  32. package/dist/lib/famix/model/famix/invocation.js +1 -1
  33. package/dist/lib/famix/model/famix/method.js +1 -1
  34. package/dist/lib/famix/model/famix/module.js +2 -2
  35. package/dist/lib/famix/model/famix/named_entity.js +1 -1
  36. package/dist/lib/famix/model/famix/parameter.js +1 -1
  37. package/dist/lib/famix/model/famix/parameter_concretisation.js +1 -1
  38. package/dist/lib/famix/model/famix/parameter_type.js +1 -1
  39. package/dist/lib/famix/model/famix/parametric_arrow_function.js +1 -1
  40. package/dist/lib/famix/model/famix/parametric_class.js +1 -1
  41. package/dist/lib/famix/model/famix/parametric_function.js +1 -1
  42. package/dist/lib/famix/model/famix/parametric_interface.js +1 -1
  43. package/dist/lib/famix/model/famix/parametric_method.js +1 -1
  44. package/dist/lib/famix/model/famix/primitive_type.js +1 -1
  45. package/dist/lib/famix/model/famix/property.js +1 -1
  46. package/dist/lib/famix/model/famix/reference.js +1 -1
  47. package/dist/lib/famix/model/famix/scoping_entity.js +1 -1
  48. package/dist/lib/famix/model/famix/script_entity.js +1 -1
  49. package/dist/lib/famix/model/famix/source_anchor.js +1 -1
  50. package/dist/lib/famix/model/famix/source_language.js +1 -1
  51. package/dist/lib/famix/model/famix/sourced_entity.js +1 -1
  52. package/dist/lib/famix/model/famix/structural_entity.js +1 -1
  53. package/dist/lib/famix/model/famix/type.js +1 -1
  54. package/dist/lib/famix/model/famix/variable.js +1 -1
  55. package/dist/lib/ts-complex/cyclomatic-service.js +1 -1
  56. package/dist/refactorer/refactor-getter-setter.js +18 -8
  57. package/dist/ts2famix-cli-wrapper.js +18 -8
  58. package/dist/ts2famix-cli.js +18 -8
  59. package/dist/ts2famix-tsconfig.js +18 -8
  60. package/doc-uml/famix-typescript-model.puml +619 -607
  61. package/doc-uml/famix-typescript-model.svg +1 -1
  62. package/eslint.config.mjs +28 -28
  63. package/jest.config.json +1 -1
  64. package/package.json +70 -70
  65. package/src/analyze.ts +120 -120
  66. package/src/analyze_functions/process_functions.ts +1069 -1040
  67. package/src/famix_functions/EntityDictionary.ts +2061 -2016
  68. package/src/famix_functions/helpers_creation.ts +143 -135
  69. package/src/fqn.ts +416 -50
  70. package/src/generate_uml.sh +20 -20
  71. package/src/lib/famix/License.md +22 -22
  72. package/src/lib/famix/famix_JSON_exporter.ts +56 -56
  73. package/src/lib/famix/famix_base_element.ts +22 -22
  74. package/src/lib/famix/famix_repository.ts +288 -278
  75. package/src/lib/famix/index.ts +8 -8
  76. package/src/lib/famix/model/famix/access.ts +50 -50
  77. package/src/lib/famix/model/famix/accessor.ts +15 -15
  78. package/src/lib/famix/model/famix/alias.ts +39 -39
  79. package/src/lib/famix/model/famix/arrow_function.ts +15 -15
  80. package/src/lib/famix/model/famix/behavioral_entity.ts +97 -97
  81. package/src/lib/famix/model/famix/class.ts +85 -85
  82. package/src/lib/famix/model/famix/comment.ts +47 -47
  83. package/src/lib/famix/model/famix/concretisation.ts +40 -40
  84. package/src/lib/famix/model/famix/container_entity.ts +160 -160
  85. package/src/lib/famix/model/famix/decorator.ts +37 -37
  86. package/src/lib/famix/model/famix/entity.ts +15 -15
  87. package/src/lib/famix/model/famix/enum.ts +30 -30
  88. package/src/lib/famix/model/famix/enum_value.ts +28 -28
  89. package/src/lib/famix/model/famix/function.ts +15 -15
  90. package/src/lib/famix/model/famix/import_clause.ts +51 -51
  91. package/src/lib/famix/model/famix/index.ts +41 -41
  92. package/src/lib/famix/model/famix/indexed_file_anchor.ts +46 -46
  93. package/src/lib/famix/model/famix/inheritance.ts +40 -40
  94. package/src/lib/famix/model/famix/interface.ts +75 -75
  95. package/src/lib/famix/model/famix/invocation.ts +65 -65
  96. package/src/lib/famix/model/famix/method.ts +89 -89
  97. package/src/lib/famix/model/famix/module.ts +71 -71
  98. package/src/lib/famix/model/famix/named_entity.ts +95 -95
  99. package/src/lib/famix/model/famix/parameter.ts +28 -28
  100. package/src/lib/famix/model/famix/parameter_concretisation.ts +51 -51
  101. package/src/lib/famix/model/famix/parameter_type.ts +58 -58
  102. package/src/lib/famix/model/famix/parametric_arrow_function.ts +32 -32
  103. package/src/lib/famix/model/famix/parametric_class.ts +49 -49
  104. package/src/lib/famix/model/famix/parametric_function.ts +32 -32
  105. package/src/lib/famix/model/famix/parametric_interface.ts +49 -49
  106. package/src/lib/famix/model/famix/parametric_method.ts +32 -32
  107. package/src/lib/famix/model/famix/primitive_type.ts +15 -15
  108. package/src/lib/famix/model/famix/property.ts +94 -94
  109. package/src/lib/famix/model/famix/reference.ts +40 -40
  110. package/src/lib/famix/model/famix/scoping_entity.ts +35 -35
  111. package/src/lib/famix/model/famix/script_entity.ts +34 -34
  112. package/src/lib/famix/model/famix/source_anchor.ts +30 -30
  113. package/src/lib/famix/model/famix/source_language.ts +35 -35
  114. package/src/lib/famix/model/famix/sourced_entity.ts +70 -70
  115. package/src/lib/famix/model/famix/structural_entity.ts +43 -43
  116. package/src/lib/famix/model/famix/type.ts +87 -87
  117. package/src/lib/famix/model/famix/variable.ts +27 -27
  118. package/src/lib/famix/package.json +28 -28
  119. package/src/lib/ts-complex/cyclomatic-service.ts +83 -83
  120. package/src/refactorer/refactor-getter-setter.ts +140 -140
  121. package/src/ts2famix-cli-wrapper.ts +21 -21
  122. package/src/ts2famix-cli.ts +60 -60
  123. package/tsconfig.check-tests.json +14 -14
  124. package/tsconfig.json +73 -72
  125. package/.vscode/settings.json +0 -4
  126. package/TODO +0 -1
  127. package/arwea-fix.json +0 -1
  128. package/bogus.ts +0 -3
  129. package/class-diagram.puml +0 -792
  130. package/debug.txt +0 -13332
  131. package/debuglog.txt +0 -12073
  132. package/dist/famix2puml.js +0 -126
  133. package/dist/getClasses-arrow-body.js +0 -43
  134. package/dist/lib/famix/src/famix_JSON_exporter.js +0 -55
  135. package/dist/lib/famix/src/famix_base_element.js +0 -18
  136. package/dist/lib/famix/src/famix_repository.js +0 -224
  137. package/dist/lib/famix/src/index.js +0 -31
  138. package/dist/lib/famix/src/model/famix/access.js +0 -40
  139. package/dist/lib/famix/src/model/famix/accessor.js +0 -17
  140. package/dist/lib/famix/src/model/famix/alias.js +0 -33
  141. package/dist/lib/famix/src/model/famix/arrowFunction.js +0 -17
  142. package/dist/lib/famix/src/model/famix/arrow_function.js +0 -17
  143. package/dist/lib/famix/src/model/famix/behavioral_entity.js +0 -79
  144. package/dist/lib/famix/src/model/famix/class.js +0 -71
  145. package/dist/lib/famix/src/model/famix/comment.js +0 -39
  146. package/dist/lib/famix/src/model/famix/concretisation.js +0 -31
  147. package/dist/lib/famix/src/model/famix/container_entity.js +0 -126
  148. package/dist/lib/famix/src/model/famix/decorator.js +0 -32
  149. package/dist/lib/famix/src/model/famix/entity.js +0 -17
  150. package/dist/lib/famix/src/model/famix/enum.js +0 -31
  151. package/dist/lib/famix/src/model/famix/enum_value.js +0 -25
  152. package/dist/lib/famix/src/model/famix/function.js +0 -17
  153. package/dist/lib/famix/src/model/famix/implicit_variable.js +0 -17
  154. package/dist/lib/famix/src/model/famix/import_clause.js +0 -41
  155. package/dist/lib/famix/src/model/famix/index.js +0 -86
  156. package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +0 -38
  157. package/dist/lib/famix/src/model/famix/inheritance.js +0 -33
  158. package/dist/lib/famix/src/model/famix/interface.js +0 -64
  159. package/dist/lib/famix/src/model/famix/invocation.js +0 -54
  160. package/dist/lib/famix/src/model/famix/method.js +0 -67
  161. package/dist/lib/famix/src/model/famix/module.js +0 -60
  162. package/dist/lib/famix/src/model/famix/named_entity.js +0 -78
  163. package/dist/lib/famix/src/model/famix/parameter.js +0 -25
  164. package/dist/lib/famix/src/model/famix/parameterConcretisation.js +0 -44
  165. package/dist/lib/famix/src/model/famix/parameter_concretisation.js +0 -44
  166. package/dist/lib/famix/src/model/famix/parameter_type.js +0 -45
  167. package/dist/lib/famix/src/model/famix/parametricArrowFunction.js +0 -29
  168. package/dist/lib/famix/src/model/famix/parametric_arrow_function.js +0 -31
  169. package/dist/lib/famix/src/model/famix/parametric_class.js +0 -44
  170. package/dist/lib/famix/src/model/famix/parametric_function.js +0 -31
  171. package/dist/lib/famix/src/model/famix/parametric_interface.js +0 -44
  172. package/dist/lib/famix/src/model/famix/parametric_method.js +0 -31
  173. package/dist/lib/famix/src/model/famix/primitive_type.js +0 -17
  174. package/dist/lib/famix/src/model/famix/property.js +0 -73
  175. package/dist/lib/famix/src/model/famix/reference.js +0 -33
  176. package/dist/lib/famix/src/model/famix/scoping_entity.js +0 -36
  177. package/dist/lib/famix/src/model/famix/script_entity.js +0 -29
  178. package/dist/lib/famix/src/model/famix/source_anchor.js +0 -27
  179. package/dist/lib/famix/src/model/famix/source_language.js +0 -35
  180. package/dist/lib/famix/src/model/famix/sourced_entity.js +0 -60
  181. package/dist/lib/famix/src/model/famix/structural_entity.js +0 -39
  182. package/dist/lib/famix/src/model/famix/text_anchor.js +0 -38
  183. package/dist/lib/famix/src/model/famix/type.js +0 -73
  184. package/dist/lib/famix/src/model/famix/variable.js +0 -24
  185. package/fqn-model.json +0 -1
  186. package/iterateGenericTypes.ts +0 -69
  187. package/out/class-diagram/class-diagram.svg +0 -1
  188. package/sample.json +0 -1
  189. package/sample.ts +0 -1
  190. package/stats.txt +0 -3091
  191. package/tabby-debug-output.txt +0 -19433
  192. package/ts2famix.log +0 -22656
  193. package/validate-references.js +0 -103
@@ -1,1040 +1,1069 @@
1
- import { ClassDeclaration, MethodDeclaration, VariableStatement, FunctionDeclaration, VariableDeclaration, InterfaceDeclaration, ParameterDeclaration, ConstructorDeclaration, MethodSignature, SourceFile, ModuleDeclaration, PropertyDeclaration, PropertySignature, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ExportedDeclarations, CommentRange, EnumDeclaration, EnumMember, TypeParameterDeclaration, TypeAliasDeclaration, SyntaxKind, FunctionExpression, Block, Identifier, ExpressionWithTypeArguments, ImportDeclaration, Node, ArrowFunction, Scope, ClassExpression } from "ts-morph";
2
- import * as Famix from "../lib/famix/model/famix";
3
- import { calculate } from "../lib/ts-complex/cyclomatic-service";
4
- import * as fs from 'fs';
5
- import { logger, entityDictionary } from "../analyze";
6
- import { getFQN } from "../fqn";
7
- import { InvocableType } from "src/famix_functions/EntityDictionary";
8
-
9
- export type AccessibleTSMorphElement = ParameterDeclaration | VariableDeclaration | PropertyDeclaration | EnumMember;
10
- export type FamixID = number;
11
-
12
- export const methodsAndFunctionsWithId = new Map<number, InvocableType>(); // Maps the Famix method, constructor, getter, setter and function ids to their ts-morph method, constructor, getter, setter or function object
13
-
14
- export const accessMap = new Map<FamixID, AccessibleTSMorphElement>(); // Maps the Famix parameter, variable, property and enum value ids to their ts-morph parameter, variable, property or enum member object
15
- export const classes = new Array<ClassDeclaration>(); // Array of all the classes of the source files
16
- export const interfaces = new Array<InterfaceDeclaration>(); // Array of all the interfaces of the source files
17
- export const modules = new Array<SourceFile>(); // Array of all the source files which are modules
18
- export const listOfExportMaps = new Array<ReadonlyMap<string, ExportedDeclarations[]>>(); // Array of all the export maps
19
- export let currentCC: { [key: string]: number }; // Stores the cyclomatic complexity metrics for the current source file
20
-
21
- /**
22
- * Checks if the file has any imports or exports to be considered a module
23
- * @param sourceFile A source file
24
- * @returns A boolean indicating if the file is a module
25
- */
26
- function isSourceFileAModule(sourceFile: SourceFile): boolean {
27
- return sourceFile.getImportDeclarations().length > 0 || sourceFile.getExportedDeclarations().size > 0;
28
- }
29
-
30
- /**
31
- * Gets the path of a module to be imported
32
- * @param importDecl An import declaration
33
- * @returns The path of the module to be imported
34
- */
35
- export function getModulePath(importDecl: ImportDeclaration): string {
36
- let path: string;
37
- if (importDecl.getModuleSpecifierSourceFile() === undefined) {
38
- if (importDecl.getModuleSpecifierValue().substring(importDecl.getModuleSpecifierValue().length - 3) === ".ts") {
39
- path = importDecl.getModuleSpecifierValue();
40
- }
41
- else {
42
- path = importDecl.getModuleSpecifierValue() + ".ts";
43
- }
44
- }
45
- else {
46
- path = importDecl.getModuleSpecifierSourceFile()!.getFilePath();
47
- }
48
- return path;
49
- }
50
-
51
- /**
52
- * Gets the interfaces implemented or extended by a class or an interface
53
- * @param interfaces An array of interfaces
54
- * @param subClass A class or an interface
55
- * @returns An array of InterfaceDeclaration and ExpressionWithTypeArguments containing the interfaces implemented or extended by the subClass
56
- */
57
- export function getImplementedOrExtendedInterfaces(interfaces: Array<InterfaceDeclaration>, subClass: ClassDeclaration | InterfaceDeclaration): Array<InterfaceDeclaration | ExpressionWithTypeArguments> {
58
- let impOrExtInterfaces: Array<ExpressionWithTypeArguments>;
59
- if (subClass instanceof ClassDeclaration) {
60
- impOrExtInterfaces = subClass.getImplements();
61
- }
62
- else {
63
- impOrExtInterfaces = subClass.getExtends();
64
- }
65
-
66
- const interfacesNames = interfaces.map(i => i.getName());
67
- const implementedOrExtendedInterfaces = new Array<InterfaceDeclaration | ExpressionWithTypeArguments>();
68
-
69
- impOrExtInterfaces.forEach(i => {
70
- if (interfacesNames.includes(i.getExpression().getText())) {
71
- implementedOrExtendedInterfaces.push(interfaces[interfacesNames.indexOf(i.getExpression().getText())]);
72
- }
73
- else {
74
- implementedOrExtendedInterfaces.push(i);
75
- }
76
- });
77
-
78
- return implementedOrExtendedInterfaces;
79
- }
80
-
81
- /**
82
- * Builds a Famix model for an array of source files
83
- * @param sourceFiles An array of source files
84
- */
85
- export function processFiles(sourceFiles: Array<SourceFile>): void {
86
- sourceFiles.forEach(file => {
87
- logger.info(`File: >>>>>>>>>> ${file.getFilePath()}`);
88
-
89
- // Computes the cyclomatic complexity metrics for the current source file if it exists (i.e. if it is not from a jest test)
90
- if (fs.existsSync(file.getFilePath()))
91
- currentCC = calculate(file.getFilePath());
92
- else
93
- currentCC = {};
94
-
95
- processFile(file);
96
- });
97
- }
98
-
99
- /**
100
- * Builds a Famix model for a source file
101
- * @param f A source file
102
- */
103
- function processFile(f: SourceFile): void {
104
- const isModule = isSourceFileAModule(f);
105
-
106
- if (isModule) {
107
- modules.push(f);
108
- }
109
-
110
- const exportMap = f.getExportedDeclarations();
111
- if (exportMap) listOfExportMaps.push(exportMap);
112
-
113
- const fmxFile = entityDictionary.createOrGetFamixFile(f, isModule);
114
-
115
- logger.debug(`processFile: file: ${f.getBaseName()}, fqn = ${fmxFile.fullyQualifiedName}`);
116
-
117
- processComments(f, fmxFile);
118
-
119
- processAliases(f, fmxFile);
120
-
121
- processClasses(f, fmxFile);
122
-
123
- processInterfaces(f, fmxFile);
124
-
125
- processVariables(f, fmxFile);
126
-
127
- processEnums(f, fmxFile);
128
-
129
- processFunctions(f, fmxFile);
130
-
131
- processModules(f, fmxFile);
132
- }
133
-
134
- export function isAmbient(node: ModuleDeclaration): boolean {
135
- // An ambient module has the DeclareKeyword modifier.
136
- return (node.getModifiers()?.some(modifier => modifier.getKind() === SyntaxKind.DeclareKeyword)) ?? false;
137
- }
138
-
139
- export function isNamespace(node: ModuleDeclaration): boolean {
140
- // Check if the module declaration has a namespace keyword.
141
- // This approach uses the getChildren() method to inspect the syntax directly.
142
- return node.getChildrenOfKind(SyntaxKind.NamespaceKeyword).length > 0;
143
- }
144
-
145
- /**
146
- * Builds a Famix model for a module (also namespace)
147
- * @param m A namespace
148
- * @returns A Famix.Module representing the module
149
- */
150
- function processModule(m: ModuleDeclaration): Famix.Module {
151
- const fmxModule = entityDictionary.createOrGetFamixModule(m);
152
-
153
- logger.debug(`module: ${m.getName()}, (${m.getType().getText()}), ${fmxModule.fullyQualifiedName}`);
154
-
155
- processComments(m, fmxModule);
156
-
157
- processAliases(m, fmxModule);
158
-
159
- processClasses(m, fmxModule);
160
-
161
- processInterfaces(m, fmxModule);
162
-
163
- processVariables(m, fmxModule);
164
-
165
- processEnums(m, fmxModule);
166
-
167
- processFunctions(m, fmxModule);
168
-
169
- processModules(m, fmxModule);
170
-
171
- return fmxModule;
172
- }
173
-
174
- type ContainerTypes = SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ArrowFunction;
175
-
176
- type ScopedTypes = Famix.ScriptEntity | Famix.Module | Famix.Function | Famix.Method | Famix.Accessor;
177
-
178
- /**
179
- * Builds a Famix model for the aliases of a container
180
- * @param m A container (a source file, a namespace, a function or a method)
181
- * @param fmxScope The Famix model of the container
182
- */
183
- function processAliases(m: ContainerTypes, fmxScope: ScopedTypes): void {
184
- logger.debug(`processAliases: ---------- Finding Aliases:`);
185
- m.getTypeAliases().forEach(a => {
186
- const fmxAlias = processAlias(a);
187
- fmxScope.addAlias(fmxAlias);
188
- });
189
- }
190
-
191
- /**
192
- * Builds a Famix model for the classes of a container
193
- * @param m A container (a source file or a namespace)
194
- * @param fmxScope The Famix model of the container
195
- */
196
- function processClasses(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
197
- logger.debug(`processClasses: ---------- Finding Classes:`);
198
- const classesInArrowFunctions = getClassesDeclaredInArrowFunctions(m);
199
- const classes = m.getClasses().concat(classesInArrowFunctions);
200
- classes.forEach(c => {
201
- const fmxClass = processClass(c);
202
- fmxScope.addType(fmxClass);
203
- });
204
- }
205
-
206
- function getArrowFunctionClasses(f: ArrowFunction): ClassDeclaration[] {
207
- const classes: ClassDeclaration[] = [];
208
-
209
- function findClasses(node: Node) {
210
- if (node.getKind() === SyntaxKind.ClassDeclaration) {
211
- classes.push(node as ClassDeclaration);
212
- }
213
- node.getChildren().forEach(findClasses);
214
- }
215
-
216
- findClasses(f);
217
- return classes;
218
- }
219
-
220
- /**
221
- * ts-morph doesn't find classes in arrow functions, so we need to find them manually
222
- * @param s A source file
223
- * @returns the ClassDeclaration objects found in arrow functions of the source file
224
- */
225
- function getClassesDeclaredInArrowFunctions(s: SourceFile | ModuleDeclaration): ClassDeclaration[] {
226
- const arrowFunctions = s.getDescendantsOfKind(SyntaxKind.ArrowFunction);
227
- const classesInArrowFunctions = arrowFunctions.map(f => getArrowFunctionClasses(f)).flat();
228
- return classesInArrowFunctions;
229
- }
230
-
231
- /**
232
- * Builds a Famix model for the interfaces of a container
233
- * @param m A container (a source file or a namespace)
234
- * @param fmxScope The Famix model of the container
235
- */
236
- function processInterfaces(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
237
- logger.debug(`processInterfaces: ---------- Finding Interfaces:`);
238
- m.getInterfaces().forEach(i => {
239
- const fmxInterface = processInterface(i);
240
- fmxScope.addType(fmxInterface);
241
- });
242
- }
243
-
244
- /**
245
- * Builds a Famix model for the variables of a container
246
- * @param m A container (a source file, a namespace, a function or a method)
247
- * @param fmxScope The Famix model of the container
248
- */
249
- function processVariables(m: ContainerTypes, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Function | Famix.Method | Famix.Accessor): void {
250
- logger.debug(`processVariables: ---------- Finding Variables:`);
251
- m.getVariableStatements().forEach(v => {
252
- const fmxVariables = processVariableStatement(v);
253
- fmxVariables.forEach(fmxVariable => {
254
- fmxScope.addVariable(fmxVariable);
255
- });
256
- });
257
- }
258
-
259
- /**
260
- * Builds a Famix model for the enums of a container
261
- * @param m A container (a source file, a namespace, a function or a method)
262
- * @param fmxScope The Famix model of the container
263
- */
264
- function processEnums(m: ContainerTypes, fmxScope: ScopedTypes): void {
265
- logger.debug(`processEnums: ---------- Finding Enums:`);
266
- m.getEnums().forEach(e => {
267
- const fmxEnum = processEnum(e);
268
- fmxScope.addType(fmxEnum);
269
- });
270
- }
271
-
272
- /**
273
- * Builds a Famix model for the functions of a container
274
- * @param m A container (a source file, a namespace, a function or a method)
275
- * @param fmxScope The Famix model of the container
276
- */
277
- function processFunctions(m: ContainerTypes, fmxScope: ScopedTypes): void {
278
- logger.debug(`Finding Functions:`);
279
- m.getFunctions().forEach(f => {
280
- const fmxFunction = processFunction(f);
281
- fmxScope.addFunction(fmxFunction);
282
- });
283
-
284
- //find arrow functions
285
- logger.debug(`Finding Functions:`);
286
- const arrowFunctions = m.getDescendantsOfKind(SyntaxKind.ArrowFunction);
287
- arrowFunctions.forEach(af => {
288
- const fmxFunction = processFunction(af);
289
- fmxScope.addFunction(fmxFunction);
290
- });
291
- }
292
-
293
- /**
294
- * Builds a Famix model for the modules of a container.
295
- * @param m A container (a source file or a namespace)
296
- * @param fmxScope The Famix model of the container
297
- */
298
- function processModules(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
299
- logger.debug(`Finding Modules:`);
300
- m.getModules().forEach(md => {
301
- const fmxModule = processModule(md);
302
- fmxScope.addModule(fmxModule);
303
- });
304
- }
305
-
306
- /**
307
- * Builds a Famix model for an alias
308
- * @param a An alias
309
- * @returns A Famix.Alias representing the alias
310
- */
311
- function processAlias(a: TypeAliasDeclaration): Famix.Alias {
312
- const fmxAlias = entityDictionary.createFamixAlias(a);
313
-
314
- logger.debug(`Alias: ${a.getName()}, (${a.getType().getText()}), fqn = ${fmxAlias.fullyQualifiedName}`);
315
-
316
- processComments(a, fmxAlias);
317
-
318
- return fmxAlias;
319
- }
320
-
321
- /**
322
- * Builds a Famix model for a class
323
- * @param c A class
324
- * @returns A Famix.Class or a Famix.ParametricClass representing the class
325
- */
326
- function processClass(c: ClassDeclaration): Famix.Class | Famix.ParametricClass {
327
- classes.push(c);
328
-
329
- const fmxClass = entityDictionary.createOrGetFamixClass(c);
330
-
331
- logger.debug(`Class: ${c.getName()}, (${c.getType().getText()}), fqn = ${fmxClass.fullyQualifiedName}`);
332
-
333
- processComments(c, fmxClass);
334
-
335
- processDecorators(c, fmxClass);
336
-
337
- processStructuredType(c, fmxClass);
338
-
339
- c.getConstructors().forEach(con => {
340
- const fmxCon = processMethod(con);
341
- fmxClass.addMethod(fmxCon);
342
- });
343
-
344
- c.getGetAccessors().forEach(acc => {
345
- const fmxAcc = processMethod(acc);
346
- fmxClass.addMethod(fmxAcc);
347
- });
348
-
349
- c.getSetAccessors().forEach(acc => {
350
- const fmxAcc = processMethod(acc);
351
- fmxClass.addMethod(fmxAcc);
352
- });
353
-
354
- return fmxClass;
355
- }
356
-
357
- /**
358
- * Builds a Famix model for an interface
359
- * @param i An interface
360
- * @returns A Famix.Interface or a Famix.ParametricInterface representing the interface
361
- */
362
- function processInterface(i: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
363
- interfaces.push(i);
364
-
365
- const fmxInterface = entityDictionary.createOrGetFamixInterface(i);
366
-
367
- logger.debug(`Interface: ${i.getName()}, (${i.getType().getText()}), fqn = ${fmxInterface.fullyQualifiedName}`);
368
-
369
- processComments(i, fmxInterface);
370
-
371
- processStructuredType(i, fmxInterface);
372
-
373
- return fmxInterface;
374
- }
375
-
376
- /**
377
- * Builds a Famix model for the type parameters, properties and methods of a structured type
378
- * @param c A structured type (a class or an interface)
379
- * @param fmxScope The Famix model of the structured type
380
- */
381
- function processStructuredType(c: ClassDeclaration | InterfaceDeclaration, fmxScope: Famix.Class | Famix.ParametricClass | Famix.Interface | Famix.ParametricInterface): void {
382
- logger.debug(`Finding Properties and Methods:`);
383
- if (fmxScope instanceof Famix.ParametricClass || fmxScope instanceof Famix.ParametricInterface) {
384
- processTypeParameters(c, fmxScope);
385
- }
386
-
387
- c.getProperties().forEach(prop => {
388
- const fmxProperty = processProperty(prop);
389
- fmxScope.addProperty(fmxProperty);
390
- });
391
-
392
- c.getMethods().forEach(m => {
393
- const fmxMethod = processMethod(m);
394
- fmxScope.addMethod(fmxMethod);
395
- });
396
- }
397
-
398
- /**
399
- * Builds a Famix model for a property
400
- * @param p A property
401
- * @returns A Famix.Property representing the property
402
- */
403
- function processProperty(p: PropertyDeclaration | PropertySignature): Famix.Property {
404
- const fmxProperty = entityDictionary.createFamixProperty(p);
405
-
406
- logger.debug(`property: ${p.getName()}, (${p.getType().getText()}), fqn = ${fmxProperty.fullyQualifiedName}`);
407
- logger.debug(` ---> It's a Property${(p instanceof PropertySignature) ? "Signature" : "Declaration"}!`);
408
- const ancestor = p.getFirstAncestorOrThrow();
409
- logger.debug(` ---> Its first ancestor is a ${ancestor.getKindName()}`);
410
-
411
- // decorators
412
- if (!(p instanceof PropertySignature)) {
413
- processDecorators(p, fmxProperty);
414
- // only add access if the p's first ancestor is not a PropertyDeclaration
415
- if (ancestor.getKindName() !== "PropertyDeclaration") {
416
- logger.debug(`adding access to map: ${p.getName()}, (${p.getType().getText()}) Famix ${fmxProperty.name} id: ${fmxProperty.id}`);
417
- accessMap.set(fmxProperty.id, p);
418
- }
419
- }
420
-
421
- processComments(p, fmxProperty);
422
-
423
- return fmxProperty;
424
- }
425
-
426
- /**
427
- * Builds a Famix model for a method or an accessor
428
- * @param m A method or an accessor
429
- * @returns A Famix.Method or a Famix.Accessor representing the method or the accessor
430
- */
431
- function processMethod(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration): Famix.Method | Famix.Accessor {
432
- const fmxMethod = entityDictionary.createOrGetFamixMethod(m, currentCC);
433
-
434
- logger.debug(`Method: ${!(m instanceof ConstructorDeclaration) ? m.getName() : "constructor"}, (${m.getType().getText()}), parent: ${(m.getParent() as ClassDeclaration | InterfaceDeclaration).getName()}, fqn = ${fmxMethod.fullyQualifiedName}`);
435
-
436
- processComments(m, fmxMethod);
437
-
438
- processTypeParameters(m, fmxMethod);
439
-
440
- processParameters(m, fmxMethod);
441
-
442
- if (!(m instanceof MethodSignature)) {
443
- processAliases(m, fmxMethod);
444
-
445
- processVariables(m, fmxMethod);
446
-
447
- processEnums(m, fmxMethod);
448
-
449
- processFunctions(m, fmxMethod);
450
-
451
- processFunctionExpressions(m, fmxMethod);
452
-
453
- methodsAndFunctionsWithId.set(fmxMethod.id, m);
454
- }
455
-
456
- if (m instanceof MethodDeclaration || m instanceof GetAccessorDeclaration || m instanceof SetAccessorDeclaration) {
457
- processDecorators(m, fmxMethod);
458
- }
459
-
460
- return fmxMethod;
461
- }
462
-
463
- /**
464
- * Builds a Famix model for a function
465
- * @param f A function
466
- * @returns A Famix.Function representing the function
467
- */
468
- function processFunction(f: FunctionDeclaration | FunctionExpression | ArrowFunction): Famix.Function {
469
-
470
- logger.debug(`Function: ${(f instanceof ArrowFunction ? "anonymous" : f.getName() ? f.getName() : "anonymous")}, (${f.getType().getText()}), fqn = ${getFQN(f)}`);
471
-
472
- let fmxFunction;
473
- if (f instanceof ArrowFunction) {
474
- fmxFunction = entityDictionary.createOrGetFamixArrowFunction(f, currentCC);
475
- } else {
476
- fmxFunction = entityDictionary.createOrGetFamixFunction(f, currentCC);
477
- }
478
-
479
- processComments(f, fmxFunction);
480
-
481
- processAliases(f, fmxFunction);
482
-
483
- processTypeParameters(f, fmxFunction);
484
-
485
- processParameters(f, fmxFunction);
486
-
487
- processVariables(f, fmxFunction);
488
-
489
- processEnums(f, fmxFunction);
490
-
491
- processFunctions(f, fmxFunction);
492
-
493
- if (f instanceof FunctionDeclaration && !(f.getParent() instanceof Block)) {
494
- processFunctionExpressions(f, fmxFunction);
495
- }
496
-
497
- methodsAndFunctionsWithId.set(fmxFunction.id, f);
498
-
499
- return fmxFunction;
500
- }
501
-
502
- /**
503
- * Builds a Famix model for the function expressions of a function or a method
504
- * @param f A function or a method
505
- * @param fmxScope The Famix model of the function or the method
506
- */
507
- function processFunctionExpressions(f: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.Function | Famix.Method | Famix.Accessor): void {
508
- logger.debug(`Finding Function Expressions:`);
509
- const functionExpressions = f.getDescendantsOfKind(SyntaxKind.FunctionExpression);
510
- functionExpressions.forEach((func) => {
511
- const fmxFunc = processFunction(func);
512
- fmxScope.addFunction(fmxFunc);
513
- });
514
- }
515
-
516
- /**
517
- * Builds a Famix model for the parameters of a method or a function
518
- * @param m A method or a function
519
- * @param fmxScope The Famix model of the method or the function
520
- */
521
- function processParameters(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction, fmxScope: Famix.Method | Famix.Accessor | Famix.Function): void {
522
- logger.debug(`Finding Parameters:`);
523
- m.getParameters().forEach(param => {
524
- const fmxParam = processParameter(param);
525
- fmxScope.addParameter(fmxParam);
526
- // Additional handling for Parameter Properties in constructors
527
- if (m instanceof ConstructorDeclaration) {
528
- // Check if the parameter has any visibility modifier
529
- if (param.hasModifier(SyntaxKind.PrivateKeyword) || param.hasModifier(SyntaxKind.PublicKeyword) || param.hasModifier(SyntaxKind.ProtectedKeyword) || param.hasModifier(SyntaxKind.ReadonlyKeyword)) {
530
- const classOfConstructor = m.getParent();
531
- logger.info(`Parameter Property ${param.getName()} in constructor of ${classOfConstructor.getName()}.`);
532
- // Treat the parameter as a property and add it to the class
533
- const fmxProperty = processParameterAsProperty(param, classOfConstructor);
534
- fmxProperty.readOnly = param.hasModifier(SyntaxKind.ReadonlyKeyword);
535
- }
536
- }
537
-
538
- });
539
- }
540
-
541
- // This function should create a Famix.Property model from a ParameterDeclaration
542
- // You'll need to implement it according to your Famix model structure
543
- function processParameterAsProperty(param: ParameterDeclaration, classDecl: ClassDeclaration | ClassExpression): Famix.Property {
544
- // Convert the parameter into a Property
545
- const propertyRepresentation = convertParameterToPropertyRepresentation(param);
546
-
547
- // Add the property to the class so we can have a PropertyDeclaration object
548
- classDecl.addProperty(propertyRepresentation);
549
-
550
- const property = classDecl.getProperty(propertyRepresentation.name);
551
- if (!property) {
552
- throw new Error(`Property ${propertyRepresentation.name} not found in class ${classDecl.getName()}`);
553
- }
554
- const fmxProperty = entityDictionary.createFamixProperty(property);
555
- if (classDecl instanceof ClassDeclaration) {
556
- const fmxClass = entityDictionary.createOrGetFamixClass(classDecl);
557
- fmxClass.addProperty(fmxProperty);
558
- } else {
559
- throw new Error("Unexpected type ClassExpression.");
560
- }
561
-
562
- processComments(property, fmxProperty);
563
-
564
- // remove the property from the class
565
- property.remove();
566
-
567
- return fmxProperty;
568
-
569
- }
570
-
571
- function convertParameterToPropertyRepresentation(param: ParameterDeclaration) {
572
- // Extract name
573
- const paramName = param.getName();
574
-
575
- // Extract type
576
- const paramType = param.getType().getText(param);
577
-
578
- // Determine visibility
579
- let scope: Scope;
580
- if (param.hasModifier(SyntaxKind.PrivateKeyword)) {
581
- scope = Scope.Private;
582
- } else if (param.hasModifier(SyntaxKind.ProtectedKeyword)) {
583
- scope = Scope.Protected;
584
- } else if (param.hasModifier(SyntaxKind.PublicKeyword)) {
585
- scope = Scope.Public;
586
- } else {
587
- throw new Error(`Parameter property ${paramName} in constructor does not have a visibility modifier.`);
588
- }
589
-
590
- // Determine if readonly
591
- const isReadonly = param.hasModifier(SyntaxKind.ReadonlyKeyword);
592
-
593
- // Create a representation of the property
594
- const propertyRepresentation = {
595
- name: paramName,
596
- type: paramType,
597
- scope: scope,
598
- isReadonly: isReadonly,
599
- };
600
-
601
- return propertyRepresentation;
602
- }
603
-
604
- /**
605
- * Builds a Famix model for a parameter
606
- * @param paramDecl A parameter
607
- * @returns A Famix.Parameter representing the parameter
608
- */
609
- function processParameter(paramDecl: ParameterDeclaration): Famix.Parameter {
610
- const fmxParam = entityDictionary.createOrGetFamixParameter(paramDecl); // create or GET
611
-
612
- logger.debug(`parameter: ${paramDecl.getName()}, (${paramDecl.getType().getText()}), fqn = ${fmxParam.fullyQualifiedName}`);
613
-
614
- processComments(paramDecl, fmxParam);
615
-
616
- processDecorators(paramDecl, fmxParam);
617
-
618
- const parent = paramDecl.getParent();
619
-
620
- if (!(parent instanceof MethodSignature)) {
621
- logger.debug(`adding access: ${paramDecl.getName()}, (${paramDecl.getType().getText()}) Famix ${fmxParam.name}`);
622
- accessMap.set(fmxParam.id, paramDecl);
623
- }
624
-
625
- return fmxParam;
626
- }
627
-
628
- /**
629
- * Builds a Famix model for the type parameters of a class, an interface, a method or a function
630
- * @param e A class, an interface, a method or a function
631
- * @param fmxScope The Famix model of the class, the interface, the method or the function
632
- */
633
- function processTypeParameters(e: ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction, fmxScope: Famix.ParametricClass | Famix.ParametricInterface | Famix.Method | Famix.Accessor | Famix.Function | Famix.ArrowFunction): void {
634
- logger.debug(`Finding Type Parameters:`);
635
- e.getTypeParameters().forEach(tp => {
636
- const fmxParam = processTypeParameter(tp);
637
- fmxScope.addGenericParameter(fmxParam);
638
- });
639
- }
640
-
641
- /**
642
- * Builds a Famix model for a type parameter
643
- * @param tp A type parameter
644
- * @returns A Famix.TypeParameter representing the type parameter
645
- */
646
- function processTypeParameter(tp: TypeParameterDeclaration): Famix.ParameterType {
647
- const fmxTypeParameter = entityDictionary.createFamixParameterType(tp);
648
-
649
- logger.debug(`type parameter: ${tp.getName()}, (${tp.getType().getText()}), fqn = ${fmxTypeParameter.fullyQualifiedName}`);
650
-
651
- processComments(tp, fmxTypeParameter);
652
-
653
- return fmxTypeParameter;
654
- }
655
-
656
- /**
657
- * Builds a Famix model for the variables of a variable statement
658
- * @param v A variable statement
659
- * @returns An array of Famix.Variable representing the variables
660
- */
661
- function processVariableStatement(v: VariableStatement): Array<Famix.Variable> {
662
- const fmxVariables = new Array<Famix.Variable>();
663
-
664
- logger.debug(`Variable statement: ${v.getText()}, (${v.getType().getText()}), ${v.getDeclarationKindKeywords()[0]}, fqn = ${v.getDeclarations()[0].getName()}`);
665
-
666
- v.getDeclarations().forEach(variable => {
667
- const fmxVar = processVariable(variable);
668
- processComments(v, fmxVar);
669
- fmxVariables.push(fmxVar);
670
- });
671
-
672
- return fmxVariables;
673
- }
674
-
675
- /**
676
- * Builds a Famix model for a variable
677
- * @param v A variable
678
- * @returns A Famix.Variable representing the variable
679
- */
680
- function processVariable(v: VariableDeclaration): Famix.Variable {
681
- const fmxVar = entityDictionary.createOrGetFamixVariable(v);
682
-
683
- logger.debug(`variable: ${v.getName()}, (${v.getType().getText()}), ${v.getInitializer() ? "initializer: " + v.getInitializer()!.getText() : "initializer: "}, fqn = ${fmxVar.fullyQualifiedName}`);
684
-
685
- processComments(v, fmxVar);
686
-
687
- logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxVar.name}`);
688
- accessMap.set(fmxVar.id, v);
689
-
690
- return fmxVar;
691
- }
692
-
693
- /**
694
- * Builds a Famix model for an enum
695
- * @param e An enum
696
- * @returns A Famix.Enum representing the enum
697
- */
698
- function processEnum(e: EnumDeclaration): Famix.Enum {
699
- const fmxEnum = entityDictionary.createOrGetFamixEnum(e);
700
-
701
- logger.debug(`enum: ${e.getName()}, (${e.getType().getText()}), fqn = ${fmxEnum.fullyQualifiedName}`);
702
-
703
- processComments(e, fmxEnum);
704
-
705
- e.getMembers().forEach(m => {
706
- const fmxEnumValue = processEnumValue(m);
707
- fmxEnum.addValue(fmxEnumValue);
708
- });
709
-
710
- return fmxEnum;
711
- }
712
-
713
- /**
714
- * Builds a Famix model for an enum member
715
- * @param v An enum member
716
- * @returns A Famix.EnumValue representing the enum member
717
- */
718
- function processEnumValue(v: EnumMember): Famix.EnumValue {
719
- const fmxEnumValue = entityDictionary.createFamixEnumValue(v);
720
-
721
- logger.debug(`enum value: ${v.getName()}, (${v.getType().getText()}), fqn = ${fmxEnumValue.fullyQualifiedName}`);
722
-
723
- processComments(v, fmxEnumValue);
724
-
725
- logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxEnumValue.name}`);
726
- accessMap.set(fmxEnumValue.id, v);
727
-
728
- return fmxEnumValue;
729
- }
730
-
731
- /**
732
- * Builds a Famix model for the decorators of a class, a method, a parameter or a property
733
- * @param e A class, a method, a parameter or a property
734
- * @param fmxScope The Famix model of the class, the method, the parameter or the property
735
- */
736
- function processDecorators(e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration, fmxScope: Famix.Class | Famix.ParametricClass | Famix.Method | Famix.Accessor | Famix.Parameter | Famix.Property): void {
737
- logger.debug(`Finding Decorators:`);
738
- e.getDecorators().forEach(dec => {
739
- const fmxDec = processDecorator(dec, e);
740
- fmxScope.addDecorator(fmxDec);
741
- });
742
- }
743
-
744
- /**
745
- * Builds a Famix model for a decorator
746
- * @param d A decorator
747
- * @param e A class, a method, a parameter or a property
748
- * @returns A Famix.Decorator representing the decorator
749
- */
750
- function processDecorator(d: Decorator, e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration): Famix.Decorator {
751
- const fmxDec = entityDictionary.createOrGetFamixDecorator(d, e);
752
-
753
- logger.debug(`decorator: ${d.getName()}, (${d.getType().getText()}), fqn = ${fmxDec.fullyQualifiedName}`);
754
-
755
- processComments(d, fmxDec);
756
-
757
- return fmxDec;
758
- }
759
-
760
- /**
761
- * Builds a Famix model for the comments
762
- * @param e A ts-morph element
763
- * @param fmxScope The Famix model of the named entity
764
- */
765
- function processComments(e: SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | Decorator | EnumDeclaration | EnumMember | TypeParameterDeclaration | VariableStatement | TypeAliasDeclaration | ArrowFunction, fmxScope: Famix.NamedEntity): void {
766
- logger.debug(`Process comments:`);
767
- e.getLeadingCommentRanges().forEach(c => {
768
- const fmxComment = processComment(c, fmxScope);
769
- logger.debug(`leading comments, addComment: '${c.getText()}'`);
770
- fmxScope.addComment(fmxComment); // redundant, but just in case
771
- });
772
- e.getTrailingCommentRanges().forEach(c => {
773
- const fmxComment = processComment(c, fmxScope);
774
- logger.debug(`trailing comments, addComment: '${c.getText()}'`);
775
- fmxScope.addComment(fmxComment);
776
- });
777
- }
778
-
779
- /**
780
- * Builds a Famix model for a comment
781
- * @param c A comment
782
- * @param fmxScope The Famix model of the comment's container
783
- * @returns A Famix.Comment representing the comment
784
- */
785
- function processComment(c: CommentRange, fmxScope: Famix.NamedEntity): Famix.Comment {
786
- const isJSDoc = c.getText().startsWith("/**");
787
- logger.debug(`processComment: comment: ${c.getText()}, isJSDoc = ${isJSDoc}`);
788
- const fmxComment = entityDictionary.createFamixComment(c, fmxScope, isJSDoc);
789
-
790
- return fmxComment;
791
- }
792
-
793
- /**
794
- * Builds a Famix model for the accesses on the parameters, variables, properties and enum members of the source files
795
- * @param accessMap A map of parameters, variables, properties and enum members with their id
796
- */
797
- export function processAccesses(accessMap: Map<FamixID, AccessibleTSMorphElement>): void {
798
- logger.debug(`Creating accesses:`);
799
- accessMap.forEach((v, id) => {
800
- logger.debug(`Accesses to ${v.getName()}`);
801
- // try {
802
- const temp_nodes = v.findReferencesAsNodes() as Array<Identifier>;
803
- temp_nodes.forEach(node => processNodeForAccesses(node, id));
804
- // } catch (error) {
805
- // logger.error(`> WARNING: got exception "${error}".\nContinuing...`);
806
- // }
807
- });
808
- }
809
-
810
- /**
811
- * Builds a Famix model for an access on a parameter, variable, property or enum member
812
- * @param n A node
813
- * @param id An id of a parameter, a variable, a property or an enum member
814
- */
815
- function processNodeForAccesses(n: Identifier, id: number): void {
816
- // try {
817
- // sometimes node's first ancestor is a PropertyDeclaration, which is not an access
818
- // see https://github.com/fuhrmanator/FamixTypeScriptImporter/issues/9
819
- // check for a node whose first ancestor is a property declaration and bail?
820
- // This may be a bug in ts-morph?
821
- if (n.getFirstAncestorOrThrow().getKindName() === "PropertyDeclaration") {
822
- logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})'s first ancestor is a PropertyDeclaration. Skipping...`);
823
- return;
824
- }
825
- entityDictionary.createFamixAccess(n, id);
826
- logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})`);
827
- // } catch (error) {
828
- // logger.error(`> Got exception "${error}".\nScopeDeclaration invalid for "${n.getSymbol().fullyQualifiedName}".\nContinuing...`);
829
- // }
830
- }
831
-
832
-
833
- // exports has name -> Declaration -- the declaration can be used to find the FamixElement
834
-
835
- // handle `import path = require("path")` for example
836
- export function processImportClausesForImportEqualsDeclarations(sourceFiles: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
837
- logger.info(`Creating import clauses from ImportEqualsDeclarations in source files:`);
838
- sourceFiles.forEach(sourceFile => {
839
- sourceFile.forEachDescendant(node => {
840
- if (Node.isImportEqualsDeclaration(node)) {
841
- // You've found an ImportEqualsDeclaration
842
- logger.info("Declaration Name:", node.getName());
843
- logger.info("Module Reference Text:", node.getModuleReference().getText());
844
- // what's the name of the imported entity?
845
- // const importedEntity = node.getName();
846
- // create a famix import clause
847
- const namedImport = node.getNameNode();
848
- entityDictionary.oldCreateOrGetFamixImportClause({
849
- importDeclaration: node,
850
- importerSourceFile: sourceFile,
851
- moduleSpecifierFilePath: node.getModuleReference().getText(),
852
- importElement: namedImport,
853
- isInExports: exports.find(e => e.has(namedImport.getText())) !== undefined,
854
- isDefaultExport: false
855
- });
856
- // entityDictionary.createFamixImportClause(importedEntity, importingEntity);
857
- }
858
- });
859
- }
860
- );
861
- }
862
-
863
- /**
864
- * Builds a Famix model for the import clauses of the source files which are modules
865
- * @param modules An array of modules
866
- * @param exports An array of maps of exported declarations
867
- */
868
- export function processImportClausesForModules(modules: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
869
- logger.info(`Creating import clauses from ${modules.length} modules:`);
870
- modules.forEach(module => {
871
- const modulePath = module.getFilePath(); // + module.getBaseName();
872
- module.getImportDeclarations().forEach(impDecl => {
873
- logger.info(`Importing ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
874
- const path = getModulePath(impDecl);
875
-
876
- impDecl.getNamedImports().forEach(namedImport => {
877
- logger.info(`Importing (named) ${namedImport.getName()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
878
- const importedEntityName = namedImport.getName();
879
- const importFoundInExports = isInExports(exports, importedEntityName);
880
- entityDictionary.oldCreateOrGetFamixImportClause({
881
- importDeclaration: impDecl,
882
- importerSourceFile: module,
883
- moduleSpecifierFilePath: path,
884
- importElement: namedImport,
885
- isInExports: importFoundInExports,
886
- isDefaultExport: false
887
- });
888
- });
889
-
890
- const defaultImport = impDecl.getDefaultImport();
891
- if (defaultImport !== undefined) {
892
- logger.info(`Importing (default) ${defaultImport.getText()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
893
- // call with module, impDecl.getModuleSpecifierValue(), path, defaultImport, false, true
894
- entityDictionary.oldCreateOrGetFamixImportClause({
895
- importDeclaration: impDecl,
896
- importerSourceFile: module,
897
- moduleSpecifierFilePath: path,
898
- importElement: defaultImport,
899
- isInExports: false,
900
- isDefaultExport: true
901
- });
902
- }
903
-
904
- const namespaceImport = impDecl.getNamespaceImport();
905
- if (namespaceImport !== undefined) {
906
- logger.info(`Importing (namespace) ${namespaceImport.getText()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
907
- entityDictionary.oldCreateOrGetFamixImportClause({
908
- importDeclaration: impDecl,
909
- importerSourceFile: module,
910
- moduleSpecifierFilePath: path,
911
- importElement: namespaceImport,
912
- isInExports: false,
913
- isDefaultExport: false
914
- });
915
- // entityDictionary.createFamixImportClause(module, impDecl.getModuleSpecifierValue(), path, namespaceImport, false, false);
916
- }
917
- });
918
- });
919
- }
920
-
921
- function isInExports(exports: ReadonlyMap<string, ExportedDeclarations[]>[], importedEntityName: string) {
922
- let importFoundInExports = false;
923
- exports.forEach(e => {
924
- if (e.has(importedEntityName)) {
925
- importFoundInExports = true;
926
- }
927
- });
928
- return importFoundInExports;
929
- }
930
-
931
- /**
932
- * Builds a Famix model for the inheritances of the classes and interfaces of the source files
933
- * @param classes An array of classes
934
- * @param interfaces An array of interfaces
935
- */
936
- export function processInheritances(classes: ClassDeclaration[], interfaces: InterfaceDeclaration[]): void {
937
- logger.info(`processInheritances: Creating inheritances:`);
938
- classes.forEach(cls => {
939
- logger.debug(`processInheritances: Checking class inheritance for ${cls.getName()}`);
940
- try {
941
- const extClass = cls.getBaseClass();
942
- if (extClass !== undefined) {
943
- entityDictionary.createOrGetFamixInheritance(cls, extClass);
944
-
945
- logger.debug(`processInheritances: class: ${cls.getName()}, (${cls.getType().getText()}), extClass: ${extClass.getName()}, (${extClass.getType().getText()})`);
946
- }
947
-
948
- logger.debug(`processInheritances: Checking interface inheritance for ${cls.getName()}`);
949
- const implementedInterfaces = getImplementedOrExtendedInterfaces(interfaces, cls);
950
- implementedInterfaces.forEach(impInter => {
951
- entityDictionary.createOrGetFamixInheritance(cls, impInter);
952
-
953
- logger.debug(`processInheritances: class: ${cls.getName()}, (${cls.getType().getText()}), impInter: ${(impInter instanceof InterfaceDeclaration) ? impInter.getName() : impInter.getExpression().getText()}, (${(impInter instanceof InterfaceDeclaration) ? impInter.getType().getText() : impInter.getExpression().getText()})`);
954
- });
955
- }
956
- catch (error) {
957
- logger.error(`> WARNING: got exception ${error}. Continuing...`);
958
- }
959
- });
960
-
961
- interfaces.forEach(inter => {
962
- try {
963
- logger.debug(`processInheritances: Checking interface inheritance for ${inter.getName()}`);
964
- const extendedInterfaces = getImplementedOrExtendedInterfaces(interfaces, inter);
965
- extendedInterfaces.forEach(extInter => {
966
- entityDictionary.createOrGetFamixInheritance(inter, extInter);
967
-
968
- logger.debug(`processInheritances: inter: ${inter.getName()}, (${inter.getType().getText()}), extInter: ${(extInter instanceof InterfaceDeclaration) ? extInter.getName() : extInter.getExpression().getText()}, (${(extInter instanceof InterfaceDeclaration) ? extInter.getType().getText() : extInter.getExpression().getText()})`);
969
- });
970
- }
971
- catch (error) {
972
- logger.error(`> WARNING: got exception ${error}. Continuing...`);
973
- }
974
- });
975
- }
976
-
977
- /**
978
- * Builds a Famix model for the invocations of the methods and functions of the source files
979
- * @param methodsAndFunctionsWithId A map of methods and functions with their id
980
- */
981
- export function processInvocations(methodsAndFunctionsWithId: Map<number, InvocableType>): void {
982
- logger.info(`Creating invocations:`);
983
- methodsAndFunctionsWithId.forEach((invocable, id) => {
984
- if (!(invocable instanceof ArrowFunction)) { // ArrowFunctions are not directly invoked
985
- logger.debug(`Invocations to ${(invocable instanceof MethodDeclaration || invocable instanceof GetAccessorDeclaration || invocable instanceof SetAccessorDeclaration || invocable instanceof FunctionDeclaration) ? invocable.getName() : ((invocable instanceof ConstructorDeclaration) ? 'constructor' : (invocable.getName() ? invocable.getName() : 'anonymous'))} (${invocable.getType().getText()})`);
986
- try {
987
- const nodesReferencingInvocable = invocable.findReferencesAsNodes() as Array<Identifier>;
988
- nodesReferencingInvocable.forEach(
989
- nodeReferencingInvocable => processNodeForInvocations(nodeReferencingInvocable, invocable, id));
990
- } catch (error) {
991
- logger.error(`> WARNING: got exception ${error}. Continuing...`);
992
- }
993
- } else {
994
- logger.debug(`Skipping invocation to ArrowFunction: ${(invocable.getBodyText())}`);
995
- }
996
- });
997
- }
998
-
999
- /**
1000
- * Builds a Famix model for an invocation of a method or a function
1001
- * @param nodeReferencingInvocable A node
1002
- * @param invocable A method or a function
1003
- * @param id The id of the method or the function
1004
- */
1005
- function processNodeForInvocations(nodeReferencingInvocable: Identifier, invocable: InvocableType, id: number): void {
1006
- try {
1007
- entityDictionary.createFamixInvocation(nodeReferencingInvocable, invocable, id);
1008
- logger.debug(`node: ${nodeReferencingInvocable.getKindName()}, (${nodeReferencingInvocable.getType().getText()})`);
1009
- } catch (error) {
1010
- logger.error(`> WARNING: got exception ${error}. ScopeDeclaration invalid for ${nodeReferencingInvocable.getSymbol()!.getFullyQualifiedName()}. Continuing...`);
1011
- }
1012
- }
1013
-
1014
- /**
1015
- * Builds a Famix model for the inheritances of the classes and interfaces of the source files
1016
- * @param classes An array of classes
1017
- * @param interfaces An array of interfaces
1018
- */
1019
- // export function processConcretisations(classes: ClassDeclaration[], interfaces: InterfaceDeclaration[], functions: Map<number, MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction>): void {
1020
- // logger.info(`processConcretisations: Creating concretisations:`);
1021
- // classes.forEach(cls => {
1022
- // logger.debug(`processConcretisations: Checking class concretisation for ${cls.getName()}`);
1023
- // entityDictionary.createFamixConcretisationClassOrInterfaceSpecialisation(cls);
1024
- // entityDictionary.createFamixConcretisationGenericInstantiation(cls);
1025
- // entityDictionary.createFamixConcretisationInterfaceClass(cls);
1026
- // entityDictionary.createFamixConcretisationTypeInstanciation(cls);
1027
-
1028
- // });
1029
- // interfaces.forEach(inter => {
1030
- // logger.debug(`processConcretisations: Checking interface concretisation for ${inter.getName()}`);
1031
- // entityDictionary.createFamixConcretisationTypeInstanciation(inter);
1032
- // entityDictionary.createFamixConcretisationClassOrInterfaceSpecialisation(inter);
1033
- // });
1034
- // functions.forEach(func => {
1035
- // if(func instanceof FunctionDeclaration || func instanceof MethodDeclaration ){
1036
- // logger.debug(`processConcretisations: Checking Method concretisation`);
1037
- // entityDictionary.createFamixConcretisationFunctionInstantiation(func);
1038
- // }
1039
- // });
1040
- // }
1
+ import { ClassDeclaration, MethodDeclaration, VariableStatement, FunctionDeclaration, VariableDeclaration, InterfaceDeclaration, ParameterDeclaration, ConstructorDeclaration, MethodSignature, SourceFile, ModuleDeclaration, PropertyDeclaration, PropertySignature, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ExportedDeclarations, CommentRange, EnumDeclaration, EnumMember, TypeParameterDeclaration, TypeAliasDeclaration, SyntaxKind, FunctionExpression, Block, Identifier, ExpressionWithTypeArguments, ImportDeclaration, Node, ArrowFunction, Scope, ClassExpression } from "ts-morph";
2
+ import * as Famix from "../lib/famix/model/famix";
3
+ import { calculate } from "../lib/ts-complex/cyclomatic-service";
4
+ import * as fs from 'fs';
5
+ import { logger, entityDictionary } from "../analyze";
6
+ import { getFQN } from "../fqn";
7
+ import { InvocableType } from "src/famix_functions/EntityDictionary";
8
+
9
+ export type AccessibleTSMorphElement = ParameterDeclaration | VariableDeclaration | PropertyDeclaration | EnumMember;
10
+ export type FamixID = number;
11
+
12
+ export const methodsAndFunctionsWithId = new Map<number, InvocableType>(); // Maps the Famix method, constructor, getter, setter and function ids to their ts-morph method, constructor, getter, setter or function object
13
+
14
+ export const accessMap = new Map<FamixID, AccessibleTSMorphElement>(); // Maps the Famix parameter, variable, property and enum value ids to their ts-morph parameter, variable, property or enum member object
15
+ export const classes = new Array<ClassDeclaration>(); // Array of all the classes of the source files
16
+ export const interfaces = new Array<InterfaceDeclaration>(); // Array of all the interfaces of the source files
17
+ export const modules = new Array<SourceFile>(); // Array of all the source files which are modules
18
+ export const listOfExportMaps = new Array<ReadonlyMap<string, ExportedDeclarations[]>>(); // Array of all the export maps
19
+ export let currentCC: { [key: string]: number }; // Stores the cyclomatic complexity metrics for the current source file
20
+ const processedNodesWithTypeParams = new Set<number>(); // Set of nodes that have been processed and have type parameters
21
+
22
+ /**
23
+ * Checks if the file has any imports or exports to be considered a module
24
+ * @param sourceFile A source file
25
+ * @returns A boolean indicating if the file is a module
26
+ */
27
+ function isSourceFileAModule(sourceFile: SourceFile): boolean {
28
+ return sourceFile.getImportDeclarations().length > 0 || sourceFile.getExportedDeclarations().size > 0;
29
+ }
30
+
31
+ /**
32
+ * Gets the path of a module to be imported
33
+ * @param importDecl An import declaration
34
+ * @returns The path of the module to be imported
35
+ */
36
+ export function getModulePath(importDecl: ImportDeclaration): string {
37
+ let path: string;
38
+ if (importDecl.getModuleSpecifierSourceFile() === undefined) {
39
+ if (importDecl.getModuleSpecifierValue().substring(importDecl.getModuleSpecifierValue().length - 3) === ".ts") {
40
+ path = importDecl.getModuleSpecifierValue();
41
+ }
42
+ else {
43
+ path = importDecl.getModuleSpecifierValue() + ".ts";
44
+ }
45
+ }
46
+ else {
47
+ path = importDecl.getModuleSpecifierSourceFile()!.getFilePath();
48
+ }
49
+ return path;
50
+ }
51
+
52
+
53
+ /**
54
+ * Gets the interfaces implemented or extended by a class or an interface
55
+ * @param interfaces An array of interfaces
56
+ * @param subClass A class or an interface
57
+ * @returns An array of InterfaceDeclaration and ExpressionWithTypeArguments containing the interfaces implemented or extended by the subClass
58
+ */
59
+ export function getImplementedOrExtendedInterfaces(interfaces: Array<InterfaceDeclaration>, subClass: ClassDeclaration | InterfaceDeclaration): Array<InterfaceDeclaration | ExpressionWithTypeArguments> {
60
+ let impOrExtInterfaces: Array<ExpressionWithTypeArguments>;
61
+ if (subClass instanceof ClassDeclaration) {
62
+ impOrExtInterfaces = subClass.getImplements();
63
+ }
64
+ else {
65
+ impOrExtInterfaces = subClass.getExtends();
66
+ }
67
+
68
+ const interfacesNames = interfaces.map(i => i.getName());
69
+ const implementedOrExtendedInterfaces = new Array<InterfaceDeclaration | ExpressionWithTypeArguments>();
70
+
71
+ impOrExtInterfaces.forEach(i => {
72
+ if (interfacesNames.includes(i.getExpression().getText())) {
73
+ implementedOrExtendedInterfaces.push(interfaces[interfacesNames.indexOf(i.getExpression().getText())]);
74
+ }
75
+ else {
76
+ implementedOrExtendedInterfaces.push(i);
77
+ }
78
+ });
79
+
80
+ return implementedOrExtendedInterfaces;
81
+ }
82
+
83
+ export function processFiles(sourceFiles: Array<SourceFile>): void {
84
+ sourceFiles.forEach(file => {
85
+ logger.info(`File: >>>>>>>>>> ${file.getFilePath()}`);
86
+
87
+ if (fs.existsSync(file.getFilePath())) {
88
+ currentCC = calculate(file.getFilePath());
89
+ } else {
90
+ currentCC = {};
91
+ }
92
+
93
+ processFile(file);
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Builds a Famix model for a source file
99
+ * @param f A source file
100
+ */
101
+ function processFile(f: SourceFile): void {
102
+ const isModule = isSourceFileAModule(f);
103
+
104
+ if (isModule) {
105
+ modules.push(f);
106
+ }
107
+
108
+ const exportMap = f.getExportedDeclarations();
109
+ if (exportMap) listOfExportMaps.push(exportMap);
110
+
111
+ const fmxFile = entityDictionary.createOrGetFamixFile(f, isModule);
112
+
113
+ logger.debug(`processFile: file: ${f.getBaseName()}, fqn = ${fmxFile.fullyQualifiedName}`);
114
+
115
+ processComments(f, fmxFile);
116
+ processAliases(f, fmxFile);
117
+ processClasses(f, fmxFile);
118
+ processInterfaces(f, fmxFile);
119
+ processModules(f, fmxFile);
120
+ processVariables(f, fmxFile); // This will handle our object literal methods
121
+ processEnums(f, fmxFile);
122
+ processFunctions(f, fmxFile);
123
+
124
+
125
+ }
126
+
127
+
128
+ export function isAmbient(node: ModuleDeclaration): boolean {
129
+ // An ambient module has the DeclareKeyword modifier.
130
+ return (node.getModifiers()?.some(modifier => modifier.getKind() === SyntaxKind.DeclareKeyword)) ?? false;
131
+ }
132
+
133
+ export function isNamespace(node: ModuleDeclaration): boolean {
134
+ // Check if the module declaration has a namespace keyword.
135
+ // This approach uses the getChildren() method to inspect the syntax directly.
136
+ return node.getChildrenOfKind(SyntaxKind.NamespaceKeyword).length > 0;
137
+ }
138
+
139
+ /**
140
+ * Builds a Famix model for a module (also namespace)
141
+ * @param m A namespace
142
+ * @returns A Famix.Module representing the module
143
+ */
144
+ function processModule(m: ModuleDeclaration): Famix.Module {
145
+ const fmxModule = entityDictionary.createOrGetFamixModule(m);
146
+
147
+ logger.debug(`module: ${m.getName()}, (${m.getType().getText()}), ${fmxModule.fullyQualifiedName}`);
148
+
149
+ processComments(m, fmxModule);
150
+
151
+ processAliases(m, fmxModule);
152
+
153
+ processClasses(m, fmxModule);
154
+
155
+ processInterfaces(m, fmxModule);
156
+
157
+ processVariables(m, fmxModule);
158
+
159
+ processEnums(m, fmxModule);
160
+
161
+ processFunctions(m, fmxModule);
162
+
163
+ processModules(m, fmxModule);
164
+
165
+ return fmxModule;
166
+ }
167
+
168
+ type ContainerTypes = SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ArrowFunction;
169
+
170
+ type ScopedTypes = Famix.ScriptEntity | Famix.Module | Famix.Function | Famix.Method | Famix.Accessor;
171
+
172
+ /**
173
+ * Builds a Famix model for the aliases of a container
174
+ * @param m A container (a source file, a namespace, a function or a method)
175
+ * @param fmxScope The Famix model of the container
176
+ */
177
+ function processAliases(m: ContainerTypes, fmxScope: ScopedTypes): void {
178
+ logger.debug(`processAliases: ---------- Finding Aliases:`);
179
+ m.getTypeAliases().forEach(a => {
180
+ const fmxAlias = processAlias(a);
181
+ fmxScope.addAlias(fmxAlias);
182
+ });
183
+ }
184
+
185
+ /**
186
+ * Builds a Famix model for the classes of a container
187
+ * @param m A container (a source file or a namespace)
188
+ * @param fmxScope The Famix model of the container
189
+ */
190
+ function processClasses(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
191
+ logger.debug(`processClasses: ---------- Finding Classes:`);
192
+ const classesInArrowFunctions = getClassesDeclaredInArrowFunctions(m);
193
+ const classes = m.getClasses().concat(classesInArrowFunctions);
194
+ classes.forEach(c => {
195
+ const fmxClass = processClass(c);
196
+ fmxScope.addType(fmxClass);
197
+ });
198
+ }
199
+
200
+ function getArrowFunctionClasses(f: ArrowFunction): ClassDeclaration[] {
201
+ const classes: ClassDeclaration[] = [];
202
+
203
+ function findClasses(node: Node) {
204
+ if (node.getKind() === SyntaxKind.ClassDeclaration) {
205
+ classes.push(node as ClassDeclaration);
206
+ }
207
+ node.getChildren().forEach(findClasses);
208
+ }
209
+
210
+ findClasses(f);
211
+ return classes;
212
+ }
213
+
214
+ /**
215
+ * ts-morph doesn't find classes in arrow functions, so we need to find them manually
216
+ * @param s A source file
217
+ * @returns the ClassDeclaration objects found in arrow functions of the source file
218
+ */
219
+ function getClassesDeclaredInArrowFunctions(s: SourceFile | ModuleDeclaration): ClassDeclaration[] {
220
+ const arrowFunctions = s.getDescendantsOfKind(SyntaxKind.ArrowFunction);
221
+ const classesInArrowFunctions = arrowFunctions.map(f => getArrowFunctionClasses(f)).flat();
222
+ return classesInArrowFunctions;
223
+ }
224
+
225
+ /**
226
+ * Builds a Famix model for the interfaces of a container
227
+ * @param m A container (a source file or a namespace)
228
+ * @param fmxScope The Famix model of the container
229
+ */
230
+ function processInterfaces(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
231
+ logger.debug(`processInterfaces: ---------- Finding Interfaces:`);
232
+ m.getInterfaces().forEach(i => {
233
+ const fmxInterface = processInterface(i);
234
+ fmxScope.addType(fmxInterface);
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Builds a Famix model for the variables of a container
240
+ * @param m A container (a source file, a namespace, a function or a method)
241
+ * @param fmxScope The Famix model of the container
242
+ */
243
+ function processVariables(m: ContainerTypes, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Function | Famix.Method | Famix.Accessor): void {
244
+ logger.debug(`processVariables: ---------- Finding Variables:`);
245
+ m.getVariableStatements().forEach(v => {
246
+ const fmxVariables = processVariableStatement(v);
247
+ fmxVariables.forEach(fmxVariable => {
248
+ fmxScope.addVariable(fmxVariable);
249
+ });
250
+
251
+ // Check each VariableDeclaration for object literal methods
252
+ v.getDeclarations().forEach(varDecl => {
253
+ const varName = varDecl.getName();
254
+ console.log(`Checking variable: ${varName} at pos=${varDecl.getStart()}`);
255
+ const initializer = varDecl.getInitializer();
256
+ if (initializer && Node.isObjectLiteralExpression(initializer)) {
257
+ initializer.getProperties().forEach(prop => {
258
+ if (Node.isPropertyAssignment(prop)) {
259
+ const nested = prop.getInitializer();
260
+ if (nested && Node.isObjectLiteralExpression(nested)) {
261
+ nested.getDescendantsOfKind(SyntaxKind.MethodDeclaration).forEach(method => {
262
+ console.log(`Found object literal method: ${method.getName()} at pos=${method.getStart()}`);
263
+ entityDictionary.createOrGetFamixMethod(method, currentCC);
264
+ });
265
+ }
266
+ }
267
+ });
268
+ }
269
+ });
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Builds a Famix model for the enums of a container
275
+ * @param m A container (a source file, a namespace, a function or a method)
276
+ * @param fmxScope The Famix model of the container
277
+ */
278
+ function processEnums(m: ContainerTypes, fmxScope: ScopedTypes): void {
279
+ logger.debug(`processEnums: ---------- Finding Enums:`);
280
+ m.getEnums().forEach(e => {
281
+ const fmxEnum = processEnum(e);
282
+ fmxScope.addType(fmxEnum);
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Builds a Famix model for the functions of a container
288
+ * @param m A container (a source file, a namespace, a function or a method)
289
+ * @param fmxScope The Famix model of the container
290
+ */
291
+ function processFunctions(m: ContainerTypes, fmxScope: ScopedTypes): void {
292
+ logger.debug(`Finding Functions:`);
293
+ m.getFunctions().forEach(f => {
294
+ const fmxFunction = processFunction(f);
295
+ fmxScope.addFunction(fmxFunction);
296
+ });
297
+
298
+ //find arrow functions
299
+ logger.debug(`Finding Functions:`);
300
+ const arrowFunctions = m.getDescendantsOfKind(SyntaxKind.ArrowFunction);
301
+ arrowFunctions.forEach(af => {
302
+ const fmxFunction = processFunction(af);
303
+ fmxScope.addFunction(fmxFunction);
304
+ });
305
+ }
306
+
307
+ /**
308
+ * Builds a Famix model for the modules of a container.
309
+ * @param m A container (a source file or a namespace)
310
+ * @param fmxScope The Famix model of the container
311
+ */
312
+ function processModules(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module): void {
313
+ logger.debug(`Finding Modules:`);
314
+ m.getModules().forEach(md => {
315
+ const fmxModule = processModule(md);
316
+ fmxScope.addModule(fmxModule);
317
+ });
318
+ }
319
+
320
+ /**
321
+ * Builds a Famix model for an alias
322
+ * @param a An alias
323
+ * @returns A Famix.Alias representing the alias
324
+ */
325
+ function processAlias(a: TypeAliasDeclaration): Famix.Alias {
326
+ const fmxAlias = entityDictionary.createFamixAlias(a);
327
+
328
+ logger.debug(`Alias: ${a.getName()}, (${a.getType().getText()}), fqn = ${fmxAlias.fullyQualifiedName}`);
329
+
330
+ processComments(a, fmxAlias);
331
+
332
+ return fmxAlias;
333
+ }
334
+
335
+ /**
336
+ * Builds a Famix model for a class
337
+ * @param c A class
338
+ * @returns A Famix.Class or a Famix.ParametricClass representing the class
339
+ */
340
+ function processClass(c: ClassDeclaration): Famix.Class | Famix.ParametricClass {
341
+ classes.push(c);
342
+
343
+ const fmxClass = entityDictionary.createOrGetFamixClass(c);
344
+
345
+ logger.debug(`Class: ${c.getName()}, (${c.getType().getText()}), fqn = ${fmxClass.fullyQualifiedName}`);
346
+
347
+ processComments(c, fmxClass);
348
+
349
+ processDecorators(c, fmxClass);
350
+
351
+ processStructuredType(c, fmxClass);
352
+
353
+ c.getConstructors().forEach(con => {
354
+ const fmxCon = processMethod(con);
355
+ fmxClass.addMethod(fmxCon);
356
+ });
357
+
358
+ c.getGetAccessors().forEach(acc => {
359
+ const fmxAcc = processMethod(acc);
360
+ fmxClass.addMethod(fmxAcc);
361
+ });
362
+
363
+ c.getSetAccessors().forEach(acc => {
364
+ const fmxAcc = processMethod(acc);
365
+ fmxClass.addMethod(fmxAcc);
366
+ });
367
+
368
+ return fmxClass;
369
+ }
370
+
371
+ /**
372
+ * Builds a Famix model for an interface
373
+ * @param i An interface
374
+ * @returns A Famix.Interface or a Famix.ParametricInterface representing the interface
375
+ */
376
+ function processInterface(i: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
377
+ interfaces.push(i);
378
+
379
+ const fmxInterface = entityDictionary.createOrGetFamixInterface(i);
380
+
381
+ logger.debug(`Interface: ${i.getName()}, (${i.getType().getText()}), fqn = ${fmxInterface.fullyQualifiedName}`);
382
+
383
+ processComments(i, fmxInterface);
384
+
385
+ processStructuredType(i, fmxInterface);
386
+
387
+ return fmxInterface;
388
+ }
389
+
390
+ /**
391
+ * Builds a Famix model for the type parameters, properties and methods of a structured type
392
+ * @param c A structured type (a class or an interface)
393
+ * @param fmxScope The Famix model of the structured type
394
+ */
395
+ function processStructuredType(c: ClassDeclaration | InterfaceDeclaration, fmxScope: Famix.Class | Famix.ParametricClass | Famix.Interface | Famix.ParametricInterface): void {
396
+ logger.debug(`Finding Properties and Methods:`);
397
+ if (fmxScope instanceof Famix.ParametricClass || fmxScope instanceof Famix.ParametricInterface) {
398
+ processTypeParameters(c, fmxScope);
399
+ }
400
+
401
+ c.getProperties().forEach(prop => {
402
+ const fmxProperty = processProperty(prop);
403
+ fmxScope.addProperty(fmxProperty);
404
+ });
405
+
406
+ c.getMethods().forEach(m => {
407
+ const fmxMethod = processMethod(m);
408
+ fmxScope.addMethod(fmxMethod);
409
+ });
410
+ }
411
+
412
+ /**
413
+ * Builds a Famix model for a property
414
+ * @param p A property
415
+ * @returns A Famix.Property representing the property
416
+ */
417
+ function processProperty(p: PropertyDeclaration | PropertySignature): Famix.Property {
418
+ const fmxProperty = entityDictionary.createFamixProperty(p);
419
+
420
+ logger.debug(`property: ${p.getName()}, (${p.getType().getText()}), fqn = ${fmxProperty.fullyQualifiedName}`);
421
+ logger.debug(` ---> It's a Property${(p instanceof PropertySignature) ? "Signature" : "Declaration"}!`);
422
+ const ancestor = p.getFirstAncestorOrThrow();
423
+ logger.debug(` ---> Its first ancestor is a ${ancestor.getKindName()}`);
424
+
425
+ // decorators
426
+ if (!(p instanceof PropertySignature)) {
427
+ processDecorators(p, fmxProperty);
428
+ // only add access if the p's first ancestor is not a PropertyDeclaration
429
+ if (ancestor.getKindName() !== "PropertyDeclaration") {
430
+ logger.debug(`adding access to map: ${p.getName()}, (${p.getType().getText()}) Famix ${fmxProperty.name} id: ${fmxProperty.id}`);
431
+ accessMap.set(fmxProperty.id, p);
432
+ }
433
+ }
434
+
435
+ processComments(p, fmxProperty);
436
+
437
+ return fmxProperty;
438
+ }
439
+
440
+ /**
441
+ * Builds a Famix model for a method or an accessor
442
+ * @param m A method or an accessor
443
+ * @returns A Famix.Method or a Famix.Accessor representing the method or the accessor
444
+ */
445
+ function processMethod(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration): Famix.Method | Famix.Accessor {
446
+ const fmxMethod = entityDictionary.createOrGetFamixMethod(m, currentCC);
447
+
448
+ logger.debug(`Method: ${!(m instanceof ConstructorDeclaration) ? m.getName() : "constructor"}, (${m.getType().getText()}), parent: ${(m.getParent() as ClassDeclaration | InterfaceDeclaration).getName()}, fqn = ${fmxMethod.fullyQualifiedName}`);
449
+
450
+ processComments(m, fmxMethod);
451
+
452
+ processTypeParameters(m, fmxMethod);
453
+
454
+ processParameters(m, fmxMethod);
455
+
456
+ if (!(m instanceof MethodSignature)) {
457
+ processAliases(m, fmxMethod);
458
+
459
+ processVariables(m, fmxMethod);
460
+
461
+ processEnums(m, fmxMethod);
462
+
463
+ processFunctions(m, fmxMethod);
464
+
465
+ processFunctionExpressions(m, fmxMethod);
466
+
467
+ methodsAndFunctionsWithId.set(fmxMethod.id, m);
468
+ }
469
+
470
+ if (m instanceof MethodDeclaration || m instanceof GetAccessorDeclaration || m instanceof SetAccessorDeclaration) {
471
+ processDecorators(m, fmxMethod);
472
+ }
473
+
474
+ return fmxMethod;
475
+ }
476
+
477
+ /**
478
+ * Builds a Famix model for a function
479
+ * @param f A function
480
+ * @returns A Famix.Function representing the function
481
+ */
482
+ function processFunction(f: FunctionDeclaration | FunctionExpression | ArrowFunction): Famix.Function {
483
+
484
+ logger.debug(`Function: ${(f instanceof ArrowFunction ? "anonymous" : f.getName() ? f.getName() : "anonymous")}, (${f.getType().getText()}), fqn = ${getFQN(f)}`);
485
+
486
+ let fmxFunction;
487
+ if (f instanceof ArrowFunction) {
488
+ fmxFunction = entityDictionary.createOrGetFamixArrowFunction(f, currentCC);
489
+ } else {
490
+ fmxFunction = entityDictionary.createOrGetFamixFunction(f, currentCC);
491
+ }
492
+
493
+ processComments(f, fmxFunction);
494
+
495
+ processAliases(f, fmxFunction);
496
+
497
+ processTypeParameters(f, fmxFunction);
498
+
499
+ processParameters(f, fmxFunction);
500
+
501
+ processVariables(f, fmxFunction);
502
+
503
+ processEnums(f, fmxFunction);
504
+
505
+ processFunctions(f, fmxFunction);
506
+
507
+ if (f instanceof FunctionDeclaration && !(f.getParent() instanceof Block)) {
508
+ processFunctionExpressions(f, fmxFunction);
509
+ }
510
+
511
+ methodsAndFunctionsWithId.set(fmxFunction.id, f);
512
+
513
+ return fmxFunction;
514
+ }
515
+
516
+ /**
517
+ * Builds a Famix model for the function expressions of a function or a method
518
+ * @param f A function or a method
519
+ * @param fmxScope The Famix model of the function or the method
520
+ */
521
+ function processFunctionExpressions(f: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.Function | Famix.Method | Famix.Accessor): void {
522
+ logger.debug(`Finding Function Expressions:`);
523
+ const functionExpressions = f.getDescendantsOfKind(SyntaxKind.FunctionExpression);
524
+ functionExpressions.forEach((func) => {
525
+ const fmxFunc = processFunction(func);
526
+ fmxScope.addFunction(fmxFunc);
527
+ });
528
+ }
529
+
530
+ /**
531
+ * Builds a Famix model for the parameters of a method or a function
532
+ * @param m A method or a function
533
+ * @param fmxScope The Famix model of the method or the function
534
+ */
535
+ function processParameters(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction, fmxScope: Famix.Method | Famix.Accessor | Famix.Function): void {
536
+ logger.debug(`Finding Parameters:`);
537
+ m.getParameters().forEach(param => {
538
+ const fmxParam = processParameter(param);
539
+ fmxScope.addParameter(fmxParam);
540
+ // Additional handling for Parameter Properties in constructors
541
+ if (m instanceof ConstructorDeclaration) {
542
+ // Check if the parameter has any visibility modifier
543
+ if (param.hasModifier(SyntaxKind.PrivateKeyword) || param.hasModifier(SyntaxKind.PublicKeyword) || param.hasModifier(SyntaxKind.ProtectedKeyword) || param.hasModifier(SyntaxKind.ReadonlyKeyword)) {
544
+ const classOfConstructor = m.getParent();
545
+ logger.info(`Parameter Property ${param.getName()} in constructor of ${classOfConstructor.getName()}.`);
546
+ // Treat the parameter as a property and add it to the class
547
+ const fmxProperty = processParameterAsProperty(param, classOfConstructor);
548
+ fmxProperty.readOnly = param.hasModifier(SyntaxKind.ReadonlyKeyword);
549
+ }
550
+ }
551
+
552
+ });
553
+ }
554
+
555
+ // This function should create a Famix.Property model from a ParameterDeclaration
556
+ // You'll need to implement it according to your Famix model structure
557
+ function processParameterAsProperty(param: ParameterDeclaration, classDecl: ClassDeclaration | ClassExpression): Famix.Property {
558
+ // Convert the parameter into a Property
559
+ const propertyRepresentation = convertParameterToPropertyRepresentation(param);
560
+
561
+ // Add the property to the class so we can have a PropertyDeclaration object
562
+ classDecl.addProperty(propertyRepresentation);
563
+
564
+ const property = classDecl.getProperty(propertyRepresentation.name);
565
+ if (!property) {
566
+ throw new Error(`Property ${propertyRepresentation.name} not found in class ${classDecl.getName()}`);
567
+ }
568
+ const fmxProperty = entityDictionary.createFamixProperty(property);
569
+ if (classDecl instanceof ClassDeclaration) {
570
+ const fmxClass = entityDictionary.createOrGetFamixClass(classDecl);
571
+ fmxClass.addProperty(fmxProperty);
572
+ } else {
573
+ throw new Error("Unexpected type ClassExpression.");
574
+ }
575
+
576
+ processComments(property, fmxProperty);
577
+
578
+ // remove the property from the class
579
+ property.remove();
580
+
581
+ return fmxProperty;
582
+
583
+ }
584
+
585
+ function convertParameterToPropertyRepresentation(param: ParameterDeclaration) {
586
+ // Extract name
587
+ const paramName = param.getName();
588
+
589
+ // Extract type
590
+ const paramType = param.getType().getText(param);
591
+
592
+ // Determine visibility
593
+ let scope: Scope;
594
+ if (param.hasModifier(SyntaxKind.PrivateKeyword)) {
595
+ scope = Scope.Private;
596
+ } else if (param.hasModifier(SyntaxKind.ProtectedKeyword)) {
597
+ scope = Scope.Protected;
598
+ } else if (param.hasModifier(SyntaxKind.PublicKeyword)) {
599
+ scope = Scope.Public;
600
+ } else {
601
+ throw new Error(`Parameter property ${paramName} in constructor does not have a visibility modifier.`);
602
+ }
603
+
604
+ // Determine if readonly
605
+ const isReadonly = param.hasModifier(SyntaxKind.ReadonlyKeyword);
606
+
607
+ // Create a representation of the property
608
+ const propertyRepresentation = {
609
+ name: paramName,
610
+ type: paramType,
611
+ scope: scope,
612
+ isReadonly: isReadonly,
613
+ };
614
+
615
+ return propertyRepresentation;
616
+ }
617
+
618
+ /**
619
+ * Builds a Famix model for a parameter
620
+ * @param paramDecl A parameter
621
+ * @returns A Famix.Parameter representing the parameter
622
+ */
623
+ function processParameter(paramDecl: ParameterDeclaration): Famix.Parameter {
624
+ const fmxParam = entityDictionary.createOrGetFamixParameter(paramDecl); // create or GET
625
+
626
+ logger.debug(`parameter: ${paramDecl.getName()}, (${paramDecl.getType().getText()}), fqn = ${fmxParam.fullyQualifiedName}`);
627
+
628
+ processComments(paramDecl, fmxParam);
629
+
630
+ processDecorators(paramDecl, fmxParam);
631
+
632
+ const parent = paramDecl.getParent();
633
+
634
+ if (!(parent instanceof MethodSignature)) {
635
+ logger.debug(`adding access: ${paramDecl.getName()}, (${paramDecl.getType().getText()}) Famix ${fmxParam.name}`);
636
+ accessMap.set(fmxParam.id, paramDecl);
637
+ }
638
+
639
+ return fmxParam;
640
+ }
641
+
642
+ function processTypeParameters(
643
+ e: ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction,
644
+ fmxScope: Famix.ParametricClass | Famix.ParametricInterface | Famix.Method | Famix.Accessor | Famix.Function | Famix.ArrowFunction
645
+ ): void {
646
+ logger.debug(`Finding Type Parameters:`);
647
+ const nodeStart = e.getStart();
648
+
649
+ // Check if this node has already been processed
650
+ if (processedNodesWithTypeParams.has(nodeStart)) {
651
+ return;
652
+ }
653
+
654
+ // Get type parameters
655
+ const typeParams = e.getTypeParameters();
656
+
657
+ // Process each type parameter
658
+ typeParams.forEach((tp) => {
659
+ const fmxParam = processTypeParameter(tp);
660
+ fmxScope.addGenericParameter(fmxParam);
661
+ });
662
+
663
+ // Log if no type parameters were found
664
+ if (typeParams.length === 0) {
665
+ logger.debug(`[processTypeParameters] No type parameters found for this node`);
666
+ }
667
+
668
+ // Mark this node as processed
669
+ processedNodesWithTypeParams.add(nodeStart);
670
+ }
671
+
672
+ /**
673
+ * Builds a Famix model for a type parameter
674
+ * @param tp A type parameter
675
+ * @returns A Famix.TypeParameter representing the type parameter
676
+ */
677
+ function processTypeParameter(tp: TypeParameterDeclaration): Famix.ParameterType {
678
+ const fmxTypeParameter = entityDictionary.createFamixParameterType(tp);
679
+ logger.debug(`type parameter: ${tp.getName()}, (${tp.getType().getText()}), fqn = ${fmxTypeParameter.fullyQualifiedName}`);
680
+ processComments(tp, fmxTypeParameter);
681
+ return fmxTypeParameter;
682
+ }
683
+
684
+ /**
685
+ * Builds a Famix model for the variables of a variable statement
686
+ * @param v A variable statement
687
+ * @returns An array of Famix.Variable representing the variables
688
+ */
689
+ function processVariableStatement(v: VariableStatement): Array<Famix.Variable> {
690
+ const fmxVariables = new Array<Famix.Variable>();
691
+
692
+ logger.debug(`Variable statement: ${v.getText()}, (${v.getType().getText()}), ${v.getDeclarationKindKeywords()[0]}, fqn = ${v.getDeclarations()[0].getName()}`);
693
+
694
+ v.getDeclarations().forEach(variable => {
695
+ const fmxVar = processVariable(variable);
696
+ processComments(v, fmxVar);
697
+ fmxVariables.push(fmxVar);
698
+ });
699
+
700
+ return fmxVariables;
701
+ }
702
+
703
+ /**
704
+ * Builds a Famix model for a variable
705
+ * @param v A variable
706
+ * @returns A Famix.Variable representing the variable
707
+ */
708
+ function processVariable(v: VariableDeclaration): Famix.Variable {
709
+ const fmxVar = entityDictionary.createOrGetFamixVariable(v);
710
+
711
+ logger.debug(`variable: ${v.getName()}, (${v.getType().getText()}), ${v.getInitializer() ? "initializer: " + v.getInitializer()!.getText() : "initializer: "}, fqn = ${fmxVar.fullyQualifiedName}`);
712
+
713
+ processComments(v, fmxVar);
714
+
715
+ logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxVar.name}`);
716
+ accessMap.set(fmxVar.id, v);
717
+
718
+ return fmxVar;
719
+ }
720
+
721
+ /**
722
+ * Builds a Famix model for an enum
723
+ * @param e An enum
724
+ * @returns A Famix.Enum representing the enum
725
+ */
726
+ function processEnum(e: EnumDeclaration): Famix.Enum {
727
+ const fmxEnum = entityDictionary.createOrGetFamixEnum(e);
728
+
729
+ logger.debug(`enum: ${e.getName()}, (${e.getType().getText()}), fqn = ${fmxEnum.fullyQualifiedName}`);
730
+
731
+ processComments(e, fmxEnum);
732
+
733
+ e.getMembers().forEach(m => {
734
+ const fmxEnumValue = processEnumValue(m);
735
+ fmxEnum.addValue(fmxEnumValue);
736
+ });
737
+
738
+ return fmxEnum;
739
+ }
740
+
741
+ /**
742
+ * Builds a Famix model for an enum member
743
+ * @param v An enum member
744
+ * @returns A Famix.EnumValue representing the enum member
745
+ */
746
+ function processEnumValue(v: EnumMember): Famix.EnumValue {
747
+ const fmxEnumValue = entityDictionary.createFamixEnumValue(v);
748
+
749
+ logger.debug(`enum value: ${v.getName()}, (${v.getType().getText()}), fqn = ${fmxEnumValue.fullyQualifiedName}`);
750
+
751
+ processComments(v, fmxEnumValue);
752
+
753
+ logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxEnumValue.name}`);
754
+ accessMap.set(fmxEnumValue.id, v);
755
+
756
+ return fmxEnumValue;
757
+ }
758
+
759
+ /**
760
+ * Builds a Famix model for the decorators of a class, a method, a parameter or a property
761
+ * @param e A class, a method, a parameter or a property
762
+ * @param fmxScope The Famix model of the class, the method, the parameter or the property
763
+ */
764
+ function processDecorators(e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration, fmxScope: Famix.Class | Famix.ParametricClass | Famix.Method | Famix.Accessor | Famix.Parameter | Famix.Property): void {
765
+ logger.debug(`Finding Decorators:`);
766
+ e.getDecorators().forEach(dec => {
767
+ const fmxDec = processDecorator(dec, e);
768
+ fmxScope.addDecorator(fmxDec);
769
+ });
770
+ }
771
+
772
+ /**
773
+ * Builds a Famix model for a decorator
774
+ * @param d A decorator
775
+ * @param e A class, a method, a parameter or a property
776
+ * @returns A Famix.Decorator representing the decorator
777
+ */
778
+ function processDecorator(d: Decorator, e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration): Famix.Decorator {
779
+ const fmxDec = entityDictionary.createOrGetFamixDecorator(d, e);
780
+
781
+ logger.debug(`decorator: ${d.getName()}, (${d.getType().getText()}), fqn = ${fmxDec.fullyQualifiedName}`);
782
+
783
+ processComments(d, fmxDec);
784
+
785
+ return fmxDec;
786
+ }
787
+
788
+ /**
789
+ * Builds a Famix model for the comments
790
+ * @param e A ts-morph element
791
+ * @param fmxScope The Famix model of the named entity
792
+ */
793
+ function processComments(e: SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | Decorator | EnumDeclaration | EnumMember | TypeParameterDeclaration | VariableStatement | TypeAliasDeclaration | ArrowFunction, fmxScope: Famix.NamedEntity): void {
794
+ logger.debug(`Process comments:`);
795
+ e.getLeadingCommentRanges().forEach(c => {
796
+ const fmxComment = processComment(c, fmxScope);
797
+ logger.debug(`leading comments, addComment: '${c.getText()}'`);
798
+ fmxScope.addComment(fmxComment); // redundant, but just in case
799
+ });
800
+ e.getTrailingCommentRanges().forEach(c => {
801
+ const fmxComment = processComment(c, fmxScope);
802
+ logger.debug(`trailing comments, addComment: '${c.getText()}'`);
803
+ fmxScope.addComment(fmxComment);
804
+ });
805
+ }
806
+
807
+ /**
808
+ * Builds a Famix model for a comment
809
+ * @param c A comment
810
+ * @param fmxScope The Famix model of the comment's container
811
+ * @returns A Famix.Comment representing the comment
812
+ */
813
+ function processComment(c: CommentRange, fmxScope: Famix.NamedEntity): Famix.Comment {
814
+ const isJSDoc = c.getText().startsWith("/**");
815
+ logger.debug(`processComment: comment: ${c.getText()}, isJSDoc = ${isJSDoc}`);
816
+ const fmxComment = entityDictionary.createFamixComment(c, fmxScope, isJSDoc);
817
+
818
+ return fmxComment;
819
+ }
820
+
821
+ /**
822
+ * Builds a Famix model for the accesses on the parameters, variables, properties and enum members of the source files
823
+ * @param accessMap A map of parameters, variables, properties and enum members with their id
824
+ */
825
+ export function processAccesses(accessMap: Map<FamixID, AccessibleTSMorphElement>): void {
826
+ logger.debug(`Creating accesses:`);
827
+ accessMap.forEach((v, id) => {
828
+ logger.debug(`Accesses to ${v.getName()}`);
829
+ // try {
830
+ const temp_nodes = v.findReferencesAsNodes() as Array<Identifier>;
831
+ temp_nodes.forEach(node => processNodeForAccesses(node, id));
832
+ // } catch (error) {
833
+ // logger.error(`> WARNING: got exception "${error}".\nContinuing...`);
834
+ // }
835
+ });
836
+ }
837
+
838
+ /**
839
+ * Builds a Famix model for an access on a parameter, variable, property or enum member
840
+ * @param n A node
841
+ * @param id An id of a parameter, a variable, a property or an enum member
842
+ */
843
+ function processNodeForAccesses(n: Identifier, id: number): void {
844
+ // try {
845
+ // sometimes node's first ancestor is a PropertyDeclaration, which is not an access
846
+ // see https://github.com/fuhrmanator/FamixTypeScriptImporter/issues/9
847
+ // check for a node whose first ancestor is a property declaration and bail?
848
+ // This may be a bug in ts-morph?
849
+ if (n.getFirstAncestorOrThrow().getKindName() === "PropertyDeclaration") {
850
+ logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})'s first ancestor is a PropertyDeclaration. Skipping...`);
851
+ return;
852
+ }
853
+ entityDictionary.createFamixAccess(n, id);
854
+ logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})`);
855
+ // } catch (error) {
856
+ // logger.error(`> Got exception "${error}".\nScopeDeclaration invalid for "${n.getSymbol().fullyQualifiedName}".\nContinuing...`);
857
+ // }
858
+ }
859
+
860
+
861
+ // exports has name -> Declaration -- the declaration can be used to find the FamixElement
862
+
863
+ // handle `import path = require("path")` for example
864
+ export function processImportClausesForImportEqualsDeclarations(sourceFiles: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
865
+ logger.info(`Creating import clauses from ImportEqualsDeclarations in source files:`);
866
+ sourceFiles.forEach(sourceFile => {
867
+ sourceFile.forEachDescendant(node => {
868
+ if (Node.isImportEqualsDeclaration(node)) {
869
+ // You've found an ImportEqualsDeclaration
870
+ logger.info("Declaration Name:", node.getName());
871
+ logger.info("Module Reference Text:", node.getModuleReference().getText());
872
+ // what's the name of the imported entity?
873
+ // const importedEntity = node.getName();
874
+ // create a famix import clause
875
+ const namedImport = node.getNameNode();
876
+ entityDictionary.oldCreateOrGetFamixImportClause({
877
+ importDeclaration: node,
878
+ importerSourceFile: sourceFile,
879
+ moduleSpecifierFilePath: node.getModuleReference().getText(),
880
+ importElement: namedImport,
881
+ isInExports: exports.find(e => e.has(namedImport.getText())) !== undefined,
882
+ isDefaultExport: false
883
+ });
884
+ // entityDictionary.createFamixImportClause(importedEntity, importingEntity);
885
+ }
886
+ });
887
+ }
888
+ );
889
+ }
890
+
891
+ /**
892
+ * Builds a Famix model for the import clauses of the source files which are modules
893
+ * @param modules An array of modules
894
+ * @param exports An array of maps of exported declarations
895
+ */
896
+ export function processImportClausesForModules(modules: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
897
+ logger.info(`Creating import clauses from ${modules.length} modules:`);
898
+ modules.forEach(module => {
899
+ const modulePath = module.getFilePath();
900
+ module.getImportDeclarations().forEach(impDecl => {
901
+ logger.info(`Importing ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
902
+ const path = getModulePath(impDecl);
903
+
904
+ impDecl.getNamedImports().forEach(namedImport => {
905
+ logger.info(`Importing (named) ${namedImport.getName()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
906
+ const importedEntityName = namedImport.getName();
907
+ const importFoundInExports = isInExports(exports, importedEntityName);
908
+ logger.debug(`Processing ImportSpecifier: ${namedImport.getText()}, pos=${namedImport.getStart()}`);
909
+ entityDictionary.oldCreateOrGetFamixImportClause({
910
+ importDeclaration: impDecl,
911
+ importerSourceFile: module,
912
+ moduleSpecifierFilePath: path,
913
+ importElement: namedImport,
914
+ isInExports: importFoundInExports,
915
+ isDefaultExport: false
916
+ });
917
+ });
918
+
919
+ const defaultImport = impDecl.getDefaultImport();
920
+ if (defaultImport !== undefined) {
921
+ logger.info(`Importing (default) ${defaultImport.getText()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
922
+ logger.debug(`Processing Default Import: ${defaultImport.getText()}, pos=${defaultImport.getStart()}`);
923
+ entityDictionary.oldCreateOrGetFamixImportClause({
924
+ importDeclaration: impDecl,
925
+ importerSourceFile: module,
926
+ moduleSpecifierFilePath: path,
927
+ importElement: defaultImport,
928
+ isInExports: false,
929
+ isDefaultExport: true
930
+ });
931
+ }
932
+
933
+ const namespaceImport = impDecl.getNamespaceImport();
934
+ if (namespaceImport !== undefined) {
935
+ logger.info(`Importing (namespace) ${namespaceImport.getText()} from ${impDecl.getModuleSpecifierValue()} in ${modulePath}`);
936
+ entityDictionary.oldCreateOrGetFamixImportClause({
937
+ importDeclaration: impDecl,
938
+ importerSourceFile: module,
939
+ moduleSpecifierFilePath: path,
940
+ importElement: namespaceImport,
941
+ isInExports: false,
942
+ isDefaultExport: false
943
+ });
944
+ }
945
+ });
946
+ });
947
+ }
948
+
949
+ function isInExports(exports: ReadonlyMap<string, ExportedDeclarations[]>[], importedEntityName: string) {
950
+ let importFoundInExports = false;
951
+ exports.forEach(e => {
952
+ if (e.has(importedEntityName)) {
953
+ importFoundInExports = true;
954
+ }
955
+ });
956
+ return importFoundInExports;
957
+ }
958
+
959
+ /**
960
+ * Builds a Famix model for the inheritances of the classes and interfaces of the source files
961
+ * @param classes An array of classes
962
+ * @param interfaces An array of interfaces
963
+ */
964
+ export function processInheritances(classes: ClassDeclaration[], interfaces: InterfaceDeclaration[]): void {
965
+ logger.info(`Creating inheritances:`);
966
+ classes.forEach(cls => {
967
+ logger.debug(`Checking class inheritance for ${cls.getName()}`);
968
+ const baseClass = cls.getBaseClass();
969
+ if (baseClass !== undefined) {
970
+ entityDictionary.createOrGetFamixInheritance(cls, baseClass);
971
+ logger.debug(`class: ${cls.getName()}, (${cls.getType().getText()}), extClass: ${baseClass.getName()}, (${baseClass.getType().getText()})`);
972
+ } // this is false when the class extends an undefined class
973
+ else {
974
+ // check for "extends" of unresolved class
975
+ const undefinedExtendedClass = cls.getExtends();
976
+ if (undefinedExtendedClass) {
977
+ entityDictionary.createOrGetFamixInheritance(cls, undefinedExtendedClass);
978
+ logger.debug(`class: ${cls.getName()}, (${cls.getType().getText()}), undefinedExtendedClass: ${undefinedExtendedClass.getText()}`);
979
+ }
980
+ }
981
+
982
+ logger.debug(`Checking interface inheritance for ${cls.getName()}`);
983
+ const implementedInterfaces = getImplementedOrExtendedInterfaces(interfaces, cls);
984
+ implementedInterfaces.forEach(implementedIF => {
985
+ entityDictionary.createOrGetFamixInheritance(cls, implementedIF);
986
+ logger.debug(`class: ${cls.getName()}, (${cls.getType().getText()}), impInter: ${(implementedIF instanceof InterfaceDeclaration) ? implementedIF.getName() : implementedIF.getExpression().getText()}, (${(implementedIF instanceof InterfaceDeclaration) ? implementedIF.getType().getText() : implementedIF.getExpression().getText()})`);
987
+ });
988
+ });
989
+
990
+ interfaces.forEach(interFace => {
991
+ try {
992
+ logger.debug(`Checking interface inheritance for ${interFace.getName()}`);
993
+ const extendedInterfaces = getImplementedOrExtendedInterfaces(interfaces, interFace);
994
+ extendedInterfaces.forEach(extendedInterface => {
995
+ entityDictionary.createOrGetFamixInheritance(interFace, extendedInterface);
996
+
997
+ logger.debug(`interFace: ${interFace.getName()}, (${interFace.getType().getText()}), extendedInterface: ${(extendedInterface instanceof InterfaceDeclaration) ? extendedInterface.getName() : extendedInterface.getExpression().getText()}, (${(extendedInterface instanceof InterfaceDeclaration) ? extendedInterface.getType().getText() : extendedInterface.getExpression().getText()})`);
998
+ });
999
+ }
1000
+ catch (error) {
1001
+ logger.error(`> WARNING: got exception ${error}. Continuing...`);
1002
+ }
1003
+ });
1004
+ }
1005
+
1006
+ /**
1007
+ * Builds a Famix model for the invocations of the methods and functions of the source files
1008
+ * @param methodsAndFunctionsWithId A map of methods and functions with their id
1009
+ */
1010
+ export function processInvocations(methodsAndFunctionsWithId: Map<number, InvocableType>): void {
1011
+ logger.info(`Creating invocations:`);
1012
+ methodsAndFunctionsWithId.forEach((invocable, id) => {
1013
+ if (!(invocable instanceof ArrowFunction)) { // ArrowFunctions are not directly invoked
1014
+ logger.debug(`Invocations to ${(invocable instanceof MethodDeclaration || invocable instanceof GetAccessorDeclaration || invocable instanceof SetAccessorDeclaration || invocable instanceof FunctionDeclaration) ? invocable.getName() : ((invocable instanceof ConstructorDeclaration) ? 'constructor' : (invocable.getName() ? invocable.getName() : 'anonymous'))} (${invocable.getType().getText()})`);
1015
+ try {
1016
+ const nodesReferencingInvocable = invocable.findReferencesAsNodes() as Array<Identifier>;
1017
+ nodesReferencingInvocable.forEach(
1018
+ nodeReferencingInvocable => processNodeForInvocations(nodeReferencingInvocable, invocable, id));
1019
+ } catch (error) {
1020
+ logger.error(`> WARNING: got exception ${error}. Continuing...`);
1021
+ }
1022
+ } else {
1023
+ logger.debug(`Skipping invocation to ArrowFunction: ${(invocable.getBodyText())}`);
1024
+ }
1025
+ });
1026
+ }
1027
+
1028
+ /**
1029
+ * Builds a Famix model for an invocation of a method or a function
1030
+ * @param nodeReferencingInvocable A node
1031
+ * @param invocable A method or a function
1032
+ * @param id The id of the method or the function
1033
+ */
1034
+ function processNodeForInvocations(nodeReferencingInvocable: Identifier, invocable: InvocableType, id: number): void {
1035
+ try {
1036
+ entityDictionary.createFamixInvocation(nodeReferencingInvocable, invocable, id);
1037
+ logger.debug(`node: ${nodeReferencingInvocable.getKindName()}, (${nodeReferencingInvocable.getType().getText()})`);
1038
+ } catch (error) {
1039
+ logger.error(`> WARNING: got exception ${error}. ScopeDeclaration invalid for ${nodeReferencingInvocable.getSymbol()!.getFullyQualifiedName()}. Continuing...`);
1040
+ }
1041
+ }
1042
+
1043
+ /**
1044
+ * Builds a Famix model for the inheritances of the classes and interfaces of the source files
1045
+ * @param classes An array of classes
1046
+ * @param interfaces An array of interfaces
1047
+ */
1048
+ export function processConcretisations(classes: ClassDeclaration[], interfaces: InterfaceDeclaration[], functions: Map<number, MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction>): void {
1049
+ logger.info(`processConcretisations: Creating concretisations:`);
1050
+ classes.forEach(cls => {
1051
+ logger.debug(`processConcretisations: Checking class concretisation for ${cls.getName()}`);
1052
+ entityDictionary.createFamixConcretisationClassOrInterfaceSpecialisation(cls);
1053
+ entityDictionary.createFamixConcretisationGenericInstantiation(cls);
1054
+ entityDictionary.createFamixConcretisationInterfaceClass(cls);
1055
+ entityDictionary.createFamixConcretisationTypeInstanciation(cls);
1056
+
1057
+ });
1058
+ interfaces.forEach(inter => {
1059
+ logger.debug(`processConcretisations: Checking interface concretisation for ${inter.getName()}`);
1060
+ entityDictionary.createFamixConcretisationTypeInstanciation(inter);
1061
+ entityDictionary.createFamixConcretisationClassOrInterfaceSpecialisation(inter);
1062
+ });
1063
+ functions.forEach(func => {
1064
+ if(func instanceof FunctionDeclaration || func instanceof MethodDeclaration ){
1065
+ logger.debug(`processConcretisations: Checking Method concretisation`);
1066
+ entityDictionary.createFamixConcretisationFunctionInstantiation(func);
1067
+ }
1068
+ });
1069
+ }