@eventra_dev/eventra-cli 0.1.28 → 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 +17 -148
- package/dist/engine/trackingDetector.js +3 -54
- package/dist/utils/universal.js +52 -89
- 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 -205
- package/src/engine/trackingDetector.ts +3 -80
- package/src/utils/universal.ts +64 -96
|
@@ -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,9 +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
|
-
const
|
|
11
|
+
const boundary_1 = require("./boundary");
|
|
13
12
|
function getCallName(expr) {
|
|
14
13
|
if (typescript_1.default.isIdentifier(expr))
|
|
15
14
|
return expr.text;
|
|
@@ -17,23 +16,6 @@ function getCallName(expr) {
|
|
|
17
16
|
return expr.name.text;
|
|
18
17
|
return "";
|
|
19
18
|
}
|
|
20
|
-
function getJsxAttrName(name) {
|
|
21
|
-
if (typescript_1.default.isIdentifier(name))
|
|
22
|
-
return name.text;
|
|
23
|
-
if (typescript_1.default.isJsxNamespacedName(name))
|
|
24
|
-
return name.name.text;
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
// unwrap
|
|
28
|
-
function unwrap(expr) {
|
|
29
|
-
while (typescript_1.default.isAsExpression(expr) ||
|
|
30
|
-
typescript_1.default.isParenthesizedExpression(expr) ||
|
|
31
|
-
typescript_1.default.isTypeAssertionExpression(expr) ||
|
|
32
|
-
typescript_1.default.isNonNullExpression(expr)) {
|
|
33
|
-
expr = expr.expression;
|
|
34
|
-
}
|
|
35
|
-
return expr;
|
|
36
|
-
}
|
|
37
19
|
function isValidEvent(v) {
|
|
38
20
|
return (v &&
|
|
39
21
|
v.length > 1 &&
|
|
@@ -41,31 +23,7 @@ function isValidEvent(v) {
|
|
|
41
23
|
!v.includes(" ") &&
|
|
42
24
|
/^[a-zA-Z0-9._:-]+$/.test(v));
|
|
43
25
|
}
|
|
44
|
-
function
|
|
45
|
-
let found = false;
|
|
46
|
-
function visit(node) {
|
|
47
|
-
if (typescript_1.default.isCallExpression(node)) {
|
|
48
|
-
if (!(0, trackingDetector_1.isTrackingCall)(node, checker)) {
|
|
49
|
-
typescript_1.default.forEachChild(node, visit);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
for (const arg of node.arguments) {
|
|
53
|
-
const a = unwrap(arg);
|
|
54
|
-
if ((typescript_1.default.isIdentifier(a) && a.text === prop) ||
|
|
55
|
-
(typescript_1.default.isPropertyAccessExpression(a) &&
|
|
56
|
-
a.name.text === prop)) {
|
|
57
|
-
found = true;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
typescript_1.default.forEachChild(node, visit);
|
|
62
|
-
}
|
|
63
|
-
if (fn.body) {
|
|
64
|
-
visit(fn.body);
|
|
65
|
-
}
|
|
66
|
-
return found;
|
|
67
|
-
}
|
|
68
|
-
function scanSource(source, checker, config, visited = new Set(), depth = 0, inheritedParamMap) {
|
|
26
|
+
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap) {
|
|
69
27
|
const events = new Set();
|
|
70
28
|
const detectedFunctionWrappers = new Set();
|
|
71
29
|
const detectedComponentWrappers = new Map();
|
|
@@ -74,40 +32,24 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
74
32
|
}
|
|
75
33
|
visited.add(source.fileName);
|
|
76
34
|
function visit(node) {
|
|
77
|
-
// CALL EXPRESSIONS
|
|
78
35
|
if (typescript_1.default.isCallExpression(node)) {
|
|
79
36
|
const name = getCallName(node.expression);
|
|
80
37
|
const isKnownWrapper = config.functionWrappers.some(w => w.name === name);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// fallback track(...)
|
|
89
|
-
if (!isTracking) {
|
|
90
|
-
if (typescript_1.default.isPropertyAccessExpression(node.expression) &&
|
|
91
|
-
node.expression.name.text === "track") {
|
|
92
|
-
isTracking = true;
|
|
93
|
-
}
|
|
94
|
-
else if (typescript_1.default.isIdentifier(node.expression) &&
|
|
95
|
-
node.expression.text === "track") {
|
|
96
|
-
isTracking = true;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
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 ||
|
|
42
|
+
(isAutoWrapper &&
|
|
43
|
+
resolvedFn &&
|
|
44
|
+
!(0, boundary_1.isExternalFile)(resolvedFn.getSourceFile().fileName));
|
|
99
45
|
if (!isTracking) {
|
|
100
46
|
typescript_1.default.forEachChild(node, visit);
|
|
101
47
|
return;
|
|
102
48
|
}
|
|
103
|
-
//
|
|
104
|
-
if (!isKnownWrapper &&
|
|
105
|
-
isAutoWrapper &&
|
|
106
|
-
!isTrackingByType &&
|
|
107
|
-
!name.startsWith("track")) {
|
|
49
|
+
// авто-детект wrapper
|
|
50
|
+
if (!isKnownWrapper && isAutoWrapper && name !== "track") {
|
|
108
51
|
detectedFunctionWrappers.add(name);
|
|
109
52
|
}
|
|
110
|
-
// PARAM MAP
|
|
111
53
|
const paramMap = new Map(inheritedParamMap ?? []);
|
|
112
54
|
if (resolvedFn && typescript_1.default.isFunctionLike(resolvedFn)) {
|
|
113
55
|
resolvedFn.parameters.forEach((param, i) => {
|
|
@@ -119,97 +61,24 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
119
61
|
}
|
|
120
62
|
});
|
|
121
63
|
}
|
|
122
|
-
|
|
123
|
-
// MAIN RESOLVE
|
|
64
|
+
// ✅ ТОЛЬКО правильный аргумент
|
|
124
65
|
if (resolvedFn) {
|
|
125
66
|
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
|
|
126
67
|
if (idx !== null && node.arguments[idx]) {
|
|
127
68
|
const res = (0, resolver_1.resolveNodeValue)(node.arguments[idx], checker, paramMap);
|
|
128
|
-
res.values
|
|
129
|
-
if (isValidEvent(v))
|
|
130
|
-
events.add(v);
|
|
131
|
-
});
|
|
132
|
-
handled = true;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
// fallback
|
|
136
|
-
if (!handled) {
|
|
137
|
-
for (const arg of node.arguments) {
|
|
138
|
-
const res = (0, resolver_1.resolveNodeValue)(arg, checker, paramMap);
|
|
139
|
-
res.values.forEach(v => {
|
|
140
|
-
if (isValidEvent(v))
|
|
69
|
+
for (const v of res.values) {
|
|
70
|
+
if (isValidEvent(v)) {
|
|
141
71
|
events.add(v);
|
|
142
|
-
|
|
72
|
+
}
|
|
73
|
+
}
|
|
143
74
|
}
|
|
144
75
|
}
|
|
145
76
|
// CROSS FILE
|
|
146
77
|
if (resolvedFn) {
|
|
147
78
|
const sf = resolvedFn.getSourceFile();
|
|
148
79
|
if (!visited.has(sf.fileName)) {
|
|
149
|
-
const res = scanSource(sf, checker, config, visited,
|
|
150
|
-
res.events.forEach(e => events.add(e));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// JSX
|
|
155
|
-
if (typescript_1.default.isJsxSelfClosingElement(node) ||
|
|
156
|
-
typescript_1.default.isJsxOpeningElement(node)) {
|
|
157
|
-
const tagNode = typescript_1.default.isPropertyAccessExpression(node.tagName)
|
|
158
|
-
? node.tagName.name
|
|
159
|
-
: node.tagName;
|
|
160
|
-
let symbol = checker.getSymbolAtLocation(tagNode);
|
|
161
|
-
if (symbol) {
|
|
162
|
-
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
163
|
-
}
|
|
164
|
-
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
165
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
166
|
-
}
|
|
167
|
-
if (!symbol) {
|
|
168
|
-
typescript_1.default.forEachChild(node, visit);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const tagName = typescript_1.default.isPropertyAccessExpression(node.tagName)
|
|
172
|
-
? node.tagName.name.getText()
|
|
173
|
-
: node.tagName.getText();
|
|
174
|
-
for (const decl of symbol.getDeclarations() ?? []) {
|
|
175
|
-
let fn = null;
|
|
176
|
-
if (typescript_1.default.isFunctionDeclaration(decl))
|
|
177
|
-
fn = decl;
|
|
178
|
-
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
179
|
-
const init = decl.initializer;
|
|
180
|
-
if (typescript_1.default.isArrowFunction(init) ||
|
|
181
|
-
typescript_1.default.isFunctionExpression(init)) {
|
|
182
|
-
fn = init;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (!fn)
|
|
186
|
-
continue;
|
|
187
|
-
const param = fn.parameters[0];
|
|
188
|
-
if (!param || !typescript_1.default.isObjectBindingPattern(param.name))
|
|
189
|
-
continue;
|
|
190
|
-
for (const el of param.name.elements) {
|
|
191
|
-
const prop = el.name.getText();
|
|
192
|
-
const isEvent = isPropUsedInTracking(fn, checker, prop) ||
|
|
193
|
-
config.wrappers.some(w => w.name === tagName && w.prop === prop);
|
|
194
|
-
if (!isEvent)
|
|
195
|
-
continue;
|
|
196
|
-
const attr = node.attributes.properties.find(p => {
|
|
197
|
-
if (!typescript_1.default.isJsxAttribute(p))
|
|
198
|
-
return false;
|
|
199
|
-
return getJsxAttrName(p.name) === prop;
|
|
200
|
-
});
|
|
201
|
-
if (!attr || !attr.initializer)
|
|
202
|
-
continue;
|
|
203
|
-
const expr = typescript_1.default.isJsxExpression(attr.initializer)
|
|
204
|
-
? attr.initializer.expression
|
|
205
|
-
: attr.initializer;
|
|
206
|
-
if (!expr)
|
|
207
|
-
continue;
|
|
208
|
-
const paramMap = new Map();
|
|
209
|
-
paramMap.set(prop, expr);
|
|
210
|
-
const res = scanSource(fn.getSourceFile(), checker, config, visited, depth + 1, paramMap);
|
|
80
|
+
const res = scanSource(sf, checker, config, visited, paramMap);
|
|
211
81
|
res.events.forEach(e => events.add(e));
|
|
212
|
-
detectedComponentWrappers.set(tagName, prop);
|
|
213
82
|
}
|
|
214
83
|
}
|
|
215
84
|
}
|
|
@@ -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
|
}
|