ts2famix 2.1.0-beta.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/.eslintrc.json +24 -24
  2. package/LICENSE +24 -24
  3. package/README.md +78 -78
  4. package/dist/analyze.js +3 -3
  5. package/dist/analyze_functions/process_functions.js +92 -62
  6. package/dist/famix_functions/EntityDictionary.js +672 -597
  7. package/dist/famix_functions/helpers_creation.js +18 -12
  8. package/dist/fqn.js +351 -18
  9. package/dist/lib/famix/famix_JSON_exporter.js +1 -1
  10. package/dist/lib/famix/famix_base_element.js +1 -1
  11. package/dist/lib/famix/famix_repository.js +14 -5
  12. package/dist/lib/famix/index.js +1 -1
  13. package/dist/lib/famix/model/famix/access.js +1 -1
  14. package/dist/lib/famix/model/famix/accessor.js +1 -1
  15. package/dist/lib/famix/model/famix/alias.js +1 -1
  16. package/dist/lib/famix/model/famix/arrow_function.js +1 -1
  17. package/dist/lib/famix/model/famix/behavioral_entity.js +1 -1
  18. package/dist/lib/famix/model/famix/class.js +1 -1
  19. package/dist/lib/famix/model/famix/comment.js +1 -1
  20. package/dist/lib/famix/model/famix/concretisation.js +1 -1
  21. package/dist/lib/famix/model/famix/container_entity.js +1 -1
  22. package/dist/lib/famix/model/famix/decorator.js +1 -1
  23. package/dist/lib/famix/model/famix/entity.js +1 -1
  24. package/dist/lib/famix/model/famix/enum.js +1 -1
  25. package/dist/lib/famix/model/famix/enum_value.js +1 -1
  26. package/dist/lib/famix/model/famix/function.js +1 -1
  27. package/dist/lib/famix/model/famix/import_clause.js +1 -1
  28. package/dist/lib/famix/model/famix/index.js +1 -1
  29. package/dist/lib/famix/model/famix/indexed_file_anchor.js +1 -1
  30. package/dist/lib/famix/model/famix/inheritance.js +1 -1
  31. package/dist/lib/famix/model/famix/interface.js +1 -1
  32. package/dist/lib/famix/model/famix/invocation.js +1 -1
  33. package/dist/lib/famix/model/famix/method.js +1 -1
  34. package/dist/lib/famix/model/famix/module.js +2 -2
  35. package/dist/lib/famix/model/famix/named_entity.js +1 -1
  36. package/dist/lib/famix/model/famix/parameter.js +1 -1
  37. package/dist/lib/famix/model/famix/parameter_concretisation.js +1 -1
  38. package/dist/lib/famix/model/famix/parameter_type.js +1 -1
  39. package/dist/lib/famix/model/famix/parametric_arrow_function.js +1 -1
  40. package/dist/lib/famix/model/famix/parametric_class.js +1 -1
  41. package/dist/lib/famix/model/famix/parametric_function.js +1 -1
  42. package/dist/lib/famix/model/famix/parametric_interface.js +1 -1
  43. package/dist/lib/famix/model/famix/parametric_method.js +1 -1
  44. package/dist/lib/famix/model/famix/primitive_type.js +1 -1
  45. package/dist/lib/famix/model/famix/property.js +1 -1
  46. package/dist/lib/famix/model/famix/reference.js +1 -1
  47. package/dist/lib/famix/model/famix/scoping_entity.js +1 -1
  48. package/dist/lib/famix/model/famix/script_entity.js +1 -1
  49. package/dist/lib/famix/model/famix/source_anchor.js +1 -1
  50. package/dist/lib/famix/model/famix/source_language.js +1 -1
  51. package/dist/lib/famix/model/famix/sourced_entity.js +1 -1
  52. package/dist/lib/famix/model/famix/structural_entity.js +1 -1
  53. package/dist/lib/famix/model/famix/type.js +1 -1
  54. package/dist/lib/famix/model/famix/variable.js +1 -1
  55. package/dist/lib/ts-complex/cyclomatic-service.js +1 -1
  56. package/dist/refactorer/refactor-getter-setter.js +1 -1
  57. package/dist/ts2famix-cli-wrapper.js +1 -1
  58. package/dist/ts2famix-cli.js +1 -1
  59. package/doc-uml/famix-typescript-model.puml +619 -607
  60. package/doc-uml/famix-typescript-model.svg +1 -1
  61. package/eslint.config.mjs +28 -28
  62. package/jest.config.json +1 -1
  63. package/package.json +70 -70
  64. package/src/analyze.ts +120 -120
  65. package/src/analyze_functions/process_functions.ts +1069 -1040
  66. package/src/famix_functions/EntityDictionary.ts +2061 -2016
  67. package/src/famix_functions/helpers_creation.ts +143 -135
  68. package/src/fqn.ts +416 -50
  69. package/src/generate_uml.sh +20 -20
  70. package/src/lib/famix/License.md +22 -22
  71. package/src/lib/famix/famix_JSON_exporter.ts +56 -56
  72. package/src/lib/famix/famix_base_element.ts +22 -22
  73. package/src/lib/famix/famix_repository.ts +288 -278
  74. package/src/lib/famix/index.ts +8 -8
  75. package/src/lib/famix/model/famix/access.ts +50 -50
  76. package/src/lib/famix/model/famix/accessor.ts +15 -15
  77. package/src/lib/famix/model/famix/alias.ts +39 -39
  78. package/src/lib/famix/model/famix/arrow_function.ts +15 -15
  79. package/src/lib/famix/model/famix/behavioral_entity.ts +97 -97
  80. package/src/lib/famix/model/famix/class.ts +85 -85
  81. package/src/lib/famix/model/famix/comment.ts +47 -47
  82. package/src/lib/famix/model/famix/concretisation.ts +40 -40
  83. package/src/lib/famix/model/famix/container_entity.ts +160 -160
  84. package/src/lib/famix/model/famix/decorator.ts +37 -37
  85. package/src/lib/famix/model/famix/entity.ts +15 -15
  86. package/src/lib/famix/model/famix/enum.ts +30 -30
  87. package/src/lib/famix/model/famix/enum_value.ts +28 -28
  88. package/src/lib/famix/model/famix/function.ts +15 -15
  89. package/src/lib/famix/model/famix/import_clause.ts +51 -51
  90. package/src/lib/famix/model/famix/index.ts +41 -41
  91. package/src/lib/famix/model/famix/indexed_file_anchor.ts +46 -46
  92. package/src/lib/famix/model/famix/inheritance.ts +40 -40
  93. package/src/lib/famix/model/famix/interface.ts +75 -75
  94. package/src/lib/famix/model/famix/invocation.ts +65 -65
  95. package/src/lib/famix/model/famix/method.ts +89 -89
  96. package/src/lib/famix/model/famix/module.ts +71 -71
  97. package/src/lib/famix/model/famix/named_entity.ts +95 -95
  98. package/src/lib/famix/model/famix/parameter.ts +28 -28
  99. package/src/lib/famix/model/famix/parameter_concretisation.ts +51 -51
  100. package/src/lib/famix/model/famix/parameter_type.ts +58 -58
  101. package/src/lib/famix/model/famix/parametric_arrow_function.ts +32 -32
  102. package/src/lib/famix/model/famix/parametric_class.ts +49 -49
  103. package/src/lib/famix/model/famix/parametric_function.ts +32 -32
  104. package/src/lib/famix/model/famix/parametric_interface.ts +49 -49
  105. package/src/lib/famix/model/famix/parametric_method.ts +32 -32
  106. package/src/lib/famix/model/famix/primitive_type.ts +15 -15
  107. package/src/lib/famix/model/famix/property.ts +94 -94
  108. package/src/lib/famix/model/famix/reference.ts +40 -40
  109. package/src/lib/famix/model/famix/scoping_entity.ts +35 -35
  110. package/src/lib/famix/model/famix/script_entity.ts +34 -34
  111. package/src/lib/famix/model/famix/source_anchor.ts +30 -30
  112. package/src/lib/famix/model/famix/source_language.ts +35 -35
  113. package/src/lib/famix/model/famix/sourced_entity.ts +70 -70
  114. package/src/lib/famix/model/famix/structural_entity.ts +43 -43
  115. package/src/lib/famix/model/famix/type.ts +87 -87
  116. package/src/lib/famix/model/famix/variable.ts +27 -27
  117. package/src/lib/famix/package.json +28 -28
  118. package/src/lib/ts-complex/cyclomatic-service.ts +83 -83
  119. package/src/refactorer/refactor-getter-setter.ts +140 -140
  120. package/src/ts2famix-cli-wrapper.ts +21 -21
  121. package/src/ts2famix-cli.ts +60 -60
  122. package/tsconfig.check-tests.json +14 -14
  123. package/tsconfig.json +73 -72
  124. package/.vscode/settings.json +0 -4
  125. package/TODO +0 -1
  126. package/arwea-fix.json +0 -1
  127. package/bogus.ts +0 -3
  128. package/class-diagram.puml +0 -792
  129. package/debug.txt +0 -13332
  130. package/debuglog.txt +0 -12073
  131. package/dist/famix2puml.js +0 -126
  132. package/dist/getClasses-arrow-body.js +0 -43
  133. package/dist/lib/famix/src/famix_JSON_exporter.js +0 -55
  134. package/dist/lib/famix/src/famix_base_element.js +0 -18
  135. package/dist/lib/famix/src/famix_repository.js +0 -224
  136. package/dist/lib/famix/src/index.js +0 -31
  137. package/dist/lib/famix/src/model/famix/access.js +0 -40
  138. package/dist/lib/famix/src/model/famix/accessor.js +0 -17
  139. package/dist/lib/famix/src/model/famix/alias.js +0 -33
  140. package/dist/lib/famix/src/model/famix/arrowFunction.js +0 -17
  141. package/dist/lib/famix/src/model/famix/arrow_function.js +0 -17
  142. package/dist/lib/famix/src/model/famix/behavioral_entity.js +0 -79
  143. package/dist/lib/famix/src/model/famix/class.js +0 -71
  144. package/dist/lib/famix/src/model/famix/comment.js +0 -39
  145. package/dist/lib/famix/src/model/famix/concretisation.js +0 -31
  146. package/dist/lib/famix/src/model/famix/container_entity.js +0 -126
  147. package/dist/lib/famix/src/model/famix/decorator.js +0 -32
  148. package/dist/lib/famix/src/model/famix/entity.js +0 -17
  149. package/dist/lib/famix/src/model/famix/enum.js +0 -31
  150. package/dist/lib/famix/src/model/famix/enum_value.js +0 -25
  151. package/dist/lib/famix/src/model/famix/function.js +0 -17
  152. package/dist/lib/famix/src/model/famix/implicit_variable.js +0 -17
  153. package/dist/lib/famix/src/model/famix/import_clause.js +0 -41
  154. package/dist/lib/famix/src/model/famix/index.js +0 -86
  155. package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +0 -38
  156. package/dist/lib/famix/src/model/famix/inheritance.js +0 -33
  157. package/dist/lib/famix/src/model/famix/interface.js +0 -64
  158. package/dist/lib/famix/src/model/famix/invocation.js +0 -54
  159. package/dist/lib/famix/src/model/famix/method.js +0 -67
  160. package/dist/lib/famix/src/model/famix/module.js +0 -60
  161. package/dist/lib/famix/src/model/famix/named_entity.js +0 -78
  162. package/dist/lib/famix/src/model/famix/parameter.js +0 -25
  163. package/dist/lib/famix/src/model/famix/parameterConcretisation.js +0 -44
  164. package/dist/lib/famix/src/model/famix/parameter_concretisation.js +0 -44
  165. package/dist/lib/famix/src/model/famix/parameter_type.js +0 -45
  166. package/dist/lib/famix/src/model/famix/parametricArrowFunction.js +0 -29
  167. package/dist/lib/famix/src/model/famix/parametric_arrow_function.js +0 -31
  168. package/dist/lib/famix/src/model/famix/parametric_class.js +0 -44
  169. package/dist/lib/famix/src/model/famix/parametric_function.js +0 -31
  170. package/dist/lib/famix/src/model/famix/parametric_interface.js +0 -44
  171. package/dist/lib/famix/src/model/famix/parametric_method.js +0 -31
  172. package/dist/lib/famix/src/model/famix/primitive_type.js +0 -17
  173. package/dist/lib/famix/src/model/famix/property.js +0 -73
  174. package/dist/lib/famix/src/model/famix/reference.js +0 -33
  175. package/dist/lib/famix/src/model/famix/scoping_entity.js +0 -36
  176. package/dist/lib/famix/src/model/famix/script_entity.js +0 -29
  177. package/dist/lib/famix/src/model/famix/source_anchor.js +0 -27
  178. package/dist/lib/famix/src/model/famix/source_language.js +0 -35
  179. package/dist/lib/famix/src/model/famix/sourced_entity.js +0 -60
  180. package/dist/lib/famix/src/model/famix/structural_entity.js +0 -39
  181. package/dist/lib/famix/src/model/famix/text_anchor.js +0 -38
  182. package/dist/lib/famix/src/model/famix/type.js +0 -73
  183. package/dist/lib/famix/src/model/famix/variable.js +0 -24
  184. package/fqn-model.json +0 -1
  185. package/iterateGenericTypes.ts +0 -69
  186. package/out/class-diagram/class-diagram.svg +0 -1
  187. package/sample.json +0 -1
  188. package/sample.ts +0 -1
  189. package/stats.txt +0 -3091
  190. package/tabby-debug-output.txt +0 -19433
  191. package/ts2famix.log +0 -22656
  192. package/validate-references.js +0 -103
