@eventra_dev/eventra-cli 0.1.29 → 0.1.30
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/engine/functionAnalyzer.js +15 -50
- package/dist/engine/resolver.js +57 -138
- package/dist/engine/scanner.js +16 -59
- package/dist/engine/trackingDetector.js +3 -54
- package/dist/utils/universal.js +49 -68
- package/package.json +1 -1
- package/src/engine/functionAnalyzer.ts +14 -54
- package/src/engine/resolver.ts +83 -184
- package/src/engine/scanner.ts +15 -81
- package/src/engine/trackingDetector.ts +3 -80
- package/src/utils/universal.ts +60 -74
|
@@ -10,25 +10,11 @@ function findTrackParamIndex(fn) {
|
|
|
10
10
|
fn.parameters.forEach((p, i) => {
|
|
11
11
|
if (typescript_1.default.isIdentifier(p.name)) {
|
|
12
12
|
paramIndex.set(p.name.text, i);
|
|
13
|
-
// default param: (name = "x")
|
|
14
|
-
if (p.initializer && typescript_1.default.isStringLiteral(p.initializer)) {
|
|
15
|
-
if (i === 0)
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
13
|
}
|
|
19
14
|
});
|
|
20
15
|
let result = null;
|
|
21
|
-
function
|
|
22
|
-
//
|
|
23
|
-
while (typescript_1.default.isAsExpression(node) ||
|
|
24
|
-
typescript_1.default.isParenthesizedExpression(node)) {
|
|
25
|
-
node = node.expression;
|
|
26
|
-
}
|
|
27
|
-
return node;
|
|
28
|
-
}
|
|
29
|
-
function checkArg(arg) {
|
|
30
|
-
arg = unwrap(arg);
|
|
31
|
-
// identifier → param
|
|
16
|
+
function check(arg) {
|
|
17
|
+
// identifier
|
|
32
18
|
if (typescript_1.default.isIdentifier(arg)) {
|
|
33
19
|
const idx = paramIndex.get(arg.text);
|
|
34
20
|
if (idx !== undefined) {
|
|
@@ -36,12 +22,7 @@ function findTrackParamIndex(fn) {
|
|
|
36
22
|
return true;
|
|
37
23
|
}
|
|
38
24
|
}
|
|
39
|
-
//
|
|
40
|
-
if (typescript_1.default.isStringLiteral(arg)) {
|
|
41
|
-
result = 0;
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
// template `${name}`
|
|
25
|
+
// template
|
|
45
26
|
if (typescript_1.default.isTemplateExpression(arg)) {
|
|
46
27
|
for (const span of arg.templateSpans) {
|
|
47
28
|
if (typescript_1.default.isIdentifier(span.expression)) {
|
|
@@ -52,49 +33,33 @@ function findTrackParamIndex(fn) {
|
|
|
52
33
|
}
|
|
53
34
|
}
|
|
54
35
|
}
|
|
55
|
-
result = 0;
|
|
56
|
-
return true;
|
|
57
36
|
}
|
|
58
37
|
// object { event: name }
|
|
59
38
|
if (typescript_1.default.isObjectLiteralExpression(arg)) {
|
|
60
39
|
for (const prop of arg.properties) {
|
|
61
40
|
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
62
41
|
continue;
|
|
63
|
-
const
|
|
64
|
-
if (["event", "name", "type"].includes(
|
|
65
|
-
return
|
|
42
|
+
const key = prop.name.getText();
|
|
43
|
+
if (["event", "name", "type"].includes(key)) {
|
|
44
|
+
return check(prop.initializer);
|
|
66
45
|
}
|
|
67
46
|
}
|
|
68
47
|
}
|
|
69
|
-
// call: String(name)
|
|
70
|
-
if (typescript_1.default.isCallExpression(arg)) {
|
|
71
|
-
for (const inner of arg.arguments) {
|
|
72
|
-
if (checkArg(inner))
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
48
|
return false;
|
|
77
49
|
}
|
|
78
50
|
function visit(node) {
|
|
79
51
|
if (result !== null)
|
|
80
52
|
return;
|
|
81
53
|
if (typescript_1.default.isCallExpression(node)) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (isTrackCall) {
|
|
94
|
-
for (const arg of node.arguments) {
|
|
95
|
-
if (checkArg(arg))
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
54
|
+
const expr = node.expression;
|
|
55
|
+
const isTrack = (typescript_1.default.isIdentifier(expr) && expr.text === "track") ||
|
|
56
|
+
(typescript_1.default.isPropertyAccessExpression(expr) &&
|
|
57
|
+
expr.name.text === "track");
|
|
58
|
+
if (!isTrack)
|
|
59
|
+
return;
|
|
60
|
+
for (const arg of node.arguments) {
|
|
61
|
+
if (check(arg))
|
|
62
|
+
return;
|
|
98
63
|
}
|
|
99
64
|
}
|
|
100
65
|
typescript_1.default.forEachChild(node, visit);
|
package/dist/engine/resolver.js
CHANGED
|
@@ -11,7 +11,7 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
11
11
|
return { values: [], dynamic: true };
|
|
12
12
|
}
|
|
13
13
|
seen.add(node);
|
|
14
|
-
// PARAM
|
|
14
|
+
// PARAM MAP (wrapper args)
|
|
15
15
|
if (typescript_1.default.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
16
16
|
return resolveNodeValue(paramMap.get(node.text), checker, paramMap, seen);
|
|
17
17
|
}
|
|
@@ -19,36 +19,19 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
19
19
|
if (typescript_1.default.isStringLiteral(node)) {
|
|
20
20
|
return { values: [node.text], dynamic: false };
|
|
21
21
|
}
|
|
22
|
-
// ARRAY
|
|
23
|
-
if (typescript_1.default.isArrayLiteralExpression(node)) {
|
|
24
|
-
const values = [];
|
|
25
|
-
for (const el of node.elements) {
|
|
26
|
-
const res = resolveNodeValue(el, checker, paramMap, seen);
|
|
27
|
-
if (res.values.length) {
|
|
28
|
-
values.push(...res.values);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
values: [...new Set(values)],
|
|
33
|
-
dynamic: true
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
22
|
// TEMPLATE
|
|
37
23
|
if (typescript_1.default.isTemplateExpression(node)) {
|
|
38
24
|
let results = node.head.text ? [node.head.text] : [""];
|
|
39
25
|
for (const span of node.templateSpans) {
|
|
40
26
|
const res = resolveNodeValue(span.expression, checker, paramMap, seen);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
else {
|
|
45
|
-
results = results.map(r => r + "*");
|
|
46
|
-
}
|
|
27
|
+
results = results.flatMap(r => res.values.length
|
|
28
|
+
? res.values.map(v => r + v)
|
|
29
|
+
: [r + "*"]);
|
|
47
30
|
results = results.map(r => r + span.literal.text);
|
|
48
31
|
}
|
|
49
32
|
return { values: results, dynamic: true };
|
|
50
33
|
}
|
|
51
|
-
// CONDITIONAL
|
|
34
|
+
// CONDITIONAL (a ? b : c)
|
|
52
35
|
if (typescript_1.default.isConditionalExpression(node)) {
|
|
53
36
|
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
54
37
|
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
@@ -57,23 +40,57 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
57
40
|
dynamic: true,
|
|
58
41
|
};
|
|
59
42
|
}
|
|
43
|
+
// OBJECT → берём ВСЕ значения (без привязки к ключам)
|
|
44
|
+
if (typescript_1.default.isObjectLiteralExpression(node)) {
|
|
45
|
+
const values = [];
|
|
46
|
+
for (const prop of node.properties) {
|
|
47
|
+
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
48
|
+
continue;
|
|
49
|
+
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
50
|
+
values.push(...res.values);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
values: [...new Set(values)],
|
|
54
|
+
dynamic: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ARRAY
|
|
58
|
+
if (typescript_1.default.isArrayLiteralExpression(node)) {
|
|
59
|
+
const values = [];
|
|
60
|
+
for (const el of node.elements) {
|
|
61
|
+
const res = resolveNodeValue(el, checker, paramMap, seen);
|
|
62
|
+
values.push(...res.values);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
values: [...new Set(values)],
|
|
66
|
+
dynamic: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// BINARY CONCAT
|
|
70
|
+
if (typescript_1.default.isBinaryExpression(node)) {
|
|
71
|
+
if (node.operatorToken.kind === typescript_1.default.SyntaxKind.PlusToken) {
|
|
72
|
+
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
73
|
+
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
74
|
+
return {
|
|
75
|
+
values: left.values.flatMap(l => right.values.length
|
|
76
|
+
? right.values.map(r => l + r)
|
|
77
|
+
: [l + "*"]),
|
|
78
|
+
dynamic: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
60
82
|
// IDENTIFIER
|
|
61
83
|
if (typescript_1.default.isIdentifier(node)) {
|
|
62
84
|
let symbol = checker.getSymbolAtLocation(node);
|
|
63
85
|
if (!symbol)
|
|
64
86
|
return { values: [], dynamic: true };
|
|
65
|
-
|
|
87
|
+
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
88
|
+
// const VAR = "click"
|
|
66
89
|
for (const decl of symbol.getDeclarations() ?? []) {
|
|
67
90
|
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
68
91
|
return resolveNodeValue(decl.initializer, checker, paramMap, seen);
|
|
69
92
|
}
|
|
70
|
-
|
|
71
|
-
// unwrap export
|
|
72
|
-
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
73
|
-
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
74
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
75
|
-
}
|
|
76
|
-
for (const decl of symbol.getDeclarations() ?? []) {
|
|
93
|
+
// enum
|
|
77
94
|
if (typescript_1.default.isEnumMember(decl) && decl.initializer) {
|
|
78
95
|
if (typescript_1.default.isStringLiteral(decl.initializer)) {
|
|
79
96
|
return {
|
|
@@ -83,29 +100,9 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
}
|
|
86
|
-
const type = checker.getTypeAtLocation(node);
|
|
87
|
-
// UNION
|
|
88
|
-
if (type.isUnion()) {
|
|
89
|
-
const values = [];
|
|
90
|
-
for (const t of type.types) {
|
|
91
|
-
if (t.flags & typescript_1.default.TypeFlags.StringLiteral) {
|
|
92
|
-
values.push(t.value);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (values.length) {
|
|
96
|
-
return { values, dynamic: false };
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// STRING LITERAL TYPE
|
|
100
|
-
if (type.flags & typescript_1.default.TypeFlags.StringLiteral) {
|
|
101
|
-
return {
|
|
102
|
-
values: [type.value],
|
|
103
|
-
dynamic: false
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
103
|
return { values: [], dynamic: true };
|
|
107
104
|
}
|
|
108
|
-
// PROPERTY ACCESS
|
|
105
|
+
// PROPERTY ACCESS (ENUM / CONST)
|
|
109
106
|
if (typescript_1.default.isPropertyAccessExpression(node)) {
|
|
110
107
|
const symbol = checker.getSymbolAtLocation(node);
|
|
111
108
|
if (symbol) {
|
|
@@ -124,96 +121,18 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
124
121
|
}
|
|
125
122
|
}
|
|
126
123
|
}
|
|
127
|
-
|
|
128
|
-
if (typeof constantValue === "string") {
|
|
129
|
-
return { values: [constantValue], dynamic: false };
|
|
130
|
-
}
|
|
131
|
-
const type = checker.getTypeAtLocation(node);
|
|
132
|
-
if (type.flags & typescript_1.default.TypeFlags.StringLiteral) {
|
|
133
|
-
return {
|
|
134
|
-
values: [type.value],
|
|
135
|
-
dynamic: false,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
// object literal resolve
|
|
139
|
-
if (typescript_1.default.isIdentifier(node.expression)) {
|
|
140
|
-
const objSymbol = checker.getSymbolAtLocation(node.expression);
|
|
141
|
-
if (objSymbol) {
|
|
142
|
-
for (const decl of objSymbol.getDeclarations() ?? []) {
|
|
143
|
-
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
144
|
-
decl.initializer &&
|
|
145
|
-
typescript_1.default.isObjectLiteralExpression(decl.initializer)) {
|
|
146
|
-
for (const prop of decl.initializer.properties) {
|
|
147
|
-
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
148
|
-
continue;
|
|
149
|
-
const name = prop.name.getText();
|
|
150
|
-
if (name === node.name.text) {
|
|
151
|
-
return resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// fallback
|
|
159
|
-
return resolveNodeValue(node.name, checker, paramMap, seen);
|
|
160
|
-
}
|
|
161
|
-
// ARRAY ACCESS
|
|
162
|
-
if (typescript_1.default.isElementAccessExpression(node)) {
|
|
163
|
-
const obj = resolveNodeValue(node.expression, checker, paramMap, seen);
|
|
164
|
-
if (!node.argumentExpression) {
|
|
165
|
-
return { values: obj.values, dynamic: true };
|
|
166
|
-
}
|
|
167
|
-
const index = resolveNodeValue(node.argumentExpression, checker, paramMap, seen);
|
|
168
|
-
if (index.values.length === 1) {
|
|
169
|
-
const i = Number(index.values[0]);
|
|
170
|
-
if (!Number.isNaN(i) && obj.values[i] !== undefined) {
|
|
171
|
-
return {
|
|
172
|
-
values: [obj.values[i]],
|
|
173
|
-
dynamic: false,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return obj.values.length
|
|
178
|
-
? { values: obj.values, dynamic: true }
|
|
179
|
-
: { values: [], dynamic: true };
|
|
124
|
+
return { values: [], dynamic: true };
|
|
180
125
|
}
|
|
181
|
-
//
|
|
182
|
-
if (typescript_1.default.
|
|
126
|
+
// CALL → unwrap args
|
|
127
|
+
if (typescript_1.default.isCallExpression(node)) {
|
|
183
128
|
const values = [];
|
|
184
|
-
for (const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const name = prop.name.getText();
|
|
188
|
-
if (["event", "name", "type"].includes(name)) {
|
|
189
|
-
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
190
|
-
values.push(...res.values);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return {
|
|
194
|
-
values: [...new Set(values)],
|
|
195
|
-
dynamic: true
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
// BINARY ("btn_" + name)
|
|
199
|
-
if (typescript_1.default.isBinaryExpression(node)) {
|
|
200
|
-
if (node.operatorToken.kind === typescript_1.default.SyntaxKind.PlusToken) {
|
|
201
|
-
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
202
|
-
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
203
|
-
if (!left.values.length) {
|
|
204
|
-
return { values: [], dynamic: true };
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
values: left.values.flatMap(l => right.values.length
|
|
208
|
-
? right.values.map(r => l + r)
|
|
209
|
-
: [l + "*"]),
|
|
210
|
-
dynamic: true,
|
|
211
|
-
};
|
|
129
|
+
for (const arg of node.arguments) {
|
|
130
|
+
const res = resolveNodeValue(arg, checker, paramMap, seen);
|
|
131
|
+
values.push(...res.values);
|
|
212
132
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return { values: [], dynamic: true };
|
|
133
|
+
return values.length
|
|
134
|
+
? { values, dynamic: true }
|
|
135
|
+
: { values: [], dynamic: true };
|
|
217
136
|
}
|
|
218
137
|
return { values: [], dynamic: true };
|
|
219
138
|
}
|
package/dist/engine/scanner.js
CHANGED
|
@@ -7,10 +7,8 @@ exports.scanSource = scanSource;
|
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
8
|
const resolver_1 = require("./resolver");
|
|
9
9
|
const callResolver_1 = require("./callResolver");
|
|
10
|
-
const exportResolver_1 = require("./exportResolver");
|
|
11
10
|
const functionAnalyzer_1 = require("./functionAnalyzer");
|
|
12
11
|
const boundary_1 = require("./boundary");
|
|
13
|
-
const trackingDetector_1 = require("./trackingDetector");
|
|
14
12
|
function getCallName(expr) {
|
|
15
13
|
if (typescript_1.default.isIdentifier(expr))
|
|
16
14
|
return expr.text;
|
|
@@ -25,31 +23,7 @@ function isValidEvent(v) {
|
|
|
25
23
|
!v.includes(" ") &&
|
|
26
24
|
/^[a-zA-Z0-9._:-]+$/.test(v));
|
|
27
25
|
}
|
|
28
|
-
function
|
|
29
|
-
let fn = (0, callResolver_1.resolveFunctionFromCall)(expr, checker);
|
|
30
|
-
if (fn)
|
|
31
|
-
return fn;
|
|
32
|
-
let symbol = checker.getSymbolAtLocation(expr);
|
|
33
|
-
if (!symbol)
|
|
34
|
-
return null;
|
|
35
|
-
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
36
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
37
|
-
}
|
|
38
|
-
const resolvedSymbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
39
|
-
for (const decl of resolvedSymbol.getDeclarations() ?? []) {
|
|
40
|
-
if (typescript_1.default.isFunctionDeclaration(decl)) {
|
|
41
|
-
return decl;
|
|
42
|
-
}
|
|
43
|
-
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
44
|
-
decl.initializer &&
|
|
45
|
-
(typescript_1.default.isArrowFunction(decl.initializer) ||
|
|
46
|
-
typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
47
|
-
return decl.initializer;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
function scanSource(source, checker, config, visited = new Set(), depth = 0, inheritedParamMap) {
|
|
26
|
+
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap) {
|
|
53
27
|
const events = new Set();
|
|
54
28
|
const detectedFunctionWrappers = new Set();
|
|
55
29
|
const detectedComponentWrappers = new Map();
|
|
@@ -58,42 +32,24 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
58
32
|
}
|
|
59
33
|
visited.add(source.fileName);
|
|
60
34
|
function visit(node) {
|
|
61
|
-
// CALL EXPRESSIONS
|
|
62
35
|
if (typescript_1.default.isCallExpression(node)) {
|
|
63
36
|
const name = getCallName(node.expression);
|
|
64
37
|
const isKnownWrapper = config.functionWrappers.some(w => w.name === name);
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
isTrackingByType ||
|
|
70
|
-
name.startsWith("$") ||
|
|
38
|
+
const resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
|
|
39
|
+
const isAutoWrapper = !!resolvedFn && (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn) !== null;
|
|
40
|
+
const isTracking = name === "track" ||
|
|
41
|
+
isKnownWrapper ||
|
|
71
42
|
(isAutoWrapper &&
|
|
72
43
|
resolvedFn &&
|
|
73
|
-
resolvedFn.getSourceFile &&
|
|
74
44
|
!(0, boundary_1.isExternalFile)(resolvedFn.getSourceFile().fileName));
|
|
75
|
-
if (!isTracking) {
|
|
76
|
-
if (typescript_1.default.isPropertyAccessExpression(node.expression) &&
|
|
77
|
-
node.expression.name.text === "track") {
|
|
78
|
-
isTracking = true;
|
|
79
|
-
}
|
|
80
|
-
else if (typescript_1.default.isIdentifier(node.expression) &&
|
|
81
|
-
node.expression.text === "track") {
|
|
82
|
-
isTracking = true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
45
|
if (!isTracking) {
|
|
86
46
|
typescript_1.default.forEachChild(node, visit);
|
|
87
47
|
return;
|
|
88
48
|
}
|
|
89
|
-
//
|
|
90
|
-
if (!isKnownWrapper &&
|
|
91
|
-
isAutoWrapper &&
|
|
92
|
-
!isTrackingByType &&
|
|
93
|
-
!name.startsWith("track")) {
|
|
49
|
+
// авто-детект wrapper
|
|
50
|
+
if (!isKnownWrapper && isAutoWrapper && name !== "track") {
|
|
94
51
|
detectedFunctionWrappers.add(name);
|
|
95
52
|
}
|
|
96
|
-
// PARAM MAP
|
|
97
53
|
const paramMap = new Map(inheritedParamMap ?? []);
|
|
98
54
|
if (resolvedFn && typescript_1.default.isFunctionLike(resolvedFn)) {
|
|
99
55
|
resolvedFn.parameters.forEach((param, i) => {
|
|
@@ -105,22 +61,23 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
105
61
|
}
|
|
106
62
|
});
|
|
107
63
|
}
|
|
108
|
-
//
|
|
64
|
+
// ✅ ТОЛЬКО правильный аргумент
|
|
109
65
|
if (resolvedFn) {
|
|
110
66
|
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
|
|
111
67
|
if (idx !== null && node.arguments[idx]) {
|
|
112
68
|
const res = (0, resolver_1.resolveNodeValue)(node.arguments[idx], checker, paramMap);
|
|
113
|
-
res.values
|
|
114
|
-
if (isValidEvent(v))
|
|
69
|
+
for (const v of res.values) {
|
|
70
|
+
if (isValidEvent(v)) {
|
|
115
71
|
events.add(v);
|
|
116
|
-
|
|
72
|
+
}
|
|
73
|
+
}
|
|
117
74
|
}
|
|
118
75
|
}
|
|
119
|
-
// CROSS
|
|
76
|
+
// CROSS FILE
|
|
120
77
|
if (resolvedFn) {
|
|
121
|
-
const sf = resolvedFn.getSourceFile
|
|
122
|
-
if (
|
|
123
|
-
const res = scanSource(sf, checker, config, visited,
|
|
78
|
+
const sf = resolvedFn.getSourceFile();
|
|
79
|
+
if (!visited.has(sf.fileName)) {
|
|
80
|
+
const res = scanSource(sf, checker, config, visited, paramMap);
|
|
124
81
|
res.events.forEach(e => events.add(e));
|
|
125
82
|
}
|
|
126
83
|
}
|
|
@@ -5,64 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isTrackingCall = isTrackingCall;
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
-
function
|
|
9
|
-
return ((type.flags & typescript_1.default.TypeFlags.StringLike) !== 0 ||
|
|
10
|
-
(type.flags & typescript_1.default.TypeFlags.StringLiteral) !== 0);
|
|
11
|
-
}
|
|
12
|
-
function hasTrackLikeSignature(type, checker) {
|
|
13
|
-
const sigs = type.getCallSignatures();
|
|
14
|
-
for (const sig of sigs) {
|
|
15
|
-
const params = sig.getParameters();
|
|
16
|
-
if (!params.length)
|
|
17
|
-
continue;
|
|
18
|
-
const t = checker.getTypeOfSymbolAtLocation(params[0], params[0].valueDeclaration);
|
|
19
|
-
if (isStringLike(t)) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
function isEventraLikeType(type, checker) {
|
|
26
|
-
const props = type.getProperties();
|
|
27
|
-
for (const prop of props) {
|
|
28
|
-
const decl = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
29
|
-
if (!decl)
|
|
30
|
-
continue;
|
|
31
|
-
const t = checker.getTypeOfSymbolAtLocation(prop, decl);
|
|
32
|
-
if (hasTrackLikeSignature(t, checker)) {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
function isTrackingCall(node, checker) {
|
|
8
|
+
function isTrackingCall(node) {
|
|
39
9
|
const expr = node.expression;
|
|
40
|
-
// fn(...)
|
|
41
10
|
if (typescript_1.default.isIdentifier(expr)) {
|
|
42
|
-
|
|
43
|
-
if (hasTrackLikeSignature(type, checker)) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
11
|
+
return expr.text === "track";
|
|
46
12
|
}
|
|
47
|
-
// obj.method(...)
|
|
48
13
|
if (typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
49
|
-
|
|
50
|
-
// объект похож на tracker
|
|
51
|
-
if (isEventraLikeType(objType, checker)) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
// fallback
|
|
55
|
-
const methodType = checker.getTypeAtLocation(expr.name);
|
|
56
|
-
if (hasTrackLikeSignature(methodType, checker)) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// obj["track"](...)
|
|
61
|
-
if (typescript_1.default.isElementAccessExpression(expr)) {
|
|
62
|
-
const objType = checker.getTypeAtLocation(expr.expression);
|
|
63
|
-
if (isEventraLikeType(objType, checker)) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
14
|
+
return expr.name.text === "track";
|
|
66
15
|
}
|
|
67
16
|
return false;
|
|
68
17
|
}
|