ts2famix 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/analyze.js +37 -27
  2. package/dist/analyze_functions/process_functions.js +723 -0
  3. package/dist/famix_functions/EntityDictionary.js +798 -0
  4. package/dist/famix_functions/helpers_creation.js +97 -0
  5. package/dist/fqn.js +106 -95
  6. package/dist/lib/famix/src/famix_base_element.js +8 -4
  7. package/dist/lib/famix/src/famix_repository.js +32 -15
  8. package/dist/lib/famix/src/model/famix/class.js +1 -1
  9. package/dist/lib/ts-complex/cyclomatic-service.js +2 -3
  10. package/doc-uml/metamodel-full.svg +1 -0
  11. package/doc-uml/metamodel.svg +1 -0
  12. package/package.json +4 -4
  13. package/plantuml.jar +0 -0
  14. package/src/analyze.ts +46 -29
  15. package/src/analyze_functions/process_functions.ts +838 -0
  16. package/src/famix_functions/EntityDictionary.ts +915 -0
  17. package/src/famix_functions/helpers_creation.ts +77 -0
  18. package/src/fqn.ts +101 -92
  19. package/src/generate_uml.sh +3 -0
  20. package/src/lib/famix/src/famix_base_element.ts +9 -5
  21. package/src/lib/famix/src/famix_repository.ts +45 -23
  22. package/src/lib/famix/src/model/famix/class.ts +1 -1
  23. package/src/lib/ts-complex/cyclomatic-service.ts +2 -4
  24. package/src/ts2famix-cli.ts +1 -0
  25. package/dist/analyze_functions/processAccesses.js +0 -56
  26. package/dist/analyze_functions/processFiles.js +0 -554
  27. package/dist/analyze_functions/processImportClauses.js +0 -88
  28. package/dist/analyze_functions/processInheritances.js +0 -74
  29. package/dist/analyze_functions/processInvocations.js +0 -50
  30. package/dist/famix_functions/famix_functions.js +0 -523
  31. package/dist/famix_functions/famix_functions_associations.js +0 -238
  32. package/dist/famix_functions/famix_functions_index.js +0 -135
  33. package/dist/famix_functions/famix_functions_types.js +0 -115
  34. package/docs/.gitkeep +0 -0
  35. package/src/analyze_functions/processAccesses.ts +0 -58
  36. package/src/analyze_functions/processFiles.ts +0 -667
  37. package/src/analyze_functions/processImportClauses.ts +0 -95
  38. package/src/analyze_functions/processInheritances.ts +0 -85
  39. package/src/analyze_functions/processInvocations.ts +0 -52
  40. package/src/famix_functions/famix_functions.ts +0 -562
  41. package/src/famix_functions/famix_functions_associations.ts +0 -242
  42. package/src/famix_functions/famix_functions_index.ts +0 -120
  43. package/src/famix_functions/famix_functions_types.ts +0 -106