package/src/fqn.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ArrowFunction, CallExpression, ClassDeclaration, ConstructorDeclaration, Decorator, EnumDeclaration, FunctionDeclaration, FunctionExpression, GetAccessorDeclaration, Identifier, ImportDeclaration, ImportEqualsDeclaration, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, Node, PropertyDeclaration, SetAccessorDeclaration, SourceFile, SyntaxKind, TypeParameterDeclaration, VariableDeclaration } from "ts-morph";
1
+ import { ArrowFunction, CallExpression, ClassDeclaration, ConstructorDeclaration, Decorator, EnumDeclaration, ExpressionWithTypeArguments, FunctionDeclaration, FunctionExpression, GetAccessorDeclaration, Identifier, ImportDeclaration, ImportEqualsDeclaration, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, Node, PropertyDeclaration, SetAccessorDeclaration, SourceFile, SyntaxKind, TypeParameterDeclaration, VariableDeclaration } from "ts-morph";
2
2
  import { entityDictionary, logger } from "./analyze";
3
3
  import path from "path";
4
4
  import { TSMorphTypeDeclaration } from "./famix_functions/EntityDictionary";
@@ -10,26 +10,253 @@ function isFQNNode(node: Node): node is FQNNode {
10
10
  return Node.isVariableDeclaration(node) || Node.isArrowFunction(node) || Node.isIdentifier(node) || Node.isMethodDeclaration(node) || Node.isClassDeclaration(node) || Node.isClassExpression(node) || Node.isDecorator(node) || Node.isModuleDeclaration(node) || Node.isCallExpression(node);
11
11
  }
