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.
- package/dist/analyze.js +37 -27
- package/dist/analyze_functions/process_functions.js +723 -0
- package/dist/famix_functions/EntityDictionary.js +798 -0
- package/dist/famix_functions/helpers_creation.js +97 -0
- package/dist/fqn.js +106 -95
- package/dist/lib/famix/src/famix_base_element.js +8 -4
- package/dist/lib/famix/src/famix_repository.js +32 -15
- package/dist/lib/famix/src/model/famix/class.js +1 -1
- package/dist/lib/ts-complex/cyclomatic-service.js +2 -3
- package/doc-uml/metamodel-full.svg +1 -0
- package/doc-uml/metamodel.svg +1 -0
- package/package.json +4 -4
- package/plantuml.jar +0 -0
- package/src/analyze.ts +46 -29
- package/src/analyze_functions/process_functions.ts +838 -0
- package/src/famix_functions/EntityDictionary.ts +915 -0
- package/src/famix_functions/helpers_creation.ts +77 -0
- package/src/fqn.ts +101 -92
- package/src/generate_uml.sh +3 -0
- package/src/lib/famix/src/famix_base_element.ts +9 -5
- package/src/lib/famix/src/famix_repository.ts +45 -23
- package/src/lib/famix/src/model/famix/class.ts +1 -1
- package/src/lib/ts-complex/cyclomatic-service.ts +2 -4
- package/src/ts2famix-cli.ts +1 -0
- package/dist/analyze_functions/processAccesses.js +0 -56
- package/dist/analyze_functions/processFiles.js +0 -554
- package/dist/analyze_functions/processImportClauses.js +0 -88
- package/dist/analyze_functions/processInheritances.js +0 -74
- package/dist/analyze_functions/processInvocations.js +0 -50
- package/dist/famix_functions/famix_functions.js +0 -523
- package/dist/famix_functions/famix_functions_associations.js +0 -238
- package/dist/famix_functions/famix_functions_index.js +0 -135
- package/dist/famix_functions/famix_functions_types.js +0 -115
- package/docs/.gitkeep +0 -0
- package/src/analyze_functions/processAccesses.ts +0 -58
- package/src/analyze_functions/processFiles.ts +0 -667
- package/src/analyze_functions/processImportClauses.ts +0 -95
- package/src/analyze_functions/processInheritances.ts +0 -85
- package/src/analyze_functions/processInvocations.ts +0 -52
- package/src/famix_functions/famix_functions.ts +0 -562
- package/src/famix_functions/famix_functions_associations.ts +0 -242
- package/src/famix_functions/famix_functions_index.ts +0 -120
- package/src/famix_functions/famix_functions_types.ts +0 -106
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.EntityDictionary = void 0;
|
|
30
|
+
const ts_morph_1 = require("ts-morph");
|
|
31
|
+
const Famix = __importStar(require("../lib/famix/src/model/famix"));
|
|
32
|
+
const analyze_1 = require("../analyze");
|
|
33
|
+
const grapheme_splitter_1 = __importDefault(require("grapheme-splitter"));
|
|
34
|
+
const Helpers = __importStar(require("./helpers_creation"));
|
|
35
|
+
const FQNFunctions = __importStar(require("../fqn"));
|
|
36
|
+
const famix_repository_1 = require("../lib/famix/src/famix_repository");
|
|
37
|
+
const path_1 = __importDefault(require("path"));
|
|
38
|
+
class EntityDictionary {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.famixRep = new famix_repository_1.FamixRepository();
|
|
41
|
+
this.fmxAliasMap = new Map(); // Maps the alias names to their Famix model
|
|
42
|
+
this.fmxClassMap = new Map(); // Maps the fully qualified class names to their Famix model
|
|
43
|
+
this.fmxInterfaceMap = new Map(); // Maps the interface names to their Famix model
|
|
44
|
+
this.fmxNamespaceMap = new Map(); // Maps the namespace names to their Famix model
|
|
45
|
+
this.fmxFileMap = new Map(); // Maps the source file names to their Famix model
|
|
46
|
+
this.fmxTypeMap = new Map(); // Maps the type names to their Famix model
|
|
47
|
+
this.UNKNOWN_VALUE = '(unknown due to parsing error)'; // The value to use when a name is not usable
|
|
48
|
+
this.fmxElementObjectMap = new Map();
|
|
49
|
+
this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Makes a Famix index file anchor
|
|
53
|
+
* @param sourceElement A source element
|
|
54
|
+
* @param famixElement The Famix model of the source element
|
|
55
|
+
*/
|
|
56
|
+
makeFamixIndexFileAnchor(sourceElement, famixElement) {
|
|
57
|
+
analyze_1.logger.debug("making index file anchor for '" + (sourceElement === null || sourceElement === void 0 ? void 0 : sourceElement.getText()) + "' with famixElement " + famixElement.getJSON());
|
|
58
|
+
const fmxIndexFileAnchor = new Famix.IndexedFileAnchor();
|
|
59
|
+
fmxIndexFileAnchor.setElement(famixElement);
|
|
60
|
+
this.fmxElementObjectMap.set(famixElement, sourceElement);
|
|
61
|
+
if (sourceElement !== null) {
|
|
62
|
+
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
63
|
+
const absolutePath = path_1.default.normalize(sourceElement.getSourceFile().getFilePath());
|
|
64
|
+
const positionNodeModules = absolutePath.indexOf('node_modules');
|
|
65
|
+
let pathInProject = "";
|
|
66
|
+
if (positionNodeModules !== -1) {
|
|
67
|
+
const pathFromNodeModules = absolutePath.substring(positionNodeModules);
|
|
68
|
+
pathInProject = pathFromNodeModules;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
pathInProject = this.convertToRelativePath(absolutePath, absolutePathProject);
|
|
72
|
+
}
|
|
73
|
+
// revert any backslashes to forward slashes (path.normalize on windows introduces them)
|
|
74
|
+
pathInProject = pathInProject.replace(/\\/g, "/");
|
|
75
|
+
fmxIndexFileAnchor.setFileName(pathInProject);
|
|
76
|
+
let sourceStart, sourceEnd, sourceLineStart, sourceLineEnd;
|
|
77
|
+
if (!(sourceElement instanceof ts_morph_1.CommentRange)) {
|
|
78
|
+
sourceStart = sourceElement.getStart();
|
|
79
|
+
sourceEnd = sourceElement.getEnd();
|
|
80
|
+
sourceLineStart = sourceElement.getStartLineNumber();
|
|
81
|
+
sourceLineEnd = sourceElement.getEndLineNumber();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
sourceStart = sourceElement.getPos();
|
|
85
|
+
sourceEnd = sourceElement.getEnd();
|
|
86
|
+
}
|
|
87
|
+
if (analyze_1.config.expectGraphemes) {
|
|
88
|
+
/**
|
|
89
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
90
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
91
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
92
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
93
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
94
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
95
|
+
* Famix index file anchor.
|
|
96
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
97
|
+
*/
|
|
98
|
+
const splitter = new grapheme_splitter_1.default();
|
|
99
|
+
const sourceFileText = sourceElement.getSourceFile().getFullText();
|
|
100
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
101
|
+
if (hasGraphemeClusters) {
|
|
102
|
+
const sourceElementText = sourceFileText.substring(sourceStart, sourceEnd);
|
|
103
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
104
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
105
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, sourceStart));
|
|
106
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
107
|
+
sourceStart = Helpers.indexOfSplitArray({ searchArray: sourceFileTextGraphemes,
|
|
108
|
+
targetArray: sourceElementTextGraphemes,
|
|
109
|
+
start: sourceStart - numberOfGraphemeClustersBeforeStart });
|
|
110
|
+
sourceEnd = sourceStart + sourceElementTextGraphemes.length;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
114
|
+
fmxIndexFileAnchor.setStartPos(sourceStart + 1);
|
|
115
|
+
fmxIndexFileAnchor.setEndPos(sourceEnd + 1);
|
|
116
|
+
if (!(sourceElement instanceof ts_morph_1.CommentRange)) {
|
|
117
|
+
fmxIndexFileAnchor.setStartLine(sourceLineStart);
|
|
118
|
+
fmxIndexFileAnchor.setEndLine(sourceLineEnd);
|
|
119
|
+
}
|
|
120
|
+
if (!(famixElement instanceof Famix.Association) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof ts_morph_1.CommentRange) && !(sourceElement instanceof ts_morph_1.Identifier) && !(sourceElement instanceof ts_morph_1.ImportSpecifier) && !(sourceElement instanceof ts_morph_1.ExpressionWithTypeArguments)) {
|
|
121
|
+
famixElement.setFullyQualifiedName(FQNFunctions.getFQN(sourceElement));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// sourceElement is null
|
|
126
|
+
analyze_1.logger.warn("sourceElement is null for famixElement " + famixElement.getJSON());
|
|
127
|
+
fmxIndexFileAnchor.setFileName("unknown");
|
|
128
|
+
fmxIndexFileAnchor.setStartPos(0);
|
|
129
|
+
fmxIndexFileAnchor.setEndPos(0);
|
|
130
|
+
}
|
|
131
|
+
this.famixRep.addElement(fmxIndexFileAnchor);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Creates or gets a Famix script entity or module
|
|
135
|
+
* @param f A source file
|
|
136
|
+
* @param isModule A boolean indicating if the source file is a module
|
|
137
|
+
* @returns The Famix model of the source file
|
|
138
|
+
*/
|
|
139
|
+
createOrGetFamixFile(f, isModule) {
|
|
140
|
+
let fmxFile;
|
|
141
|
+
const fileName = f.getBaseName();
|
|
142
|
+
const fullyQualifiedFilename = f.getFilePath();
|
|
143
|
+
if (!this.fmxFileMap.has(fullyQualifiedFilename)) {
|
|
144
|
+
if (isModule) {
|
|
145
|
+
fmxFile = new Famix.Module();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
fmxFile = new Famix.ScriptEntity();
|
|
149
|
+
}
|
|
150
|
+
fmxFile.setName(fileName);
|
|
151
|
+
fmxFile.setNumberOfLinesOfText(f.getEndLineNumber() - f.getStartLineNumber());
|
|
152
|
+
fmxFile.setNumberOfCharacters(f.getFullText().length);
|
|
153
|
+
this.makeFamixIndexFileAnchor(f, fmxFile);
|
|
154
|
+
this.fmxFileMap.set(fullyQualifiedFilename, fmxFile);
|
|
155
|
+
this.famixRep.addElement(fmxFile);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
fmxFile = this.fmxFileMap.get(fullyQualifiedFilename);
|
|
159
|
+
}
|
|
160
|
+
this.fmxElementObjectMap.set(fmxFile, f);
|
|
161
|
+
return fmxFile;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Creates or gets a Famix namespace
|
|
165
|
+
* @param m A namespace
|
|
166
|
+
* @returns The Famix model of the namespace
|
|
167
|
+
*/
|
|
168
|
+
createOrGetFamixNamespace(m) {
|
|
169
|
+
let fmxNamespace;
|
|
170
|
+
const namespaceName = m.getName();
|
|
171
|
+
if (!this.fmxNamespaceMap.has(namespaceName)) {
|
|
172
|
+
fmxNamespace = new Famix.Namespace();
|
|
173
|
+
fmxNamespace.setName(namespaceName);
|
|
174
|
+
this.makeFamixIndexFileAnchor(m, fmxNamespace);
|
|
175
|
+
this.fmxNamespaceMap.set(namespaceName, fmxNamespace);
|
|
176
|
+
this.famixRep.addElement(fmxNamespace);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
fmxNamespace = this.fmxNamespaceMap.get(namespaceName);
|
|
180
|
+
}
|
|
181
|
+
this.fmxElementObjectMap.set(fmxNamespace, m);
|
|
182
|
+
return fmxNamespace;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Creates a Famix alias
|
|
186
|
+
* @param a An alias
|
|
187
|
+
* @returns The Famix model of the alias
|
|
188
|
+
*/
|
|
189
|
+
createFamixAlias(a) {
|
|
190
|
+
let fmxAlias;
|
|
191
|
+
const aliasName = a.getName();
|
|
192
|
+
const aliasFullyQualifiedName = a.getType().getText(); // FQNFunctions.getFQN(a);
|
|
193
|
+
if (!this.fmxAliasMap.has(aliasFullyQualifiedName)) {
|
|
194
|
+
fmxAlias = new Famix.Alias();
|
|
195
|
+
fmxAlias.setName(a.getName());
|
|
196
|
+
const aliasNameWithGenerics = aliasName + (a.getTypeParameters().length ? ("<" + a.getTypeParameters().map(tp => tp.getName()).join(", ") + ">") : "");
|
|
197
|
+
analyze_1.logger.debug(`> NOTE: alias ${aliasName} has fully qualified name ${aliasFullyQualifiedName} and name with generics ${aliasNameWithGenerics}.`);
|
|
198
|
+
const fmxType = this.createOrGetFamixType(aliasNameWithGenerics, a);
|
|
199
|
+
fmxAlias.setAliasedEntity(fmxType);
|
|
200
|
+
this.makeFamixIndexFileAnchor(a, fmxAlias);
|
|
201
|
+
this.fmxAliasMap.set(aliasFullyQualifiedName, fmxAlias);
|
|
202
|
+
this.famixRep.addElement(fmxAlias);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
fmxAlias = this.fmxAliasMap.get(aliasFullyQualifiedName);
|
|
206
|
+
}
|
|
207
|
+
this.fmxElementObjectMap.set(fmxAlias, a);
|
|
208
|
+
return fmxAlias;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Creates or gets a Famix class or parameterizable class
|
|
212
|
+
* @param cls A class
|
|
213
|
+
* @returns The Famix model of the class
|
|
214
|
+
*/
|
|
215
|
+
createOrGetFamixClass(cls) {
|
|
216
|
+
let fmxClass;
|
|
217
|
+
const isAbstract = cls.isAbstract();
|
|
218
|
+
const classFullyQualifiedName = FQNFunctions.getFQN(cls);
|
|
219
|
+
const clsName = cls.getName();
|
|
220
|
+
if (!this.fmxClassMap.has(classFullyQualifiedName)) {
|
|
221
|
+
const isGeneric = cls.getTypeParameters().length;
|
|
222
|
+
if (isGeneric) {
|
|
223
|
+
fmxClass = new Famix.ParameterizableClass();
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
fmxClass = new Famix.Class();
|
|
227
|
+
}
|
|
228
|
+
fmxClass.setName(clsName);
|
|
229
|
+
fmxClass.setFullyQualifiedName(classFullyQualifiedName);
|
|
230
|
+
fmxClass.setIsAbstract(isAbstract);
|
|
231
|
+
this.makeFamixIndexFileAnchor(cls, fmxClass);
|
|
232
|
+
this.fmxClassMap.set(classFullyQualifiedName, fmxClass);
|
|
233
|
+
this.famixRep.addElement(fmxClass);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
fmxClass = this.fmxClassMap.get(classFullyQualifiedName);
|
|
237
|
+
}
|
|
238
|
+
this.fmxElementObjectMap.set(fmxClass, cls);
|
|
239
|
+
return fmxClass;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Creates or gets a Famix interface or parameterizable interface
|
|
243
|
+
* @param inter An interface
|
|
244
|
+
* @returns The Famix model of the interface
|
|
245
|
+
*/
|
|
246
|
+
createOrGetFamixInterface(inter) {
|
|
247
|
+
let fmxInterface;
|
|
248
|
+
const interName = inter.getName();
|
|
249
|
+
const interFullyQualifiedName = FQNFunctions.getFQN(inter);
|
|
250
|
+
if (!this.fmxInterfaceMap.has(interName)) {
|
|
251
|
+
const isGeneric = inter.getTypeParameters().length;
|
|
252
|
+
if (isGeneric) {
|
|
253
|
+
fmxInterface = new Famix.ParameterizableInterface();
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
fmxInterface = new Famix.Interface();
|
|
257
|
+
}
|
|
258
|
+
fmxInterface.setName(interName);
|
|
259
|
+
this.makeFamixIndexFileAnchor(inter, fmxInterface);
|
|
260
|
+
this.fmxInterfaceMap.set(interFullyQualifiedName, fmxInterface);
|
|
261
|
+
this.famixRep.addElement(fmxInterface);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
fmxInterface = this.fmxInterfaceMap.get(interName);
|
|
265
|
+
}
|
|
266
|
+
this.fmxElementObjectMap.set(fmxInterface, inter);
|
|
267
|
+
return fmxInterface;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Creates a Famix property
|
|
271
|
+
* @param property A property
|
|
272
|
+
* @returns The Famix model of the property
|
|
273
|
+
*/
|
|
274
|
+
createFamixProperty(property) {
|
|
275
|
+
const fmxProperty = new Famix.Property();
|
|
276
|
+
const isSignature = property instanceof ts_morph_1.PropertySignature;
|
|
277
|
+
fmxProperty.setName(property.getName());
|
|
278
|
+
let propTypeName = this.UNKNOWN_VALUE;
|
|
279
|
+
try {
|
|
280
|
+
propTypeName = property.getType().getText().trim();
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for property: ${property.getName()}. Continuing...`);
|
|
284
|
+
}
|
|
285
|
+
const fmxType = this.createOrGetFamixType(propTypeName, property);
|
|
286
|
+
fmxProperty.setDeclaredType(fmxType);
|
|
287
|
+
property.getModifiers().forEach(m => fmxProperty.addModifier(m.getText()));
|
|
288
|
+
if (!isSignature && property.getExclamationTokenNode()) {
|
|
289
|
+
fmxProperty.addModifier("!");
|
|
290
|
+
}
|
|
291
|
+
if (property.getQuestionTokenNode()) {
|
|
292
|
+
fmxProperty.addModifier("?");
|
|
293
|
+
}
|
|
294
|
+
if (property.getName().substring(0, 1) === "#") {
|
|
295
|
+
fmxProperty.addModifier("#");
|
|
296
|
+
}
|
|
297
|
+
if (fmxProperty.getModifiers().has("static")) {
|
|
298
|
+
fmxProperty.setIsClassSide(true);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
fmxProperty.setIsClassSide(false);
|
|
302
|
+
}
|
|
303
|
+
this.makeFamixIndexFileAnchor(property, fmxProperty);
|
|
304
|
+
this.famixRep.addElement(fmxProperty);
|
|
305
|
+
this.fmxElementObjectMap.set(fmxProperty, property);
|
|
306
|
+
return fmxProperty;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Creates a Famix method or accessor
|
|
310
|
+
* @param method A method or an accessor
|
|
311
|
+
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
312
|
+
* @returns The Famix model of the method or the accessor
|
|
313
|
+
*/
|
|
314
|
+
createFamixMethod(method, currentCC) {
|
|
315
|
+
let fmxMethod;
|
|
316
|
+
if (method instanceof ts_morph_1.GetAccessorDeclaration || method instanceof ts_morph_1.SetAccessorDeclaration) {
|
|
317
|
+
fmxMethod = new Famix.Accessor();
|
|
318
|
+
const isGetter = method instanceof ts_morph_1.GetAccessorDeclaration;
|
|
319
|
+
const isSetter = method instanceof ts_morph_1.SetAccessorDeclaration;
|
|
320
|
+
if (isGetter) {
|
|
321
|
+
fmxMethod.setKind("getter");
|
|
322
|
+
}
|
|
323
|
+
if (isSetter) {
|
|
324
|
+
fmxMethod.setKind("setter");
|
|
325
|
+
}
|
|
326
|
+
this.famixRep.addElement(fmxMethod);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
fmxMethod = new Famix.Method();
|
|
330
|
+
this.famixRep.addElement(fmxMethod);
|
|
331
|
+
}
|
|
332
|
+
const isConstructor = method instanceof ts_morph_1.ConstructorDeclaration;
|
|
333
|
+
const isSignature = method instanceof ts_morph_1.MethodSignature;
|
|
334
|
+
const isGeneric = method.getTypeParameters().length > 0;
|
|
335
|
+
fmxMethod.setIsGeneric(isGeneric);
|
|
336
|
+
let isAbstract = false;
|
|
337
|
+
let isStatic = false;
|
|
338
|
+
if (method instanceof ts_morph_1.MethodDeclaration || method instanceof ts_morph_1.GetAccessorDeclaration || method instanceof ts_morph_1.SetAccessorDeclaration) {
|
|
339
|
+
isAbstract = method.isAbstract();
|
|
340
|
+
isStatic = method.isStatic();
|
|
341
|
+
}
|
|
342
|
+
if (isConstructor) {
|
|
343
|
+
fmxMethod.setKind("constructor");
|
|
344
|
+
}
|
|
345
|
+
fmxMethod.setIsAbstract(isAbstract);
|
|
346
|
+
fmxMethod.setIsClassSide(isStatic);
|
|
347
|
+
fmxMethod.setIsPrivate((method instanceof ts_morph_1.MethodDeclaration || method instanceof ts_morph_1.GetAccessorDeclaration || method instanceof ts_morph_1.SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'private')) !== undefined : false);
|
|
348
|
+
fmxMethod.setIsProtected((method instanceof ts_morph_1.MethodDeclaration || method instanceof ts_morph_1.GetAccessorDeclaration || method instanceof ts_morph_1.SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'protected')) !== undefined : false);
|
|
349
|
+
fmxMethod.setSignature(Helpers.computeSignature(method.getText()));
|
|
350
|
+
let methodName;
|
|
351
|
+
if (isConstructor) {
|
|
352
|
+
methodName = "constructor";
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
methodName = method.getName();
|
|
356
|
+
}
|
|
357
|
+
fmxMethod.setName(methodName);
|
|
358
|
+
if (!isConstructor) {
|
|
359
|
+
if (method.getName().substring(0, 1) === "#") {
|
|
360
|
+
fmxMethod.setIsPrivate(true);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (!fmxMethod.getIsPrivate() && !fmxMethod.getIsProtected()) {
|
|
364
|
+
fmxMethod.setIsPublic(true);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
fmxMethod.setIsPublic(false);
|
|
368
|
+
}
|
|
369
|
+
if (!isSignature) {
|
|
370
|
+
fmxMethod.setCyclomaticComplexity(currentCC[fmxMethod.getName()]);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
fmxMethod.setCyclomaticComplexity(0);
|
|
374
|
+
}
|
|
375
|
+
let methodTypeName = this.UNKNOWN_VALUE;
|
|
376
|
+
try {
|
|
377
|
+
methodTypeName = method.getReturnType().getText().trim();
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of method: ${fmxMethod.getName()}. Continuing...`);
|
|
381
|
+
}
|
|
382
|
+
const fmxType = this.createOrGetFamixType(methodTypeName, method);
|
|
383
|
+
fmxMethod.setDeclaredType(fmxType);
|
|
384
|
+
fmxMethod.setNumberOfLinesOfCode(method.getEndLineNumber() - method.getStartLineNumber());
|
|
385
|
+
const parameters = method.getParameters();
|
|
386
|
+
fmxMethod.setNumberOfParameters(parameters.length);
|
|
387
|
+
if (!isSignature) {
|
|
388
|
+
fmxMethod.setNumberOfStatements(method.getStatements().length);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
fmxMethod.setNumberOfStatements(0);
|
|
392
|
+
}
|
|
393
|
+
this.makeFamixIndexFileAnchor(method, fmxMethod);
|
|
394
|
+
this.fmxElementObjectMap.set(fmxMethod, method);
|
|
395
|
+
return fmxMethod;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Creates a Famix function
|
|
399
|
+
* @param func A function
|
|
400
|
+
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
401
|
+
* @returns The Famix model of the function
|
|
402
|
+
*/
|
|
403
|
+
createFamixFunction(func, currentCC) {
|
|
404
|
+
const fmxFunction = new Famix.Function();
|
|
405
|
+
if (func.getName()) {
|
|
406
|
+
fmxFunction.setName(func.getName());
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
fmxFunction.setName("anonymous");
|
|
410
|
+
}
|
|
411
|
+
fmxFunction.setSignature(Helpers.computeSignature(func.getText()));
|
|
412
|
+
fmxFunction.setCyclomaticComplexity(currentCC[fmxFunction.getName()]);
|
|
413
|
+
const isGeneric = func.getTypeParameters().length > 0;
|
|
414
|
+
fmxFunction.setIsGeneric(isGeneric);
|
|
415
|
+
let functionTypeName = this.UNKNOWN_VALUE;
|
|
416
|
+
try {
|
|
417
|
+
functionTypeName = func.getReturnType().getText().trim();
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
|
|
421
|
+
}
|
|
422
|
+
const fmxType = this.createOrGetFamixType(functionTypeName, func);
|
|
423
|
+
fmxFunction.setDeclaredType(fmxType);
|
|
424
|
+
fmxFunction.setNumberOfLinesOfCode(func.getEndLineNumber() - func.getStartLineNumber());
|
|
425
|
+
const parameters = func.getParameters();
|
|
426
|
+
fmxFunction.setNumberOfParameters(parameters.length);
|
|
427
|
+
fmxFunction.setNumberOfStatements(func.getStatements().length);
|
|
428
|
+
this.makeFamixIndexFileAnchor(func, fmxFunction);
|
|
429
|
+
this.famixRep.addElement(fmxFunction);
|
|
430
|
+
this.fmxElementObjectMap.set(fmxFunction, func);
|
|
431
|
+
return fmxFunction;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Creates a Famix parameter
|
|
435
|
+
* @param param A parameter
|
|
436
|
+
* @returns The Famix model of the parameter
|
|
437
|
+
*/
|
|
438
|
+
createFamixParameter(param) {
|
|
439
|
+
const fmxParam = new Famix.Parameter();
|
|
440
|
+
let paramTypeName = this.UNKNOWN_VALUE;
|
|
441
|
+
try {
|
|
442
|
+
paramTypeName = param.getType().getText().trim();
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for parameter: ${param.getName()}. Continuing...`);
|
|
446
|
+
}
|
|
447
|
+
const fmxType = this.createOrGetFamixType(paramTypeName, param);
|
|
448
|
+
fmxParam.setDeclaredType(fmxType);
|
|
449
|
+
fmxParam.setName(param.getName());
|
|
450
|
+
this.makeFamixIndexFileAnchor(param, fmxParam);
|
|
451
|
+
this.famixRep.addElement(fmxParam);
|
|
452
|
+
this.fmxElementObjectMap.set(fmxParam, param);
|
|
453
|
+
return fmxParam;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Creates a Famix type parameter
|
|
457
|
+
* @param tp A type parameter
|
|
458
|
+
* @returns The Famix model of the type parameter
|
|
459
|
+
*/
|
|
460
|
+
createFamixParameterType(tp) {
|
|
461
|
+
const fmxParameterType = new Famix.ParameterType();
|
|
462
|
+
fmxParameterType.setName(tp.getName());
|
|
463
|
+
this.makeFamixIndexFileAnchor(tp, fmxParameterType);
|
|
464
|
+
this.famixRep.addElement(fmxParameterType);
|
|
465
|
+
this.fmxElementObjectMap.set(fmxParameterType, tp);
|
|
466
|
+
return fmxParameterType;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Creates a Famix variable
|
|
470
|
+
* @param variable A variable
|
|
471
|
+
* @returns The Famix model of the variable
|
|
472
|
+
*/
|
|
473
|
+
createFamixVariable(variable) {
|
|
474
|
+
const fmxVariable = new Famix.Variable();
|
|
475
|
+
let variableTypeName = this.UNKNOWN_VALUE;
|
|
476
|
+
try {
|
|
477
|
+
variableTypeName = variable.getType().getText().trim();
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for variable: ${variable.getName()}. Continuing...`);
|
|
481
|
+
}
|
|
482
|
+
const fmxType = this.createOrGetFamixType(variableTypeName, variable);
|
|
483
|
+
fmxVariable.setDeclaredType(fmxType);
|
|
484
|
+
fmxVariable.setName(variable.getName());
|
|
485
|
+
this.makeFamixIndexFileAnchor(variable, fmxVariable);
|
|
486
|
+
this.famixRep.addElement(fmxVariable);
|
|
487
|
+
this.fmxElementObjectMap.set(fmxVariable, variable);
|
|
488
|
+
return fmxVariable;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Creates a Famix enum
|
|
492
|
+
* @param enumEntity An enum
|
|
493
|
+
* @returns The Famix model of the enum
|
|
494
|
+
*/
|
|
495
|
+
createFamixEnum(enumEntity) {
|
|
496
|
+
const fmxEnum = new Famix.Enum();
|
|
497
|
+
fmxEnum.setName(enumEntity.getName());
|
|
498
|
+
this.makeFamixIndexFileAnchor(enumEntity, fmxEnum);
|
|
499
|
+
this.famixRep.addElement(fmxEnum);
|
|
500
|
+
this.fmxElementObjectMap.set(fmxEnum, enumEntity);
|
|
501
|
+
return fmxEnum;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Creates a Famix enum value
|
|
505
|
+
* @param enumMember An enum member
|
|
506
|
+
* @returns The Famix model of the enum member
|
|
507
|
+
*/
|
|
508
|
+
createFamixEnumValue(enumMember) {
|
|
509
|
+
const fmxEnumValue = new Famix.EnumValue();
|
|
510
|
+
let enumValueTypeName = this.UNKNOWN_VALUE;
|
|
511
|
+
try {
|
|
512
|
+
enumValueTypeName = enumMember.getType().getText().trim();
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
analyze_1.logger.error(`> WARNING: got exception ${error}. Failed to get usable name for enum value: ${enumMember.getName()}. Continuing...`);
|
|
516
|
+
}
|
|
517
|
+
const fmxType = this.createOrGetFamixType(enumValueTypeName, enumMember);
|
|
518
|
+
fmxEnumValue.setDeclaredType(fmxType);
|
|
519
|
+
fmxEnumValue.setName(enumMember.getName());
|
|
520
|
+
this.makeFamixIndexFileAnchor(enumMember, fmxEnumValue);
|
|
521
|
+
this.famixRep.addElement(fmxEnumValue);
|
|
522
|
+
this.fmxElementObjectMap.set(fmxEnumValue, enumMember);
|
|
523
|
+
return fmxEnumValue;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Creates or gets a Famix decorator
|
|
527
|
+
* @param decorator A decorator
|
|
528
|
+
* @param decoratedEntity A class, a method, a parameter or a property
|
|
529
|
+
* @returns The Famix model of the decorator
|
|
530
|
+
*/
|
|
531
|
+
createOrGetFamixDecorator(decorator, decoratedEntity) {
|
|
532
|
+
const fmxDecorator = new Famix.Decorator();
|
|
533
|
+
const decoratorName = "@" + decorator.getName();
|
|
534
|
+
const decoratorExpression = decorator.getText().substring(1);
|
|
535
|
+
fmxDecorator.setName(decoratorName);
|
|
536
|
+
fmxDecorator.setDecoratorExpression(decoratorExpression);
|
|
537
|
+
const decoratedEntityFullyQualifiedName = FQNFunctions.getFQN(decoratedEntity);
|
|
538
|
+
const fmxDecoratedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(decoratedEntityFullyQualifiedName);
|
|
539
|
+
fmxDecorator.setDecoratedEntity(fmxDecoratedEntity);
|
|
540
|
+
this.makeFamixIndexFileAnchor(decorator, fmxDecorator);
|
|
541
|
+
this.famixRep.addElement(fmxDecorator);
|
|
542
|
+
this.fmxElementObjectMap.set(fmxDecorator, decorator);
|
|
543
|
+
return fmxDecorator;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Creates a Famix comment
|
|
547
|
+
* @param comment A comment
|
|
548
|
+
* @param fmxScope The Famix model of the comment's container
|
|
549
|
+
* @param isJSDoc A boolean indicating if the comment is a JSDoc
|
|
550
|
+
* @returns The Famix model of the comment
|
|
551
|
+
*/
|
|
552
|
+
createFamixComment(comment, fmxScope, isJSDoc) {
|
|
553
|
+
analyze_1.logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.getName()}.`);
|
|
554
|
+
const fmxComment = new Famix.Comment();
|
|
555
|
+
fmxComment.setContainer(fmxScope); // adds comment to the container's comments collection
|
|
556
|
+
fmxComment.setIsJSDoc(isJSDoc);
|
|
557
|
+
this.makeFamixIndexFileAnchor(comment, fmxComment);
|
|
558
|
+
this.famixRep.addElement(fmxComment);
|
|
559
|
+
this.fmxElementObjectMap.set(fmxComment, comment);
|
|
560
|
+
return fmxComment;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Creates or gets a Famix type
|
|
564
|
+
* @param typeName A type name
|
|
565
|
+
* @param element A ts-morph element
|
|
566
|
+
* @returns The Famix model of the type
|
|
567
|
+
*/
|
|
568
|
+
createOrGetFamixType(typeName, element) {
|
|
569
|
+
let fmxType;
|
|
570
|
+
let isPrimitiveType = false;
|
|
571
|
+
let isParameterizedType = false;
|
|
572
|
+
analyze_1.logger.debug("Creating (or getting) type: '" + typeName + "' of element: " + (element === null || element === void 0 ? void 0 : element.getText()) + " of kind: " + (element === null || element === void 0 ? void 0 : element.getKindName()));
|
|
573
|
+
let ancestor;
|
|
574
|
+
if (element !== undefined) {
|
|
575
|
+
const typeAncestor = Helpers.findTypeAncestor(element);
|
|
576
|
+
const ancestorFullyQualifiedName = FQNFunctions.getFQN(typeAncestor);
|
|
577
|
+
ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName);
|
|
578
|
+
if (!ancestor) {
|
|
579
|
+
throw new Error(`Ancestor ${ancestorFullyQualifiedName} not found.`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (typeName === "number" || typeName === "string" || typeName === "boolean" || typeName === "bigint" || typeName === "symbol" || typeName === "undefined" || typeName === "null" || typeName === "any" || typeName === "unknown" || typeName === "never" || typeName === "void") {
|
|
583
|
+
isPrimitiveType = true;
|
|
584
|
+
}
|
|
585
|
+
if (!isPrimitiveType && typeName.includes("<") && typeName.includes(">") && !(typeName.includes("=>"))) {
|
|
586
|
+
isParameterizedType = true;
|
|
587
|
+
}
|
|
588
|
+
if (!this.fmxTypeMap.has(typeName)) {
|
|
589
|
+
if (isPrimitiveType) {
|
|
590
|
+
fmxType = new Famix.PrimitiveType();
|
|
591
|
+
fmxType.setIsStub(true);
|
|
592
|
+
}
|
|
593
|
+
else if (isParameterizedType) {
|
|
594
|
+
fmxType = new Famix.ParameterizedType();
|
|
595
|
+
const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">")).split(",").map(s => s.trim());
|
|
596
|
+
const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
|
|
597
|
+
parameterTypeNames.forEach(parameterTypeName => {
|
|
598
|
+
const fmxParameterType = this.createOrGetFamixType(parameterTypeName, element);
|
|
599
|
+
fmxType.addArgument(fmxParameterType);
|
|
600
|
+
});
|
|
601
|
+
const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
|
|
602
|
+
fmxType.setBaseType(fmxBaseType);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
fmxType = new Famix.Type();
|
|
606
|
+
}
|
|
607
|
+
fmxType.setName(typeName);
|
|
608
|
+
fmxType.setContainer(ancestor);
|
|
609
|
+
this.makeFamixIndexFileAnchor(element, fmxType);
|
|
610
|
+
this.famixRep.addElement(fmxType);
|
|
611
|
+
this.fmxTypeMap.set(typeName, fmxType);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
fmxType = this.fmxTypeMap.get(typeName);
|
|
615
|
+
}
|
|
616
|
+
this.fmxElementObjectMap.set(fmxType, element);
|
|
617
|
+
return fmxType;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Creates a Famix access
|
|
621
|
+
* @param node A node
|
|
622
|
+
* @param id An id of a parameter, a variable, a property or an enum member
|
|
623
|
+
*/
|
|
624
|
+
createFamixAccess(node, id) {
|
|
625
|
+
const fmxVar = this.famixRep.getFamixEntityById(id);
|
|
626
|
+
const nodeReferenceAncestor = Helpers.findAncestor(node);
|
|
627
|
+
const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
|
|
628
|
+
const accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName);
|
|
629
|
+
const fmxAccess = new Famix.Access();
|
|
630
|
+
fmxAccess.setAccessor(accessor);
|
|
631
|
+
fmxAccess.setVariable(fmxVar);
|
|
632
|
+
this.makeFamixIndexFileAnchor(node, fmxAccess);
|
|
633
|
+
this.famixRep.addElement(fmxAccess);
|
|
634
|
+
this.fmxElementObjectMap.set(fmxAccess, node);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Creates a Famix invocation
|
|
638
|
+
* @param node A node
|
|
639
|
+
* @param m A method or a function
|
|
640
|
+
* @param id The id of the method or the function
|
|
641
|
+
*/
|
|
642
|
+
createFamixInvocation(node, m, id) {
|
|
643
|
+
const fmxMethodOrFunction = this.famixRep.getFamixEntityById(id);
|
|
644
|
+
const nodeReferenceAncestor = Helpers.findAncestor(node);
|
|
645
|
+
const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
|
|
646
|
+
const sender = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName);
|
|
647
|
+
const receiverFullyQualifiedName = FQNFunctions.getFQN(m.getParent());
|
|
648
|
+
const receiver = this.famixRep.getFamixEntityByFullyQualifiedName(receiverFullyQualifiedName);
|
|
649
|
+
const fmxInvocation = new Famix.Invocation();
|
|
650
|
+
fmxInvocation.setSender(sender);
|
|
651
|
+
fmxInvocation.setReceiver(receiver);
|
|
652
|
+
fmxInvocation.addCandidate(fmxMethodOrFunction);
|
|
653
|
+
fmxInvocation.setSignature(fmxMethodOrFunction.getSignature());
|
|
654
|
+
this.makeFamixIndexFileAnchor(node, fmxInvocation);
|
|
655
|
+
this.famixRep.addElement(fmxInvocation);
|
|
656
|
+
this.fmxElementObjectMap.set(fmxInvocation, node);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Creates a Famix inheritance
|
|
660
|
+
* @param cls A class or an interface (subclass)
|
|
661
|
+
* @param inhClass The inherited class or interface (superclass)
|
|
662
|
+
*/
|
|
663
|
+
createFamixInheritance(cls, inhClass) {
|
|
664
|
+
const fmxInheritance = new Famix.Inheritance();
|
|
665
|
+
// const clsName = cls.getName();
|
|
666
|
+
const classFullyQualifiedName = FQNFunctions.getFQN(cls);
|
|
667
|
+
analyze_1.logger.debug(`createFamixInheritance: classFullyQualifiedName: class fqn = ${classFullyQualifiedName}`);
|
|
668
|
+
let subClass;
|
|
669
|
+
if (cls instanceof ts_morph_1.ClassDeclaration) {
|
|
670
|
+
subClass = this.fmxClassMap.get(classFullyQualifiedName);
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
subClass = this.fmxInterfaceMap.get(classFullyQualifiedName);
|
|
674
|
+
}
|
|
675
|
+
let inhClassName;
|
|
676
|
+
let inhClassFullyQualifiedName;
|
|
677
|
+
let superClass;
|
|
678
|
+
if (inhClass instanceof ts_morph_1.ClassDeclaration || inhClass instanceof ts_morph_1.InterfaceDeclaration) {
|
|
679
|
+
inhClassName = inhClass.getName();
|
|
680
|
+
inhClassFullyQualifiedName = FQNFunctions.getFQN(inhClass);
|
|
681
|
+
if (inhClass instanceof ts_morph_1.ClassDeclaration) {
|
|
682
|
+
superClass = this.fmxClassMap.get(inhClassFullyQualifiedName);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
superClass = this.fmxInterfaceMap.get(inhClassFullyQualifiedName);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
// inhClass is an ExpressionWithTypeArguments
|
|
690
|
+
inhClassName = inhClass.getExpression().getText();
|
|
691
|
+
// what is inhClassFullyQualifiedName? TODO
|
|
692
|
+
inhClassFullyQualifiedName = 'Undefined_Scope_from_importer.' + inhClassName;
|
|
693
|
+
}
|
|
694
|
+
if (superClass === undefined) {
|
|
695
|
+
if (inhClass instanceof ts_morph_1.ClassDeclaration) {
|
|
696
|
+
superClass = new Famix.Class();
|
|
697
|
+
this.fmxClassMap.set(inhClassFullyQualifiedName, superClass);
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
superClass = new Famix.Interface();
|
|
701
|
+
this.fmxInterfaceMap.set(inhClassFullyQualifiedName, superClass);
|
|
702
|
+
}
|
|
703
|
+
this.fmxElementObjectMap.set(superClass, inhClass);
|
|
704
|
+
superClass.setName(inhClassName);
|
|
705
|
+
superClass.setFullyQualifiedName(inhClassFullyQualifiedName);
|
|
706
|
+
superClass.setIsStub(true);
|
|
707
|
+
this.makeFamixIndexFileAnchor(inhClass, superClass);
|
|
708
|
+
this.famixRep.addElement(superClass);
|
|
709
|
+
}
|
|
710
|
+
fmxInheritance.setSubclass(subClass);
|
|
711
|
+
fmxInheritance.setSuperclass(superClass);
|
|
712
|
+
this.makeFamixIndexFileAnchor(null, fmxInheritance);
|
|
713
|
+
this.famixRep.addElement(fmxInheritance);
|
|
714
|
+
this.fmxElementObjectMap.set(fmxInheritance, null);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Creates a Famix import clause
|
|
718
|
+
* @param importClauseInfo The information needed to create a Famix import clause
|
|
719
|
+
* @param importDeclaration The import declaration
|
|
720
|
+
* @param importer A source file which is a module
|
|
721
|
+
* @param moduleSpecifierFilePath The path of the module where the export declaration is
|
|
722
|
+
* @param importElement The imported entity
|
|
723
|
+
* @param isInExports A boolean indicating if the imported entity is in the exports
|
|
724
|
+
* @param isDefaultExport A boolean indicating if the imported entity is a default export
|
|
725
|
+
*/
|
|
726
|
+
createFamixImportClause(importClauseInfo) {
|
|
727
|
+
var _a, _b;
|
|
728
|
+
const { importDeclaration, importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport } = importClauseInfo;
|
|
729
|
+
analyze_1.logger.debug(`createFamixImportClause: Creating import clause:`);
|
|
730
|
+
const fmxImportClause = new Famix.ImportClause();
|
|
731
|
+
let importedEntity;
|
|
732
|
+
let importedEntityName;
|
|
733
|
+
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
734
|
+
const absolutePath = path_1.default.normalize(moduleSpecifierFilePath);
|
|
735
|
+
// convert the path and remove any windows backslashes introduced by path.normalize
|
|
736
|
+
const pathInProject = this.convertToRelativePath(absolutePath, absolutePathProject).replace(/\\/g, "/");
|
|
737
|
+
let pathName = "{" + pathInProject + "}.";
|
|
738
|
+
// Named imports, e.g. import { ClassW } from "./complexExportModule";
|
|
739
|
+
if (importDeclaration instanceof ts_morph_1.ImportDeclaration
|
|
740
|
+
&& importElement instanceof ts_morph_1.ImportSpecifier) {
|
|
741
|
+
importedEntityName = importElement.getName();
|
|
742
|
+
pathName = pathName + importedEntityName;
|
|
743
|
+
if (isInExports) {
|
|
744
|
+
importedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(pathName);
|
|
745
|
+
}
|
|
746
|
+
if (importedEntity === undefined) {
|
|
747
|
+
importedEntity = new Famix.NamedEntity();
|
|
748
|
+
importedEntity.setName(importedEntityName);
|
|
749
|
+
if (!isInExports) {
|
|
750
|
+
importedEntity.setIsStub(true);
|
|
751
|
+
}
|
|
752
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
753
|
+
importedEntity.setFullyQualifiedName(pathName);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
// handle import equals declarations, e.g. import myModule = require("./complexExportModule");
|
|
757
|
+
// TypeScript can't determine the type of the imported module, so we create a Module entity
|
|
758
|
+
else if (importDeclaration instanceof ts_morph_1.ImportEqualsDeclaration) {
|
|
759
|
+
importedEntityName = importDeclaration === null || importDeclaration === void 0 ? void 0 : importDeclaration.getName();
|
|
760
|
+
pathName = pathName + importedEntityName;
|
|
761
|
+
importedEntity = new Famix.StructuralEntity();
|
|
762
|
+
importedEntity.setName(importedEntityName);
|
|
763
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
764
|
+
importedEntity.setFullyQualifiedName(pathName);
|
|
765
|
+
const anyType = this.createOrGetFamixType('any', undefined);
|
|
766
|
+
importedEntity.setDeclaredType(anyType);
|
|
767
|
+
}
|
|
768
|
+
else { // default imports, e.g. import ClassW from "./complexExportModule";
|
|
769
|
+
importedEntityName = importElement.getText();
|
|
770
|
+
pathName = pathName + (isDefaultExport ? "defaultExport" : "namespaceExport");
|
|
771
|
+
importedEntity = new Famix.NamedEntity();
|
|
772
|
+
importedEntity.setName(importedEntityName);
|
|
773
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
774
|
+
importedEntity.setFullyQualifiedName(pathName);
|
|
775
|
+
}
|
|
776
|
+
this.famixRep.addElement(importedEntity);
|
|
777
|
+
const importerFullyQualifiedName = FQNFunctions.getFQN(importer);
|
|
778
|
+
const fmxImporter = this.famixRep.getFamixEntityByFullyQualifiedName(importerFullyQualifiedName);
|
|
779
|
+
fmxImportClause.setImportingEntity(fmxImporter);
|
|
780
|
+
fmxImportClause.setImportedEntity(importedEntity);
|
|
781
|
+
if (importDeclaration instanceof ts_morph_1.ImportEqualsDeclaration) {
|
|
782
|
+
fmxImportClause.setModuleSpecifier(importDeclaration === null || importDeclaration === void 0 ? void 0 : importDeclaration.getModuleReference().getText());
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
fmxImportClause.setModuleSpecifier(importDeclaration === null || importDeclaration === void 0 ? void 0 : importDeclaration.getModuleSpecifierValue());
|
|
786
|
+
}
|
|
787
|
+
analyze_1.logger.debug(`createFamixImportClause: ${(_a = fmxImportClause.getImportedEntity()) === null || _a === void 0 ? void 0 : _a.getName()} (of type ${Helpers.getSubTypeName(fmxImportClause.getImportedEntity())}) is imported by ${(_b = fmxImportClause.getImportingEntity()) === null || _b === void 0 ? void 0 : _b.getName()}`);
|
|
788
|
+
// make an index file anchor for the import clause
|
|
789
|
+
this.makeFamixIndexFileAnchor(importDeclaration, fmxImportClause);
|
|
790
|
+
fmxImporter.addOutgoingImport(fmxImportClause);
|
|
791
|
+
this.famixRep.addElement(fmxImportClause);
|
|
792
|
+
this.fmxElementObjectMap.set(fmxImportClause, importDeclaration);
|
|
793
|
+
}
|
|
794
|
+
convertToRelativePath(absolutePath, absolutePathProject) {
|
|
795
|
+
return absolutePath.replace(absolutePathProject, "").slice(1);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
exports.EntityDictionary = EntityDictionary;
|