@@ -0,0 +1,838 @@
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 } from "ts-morph";
2
+ import * as Famix from "../lib/famix/src/model/famix";
3
+ import { calculate } from "../lib/ts-complex/cyclomatic-service";
4
+ import * as fs from 'fs';
5
+ import { logger , entityDictionary } from "../analyze";
6
+
7
+ export const methodsAndFunctionsWithId = new Map<number, MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression>(); // Maps the Famix method, constructor, getter, setter and function ids to their ts-morph method, constructor, getter, setter or function object
8
+ export const accessMap = new Map<number, ParameterDeclaration | VariableDeclaration | PropertyDeclaration | EnumMember>(); // Maps the Famix parameter, variable, property and enum value ids to their ts-morph parameter, variable, property or enum member object
9
+ export const classes = new Array<ClassDeclaration>(); // Array of all the classes of the source files
10
+ export const interfaces = new Array<InterfaceDeclaration>(); // Array of all the interfaces of the source files
11
+ export const modules = new Array<SourceFile>(); // Array of all the source files which are modules
12
+ export const exportedMap = new Array<ReadonlyMap<string, ExportedDeclarations[]>>(); // Array of all the exports
13
+ export let currentCC: unknown; // Stores the cyclomatic complexity metrics for the current source file
14
+
15
+ /**
16
+ * Checks if the file has any imports or exports to be considered a module
17
+ * @param sourceFile A source file
18
+ * @returns A boolean indicating if the file is a module
19
+ */
20
+ function ismodule(sourceFile: SourceFile): boolean {
21
+ return sourceFile.getImportDeclarations().length > 0 || sourceFile.getExportedDeclarations().size > 0;
22
+ }
23
+
24
+ /**
25
+ * Gets the path of a module to be imported
26
+ * @param i An import declaration
27
+ * @returns The path of the module to be imported
28
+ */
29
+ export function getModulePath(i: ImportDeclaration): string {
30
+ let path: string;
31
+ if (i.getModuleSpecifierSourceFile() === undefined) {
32
+ if (i.getModuleSpecifierValue().substring(i.getModuleSpecifierValue().length - 3) === ".ts") {
33
+ path = i.getModuleSpecifierValue();
34
+ }
35
+ else {
36
+ path = i.getModuleSpecifierValue() + ".ts";
37
+ }
38
+ }
39
+ else {
40
+ path = i.getModuleSpecifierSourceFile().getFilePath();
41
+ }
42
+ return path;
43
+ }
44
+
45
+ /**
46
+ * Gets the interfaces implemented or extended by a class or an interface
47
+ * @param interfaces An array of interfaces
48
+ * @param subClass A class or an interface
49
+ * @returns An array of InterfaceDeclaration and ExpressionWithTypeArguments containing the interfaces implemented or extended by the subClass
50
+ */
51
+ export function getImplementedOrExtendedInterfaces(interfaces: Array<InterfaceDeclaration>, subClass: ClassDeclaration | InterfaceDeclaration): Array<InterfaceDeclaration | ExpressionWithTypeArguments> {
52
+ let impOrExtInterfaces: Array<ExpressionWithTypeArguments>;
53
+ if (subClass instanceof ClassDeclaration) {
54
+ impOrExtInterfaces = subClass.getImplements();
55
+ }
56
+ else {
57
+ impOrExtInterfaces = subClass.getExtends();
58
+ }
59
+
60
+ const interfacesNames = interfaces.map(i => i.getName());
61
+ const implementedOrExtendedInterfaces = new Array<InterfaceDeclaration | ExpressionWithTypeArguments>();
62
+
63
+ impOrExtInterfaces.forEach(i => {
64
+ if (interfacesNames.includes(i.getExpression().getText())) {
65
+ implementedOrExtendedInterfaces.push(interfaces[interfacesNames.indexOf(i.getExpression().getText())]);
66
+ }
67
+ else {
68
+ implementedOrExtendedInterfaces.push(i);
69
+ }
70
+ });
71
+
72
+ return implementedOrExtendedInterfaces;
73
+ }
74
+
75
+ /**
76
+ * Builds a Famix model for an array of source files
77
+ * @param sourceFiles An array of source files
78
+ */
79
+ export function processFiles(sourceFiles: Array<SourceFile>): void {
80
+ sourceFiles.forEach(file => {
81
+ logger.info(`File: >>>>>>>>>> ${file.getFilePath()}`);
82
+
83
+ // Computes the cyclomatic complexity metrics for the current source file if it exists (i.e. if it is not from a jest test)
84
+ if (fs.existsSync(file.getFilePath()))
85
+ currentCC = calculate(file.getFilePath());
86
+ else
87
+ currentCC = 0;
88
+
89
+ processFile(file);
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Builds a Famix model for a source file
95
+ * @param f A source file
96
+ */
97
+ function processFile(f: SourceFile): void {
98
+ const isModule = ismodule(f);
99
+
100
+ if (isModule) {
101
+ modules.push(f);
102
+ exportedMap.push(f.getExportedDeclarations());
103
+ }
104
+
105
+ const fmxFile = entityDictionary.createOrGetFamixFile(f, isModule);
106
+
107
+ logger.debug(`processFile: file: ${f.getBaseName()}, fqn = ${fmxFile.getFullyQualifiedName()}`);
108
+
109
+ processComments(f, fmxFile);
110
+
111
+ processAliases(f, fmxFile);
112
+
113
+ processClasses(f, fmxFile);
114
+
115
+ processInterfaces(f, fmxFile);
116
+
117
+ processVariables(f, fmxFile);
118
+
119
+ processEnums(f, fmxFile);
120
+
121
+ processFunctions(f, fmxFile);
122
+
123
+ processNamespaces(f, fmxFile);
124
+ }
125
+
126
+ /**
127
+ * Builds a Famix model for a namespace
128
+ * @param m A namespace
129
+ * @returns A Famix.Namespace representing the namespace
130
+ */
131
+ function processNamespace(m: ModuleDeclaration): Famix.Namespace {
132
+ const fmxNamespace = entityDictionary.createOrGetFamixNamespace(m);
133
+
134
+ logger.debug(`processNamespace: namespace: ${m.getName()}, (${m.getType().getText()}), ${fmxNamespace.getFullyQualifiedName()}`);
135
+
136
+ processComments(m, fmxNamespace);
137
+
138
+ processAliases(m, fmxNamespace);
139
+
140
+ processClasses(m, fmxNamespace);
141
+
142
+ processInterfaces(m, fmxNamespace);
143
+
144
+ processVariables(m, fmxNamespace);
145
+
146
+ processEnums(m, fmxNamespace);
147
+
148
+ processFunctions(m, fmxNamespace);
149
+
150
+ processNamespaces(m, fmxNamespace);
151
+
152
+ return fmxNamespace;
153
+ }
154
+
155
+ /**
156
+ * Builds a Famix model for the aliases of a container
157
+ * @param m A container (a source file, a namespace, a function or a method)
158
+ * @param fmxScope The Famix model of the container
159
+ */
160
+ function processAliases(m: SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace | Famix.Function | Famix.Method | Famix.Accessor): void {
161
+ logger.debug(`processAliases: ---------- Finding Aliases:`);
162
+ m.getTypeAliases().forEach(a => {
163
+ const fmxAlias = processAlias(a);
164
+ fmxScope.addAlias(fmxAlias);
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Builds a Famix model for the classes of a container
170
+ * @param m A container (a source file or a namespace)
171
+ * @param fmxScope The Famix model of the container
172
+ */
173
+ function processClasses(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace): void {
174
+ logger.debug(`processClasses: ---------- Finding Classes:`);
175
+ m.getClasses().forEach(c => {
176
+ const fmxClass = processClass(c);
177
+ fmxScope.addType(fmxClass);
178
+ });
179
+ }
180
+
181
+ /**
182
+ * Builds a Famix model for the interfaces of a container
183
+ * @param m A container (a source file or a namespace)
184
+ * @param fmxScope The Famix model of the container
185
+ */
186
+ function processInterfaces(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace): void {
187
+ logger.debug(`processInterfaces: ---------- Finding Interfaces:`);
188
+ m.getInterfaces().forEach(i => {
189
+ const fmxInterface = processInterface(i);
190
+ fmxScope.addType(fmxInterface);
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Builds a Famix model for the variables of a container
196
+ * @param m A container (a source file, a namespace, a function or a method)
197
+ * @param fmxScope The Famix model of the container
198
+ */
199
+ function processVariables(m: SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace | Famix.Function | Famix.Method | Famix.Accessor): void {
200
+ logger.debug(`processVariables: ---------- Finding Variables:`);
201
+ m.getVariableStatements().forEach(v => {
202
+ const fmxVariables = processVariableStatement(v);
203
+ fmxVariables.forEach(fmxVariable => {
204
+ fmxScope.addVariable(fmxVariable);
205
+ });
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Builds a Famix model for the enums of a container
211
+ * @param m A container (a source file, a namespace, a function or a method)
212
+ * @param fmxScope The Famix model of the container
213
+ */
214
+ function processEnums(m: SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace | Famix.Function | Famix.Method | Famix.Accessor): void {
215
+ logger.debug(`processEnums: ---------- Finding Enums:`);
216
+ m.getEnums().forEach(e => {
217
+ const fmxEnum = processEnum(e);
218
+ fmxScope.addType(fmxEnum);
219
+ });
220
+ }
221
+
222
+ /**
223
+ * Builds a Famix model for the functions of a container
224
+ * @param m A container (a source file, a namespace, a function or a method)
225
+ * @param fmxScope The Famix model of the container
226
+ */
227
+ function processFunctions(m: SourceFile | ModuleDeclaration | FunctionDeclaration | FunctionExpression | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace | Famix.Function | Famix.Method | Famix.Accessor): void {
228
+ logger.debug(`Finding Functions:`);
229
+ m.getFunctions().forEach(f => {
230
+ const fmxFunction = processFunction(f);
231
+ fmxScope.addFunction(fmxFunction);
232
+ });
233
+ }
234
+
235
+ /**
236
+ * Builds a Famix model for the namespaces of a container
237
+ * @param m A container (a source file or a namespace)
238
+ * @param fmxScope The Famix model of the container
239
+ */
240
+ function processNamespaces(m: SourceFile | ModuleDeclaration, fmxScope: Famix.ScriptEntity | Famix.Module | Famix.Namespace): void {
241
+ logger.debug(`Finding Namespaces:`);
242
+ m.getModules().forEach(md => {
243
+ const fmxNsp = processNamespace(md);
244
+ fmxScope.addNamespace(fmxNsp);
245
+ });
246
+ }
247
+
248
+ /**
249
+ * Builds a Famix model for an alias
250
+ * @param a An alias
251
+ * @returns A Famix.Alias representing the alias
252
+ */
253
+ function processAlias(a: TypeAliasDeclaration): Famix.Alias {
254
+ const fmxAlias = entityDictionary.createFamixAlias(a);
255
+
256
+ logger.debug(`Alias: ${a.getName()}, (${a.getType().getText()}), fqn = ${fmxAlias.getFullyQualifiedName()}`);
257
+
258
+ processComments(a, fmxAlias);
259
+
260
+ return fmxAlias;
261
+ }
262
+
263
+ /**
264
+ * Builds a Famix model for a class
265
+ * @param c A class
266
+ * @returns A Famix.Class or a Famix.ParameterizableClass representing the class
267
+ */
268
+ function processClass(c: ClassDeclaration): Famix.Class | Famix.ParameterizableClass {
269
+ classes.push(c);
270
+
271
+ const fmxClass = entityDictionary.createOrGetFamixClass(c);
272
+
273
+ logger.debug(`Class: ${c.getName()}, (${c.getType().getText()}), fqn = ${fmxClass.getFullyQualifiedName()}`);
274
+
275
+ processComments(c, fmxClass);
276
+
277
+ processDecorators(c, fmxClass);
278
+
279
+ processStructuredType(c, fmxClass);
280
+
281
+ c.getConstructors().forEach(con => {
282
+ const fmxCon = processMethod(con);
283
+ fmxClass.addMethod(fmxCon);
284
+ });
285
+
286
+ c.getGetAccessors().forEach(acc => {
287
+ const fmxAcc = processMethod(acc);
288
+ fmxClass.addMethod(fmxAcc);
289
+ });
290
+
291
+ c.getSetAccessors().forEach(acc => {
292
+ const fmxAcc = processMethod(acc);
293
+ fmxClass.addMethod(fmxAcc);
294
+ });
295
+
296
+ return fmxClass;
297
+ }
298
+
299
+ /**
300
+ * Builds a Famix model for an interface
301
+ * @param i An interface
302
+ * @returns A Famix.Interface or a Famix.ParameterizableInterface representing the interface
303
+ */
304
+ function processInterface(i: InterfaceDeclaration): Famix.Interface | Famix.ParameterizableInterface {
305
+ interfaces.push(i);
306
+
307
+ const fmxInterface = entityDictionary.createOrGetFamixInterface(i);
308
+
309
+ logger.debug(`Interface: ${i.getName()}, (${i.getType().getText()}), fqn = ${fmxInterface.getFullyQualifiedName()}`);
310
+
311
+ processComments(i, fmxInterface);
312
+
313
+ processStructuredType(i, fmxInterface);
314
+
315
+ return fmxInterface;
316
+ }
317
+
318
+ /**
319
+ * Builds a Famix model for the type parameters, properties and methods of a structured type
320
+ * @param c A structured type (a class or an interface)
321
+ * @param fmxScope The Famix model of the structured type
322
+ */
323
+ function processStructuredType(c: ClassDeclaration | InterfaceDeclaration, fmxScope: Famix.Class | Famix.ParameterizableClass | Famix.Interface | Famix.ParameterizableInterface): void {
324
+ logger.debug(`Finding Properties and Methods:`);
325
+ if (fmxScope instanceof Famix.ParameterizableClass || fmxScope instanceof Famix.ParameterizableInterface) {
326
+ processTypeParameters(c, fmxScope);
327
+ }
328
+
329
+ c.getProperties().forEach(prop => {
330
+ const fmxProperty = processProperty(prop);
331
+ fmxScope.addProperty(fmxProperty);
332
+ });
333
+
334
+ c.getMethods().forEach(m => {
335
+ const fmxMethod = processMethod(m);
336
+ fmxScope.addMethod(fmxMethod);
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Builds a Famix model for a property
342
+ * @param p A property
343
+ * @returns A Famix.Property representing the property
344
+ */
345
+ function processProperty(p: PropertyDeclaration | PropertySignature): Famix.Property {
346
+ const fmxProperty = entityDictionary.createFamixProperty(p);
347
+
348
+ logger.debug(`property: ${p.getName()}, (${p.getType().getText()}), fqn = ${fmxProperty.getFullyQualifiedName()}`);
349
+ logger.debug(` ---> It's a Property${(p instanceof PropertySignature) ? "Signature" : "Declaration"}!`);
350
+ const ancestor = p.getFirstAncestorOrThrow();
351
+ logger.debug(` ---> Its first ancestor is a ${ancestor.getKindName()}`);
352
+
353
+ if (!(p instanceof PropertySignature)) {
354
+ processDecorators(p, fmxProperty);
355
+ // only add access if the p's first ancestor is not a PropertyDeclaration
356
+ if (ancestor.getKindName() !== "PropertyDeclaration") {
357
+ logger.debug(`adding access: ${p.getName()}, (${p.getType().getText()}) Famix ${fmxProperty.getName()}`);
358
+ accessMap.set(fmxProperty.id, p);
359
+ }
360
+ }
361
+
362
+ processComments(p, fmxProperty);
363
+
364
+ return fmxProperty;
365
+ }
366
+
367
+ /**
368
+ * Builds a Famix model for a method or an accessor
369
+ * @param m A method or an accessor
370
+ * @returns A Famix.Method or a Famix.Accessor representing the method or the accessor
371
+ */
372
+ function processMethod(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration): Famix.Method | Famix.Accessor {
373
+ const fmxMethod = entityDictionary.createFamixMethod(m, currentCC);
374
+
375
+ logger.debug(`Method: ${!(m instanceof ConstructorDeclaration) ? m.getName() : "constructor"}, (${m.getType().getText()}), parent: ${(m.getParent() as ClassDeclaration | InterfaceDeclaration).getName()}, fqn = ${fmxMethod.getFullyQualifiedName()}`);
376
+
377
+ processComments(m, fmxMethod);
378
+
379
+ processTypeParameters(m, fmxMethod);
380
+
381
+ processParameters(m, fmxMethod);
382
+
383
+ if (!(m instanceof MethodSignature)) {
384
+ processAliases(m, fmxMethod);
385
+
386
+ processVariables(m, fmxMethod);
387
+
388
+ processEnums(m, fmxMethod);
389
+
390
+ processFunctions(m, fmxMethod);
391
+
392
+ processFunctionExpressions(m, fmxMethod);
393
+
394
+ methodsAndFunctionsWithId.set(fmxMethod.id, m);
395
+ }
396
+
397
+ if (m instanceof MethodDeclaration || m instanceof GetAccessorDeclaration || m instanceof SetAccessorDeclaration) {
398
+ processDecorators(m, fmxMethod);
399
+ }
400
+
401
+ return fmxMethod;
402
+ }
403
+
404
+ /**
405
+ * Builds a Famix model for a function
406
+ * @param f A function
407
+ * @returns A Famix.Function representing the function
408
+ */
409
+ function processFunction(f: FunctionDeclaration | FunctionExpression): Famix.Function {
410
+ const fmxFunction = entityDictionary.createFamixFunction(f, currentCC);
411
+
412
+ logger.debug(`Function: ${(f.getName()) ? f.getName() : "anonymous"}, (${f.getType().getText()}), fqn = ${fmxFunction.getFullyQualifiedName()}`);
413
+
414
+ processComments(f, fmxFunction);
415
+
416
+ processAliases(f, fmxFunction);
417
+
418
+ processTypeParameters(f, fmxFunction);
419
+
420
+ processParameters(f, fmxFunction);
421
+
422
+ processVariables(f, fmxFunction);
423
+
424
+ processEnums(f, fmxFunction);
425
+
426
+ processFunctions(f, fmxFunction);
427
+
428
+ if (f instanceof FunctionDeclaration && !(f.getParent() instanceof Block)) {
429
+ processFunctionExpressions(f, fmxFunction);
430
+ }
431
+
432
+ methodsAndFunctionsWithId.set(fmxFunction.id, f);
433
+
434
+ return fmxFunction;
435
+ }
436
+
437
+ /**
438
+ * Builds a Famix model for the function expressions of a function or a method
439
+ * @param f A function or a method
440
+ * @param fmxScope The Famix model of the function or the method
441
+ */
442
+ function processFunctionExpressions(f: FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration, fmxScope: Famix.Function | Famix.Method | Famix.Accessor): void {
443
+ logger.debug(`Finding Function Expressions:`);
444
+ const functionExpressions = f.getDescendantsOfKind(SyntaxKind.FunctionExpression);
445
+ functionExpressions.forEach((func) => {
446
+ const fmxFunc = processFunction(func);
447
+ fmxScope.addFunction(fmxFunc);
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Builds a Famix model for the parameters of a method or a function
453
+ * @param m A method or a function
454
+ * @param fmxScope The Famix model of the method or the function
455
+ */
456
+ function processParameters(m: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression, fmxScope: Famix.Method | Famix.Accessor | Famix.Function): void {
457
+ logger.debug(`Finding Parameters:`);
458
+ m.getParameters().forEach(param => {
459
+ const fmxParam = processParameter(param);
460
+ fmxScope.addParameter(fmxParam);
461
+ });
462
+ }
463
+
464
+ /**
465
+ * Builds a Famix model for a parameter
466
+ * @param p A parameter
467
+ * @returns A Famix.Parameter representing the parameter
468
+ */
469
+ function processParameter(p: ParameterDeclaration): Famix.Parameter {
470
+ const fmxParam = entityDictionary.createFamixParameter(p);
471
+
472
+ logger.debug(`parameter: ${p.getName()}, (${p.getType().getText()}), fqn = ${fmxParam.getFullyQualifiedName()}`);
473
+
474
+ processComments(p, fmxParam);
475
+
476
+ processDecorators(p, fmxParam);
477
+
478
+ const parent = p.getParent();
479
+
480
+ if (!(parent instanceof MethodSignature)) {
481
+ logger.debug(`adding access: ${p.getName()}, (${p.getType().getText()}) Famix ${fmxParam.getName()}`);
482
+ accessMap.set(fmxParam.id, p);
483
+ }
484
+
485
+ return fmxParam;
486
+ }
487
+
488
+ /**
489
+ * Builds a Famix model for the type parameters of a class, an interface, a method or a function
490
+ * @param e A class, an interface, a method or a function
491
+ * @param fmxScope The Famix model of the class, the interface, the method or the function
492
+ */
493
+ function processTypeParameters(e: ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression, fmxScope: Famix.ParameterizableClass | Famix.ParameterizableInterface | Famix.Method | Famix.Accessor | Famix.Function): void {
494
+ logger.debug(`Finding Type Parameters:`);
495
+ e.getTypeParameters().forEach(tp => {
496
+ const fmxParam = processTypeParameter(tp);
497
+ fmxScope.addParameterType(fmxParam);
498
+ });
499
+ }
500
+
501
+ /**
502
+ * Builds a Famix model for a type parameter
503
+ * @param tp A type parameter
504
+ * @returns A Famix.TypeParameter representing the type parameter
505
+ */
506
+ function processTypeParameter(tp: TypeParameterDeclaration): Famix.ParameterType {
507
+ const fmxTypeParameter = entityDictionary.createFamixParameterType(tp);
508
+
509
+ logger.debug(`type parameter: ${tp.getName()}, (${tp.getType().getText()}), fqn = ${fmxTypeParameter.getFullyQualifiedName()}`);
510
+
511
+ processComments(tp, fmxTypeParameter);
512
+
513
+ return fmxTypeParameter;
514
+ }
515
+
516
+ /**
517
+ * Builds a Famix model for the variables of a variable statement
518
+ * @param v A variable statement
519
+ * @returns An array of Famix.Variable representing the variables
520
+ */
521
+ function processVariableStatement(v: VariableStatement): Array<Famix.Variable> {
522
+ const fmxVariables = new Array<Famix.Variable>();
523
+
524
+ logger.debug(`Variable statement: ${v.getText()}, (${v.getType().getText()}), ${v.getDeclarationKindKeywords()[0]}, fqn = ${v.getDeclarations()[0].getName()}`);
525
+
526
+ v.getDeclarations().forEach(variable => {
527
+ const fmxVar = processVariable(variable);
528
+ processComments(v, fmxVar);
529
+ fmxVariables.push(fmxVar);
530
+ });
531
+
532
+ return fmxVariables;
533
+ }
534
+
535
+ /**
536
+ * Builds a Famix model for a variable
537
+ * @param v A variable
538
+ * @returns A Famix.Variable representing the variable
539
+ */
540
+ function processVariable(v: VariableDeclaration): Famix.Variable {
541
+ const fmxVar = entityDictionary.createFamixVariable(v);
542
+
543
+ logger.debug(`variable: ${v.getName()}, (${v.getType().getText()}), ${v.getInitializer() ? "initializer: " + v.getInitializer().getText() : "initializer: "}, fqn = ${fmxVar.getFullyQualifiedName()}`);
544
+
545
+ processComments(v, fmxVar);
546
+
547
+ logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxVar.getName()}`);
548
+ accessMap.set(fmxVar.id, v);
549
+
550
+ return fmxVar;
551
+ }
552
+
553
+ /**
554
+ * Builds a Famix model for an enum
555
+ * @param e An enum
556
+ * @returns A Famix.Enum representing the enum
557
+ */
558
+ function processEnum(e: EnumDeclaration): Famix.Enum {
559
+ const fmxEnum = entityDictionary.createFamixEnum(e);
560
+
561
+ logger.debug(`enum: ${e.getName()}, (${e.getType().getText()}), fqn = ${fmxEnum.getFullyQualifiedName()}`);
562
+
563
+ processComments(e, fmxEnum);
564
+
565
+ e.getMembers().forEach(m => {
566
+ const fmxEnumValue = processEnumValue(m);
567
+ fmxEnum.addValue(fmxEnumValue);
568
+ });
569
+
570
+ return fmxEnum;
571
+ }
572
+
573
+ /**
574
+ * Builds a Famix model for an enum member
575
+ * @param v An enum member
576
+ * @returns A Famix.EnumValue representing the enum member
577
+ */
578
+ function processEnumValue(v: EnumMember): Famix.EnumValue {
579
+ const fmxEnumValue = entityDictionary.createFamixEnumValue(v);
580
+
581
+ logger.debug(`enum value: ${v.getName()}, (${v.getType().getText()}), fqn = ${fmxEnumValue.getFullyQualifiedName()}`);
582
+
583
+ processComments(v, fmxEnumValue);
584
+
585
+ logger.debug(`adding access: ${v.getName()}, (${v.getType().getText()}) Famix ${fmxEnumValue.getName()}`);
586
+ accessMap.set(fmxEnumValue.id, v);
587
+
588
+ return fmxEnumValue;
589
+ }
590
+
591
+ /**
592
+ * Builds a Famix model for the decorators of a class, a method, a parameter or a property
593
+ * @param e A class, a method, a parameter or a property
594
+ * @param fmxScope The Famix model of the class, the method, the parameter or the property
595
+ */
596
+ function processDecorators(e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration, fmxScope: Famix.Class | Famix.ParameterizableClass | Famix.Method | Famix.Accessor | Famix.Parameter | Famix.Property): void {
597
+ logger.debug(`Finding Decorators:`);
598
+ e.getDecorators().forEach(dec => {
599
+ const fmxDec = processDecorator(dec, e);
600
+ fmxScope.addDecorator(fmxDec);
601
+ });
602
+ }
603
+
604
+ /**
605
+ * Builds a Famix model for a decorator
606
+ * @param d A decorator
607
+ * @param e A class, a method, a parameter or a property
608
+ * @returns A Famix.Decorator representing the decorator
609
+ */
610
+ function processDecorator(d: Decorator, e: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration): Famix.Decorator {
611
+ const fmxDec = entityDictionary.createOrGetFamixDecorator(d, e);
612
+
613
+ logger.debug(`decorator: ${d.getName()}, (${d.getType().getText()}), fqn = ${fmxDec.getFullyQualifiedName()}`);
614
+
615
+ processComments(d, fmxDec);
616
+
617
+ return fmxDec;
618
+ }
619
+
620
+ /**
621
+ * Builds a Famix model for the comments
622
+ * @param e A ts-morph element
623
+ * @param fmxScope The Famix model of the named entity
624
+ */
625
+ function processComments(e: SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | Decorator | EnumDeclaration | EnumMember | TypeParameterDeclaration | VariableStatement | TypeAliasDeclaration, fmxScope: Famix.NamedEntity): void {
626
+ logger.debug(`Process comments:`);
627
+ e.getLeadingCommentRanges().forEach(c => {
628
+ const fmxComment = processComment(c, fmxScope);
629
+ logger.debug(`leading comments, addComment: '${c.getText()}'`);
630
+ fmxScope.addComment(fmxComment); // redundant, but just in case
631
+ });
632
+ e.getTrailingCommentRanges().forEach(c => {
633
+ const fmxComment = processComment(c, fmxScope);
634
+ logger.debug(`trailing comments, addComment: '${c.getText()}'`);
635
+ fmxScope.addComment(fmxComment);
636
+ });
637
+ }
638
+
639
+ /**
640
+ * Builds a Famix model for a comment
641
+ * @param c A comment
642
+ * @param fmxScope The Famix model of the comment's container
643
+ * @returns A Famix.Comment representing the comment
644
+ */
645
+ function processComment(c: CommentRange, fmxScope: Famix.NamedEntity): Famix.Comment {
646
+ const isJSDoc = c.getText().startsWith("/**");
647
+ logger.debug(`processComment: comment: ${c.getText()}, isJSDoc = ${isJSDoc}`);
648
+ const fmxComment = entityDictionary.createFamixComment(c, fmxScope, isJSDoc);
649
+
650
+ return fmxComment;
651
+ }
652
+
653
+ /**
654
+ * Builds a Famix model for the accesses on the parameters, variables, properties and enum members of the source files
655
+ * @param accessMap A map of parameters, variables, properties and enum members with their id
656
+ */
657
+ export function processAccesses(accessMap: Map<number, ParameterDeclaration | VariableDeclaration | PropertyDeclaration | EnumMember>): void {
658
+ logger.debug(`processAccesses: Creating accesses:`);
659
+ accessMap.forEach((v, id) => {
660
+ logger.debug(`processAccesses: Accesses to ${v.getName()}`);
661
+ try {
662
+ const temp_nodes = v.findReferencesAsNodes() as Array<Identifier>;
663
+ temp_nodes.forEach(node => processNodeForAccesses(node, id));
664
+ } catch (error) {
665
+ logger.error(`> WARNING: got exception ${error}. Continuing...`);
666
+ }
667
+ });
668
+ }
669
+
670
+ /**
671
+ * Builds a Famix model for an access on a parameter, variable, property or enum member
672
+ * @param n A node
673
+ * @param id An id of a parameter, a variable, a property or an enum member
674
+ */
675
+ function processNodeForAccesses(n: Identifier, id: number): void {
676
+ try {
677
+ // sometimes node's first ancestor is a PropertyDeclaration, which is not an access
678
+ // see https://github.com/fuhrmanator/FamixTypeScriptImporter/issues/9
679
+ // check for a node whose first ancestor is a property declaration and bail?
680
+ // This may be a bug in ts-morph?
681
+ if (n.getFirstAncestorOrThrow().getKindName() === "PropertyDeclaration") {
682
+ logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})'s first ancestor is a PropertyDeclaration. Skipping...`);
683
+ return;
684
+ }
685
+ entityDictionary.createFamixAccess(n, id);
686
+ logger.debug(`processNodeForAccesses: node kind: ${n.getKindName()}, ${n.getText()}, (${n.getType().getText()})`);
687
+ } catch (error) {
688
+ logger.error(`> WARNING: got exception ${error}. ScopeDeclaration invalid for ${n.getSymbol().getFullyQualifiedName()}. Continuing...`);
689
+ }
690
+ }
691
+
692
+ export function processImportClausesForImportEqualsDeclarations(sourceFiles: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
693
+ logger.info(`Creating import clauses from ImportEqualsDeclarations in source files:`);
694
+ sourceFiles.forEach(sourceFile => {
695
+ sourceFile.forEachDescendant(node => {
696
+ if (Node.isImportEqualsDeclaration(node)) {
697
+ // You've found an ImportEqualsDeclaration
698
+ logger.info("Declaration Name:", node.getName());
699
+ logger.info("Module Reference Text:", node.getModuleReference().getText());
700
+ // create a famix import clause
701
+ const namedImport = node.getNameNode();
702
+ entityDictionary.createFamixImportClause({importDeclaration: node,
703
+ importer: sourceFile,
704
+ moduleSpecifierFilePath: node.getModuleReference().getText(),
705
+ importElement: namedImport,
706
+ isInExports: exports.find(e => e.has(namedImport.getText())) !== undefined,
707
+ isDefaultExport: false});
708
+ }
709
+ });
710
+ }
711
+ );
712
+
713
+ }
714
+
715
+ /**
716
+ * Builds a Famix model for the import clauses of the source files which are modules
717
+ * @param modules An array of modules
718
+ * @param exports An array of maps of exported declarations
719
+ */
720
+ export function processImportClausesForModules(modules: Array<SourceFile>, exports: Array<ReadonlyMap<string, ExportedDeclarations[]>>): void {
721
+ logger.info(`Creating import clauses from ${modules.length} modules:`);
722
+ modules.forEach(module => {
723
+ module.getImportDeclarations().forEach(impDecl => {
724
+ logger.debug(`Importing ${impDecl.getModuleSpecifierValue()}`);
725
+ const path = getModulePath(impDecl);
726
+
727
+ impDecl.getNamedImports().forEach(namedImport => {
728
+ logger.debug(`Importing (named) ${namedImport.getName()} from ${impDecl.getModuleSpecifierValue()}`);
729
+ const importedEntityName = namedImport.getName();
730
+ let importFoundInExports = false;
731
+ exports.forEach(e => {
732
+ if (e.has(importedEntityName)) {
733
+ importFoundInExports = true;
734
+ }
735
+ });
736
+ entityDictionary.createFamixImportClause({importDeclaration: impDecl,
737
+ importer: module,
738
+ moduleSpecifierFilePath: path,
739
+ importElement: namedImport,
740
+ isInExports: importFoundInExports,
741
+ isDefaultExport: false});
742
+ });
743
+
744
+ const defaultImport = impDecl.getDefaultImport();
745
+ if (defaultImport !== undefined) {
746
+ logger.debug(`Importing (default) ${defaultImport.getText()} from ${impDecl.getModuleSpecifierValue()}`);
747
+ // call with module, impDecl.getModuleSpecifierValue(), path, defaultImport, false, true
748
+ entityDictionary.createFamixImportClause({importDeclaration: impDecl,
749
+ importer: module,
750
+ moduleSpecifierFilePath: path,
751
+ importElement: defaultImport,
752
+ isInExports: false,
753
+ isDefaultExport: true});
754
+ }
755
+
756
+ const namespaceImport = impDecl.getNamespaceImport();
757
+ if (namespaceImport !== undefined) {
758
+ logger.debug(`Importing (namespace) ${namespaceImport.getText()} from ${impDecl.getModuleSpecifierValue()}`);
759
+ entityDictionary.createFamixImportClause({importDeclaration: impDecl,
760
+ importer: module,
761
+ moduleSpecifierFilePath: path,
762
+ importElement: namespaceImport,
763
+ isInExports: false,
764
+ isDefaultExport: false});
765
+ // entityDictionary.createFamixImportClause(module, impDecl.getModuleSpecifierValue(), path, namespaceImport, false, false);
766
+ }
767
+ });
768
+ });
769
+ }
770
+
771
+ /**
772
+ * Builds a Famix model for the inheritances of the classes and interfaces of the source files
773
+ * @param classes An array of classes
774
+ * @param interfaces An array of interfaces
775
+ */
776
+ export function processInheritances(classes: ClassDeclaration[], interfaces: InterfaceDeclaration[]): void {
777
+ logger.info(`processInheritances: Creating inheritances:`);
778
+ classes.forEach(cls => {
779
+ logger.debug(`processInheritances: Checking class inheritance for ${cls.getName()}`);
780
+ const extClass = cls.getBaseClass();
781
+ if (extClass !== undefined) {
782
+ entityDictionary.createFamixInheritance(cls, extClass);
783
+
784
+ logger.debug(`processInheritances: class: ${cls.getName()}, (${cls.getType().getText()}), extClass: ${extClass.getName()}, (${extClass.getType().getText()})`);
785
+ }
786
+
787
+ logger.debug(`processInheritances: Checking interface inheritance for ${cls.getName()}`);
788
+ const implementedInterfaces = getImplementedOrExtendedInterfaces(interfaces, cls);
789
+ implementedInterfaces.forEach(impInter => {
790
+ entityDictionary.createFamixInheritance(cls, impInter);
791
+
792
+ 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()})`);
793
+ });
794
+ });
795
+
796
+ interfaces.forEach(inter => {
797
+ logger.debug(`processInheritances: Checking interface inheritance for ${inter.getName()}`);
798
+ const extendedInterfaces = getImplementedOrExtendedInterfaces(interfaces, inter);
799
+ extendedInterfaces.forEach(extInter => {
800
+ entityDictionary.createFamixInheritance(inter, extInter);
801
+
802
+ 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()})`);
803
+ });
804
+ });
805
+ }
806
+
807
+ /**
808
+ * Builds a Famix model for the invocations of the methods and functions of the source files
809
+ * @param methodsAndFunctionsWithId A map of methods and functions with their id
810
+ */
811
+ export function processInvocations(methodsAndFunctionsWithId: Map<number, MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression>): void {
812
+ logger.info(`Creating invocations:`);
813
+ methodsAndFunctionsWithId.forEach((m, id) => {
814
+ 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'))}`);
815
+ try {
816
+ const temp_nodes = m.findReferencesAsNodes() as Array<Identifier>;
817
+ temp_nodes.forEach(node => processNodeForInvocations(node, m, id));
818
+ } catch (error) {
819
+ logger.error(`> WARNING: got exception ${error}. Continuing...`);
820
+ }
821
+ });
822
+ }
823
+
824
+ /**
825
+ * Builds a Famix model for an invocation of a method or a function
826
+ * @param n A node
827
+ * @param m A method or a function
828
+ * @param id The id of the method or the function
829
+ */
830
+ function processNodeForInvocations(n: Identifier, m: MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression, id: number): void {
831
+ try {
832
+ entityDictionary.createFamixInvocation(n, m, id);
833
+
834
+ logger.debug(`node: node, (${n.getType().getText()})`);
835
+ } catch (error) {
836
+ logger.error(`> WARNING: got exception ${error}. ScopeDeclaration invalid for ${n.getSymbol().getFullyQualifiedName()}. Continuing...`);
837
+ }
838
+ }