12
12
 
13
- export function getFQN(node: FQNNode | Node): string {
14
- const absolutePathProject = entityDictionary.famixRep.getAbsolutePath();
13
+ /**
14
+ * Builds a map of method positions to their property keys in object literals.
15
+ * Scans all variable declarations in a source file, targeting object literals with any keys
16
+ * (e.g., `3: { method() {} }` or `add: { compute() {} }`), and maps each method's start position to its key.
17
+ * Logs each step for debugging.
18
+ *
19
+ * @param sourceFile The TypeScript source file to analyze
20
+ * @returns A Map where keys are method start positions and values are their property keys (e.g., "3", "add")
21
+ */
22
+ function buildStageMethodMap(sourceFile: SourceFile): Map<number, string> {
23
+ const stageMap = new Map<number, string>();
24
+
25
+ sourceFile.getVariableDeclarations().forEach(varDecl => {
26
+ // const varName = varDecl.getName();
27
+ const initializer = varDecl.getInitializer();
28
+
29
+ if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
30
+ return;
31
+ }
32
+
33
+ initializer.getProperties().forEach(prop => {
34
+ let key: string | undefined;
35
+
36
+ if (Node.isPropertyAssignment(prop)) {
37
+ const nameNode = prop.getNameNode();
38
+
39
+ if (Node.isIdentifier(nameNode)) {
40
+ key = nameNode.getText();
41
+ } else if (Node.isStringLiteral(nameNode)) {
42
+ key = nameNode.getText().replace(/^"(.+)"$/, '$1').replace(/^'(.+)'$/, '$1');
43
+ } else if (Node.isNumericLiteral(nameNode)) {
44
+ key = nameNode.getText();
45
+ } else if (Node.isComputedPropertyName(nameNode)) {
46
+ const expression = nameNode.getExpression();
47
+ if (Node.isIdentifier(expression)) {
48
+ // Resolve variable value if possible
49
+ const symbol = expression.getSymbol();
50
+ if (symbol) {
51
+ const decl = symbol.getDeclarations()[0];
52
+ if (Node.isVariableDeclaration(decl) && decl.getInitializer()) {
53
+ const init = decl.getInitializer()!;
54
+ if (Node.isStringLiteral(init) || Node.isNumericLiteral(init)) {
55
+ key = init.getText().replace(/^"(.+)"$/, '$1').replace(/^'(.+)'$/, '$1');
56
+ }
57
+ }
58
+ }
59
+ if (!key) {
60
+ key = expression.getText();
61
+ }
62
+ } else if (Node.isBinaryExpression(expression) && expression.getOperatorToken().getText() === '+') {
63
+ // Handle simple string concatenation (e.g., "A" + "B")
64
+ const left = expression.getLeft();
65
+ const right = expression.getRight();
66
+ if (Node.isStringLiteral(left) && Node.isStringLiteral(right)) {
67
+ key = left.getLiteralText() + right.getLiteralText();
68
+ }
69
+ } else if (Node.isTemplateExpression(expression)) {
70
+ // Handle template literals (e.g., `key-${1}`)
71
+ const head = expression.getHead().getLiteralText();
72
+ const spans = expression.getTemplateSpans();
73
+ if (spans.length === 1 && Node.isNumericLiteral(spans[0].getExpression())) {
74
+ const num = spans[0].getExpression().getText();
75
+ key = `${head}${num}`;
76
+ }
77
+ }
78
+ if (!key) {
79
+ key = expression.getText(); // Fallback
80
+ }
81
+ } else {
82
+ return;
83
+ }
84
+
85
+ const propInitializer = prop.getInitializer();
86
+ if (propInitializer && Node.isObjectLiteralExpression(propInitializer)) {
87
+ propInitializer.getDescendantsOfKind(SyntaxKind.MethodDeclaration).forEach(method => {
88
+ // const methodName = method.getName();
89
+ const pos = method.getStart();
90
+ if (key) {
91
+ stageMap.set(pos, key);
92
+ }
93
+ });
94
+ }
95
+ }
96
+ });
97
+ });
98
+
99
+ return stageMap;
100
+ }
101
+
102
+ /**
103
+ * Builds a map of method positions to their index in class/interface/namespace declarations
104
+ * @param sourceFile The TypeScript source file to analyze
105
+ * @returns A Map where keys are method start positions and values are their positional index (1-based)
106
+ */
107
+ function buildMethodPositionMap(sourceFile: SourceFile): Map<number, number> {
108
+ const positionMap = new Map<number, number>();
109
+ // console.log(`[buildMethodPositionMap] Starting analysis for file: ${sourceFile.getFilePath()}`);
110
+
111
+ // Helper function to process modules recursively
112
+ function processModule(moduleNode: ModuleDeclaration, modulePath: string) {
113
+ // console.log(`[buildMethodPositionMap] Processing module: ${modulePath}`);
114
+
115
+ // Handle functions directly in the module
116
+ const functions = moduleNode.getFunctions();
117
+ const functionCounts = new Map<string, number>();
118
+
119
+ functions.forEach(func => {
120
+ const funcName = func.getName() || `Unnamed_Function(${func.getStart()})`;
121
+ const count = (functionCounts.get(funcName) || 0) + 1;
122
+ functionCounts.set(funcName, count);
123
+ positionMap.set(func.getStart(), count);
124
+ // console.log(`[buildMethodPositionMap] Module function: ${funcName}, position: ${func.getStart()}, index: ${count}`);
125
+ });
126
+
127
+ // Handle classes within the module
128
+ const classes = moduleNode.getClasses();
129
+ classes.forEach(classNode => {
130
+ // console.log(`[buildMethodPositionMap] Processing class in module: ${classNode.getName() || 'Unnamed'}`);
131
+ const methods = classNode.getMethods();
132
+ const methodCounts = new Map<string, number>();
133
+
134
+ methods.forEach(method => {
135
+ const methodName = method.getName();
136
+ const count = (methodCounts.get(methodName) || 0) + 1;
137
+ methodCounts.set(methodName, count);
138
+ positionMap.set(method.getStart(), count);
139
+ // console.log(`[buildMethodPositionMap] Module class method: ${methodName}, position: ${method.getStart()}, index: ${count}`);
140
+ });
141
+ });
142
+
143
+ // Handle interfaces within the module
144
+ const interfaces = moduleNode.getInterfaces();
145
+ interfaces.forEach(interfaceNode => {
146
+ // console.log(`[buildMethodPositionMap] Processing interface in module: ${interfaceNode.getName() || 'Unnamed'}`);
147
+ const methods = interfaceNode.getMethods();
148
+ const methodCounts = new Map<string, number>();
149
+
150
+ methods.forEach(method => {
151
+ const methodName = method.getName();
152
+ const count = (methodCounts.get(methodName) || 0) + 1;
153
+ methodCounts.set(methodName, count);
154
+ positionMap.set(method.getStart(), count);
155
+ // console.log(`[buildMethodPositionMap] Module interface method: ${methodName}, position: ${method.getStart()}, index: ${count}`);
156
+ });
157
+ });
15
158
 
159
+ // Recursively process nested modules
160
+ const nestedModules = moduleNode.getModules();
161
+ nestedModules.forEach(nestedModule => {
162
+ if (Node.isModuleDeclaration(nestedModule)) {
163
+ const nestedModuleName = nestedModule.getName();
164
+ const newModulePath = `${modulePath}.${nestedModuleName}`;
165
+ processModule(nestedModule, newModulePath);
166
+ }
167
+ });
168
+
169
+ }
170
+
171
+ function trackArrowFunctions(container: Node) {
172
+ const arrows = container.getDescendantsOfKind(SyntaxKind.ArrowFunction);
173
+ arrows.forEach(arrow => {
174
+ const parent = arrow.getParent();
175
+ if (Node.isBlock(parent) || Node.isSourceFile(parent)) {
176
+ // Use negative numbers for arrow functions to distinguish from methods
177
+ positionMap.set(arrow.getStart(), -1 * (positionMap.size + 1));
178
+ // console.log(`[buildMethodPositionMap] Arrow function at ${arrow.getStart()}`);
179
+ }
180
+ });
181
+ }
182
+
183
+ // Handle top-level classes
184
+ sourceFile.getClasses().forEach(classNode => {
185
+ // console.log(`[buildMethodPositionMap] Processing class: ${classNode.getName() || 'Unnamed'}`);
186
+ const methods = classNode.getMethods();
187
+ const methodCounts = new Map<string, number>();
188
+
189
+ methods.forEach(method => {
190
+ const methodName = method.getName();
191
+ const count = (methodCounts.get(methodName) || 0) + 1;
192
+ methodCounts.set(methodName, count);
193
+ positionMap.set(method.getStart(), count);
194
+ // console.log(`[buildMethodPositionMap] Class method: ${methodName}, position: ${method.getStart()}, index: ${count}`);
195
+ });
196
+
197
+ methods.forEach(method => trackArrowFunctions(method));
198
+ });
199
+
200
+ // Handle top-level interfaces
201
+ sourceFile.getInterfaces().forEach(interfaceNode => {
202
+ // console.log(`[buildMethodPositionMap] Processing interface: ${interfaceNode.getName() || 'Unnamed'}`);
203
+ const methods = interfaceNode.getMethods();
204
+ const methodCounts = new Map<string, number>();
205
+
206
+ methods.forEach(method => {
207
+ const methodName = method.getName();
208
+ const count = (methodCounts.get(methodName) || 0) + 1;
209
+ methodCounts.set(methodName, count);
210
+ positionMap.set(method.getStart(), count);
211
+ // console.log(`[buildMethodPositionMap] Interface method: ${methodName}, position: ${method.getStart()}, index: ${count}`);
212
+ });
213
+ methods.forEach(method => trackArrowFunctions(method));
214
+
215
+ });
216
+
217
+ // Handle top-level namespaces/modules
218
+ sourceFile.getModules().forEach(moduleNode => {
219
+ if (Node.isModuleDeclaration(moduleNode)) {
220
+ const moduleName = moduleNode.getName();
221
+ processModule(moduleNode, moduleName);
222
+ }
223
+ });
224
+
225
+
226
+ // console.log(`[buildMethodPositionMap] Final positionMap:`, Array.from(positionMap.entries()));
227
+ return positionMap;
228
+ }
229
+
230
+ /**
231
+ * Generates a fully qualified name (FQN) for a given AST node.
232
+ * Constructs an FQN by traversing the node's ancestry, adding names and keys
233
+ * (numeric or string from object literals ...) as needed, prefixed with the file's relative path.
234
+ *
235
+ * @param node The AST node to generate an FQN for
236
+ * @returns A string representing the node's FQN (e.g., "{path}.operations.add.compute[MethodDeclaration]")
237
+ */
238
+ export function getFQN(node: FQNNode | Node): string {
16
239
  const sourceFile = node.getSourceFile();
240
+ const absolutePathProject = entityDictionary.famixRep.getAbsolutePath();
17
241
  const parts: string[] = [];
18
242
  let currentNode: Node | undefined = node;
19
243
 
244
+ const stageMap = buildStageMethodMap(sourceFile);
245
+ const methodPositionMap = buildMethodPositionMap(sourceFile);
246
+
20
247
  while (currentNode && !Node.isSourceFile(currentNode)) {
21
248
  const { line, column } = sourceFile.getLineAndColumnAtPos(currentNode.getStart());
22
249
  const lc = `${line}:${column}`;
23
- if (Node.isClassDeclaration(currentNode) ||
24
- Node.isClassExpression(currentNode) ||
250
+
251
+ if (Node.isClassDeclaration(currentNode) ||
252
+ Node.isClassExpression(currentNode) ||
25
253
  Node.isInterfaceDeclaration(currentNode) ||
26
- Node.isFunctionDeclaration(currentNode) ||
27
- Node.isMethodDeclaration(currentNode) ||
28
- Node.isModuleDeclaration(currentNode) ||
29
- Node.isVariableDeclaration(currentNode) ||
254
+ Node.isFunctionDeclaration(currentNode) ||
255
+ Node.isMethodDeclaration(currentNode) ||
256
+ Node.isModuleDeclaration(currentNode) ||
257
+ Node.isVariableDeclaration(currentNode) ||
30
258
  Node.isGetAccessorDeclaration(currentNode) ||
31
259
  Node.isSetAccessorDeclaration(currentNode) ||
32
- Node.isTypeParameterDeclaration(currentNode) ||
33
260
  Node.isPropertyDeclaration(currentNode) ||
34
261
  Node.isParameterDeclaration(currentNode) ||
35
262
  Node.isDecorator(currentNode) ||
@@ -41,44 +268,117 @@ export function getFQN(node: FQNNode | Node): string {
41
268
  Node.isArrayLiteralExpression(currentNode) ||
42
269
  Node.isImportSpecifier(currentNode) ||
43
270
  Node.isIdentifier(currentNode)) {
44
- const name = Node.isIdentifier(currentNode) ? currentNode.getText()
45
- : getNameOfNode(currentNode) /* currentNode.getName() */ || 'Unnamed_' + currentNode.getKindName() + `(${lc})`;
271
+ let name: string;
272
+ if (Node.isImportSpecifier(currentNode)) {
273
+ const alias = currentNode.getAliasNode()?.getText();
274
+ if (alias) {
275
+ let importDecl: Node | undefined = currentNode;
276
+ while (importDecl && !Node.isImportDeclaration(importDecl)) {
277
+ importDecl = importDecl.getParent();
278
+ }
279
+ const moduleSpecifier = importDecl && Node.isImportDeclaration(importDecl)
280
+ ? importDecl.getModuleSpecifier().getLiteralText()
281
+ : "unknown";
282
+ name = currentNode.getName();
283
+ name = `${name} as ${alias}[ImportSpecifier<${moduleSpecifier}>]`;
284
+ } else {
285
+ name = currentNode.getName();
286
+ }
287
+ } else {
288
+ // if constructor, use "constructor" as name
289
+ if (Node.isConstructorDeclaration(currentNode)) {
290
+ name = "constructor";
291
+ } else {
292
+ name = Node.isIdentifier(currentNode) ? currentNode.getText()
293
+ : 'getName' in currentNode && typeof currentNode['getName'] === 'function'
294
+ ? ((currentNode as { getName(): string }).getName() +
295
+ ((Node.isClassDeclaration(currentNode) ||
296
+ Node.isInterfaceDeclaration(currentNode) ||
297
+ Node.isMethodDeclaration(currentNode) ||
298
+ Node.isFunctionDeclaration(currentNode)) &&
299
+ 'getTypeParameters' in currentNode &&
300
+ currentNode.getTypeParameters().length > 0
301
+ ? getParameters(currentNode)
302
+ : ''))
303
+ : `Unnamed_${currentNode.getKindName()}(${lc})`;
304
+ }
305
+ }
306
+
307
+ if (Node.isMethodSignature(currentNode)) {
308
+ const method = currentNode as MethodSignature;
309
+ const params = method.getParameters().map(p => {
310
+ const typeText = p.getType().getText().replace(/\s+/g, "");
311
+ return typeText || "any"; // Fallback for untyped parameters
312
+ });
313
+ const returnType = method.getReturnType().getText().replace(/\s+/g, "") || "void";
314
+ name = `${name}(${params.join(",")}):${returnType}`;
315
+ }
316
+
317
+ parts.unshift(name);
318
+
319
+ // Apply positional index for MethodDeclaration, MethodSignature, and FunctionDeclaration
320
+ if (Node.isMethodDeclaration(currentNode) ||
321
+ Node.isMethodSignature(currentNode) ||
322
+ Node.isFunctionDeclaration(currentNode)) {
323
+ const key = stageMap.get(currentNode.getStart());
324
+ if (key) {
325
+ parts.unshift(key);
326
+ // console.log(`[getFQN] Applied stageMap key: ${key} for ${currentNode.getKindName()} at position ${currentNode.getStart()}`);
327
+ } else {
328
+ const positionIndex = methodPositionMap.get(currentNode.getStart());
329
+ if (positionIndex && positionIndex > 1) {
330
+ parts.unshift(positionIndex.toString());
331
+ // console.log(`[getFQN] Applied positionIndex: ${positionIndex} for ${currentNode.getKindName()} at position ${currentNode.getStart()}`);
332
+ } else {
333
+ console.log(`[getFQN] No positionIndex applied for ${currentNode.getKindName()} at position ${currentNode.getStart()}, positionIndex: ${positionIndex || 'none'}`);
334
+ }
335
+ }
336
+ }
337
+ }
338
+ else if (Node.isArrowFunction(currentNode) ||
339
+ Node.isBlock(currentNode) ||
340
+ Node.isForInStatement(currentNode) ||
341
+ Node.isForOfStatement(currentNode) ||
342
+ Node.isForStatement(currentNode) ||
343
+ Node.isCatchClause(currentNode)) {
344
+ const name = `${currentNode.getKindName()}(${lc})`;
46
345
  parts.unshift(name);
47
346
  }
48
- // unnamed nodes
49
- else if (Node.isArrowFunction(currentNode) ||
50
- Node.isBlock(currentNode) ||
51
- Node.isForInStatement(currentNode) ||
52
- Node.isForOfStatement(currentNode) ||
53
- Node.isForStatement(currentNode) ||
54
- Node.isCatchClause(currentNode)) {
55
- parts.unshift(`${currentNode.getKindName()}(${lc})`);
56
- } else if (Node.isConstructorDeclaration(currentNode)) {
57
- parts.unshift(`constructor`);
347
+ else if (Node.isTypeParameterDeclaration(currentNode)) {
348
+ const arrowParent = currentNode.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
349
+ if (arrowParent) {
350
+ const arrowIndex = Math.abs(methodPositionMap.get(arrowParent.getStart()) || 0);
351
+ if (arrowIndex > 0) {
352
+ parts.unshift(arrowIndex.toString());
353
+ }
354
+ }
355
+ parts.unshift(currentNode.getName());
356
+ // Removed continue to allow ancestor processing
357
+ }
358
+ else if (Node.isConstructorDeclaration(currentNode)) {
359
+ const name = "constructor";
360
+ parts.unshift(name);
58
361
  } else {
59
- // For other kinds, you might want to handle them specifically or ignore
60
- logger.debug(`Ignoring node kind: ${currentNode.getKindName()}`);
362
+ console.log(`[getFQN] Ignoring node kind: ${currentNode.getKindName()}`);
61
363
  }
364
+
62
365
  currentNode = currentNode.getParent();
63
366
  }
64
367
 
65
-
66
-
67
- // Prepend the relative path of the source file
68
368
  let relativePath = entityDictionary.convertToRelativePath(
69
- path.normalize(sourceFile.getFilePath()),
70
- absolutePathProject).replace(/\\/sg, "/");
71
-
72
- if (relativePath.includes("..")) {
73
- logger.error(`Relative path contains ../: ${relativePath}`);
74
- }
369
+ path.normalize(sourceFile.getFilePath()),
370
+ absolutePathProject
371
+ ).replace(/\\/g, "/");
372
+
373
+ // if (relativePath.includes("..")) {
374
+ // }
75
375
  if (relativePath.startsWith("/")) {
76
- relativePath = relativePath.substring(1);
376
+ relativePath = relativePath.slice(1);
77
377
  }
78
378
  parts.unshift(`{${relativePath}}`);
79
- const fqn = parts.join(".") + `[${node.getKindName()}]`; // disambiguate
80
379
 
81
- logger.debug(`Generated FQN: ${fqn} for node: ${node.getKindName()}`);
380
+ const fqn = parts.join(".") + `[${node.getKindName()}]`;
381
+ // console.log(`[getFQN] Final FQN: ${fqn}`);
82
382
  return fqn;
83
383
  }
84
384
 
@@ -121,14 +421,14 @@ export function getNameOfNode(a: Node): string {
121
421
  let cKind: ClassDeclaration | undefined;
122
422
  let iKind: InterfaceDeclaration | undefined;
123
423
  let mKind: MethodDeclaration | undefined;
124
- let fKind: FunctionDeclaration | undefined;
424
+ let fKind: FunctionDeclaration | undefined;
125
425
  let alias: TSMorphTypeDeclaration | undefined;
126
426
  switch (a.getKind()) {
127
427
  case SyntaxKind.SourceFile:
128
428
  return a.asKind(SyntaxKind.SourceFile)!.getBaseName();
129
429
 
130
430
  case SyntaxKind.ModuleDeclaration:
131
- return a.asKind(SyntaxKind.ModuleDeclaration)!.getName();
431
+ return a.asKind(SyntaxKind.ModuleDeclaration)!.getName();
132
432
 
133
433
  case SyntaxKind.ClassDeclaration:
134
434
  cKind = a.asKind(SyntaxKind.ClassDeclaration);
@@ -145,13 +445,13 @@ export function getNameOfNode(a: Node): string {
145
445
  } else {
146
446
  return iKind?.getName() || "";
147
447
  }
148
-
448
+
149
449
  case SyntaxKind.PropertyDeclaration:
150
- return a.asKind(SyntaxKind.PropertyDeclaration)!.getName();
450
+ return a.asKind(SyntaxKind.PropertyDeclaration)!.getName();
151
451
 
152
452
  case SyntaxKind.PropertySignature:
153
- return a.asKind(SyntaxKind.PropertySignature)!.getName();
154
-
453
+ return a.asKind(SyntaxKind.PropertySignature)!.getName();
454
+
155
455
  case SyntaxKind.MethodDeclaration:
156
456
  mKind = a.asKind(SyntaxKind.MethodDeclaration);
157
457
  if (mKind && mKind.getTypeParameters().length > 0) {
@@ -161,14 +461,14 @@ export function getNameOfNode(a: Node): string {
161
461
  }
162
462
 
163
463
  case SyntaxKind.MethodSignature:
164
- return a.asKind(SyntaxKind.MethodSignature)!.getName();
464
+ return a.asKind(SyntaxKind.MethodSignature)!.getName();
165
465
 
166
466
  case SyntaxKind.GetAccessor:
167
467
  return a.asKind(SyntaxKind.GetAccessor)!.getName();
168
468
 
169
469
  case SyntaxKind.SetAccessor:
170
470
  return a.asKind(SyntaxKind.SetAccessor)!.getName();
171
-
471
+
172
472
  case SyntaxKind.FunctionDeclaration:
173
473
  fKind = a.asKind(SyntaxKind.FunctionDeclaration);
174
474
  if (fKind && fKind.getTypeParameters().length > 0) {
@@ -179,15 +479,15 @@ export function getNameOfNode(a: Node): string {
179
479
 
180
480
  case SyntaxKind.FunctionExpression:
181
481
  return a.asKind(SyntaxKind.FunctionExpression)?.getName() || "anonymous";
182
-
482
+
183
483
  case SyntaxKind.Parameter:
184
484
  return a.asKind(SyntaxKind.Parameter)!.getName();
185
-
485
+
186
486
  case SyntaxKind.VariableDeclaration:
187
487
  return a.asKind(SyntaxKind.VariableDeclaration)!.getName();
188
488
 
189
489
  case SyntaxKind.Decorator:
190
- return "@" + a.asKind(SyntaxKind.Decorator)!.getName();
490
+ return "@" + a.asKind(SyntaxKind.Decorator)!.getName();
191
491
 
192
492
  case SyntaxKind.TypeParameter:
193
493
  return a.asKind(SyntaxKind.TypeParameter)!.getName();
@@ -207,13 +507,13 @@ export function getNameOfNode(a: Node): string {
207
507
  return a.asKind(SyntaxKind.TypeAliasDeclaration)!.getName();
208
508
 
209
509
  case SyntaxKind.Constructor:
210
- return "constructor";
211
-
510
+ return "constructor";
511
+
212
512
  default:
213
513
  // throw new Error(`getNameOfNode called on a node that doesn't have a name: ${a.getKindName()}`);
214
514
  // ancestor hasn't got a useful name
215
515
  return "";
216
- }
516
+ }
217
517
  }
218
518
 
219
519
  /**
@@ -241,3 +541,69 @@ export function getParameters(a: Node): string {
241
541
  }
242
542
  return paramString;
243
543
  }
544
+
545
+ /**
546
+ * Gets the FQN of an unresolved interface that is being implemented or extended
547
+ * @param unresolvedInheritedClassOrInterface The expression with type arguments representing the interface
548
+ * @returns The FQN of the unresolved interface
549
+ */
550
+ export function getFQNUnresolvedInheritedClassOrInterface(unresolvedInheritedClassOrInterface: ExpressionWithTypeArguments): string {
551
+ // Check for either ClassDeclaration or InterfaceDeclaration ancestor
552
+ const classAncestor = unresolvedInheritedClassOrInterface.getFirstAncestorByKind(SyntaxKind.ClassDeclaration);
553
+ const interfaceAncestor = unresolvedInheritedClassOrInterface.getFirstAncestorByKind(SyntaxKind.InterfaceDeclaration);
554
+
555
+ // Validate the context
556
+ if (!classAncestor && !interfaceAncestor) {
557
+ throw new Error("getFQNUnresolvedClassOrInterface called on a node that is not in an implements or extends context");
558
+ }
559
+
560
+ // Check if it's a valid implements/extends context
561
+ let isValidContext = false;
562
+
563
+ let classExtendsClass = false;
564
+
565
+ if (classAncestor) {
566
+ // check if the class is extending or implementing an interface
567
+ const extendsClause = classAncestor.getExtends();
568
+ const implementsClause = classAncestor.getImplements();
569
+ isValidContext = (extendsClause !== undefined) || (implementsClause && implementsClause.length > 0);
570
+ classExtendsClass = extendsClause !== undefined;
571
+ } else if (interfaceAncestor) {
572
+ // Check extends clause for interfaces
573
+ const extendsClause = interfaceAncestor.getExtends();
574
+ isValidContext = extendsClause && extendsClause.length > 0;
575
+ }
576
+
577
+ if (!isValidContext) {
578
+ throw new Error("getFQNUnresolvedInterface called on a node that is not in a valid implements or extends context");
579
+ }
580
+
581
+ // get the name of the interface
582
+ const name = unresolvedInheritedClassOrInterface.getExpression().getText();
583
+
584
+ // Find where it's imported - search the entire source file
585
+ const sourceFile = unresolvedInheritedClassOrInterface.getSourceFile();
586
+ const importDecls = sourceFile.getImportDeclarations();
587
+
588
+ for (const importDecl of importDecls) {
589
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
590
+ const importClause = importDecl.getImportClause();
591
+
592
+ if (importClause) {
593
+ const namedImports = importClause.getNamedImports();
594
+ // declarationName is ClassDeclaration if "class extends class"
595
+ const declarationName = classExtendsClass ? "ClassDeclaration" : "InterfaceDeclaration";
596
+
597
+ for (const namedImport of namedImports) {
598
+ if (namedImport.getName() === name) {
599
+ logger.debug(`Found import for ${name} in ${moduleSpecifier}`);
600
+ return `{module:${moduleSpecifier}}.${name}[${declarationName}]`;
601
+ }
602
+ }
603
+ }
604
+ }
605
+
606
+ // If not found, return a default FQN format
607
+ return `{unknown-module}.${name}[InterfaceDeclaration]`;
608
+ }
609
+
@@ -1,20 +1,20 @@
1
- #!/usr/bin/env bash
2
- # Gets latest plantuml
3
- rm -f plantuml.jar
4
- wget --no-verbose https://downloads.sourceforge.net/project/plantuml/plantuml.jar
5
- # echo the version
6
- echo "PlantUML version: $(java -jar plantuml.jar -version)"
7
- echo "tplant version: $(npx tplant --version)"
8
- # Builds metamodel plantuml from TypeScript sources
9
- npx tplant -i src/lib/famix/**/*.ts -o doc-metamodel/famix-typescript-model.puml
10
- sed -i 's/@startuml/& metamodel/' doc-metamodel/famix-typescript-model.puml
11
- # Converts plantuml source to SVG image
12
- java -jar plantuml.jar -v -tsvg doc-metamodel/famix-typescript-model.puml
13
- mv doc-metamodel/metamodel.svg doc-metamodel/metamodel-full.svg
14
- # Inserts include line to customize the file
15
- sed -i '/@startuml metamodel/a !include skins.include.puml' doc-metamodel/famix-typescript-model.puml
16
- # Converts plantuml source to SVG image
17
- java -jar plantuml.jar -v -tsvg doc-metamodel/famix-typescript-model.puml
18
- # Moves artifacts
19
- rm -f doc-uml/*
20
- mv doc-metamodel/*.svg doc-uml
1
+ #!/usr/bin/env bash
2
+ # Gets latest plantuml
3
+ rm -f plantuml.jar
4
+ wget --no-verbose https://downloads.sourceforge.net/project/plantuml/plantuml.jar
5
+ # echo the version
6
+ echo "PlantUML version: $(java -jar plantuml.jar -version)"
7
+ echo "tplant version: $(npx tplant --version)"
8
+ # Builds metamodel plantuml from TypeScript sources
9
+ npx tplant -i src/lib/famix/**/*.ts -o doc-metamodel/famix-typescript-model.puml
10
+ sed -i 's/@startuml/& metamodel/' doc-metamodel/famix-typescript-model.puml
11
+ # Converts plantuml source to SVG image
12
+ java -jar plantuml.jar -v -tsvg doc-metamodel/famix-typescript-model.puml
13
+ mv doc-metamodel/metamodel.svg doc-metamodel/metamodel-full.svg
14
+ # Inserts include line to customize the file
15
+ sed -i '/@startuml metamodel/a !include skins.include.puml' doc-metamodel/famix-typescript-model.puml
16
+ # Converts plantuml source to SVG image
17
+ java -jar plantuml.jar -v -tsvg doc-metamodel/famix-typescript-model.puml
18
+ # Moves artifacts
19
+ rm -f doc-uml/*
20
+ mv doc-metamodel/*.svg doc-uml
@@ -1,23 +1,23 @@
1
- Portions of the code in the famix directory:
2
-
3
- MIT License
4
-
5
- Copyright (c) 2019 Pascal Erni
6
-
7
- Permission is hereby granted, free of charge, to any person obtaining a copy
8
- of this software and associated documentation files (the "Software"), to deal
9
- in the Software without restriction, including without limitation the rights
10
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- copies of the Software, and to permit persons to whom the Software is
12
- furnished to do so, subject to the following conditions:
13
-
14
- The above copyright notice and this permission notice shall be included in all
15
- copies or substantial portions of the Software.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ Portions of the code in the famix directory:
2
+
3
+ MIT License
4
+
5
+ Copyright (c) 2019 Pascal Erni
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
  SOFTWARE.