ts2famix 2.0.3 → 2.1.0-beta.2

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