ts2famix 1.0.15 → 1.0.17
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.
|
@@ -22,11 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.FamixFunctionsIndex = void 0;
|
|
27
30
|
const ts_morph_1 = require("ts-morph");
|
|
28
31
|
const Famix = __importStar(require("../lib/famix/src/model/famix"));
|
|
29
32
|
const fqn_1 = require("../fqn");
|
|
33
|
+
const grapheme_splitter_1 = __importDefault(require("grapheme-splitter"));
|
|
30
34
|
/**
|
|
31
35
|
* This class is used to build a Famix model for the index file anchors
|
|
32
36
|
*/
|
|
@@ -50,8 +54,40 @@ class FamixFunctionsIndex {
|
|
|
50
54
|
if (sourceElement !== null) {
|
|
51
55
|
fmxIndexFileAnchor.setFileName(sourceElement.getSourceFile().getFilePath());
|
|
52
56
|
if (!(sourceElement instanceof ts_morph_1.CommentRange)) {
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
// sourceElement.getStart() and sourceElement.getEnd() are based on text that has not been split into grapheme clusters, so we need to split the text and then adjust the start and end positions by finding the string inside the split text that has been joined together to form the original text
|
|
58
|
+
/**
|
|
59
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
60
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
61
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
62
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
63
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
64
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
65
|
+
* Famix index file anchor.
|
|
66
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
67
|
+
*/
|
|
68
|
+
const splitter = new grapheme_splitter_1.default();
|
|
69
|
+
const sourceFileText = sourceElement.getSourceFile().getFullText();
|
|
70
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
71
|
+
if (hasGraphemeClusters) {
|
|
72
|
+
const sourceElementText = sourceFileText.substring(sourceElement.getStart(), sourceElement.getEnd());
|
|
73
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
74
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
75
|
+
const start = sourceElement.getStart();
|
|
76
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, start));
|
|
77
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
78
|
+
const newStart = indexOfSplitArray({ searchArray: sourceFileTextGraphemes,
|
|
79
|
+
targetArray: sourceElementTextGraphemes,
|
|
80
|
+
start: start - numberOfGraphemeClustersBeforeStart });
|
|
81
|
+
const newEnd = newStart + sourceElementTextGraphemes.length;
|
|
82
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
83
|
+
fmxIndexFileAnchor.setStartPos(newStart + 1);
|
|
84
|
+
fmxIndexFileAnchor.setEndPos(newEnd + 1);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
88
|
+
fmxIndexFileAnchor.setStartPos(sourceElement.getStart() + 1);
|
|
89
|
+
fmxIndexFileAnchor.setEndPos(sourceElement.getEnd() + 1);
|
|
90
|
+
}
|
|
55
91
|
}
|
|
56
92
|
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)) {
|
|
57
93
|
famixElement.setFullyQualifiedName(this.FQNFunctions.getFQN(sourceElement));
|
|
@@ -60,3 +96,23 @@ class FamixFunctionsIndex {
|
|
|
60
96
|
}
|
|
61
97
|
}
|
|
62
98
|
exports.FamixFunctionsIndex = FamixFunctionsIndex;
|
|
99
|
+
/**
|
|
100
|
+
* This function works like indexOf, but it works with arrays of grapheme clusters.
|
|
101
|
+
* @param targetArray
|
|
102
|
+
*/
|
|
103
|
+
function indexOfSplitArray(params) {
|
|
104
|
+
const { searchArray, targetArray, start = 0 } = params;
|
|
105
|
+
for (let i = start; i <= searchArray.length - targetArray.length; i++) {
|
|
106
|
+
let found = true;
|
|
107
|
+
for (let j = 0; j < targetArray.length; j++) {
|
|
108
|
+
if (searchArray[i + j] !== targetArray[j]) {
|
|
109
|
+
found = false;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (found) {
|
|
114
|
+
return i; // Return the index where the target array was found
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return -1; // Return -1 if the target array was not found in the search array
|
|
118
|
+
}
|
|
@@ -52,14 +52,14 @@ class FamixFunctionsTypes {
|
|
|
52
52
|
let fmxType;
|
|
53
53
|
let isPrimitiveType = false;
|
|
54
54
|
let isParameterizedType = false;
|
|
55
|
-
console.info("Creating (or getting) type: " + typeName + "' of element: " + element.getText() + " of kind: " + element.getKindName());
|
|
55
|
+
console.info("Creating (or getting) type: '" + typeName + "' of element: " + element.getText() + " of kind: " + element.getKindName());
|
|
56
56
|
const typeAncestor = this.findTypeAncestor(element);
|
|
57
57
|
const ancestorFullyQualifiedName = this.FQNFunctions.getFQN(typeAncestor);
|
|
58
58
|
const ancestor = this.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName);
|
|
59
59
|
if (!ancestor) {
|
|
60
60
|
throw new Error(`Ancestor ${ancestorFullyQualifiedName} not found.`);
|
|
61
61
|
}
|
|
62
|
-
if (typeName === "number" || typeName === "string" || typeName === "boolean" || typeName === "bigint" || typeName === "symbol" || typeName === "undefined" || typeName === "null") {
|
|
62
|
+
if (typeName === "number" || typeName === "string" || typeName === "boolean" || typeName === "bigint" || typeName === "symbol" || typeName === "undefined" || typeName === "null" || typeName === "any" || typeName === "unknown" || typeName === "never" || typeName === "void") {
|
|
63
63
|
isPrimitiveType = true;
|
|
64
64
|
}
|
|
65
65
|
if (!isPrimitiveType && typeName.includes("<") && typeName.includes(">") && !(typeName.includes("=>"))) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts2famix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "A TypeScript to JSON importer for Moose 10.",
|
|
5
5
|
"main": "dist/ts2famix-cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@types/jest": "^29.5.
|
|
30
|
-
"@types/node": "^20.6.
|
|
29
|
+
"@types/jest": "^29.5.5",
|
|
30
|
+
"@types/node": "^20.6.3",
|
|
31
31
|
"@types/yargs": "^17.0.24",
|
|
32
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
33
|
-
"@typescript-eslint/parser": "^6.
|
|
32
|
+
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
|
33
|
+
"@typescript-eslint/parser": "^6.7.2",
|
|
34
34
|
"eslint": "^8.49.0",
|
|
35
|
-
"jest": "^29.
|
|
35
|
+
"jest": "^29.7.0",
|
|
36
36
|
"tplant": "^3.1.2",
|
|
37
37
|
"ts-jest": "^29.1.1",
|
|
38
38
|
"ts-node": "^10.9.1",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"typescript": "^5.2.2"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"grapheme-splitter": "^1.0.4",
|
|
43
44
|
"ts-morph": "^19.0.0",
|
|
44
45
|
"tsutils": "^3.21.0",
|
|
45
46
|
"yargs": "^17.7.2"
|
|
@@ -2,6 +2,7 @@ import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifi
|
|
|
2
2
|
import * as Famix from "../lib/famix/src/model/famix";
|
|
3
3
|
import { FamixRepository } from "../lib/famix/src/famix_repository";
|
|
4
4
|
import { FQNFunctions } from "../fqn";
|
|
5
|
+
import GraphemeSplitter from "grapheme-splitter";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* This class is used to build a Famix model for the index file anchors
|
|
@@ -30,10 +31,42 @@ export class FamixFunctionsIndex {
|
|
|
30
31
|
|
|
31
32
|
if (sourceElement !== null) {
|
|
32
33
|
fmxIndexFileAnchor.setFileName(sourceElement.getSourceFile().getFilePath());
|
|
33
|
-
|
|
34
34
|
if (!(sourceElement instanceof CommentRange)) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// sourceElement.getStart() and sourceElement.getEnd() are based on text that has not been split into grapheme clusters, so we need to split the text and then adjust the start and end positions by finding the string inside the split text that has been joined together to form the original text
|
|
36
|
+
/**
|
|
37
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
38
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
39
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
40
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
41
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
42
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
43
|
+
* Famix index file anchor.
|
|
44
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
45
|
+
*/
|
|
46
|
+
const splitter = new GraphemeSplitter();
|
|
47
|
+
const sourceFileText = sourceElement.getSourceFile().getFullText();
|
|
48
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
49
|
+
if (hasGraphemeClusters) {
|
|
50
|
+
const sourceElementText = sourceFileText.substring(sourceElement.getStart(), sourceElement.getEnd());
|
|
51
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
52
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
53
|
+
const start = sourceElement.getStart();
|
|
54
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, start));
|
|
55
|
+
|
|
56
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
57
|
+
const newStart = indexOfSplitArray({searchArray: sourceFileTextGraphemes,
|
|
58
|
+
targetArray: sourceElementTextGraphemes,
|
|
59
|
+
start: start - numberOfGraphemeClustersBeforeStart});
|
|
60
|
+
const newEnd = newStart + sourceElementTextGraphemes.length;
|
|
61
|
+
|
|
62
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
63
|
+
fmxIndexFileAnchor.setStartPos(newStart + 1);
|
|
64
|
+
fmxIndexFileAnchor.setEndPos(newEnd + 1);
|
|
65
|
+
} else {
|
|
66
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
67
|
+
fmxIndexFileAnchor.setStartPos(sourceElement.getStart() + 1);
|
|
68
|
+
fmxIndexFileAnchor.setEndPos(sourceElement.getEnd() + 1);
|
|
69
|
+
}
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
if (!(famixElement instanceof Famix.Association) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof CommentRange) && !(sourceElement instanceof Identifier) && !(sourceElement instanceof ImportSpecifier) && !(sourceElement instanceof ExpressionWithTypeArguments)) {
|
|
@@ -42,3 +75,30 @@ export class FamixFunctionsIndex {
|
|
|
42
75
|
}
|
|
43
76
|
}
|
|
44
77
|
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
interface SearchParameters {
|
|
81
|
+
searchArray: string[];
|
|
82
|
+
targetArray: string[];
|
|
83
|
+
start?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* This function works like indexOf, but it works with arrays of grapheme clusters.
|
|
87
|
+
* @param targetArray
|
|
88
|
+
*/
|
|
89
|
+
function indexOfSplitArray(params: SearchParameters): number {
|
|
90
|
+
const {searchArray, targetArray, start = 0} = params;
|
|
91
|
+
for (let i = start; i <= searchArray.length - targetArray.length; i++) {
|
|
92
|
+
let found = true;
|
|
93
|
+
for (let j = 0; j < targetArray.length; j++) {
|
|
94
|
+
if (searchArray[i + j] !== targetArray[j]) {
|
|
95
|
+
found = false;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (found) {
|
|
100
|
+
return i; // Return the index where the target array was found
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return -1; // Return -1 if the target array was not found in the search array
|
|
104
|
+
}
|
|
@@ -34,7 +34,7 @@ export class FamixFunctionsTypes {
|
|
|
34
34
|
let isPrimitiveType = false;
|
|
35
35
|
let isParameterizedType = false;
|
|
36
36
|
|
|
37
|
-
console.info("Creating (or getting) type: " + typeName + "' of element: " + element.getText() + " of kind: " + element.getKindName());
|
|
37
|
+
console.info("Creating (or getting) type: '" + typeName + "' of element: " + element.getText() + " of kind: " + element.getKindName());
|
|
38
38
|
|
|
39
39
|
const typeAncestor = this.findTypeAncestor(element);
|
|
40
40
|
const ancestorFullyQualifiedName = this.FQNFunctions.getFQN(typeAncestor);
|
|
@@ -43,7 +43,7 @@ export class FamixFunctionsTypes {
|
|
|
43
43
|
throw new Error(`Ancestor ${ancestorFullyQualifiedName} not found.`);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
if (typeName === "number" || typeName === "string" || typeName === "boolean" || typeName === "bigint" || typeName === "symbol" || typeName === "undefined" || typeName === "null") {
|
|
46
|
+
if (typeName === "number" || typeName === "string" || typeName === "boolean" || typeName === "bigint" || typeName === "symbol" || typeName === "undefined" || typeName === "null" || typeName === "any" || typeName === "unknown" || typeName === "never" || typeName === "void") {
|
|
47
47
|
isPrimitiveType = true;
|
|
48
48
|
}
|
|
49
49
|
|