@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
package/dist/utils/universal.js
CHANGED
|
@@ -5,73 +5,62 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.parseUniversal = parseUniversal;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
//
|
|
8
|
+
// Remove comments
|
|
9
9
|
function stripComments(input) {
|
|
10
10
|
return input
|
|
11
11
|
.replace(/<!--[\s\S]*?-->/g, "")
|
|
12
12
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
13
13
|
.replace(/\/\/.*$/gm, "");
|
|
14
14
|
}
|
|
15
|
-
//
|
|
16
|
-
function
|
|
15
|
+
// Basic filter
|
|
16
|
+
function looksLikeTracking(code) {
|
|
17
|
+
return /track/i.test(code);
|
|
18
|
+
}
|
|
19
|
+
// HTML inline handlers (onclick="")
|
|
20
|
+
function extractHTMLHandlers(input) {
|
|
17
21
|
const result = [];
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
while (i < len) {
|
|
23
|
-
const ch = input[i];
|
|
24
|
-
if (inString) {
|
|
25
|
-
if (escape)
|
|
26
|
-
escape = false;
|
|
27
|
-
else if (ch === "\\")
|
|
28
|
-
escape = true;
|
|
29
|
-
else if (ch === inString)
|
|
30
|
-
inString = null;
|
|
31
|
-
i++;
|
|
32
|
-
continue;
|
|
22
|
+
const matches = input.matchAll(/\bon\w+\s*=\s*["']([^"']+)["']/g);
|
|
23
|
+
for (const m of matches) {
|
|
24
|
+
if (looksLikeTracking(m[1])) {
|
|
25
|
+
result.push(m[1]);
|
|
33
26
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
// Vue handlers (@click / v-on)
|
|
31
|
+
function extractVueHandlers(input) {
|
|
32
|
+
const result = [];
|
|
33
|
+
const matches = input.matchAll(/(?:@|v-on:)[\w:-]+\s*=\s*["']([^"']+)["']/g);
|
|
34
|
+
for (const m of matches) {
|
|
35
|
+
if (looksLikeTracking(m[1])) {
|
|
36
|
+
result.push(m[1]);
|
|
38
37
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (depth === 0) {
|
|
50
|
-
const expr = input.slice(i + 1, j - 1).trim();
|
|
51
|
-
if (expr && /[a-zA-Z0-9_$]\s*\(/.test(expr)) {
|
|
52
|
-
result.push(expr);
|
|
53
|
-
}
|
|
54
|
-
i = j;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
// Svelte handlers (on:click)
|
|
42
|
+
function extractSvelteHandlers(input) {
|
|
43
|
+
const result = [];
|
|
44
|
+
const matches = input.matchAll(/\bon:[\w]+\s*=\s*{([^}]+)}/g);
|
|
45
|
+
for (const m of matches) {
|
|
46
|
+
if (looksLikeTracking(m[1])) {
|
|
47
|
+
result.push(m[1]);
|
|
57
48
|
}
|
|
58
|
-
i++;
|
|
59
49
|
}
|
|
60
50
|
return result;
|
|
61
51
|
}
|
|
62
|
-
//
|
|
63
|
-
function
|
|
52
|
+
// JSX / template { ... }
|
|
53
|
+
function extractJSExpressions(input) {
|
|
64
54
|
const result = [];
|
|
65
|
-
const matches = input.matchAll(/\
|
|
55
|
+
const matches = input.matchAll(/\{([^}]+)\}/g);
|
|
66
56
|
for (const m of matches) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
result.push(code);
|
|
57
|
+
if (looksLikeTracking(m[1])) {
|
|
58
|
+
result.push(m[1]);
|
|
70
59
|
}
|
|
71
60
|
}
|
|
72
61
|
return result;
|
|
73
62
|
}
|
|
74
|
-
//
|
|
63
|
+
// dynamic import()
|
|
75
64
|
function extractDynamicImports(input, file) {
|
|
76
65
|
const deps = [];
|
|
77
66
|
const calls = [];
|
|
@@ -82,14 +71,12 @@ function extractDynamicImports(input, file) {
|
|
|
82
71
|
? path_1.default.resolve(process.cwd(), "." + mod)
|
|
83
72
|
: path_1.default.resolve(path_1.default.dirname(file), mod);
|
|
84
73
|
deps.push(resolved);
|
|
85
|
-
|
|
74
|
+
if (looksLikeTracking(mod)) {
|
|
75
|
+
calls.push(`import("${mod}")`);
|
|
76
|
+
}
|
|
86
77
|
}
|
|
87
78
|
return { deps, calls };
|
|
88
79
|
}
|
|
89
|
-
// Filter only probable tracking-related code
|
|
90
|
-
function isProbablyTracking(code) {
|
|
91
|
-
return /track|event|analytics/i.test(code);
|
|
92
|
-
}
|
|
93
80
|
// UNIVERSAL PARSER
|
|
94
81
|
function parseUniversal(content, file) {
|
|
95
82
|
const parts = [];
|
|
@@ -112,28 +99,22 @@ function parseUniversal(content, file) {
|
|
|
112
99
|
// INLINE <script>
|
|
113
100
|
const scripts = clean.matchAll(/<script\b([^>]*)>([\s\S]*?)<\/script>/g);
|
|
114
101
|
for (const m of scripts) {
|
|
115
|
-
const attrs = m[1];
|
|
116
|
-
if (/type=["']application\/json/.test(attrs))
|
|
117
|
-
continue;
|
|
118
102
|
if (m[2].trim()) {
|
|
119
103
|
parts.push(m[2]);
|
|
120
104
|
}
|
|
121
105
|
}
|
|
122
|
-
//
|
|
106
|
+
// TEMPLATE PART
|
|
123
107
|
const noScripts = clean.replace(/<script[\s\S]*?<\/script>/g, "");
|
|
124
|
-
//
|
|
125
|
-
parts.push(...
|
|
126
|
-
|
|
108
|
+
// handlers
|
|
109
|
+
parts.push(...extractHTMLHandlers(noScripts));
|
|
110
|
+
parts.push(...extractVueHandlers(noScripts));
|
|
111
|
+
parts.push(...extractSvelteHandlers(noScripts));
|
|
112
|
+
// JSX / Astro / Vue expressions
|
|
113
|
+
parts.push(...extractJSExpressions(clean));
|
|
114
|
+
// dynamic imports
|
|
127
115
|
const dyn = extractDynamicImports(clean, file);
|
|
128
116
|
deps.push(...dyn.deps);
|
|
129
|
-
parts.push(...dyn.calls
|
|
130
|
-
// EXPRESSIONS
|
|
131
|
-
const extracted = extractJS(clean);
|
|
132
|
-
for (const e of extracted) {
|
|
133
|
-
if (isProbablyTracking(e)) {
|
|
134
|
-
parts.push(e + ";");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
117
|
+
parts.push(...dyn.calls);
|
|
137
118
|
return {
|
|
138
119
|
code: [...new Set(parts)].join("\n"),
|
|
139
120
|
deps: [...new Set(deps.filter(Boolean))],
|
package/package.json
CHANGED
|
@@ -8,31 +8,13 @@ export function findTrackParamIndex(
|
|
|
8
8
|
fn.parameters.forEach((p, i) => {
|
|
9
9
|
if (ts.isIdentifier(p.name)) {
|
|
10
10
|
paramIndex.set(p.name.text, i);
|
|
11
|
-
|
|
12
|
-
// default param: (name = "x")
|
|
13
|
-
if (p.initializer && ts.isStringLiteral(p.initializer)) {
|
|
14
|
-
if (i === 0) return 0;
|
|
15
|
-
}
|
|
16
11
|
}
|
|
17
12
|
});
|
|
18
13
|
|
|
19
14
|
let result: number | null = null;
|
|
20
15
|
|
|
21
|
-
function
|
|
22
|
-
//
|
|
23
|
-
while (
|
|
24
|
-
ts.isAsExpression(node) ||
|
|
25
|
-
ts.isParenthesizedExpression(node)
|
|
26
|
-
) {
|
|
27
|
-
node = node.expression;
|
|
28
|
-
}
|
|
29
|
-
return node;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function checkArg(arg: ts.Expression) {
|
|
33
|
-
arg = unwrap(arg);
|
|
34
|
-
|
|
35
|
-
// identifier → param
|
|
16
|
+
function check(arg: ts.Expression): boolean {
|
|
17
|
+
// identifier
|
|
36
18
|
if (ts.isIdentifier(arg)) {
|
|
37
19
|
const idx = paramIndex.get(arg.text);
|
|
38
20
|
if (idx !== undefined) {
|
|
@@ -41,13 +23,7 @@ export function findTrackParamIndex(
|
|
|
41
23
|
}
|
|
42
24
|
}
|
|
43
25
|
|
|
44
|
-
//
|
|
45
|
-
if (ts.isStringLiteral(arg)) {
|
|
46
|
-
result = 0;
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// template `${name}`
|
|
26
|
+
// template
|
|
51
27
|
if (ts.isTemplateExpression(arg)) {
|
|
52
28
|
for (const span of arg.templateSpans) {
|
|
53
29
|
if (ts.isIdentifier(span.expression)) {
|
|
@@ -58,8 +34,6 @@ export function findTrackParamIndex(
|
|
|
58
34
|
}
|
|
59
35
|
}
|
|
60
36
|
}
|
|
61
|
-
result = 0;
|
|
62
|
-
return true;
|
|
63
37
|
}
|
|
64
38
|
|
|
65
39
|
// object { event: name }
|
|
@@ -67,21 +41,14 @@ export function findTrackParamIndex(
|
|
|
67
41
|
for (const prop of arg.properties) {
|
|
68
42
|
if (!ts.isPropertyAssignment(prop)) continue;
|
|
69
43
|
|
|
70
|
-
const
|
|
44
|
+
const key = prop.name.getText();
|
|
71
45
|
|
|
72
|
-
if (["event", "name", "type"].includes(
|
|
73
|
-
return
|
|
46
|
+
if (["event", "name", "type"].includes(key)) {
|
|
47
|
+
return check(prop.initializer);
|
|
74
48
|
}
|
|
75
49
|
}
|
|
76
50
|
}
|
|
77
51
|
|
|
78
|
-
// call: String(name)
|
|
79
|
-
if (ts.isCallExpression(arg)) {
|
|
80
|
-
for (const inner of arg.arguments) {
|
|
81
|
-
if (checkArg(inner)) return true;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
52
|
return false;
|
|
86
53
|
}
|
|
87
54
|
|
|
@@ -89,24 +56,17 @@ export function findTrackParamIndex(
|
|
|
89
56
|
if (result !== null) return;
|
|
90
57
|
|
|
91
58
|
if (ts.isCallExpression(node)) {
|
|
92
|
-
|
|
59
|
+
const expr = node.expression;
|
|
93
60
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
61
|
+
const isTrack =
|
|
62
|
+
(ts.isIdentifier(expr) && expr.text === "track") ||
|
|
63
|
+
(ts.isPropertyAccessExpression(expr) &&
|
|
64
|
+
expr.name.text === "track");
|
|
99
65
|
|
|
100
|
-
if (
|
|
101
|
-
if (node.expression.text === "track") {
|
|
102
|
-
isTrackCall = true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
66
|
+
if (!isTrack) return;
|
|
105
67
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (checkArg(arg)) return;
|
|
109
|
-
}
|
|
68
|
+
for (const arg of node.arguments) {
|
|
69
|
+
if (check(arg)) return;
|
|
110
70
|
}
|
|
111
71
|
}
|
|
112
72
|
|
package/src/engine/resolver.ts
CHANGED
|
@@ -12,13 +12,12 @@ export function resolveNodeValue(
|
|
|
12
12
|
paramMap?: Map<string, ts.Expression>,
|
|
13
13
|
seen = new Set<ts.Node>()
|
|
14
14
|
): ResolveResult {
|
|
15
|
-
|
|
16
15
|
if (seen.has(node)) {
|
|
17
16
|
return { values: [], dynamic: true };
|
|
18
17
|
}
|
|
19
18
|
seen.add(node);
|
|
20
19
|
|
|
21
|
-
// PARAM
|
|
20
|
+
// PARAM MAP (wrapper args)
|
|
22
21
|
if (ts.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
23
22
|
return resolveNodeValue(
|
|
24
23
|
paramMap.get(node.text)!,
|
|
@@ -33,37 +32,23 @@ export function resolveNodeValue(
|
|
|
33
32
|
return { values: [node.text], dynamic: false };
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
// ARRAY
|
|
37
|
-
if (ts.isArrayLiteralExpression(node)) {
|
|
38
|
-
const values: string[] = [];
|
|
39
|
-
|
|
40
|
-
for (const el of node.elements) {
|
|
41
|
-
const res = resolveNodeValue(el, checker, paramMap, seen);
|
|
42
|
-
if (res.values.length) {
|
|
43
|
-
values.push(...res.values);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
values: [...new Set(values)],
|
|
49
|
-
dynamic: true
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
35
|
// TEMPLATE
|
|
54
36
|
if (ts.isTemplateExpression(node)) {
|
|
55
37
|
let results = node.head.text ? [node.head.text] : [""];
|
|
56
38
|
|
|
57
39
|
for (const span of node.templateSpans) {
|
|
58
|
-
const res = resolveNodeValue(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
const res = resolveNodeValue(
|
|
41
|
+
span.expression,
|
|
42
|
+
checker,
|
|
43
|
+
paramMap,
|
|
44
|
+
seen
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
results = results.flatMap(r =>
|
|
48
|
+
res.values.length
|
|
49
|
+
? res.values.map(v => r + v)
|
|
50
|
+
: [r + "*"]
|
|
51
|
+
);
|
|
67
52
|
|
|
68
53
|
results = results.map(r => r + span.literal.text);
|
|
69
54
|
}
|
|
@@ -71,7 +56,7 @@ export function resolveNodeValue(
|
|
|
71
56
|
return { values: results, dynamic: true };
|
|
72
57
|
}
|
|
73
58
|
|
|
74
|
-
// CONDITIONAL
|
|
59
|
+
// CONDITIONAL (a ? b : c)
|
|
75
60
|
if (ts.isConditionalExpression(node)) {
|
|
76
61
|
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
77
62
|
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
@@ -82,12 +67,69 @@ export function resolveNodeValue(
|
|
|
82
67
|
};
|
|
83
68
|
}
|
|
84
69
|
|
|
70
|
+
// OBJECT → берём ВСЕ значения (без привязки к ключам)
|
|
71
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
72
|
+
const values: string[] = [];
|
|
73
|
+
|
|
74
|
+
for (const prop of node.properties) {
|
|
75
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
76
|
+
|
|
77
|
+
const res = resolveNodeValue(
|
|
78
|
+
prop.initializer,
|
|
79
|
+
checker,
|
|
80
|
+
paramMap,
|
|
81
|
+
seen
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
values.push(...res.values);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
values: [...new Set(values)],
|
|
89
|
+
dynamic: true,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ARRAY
|
|
94
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
95
|
+
const values: string[] = [];
|
|
96
|
+
|
|
97
|
+
for (const el of node.elements) {
|
|
98
|
+
const res = resolveNodeValue(el, checker, paramMap, seen);
|
|
99
|
+
values.push(...res.values);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
values: [...new Set(values)],
|
|
104
|
+
dynamic: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// BINARY CONCAT
|
|
109
|
+
if (ts.isBinaryExpression(node)) {
|
|
110
|
+
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
111
|
+
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
112
|
+
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
values: left.values.flatMap(l =>
|
|
116
|
+
right.values.length
|
|
117
|
+
? right.values.map(r => l + r)
|
|
118
|
+
: [l + "*"]
|
|
119
|
+
),
|
|
120
|
+
dynamic: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
85
125
|
// IDENTIFIER
|
|
86
126
|
if (ts.isIdentifier(node)) {
|
|
87
127
|
let symbol = checker.getSymbolAtLocation(node);
|
|
88
128
|
if (!symbol) return { values: [], dynamic: true };
|
|
89
129
|
|
|
90
|
-
|
|
130
|
+
symbol = resolveExportedSymbol(symbol, checker) ?? symbol;
|
|
131
|
+
|
|
132
|
+
// const VAR = "click"
|
|
91
133
|
for (const decl of symbol.getDeclarations() ?? []) {
|
|
92
134
|
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
93
135
|
return resolveNodeValue(
|
|
@@ -97,16 +139,8 @@ export function resolveNodeValue(
|
|
|
97
139
|
seen
|
|
98
140
|
);
|
|
99
141
|
}
|
|
100
|
-
}
|
|
101
142
|
|
|
102
|
-
|
|
103
|
-
symbol = resolveExportedSymbol(symbol, checker) ?? symbol;
|
|
104
|
-
|
|
105
|
-
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
106
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for (const decl of symbol.getDeclarations() ?? []) {
|
|
143
|
+
// enum
|
|
110
144
|
if (ts.isEnumMember(decl) && decl.initializer) {
|
|
111
145
|
if (ts.isStringLiteral(decl.initializer)) {
|
|
112
146
|
return {
|
|
@@ -117,37 +151,11 @@ export function resolveNodeValue(
|
|
|
117
151
|
}
|
|
118
152
|
}
|
|
119
153
|
|
|
120
|
-
const type = checker.getTypeAtLocation(node);
|
|
121
|
-
|
|
122
|
-
// UNION
|
|
123
|
-
if (type.isUnion()) {
|
|
124
|
-
const values: string[] = [];
|
|
125
|
-
|
|
126
|
-
for (const t of type.types) {
|
|
127
|
-
if (t.flags & ts.TypeFlags.StringLiteral) {
|
|
128
|
-
values.push((t as ts.StringLiteralType).value);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (values.length) {
|
|
133
|
-
return { values, dynamic: false };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// STRING LITERAL TYPE
|
|
138
|
-
if (type.flags & ts.TypeFlags.StringLiteral) {
|
|
139
|
-
return {
|
|
140
|
-
values: [(type as ts.StringLiteralType).value],
|
|
141
|
-
dynamic: false
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
154
|
return { values: [], dynamic: true };
|
|
146
155
|
}
|
|
147
156
|
|
|
148
|
-
// PROPERTY ACCESS
|
|
157
|
+
// PROPERTY ACCESS (ENUM / CONST)
|
|
149
158
|
if (ts.isPropertyAccessExpression(node)) {
|
|
150
|
-
|
|
151
159
|
const symbol = checker.getSymbolAtLocation(node);
|
|
152
160
|
|
|
153
161
|
if (symbol) {
|
|
@@ -169,130 +177,21 @@ export function resolveNodeValue(
|
|
|
169
177
|
}
|
|
170
178
|
}
|
|
171
179
|
|
|
172
|
-
|
|
173
|
-
if (typeof constantValue === "string") {
|
|
174
|
-
return { values: [constantValue], dynamic: false };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const type = checker.getTypeAtLocation(node);
|
|
178
|
-
|
|
179
|
-
if (type.flags & ts.TypeFlags.StringLiteral) {
|
|
180
|
-
return {
|
|
181
|
-
values: [(type as ts.StringLiteralType).value],
|
|
182
|
-
dynamic: false,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// object literal resolve
|
|
187
|
-
if (ts.isIdentifier(node.expression)) {
|
|
188
|
-
const objSymbol = checker.getSymbolAtLocation(node.expression);
|
|
189
|
-
|
|
190
|
-
if (objSymbol) {
|
|
191
|
-
for (const decl of objSymbol.getDeclarations() ?? []) {
|
|
192
|
-
if (
|
|
193
|
-
ts.isVariableDeclaration(decl) &&
|
|
194
|
-
decl.initializer &&
|
|
195
|
-
ts.isObjectLiteralExpression(decl.initializer)
|
|
196
|
-
) {
|
|
197
|
-
for (const prop of decl.initializer.properties) {
|
|
198
|
-
if (!ts.isPropertyAssignment(prop)) continue;
|
|
199
|
-
|
|
200
|
-
const name = prop.name.getText();
|
|
201
|
-
|
|
202
|
-
if (name === node.name.text) {
|
|
203
|
-
return resolveNodeValue(
|
|
204
|
-
prop.initializer,
|
|
205
|
-
checker,
|
|
206
|
-
paramMap,
|
|
207
|
-
seen
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// fallback
|
|
217
|
-
return resolveNodeValue(node.name, checker, paramMap, seen);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// ARRAY ACCESS
|
|
221
|
-
if (ts.isElementAccessExpression(node)) {
|
|
222
|
-
const obj = resolveNodeValue(node.expression, checker, paramMap, seen);
|
|
223
|
-
|
|
224
|
-
if (!node.argumentExpression) {
|
|
225
|
-
return { values: obj.values, dynamic: true };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const index = resolveNodeValue(
|
|
229
|
-
node.argumentExpression,
|
|
230
|
-
checker,
|
|
231
|
-
paramMap,
|
|
232
|
-
seen
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
if (index.values.length === 1) {
|
|
236
|
-
const i = Number(index.values[0]);
|
|
237
|
-
|
|
238
|
-
if (!Number.isNaN(i) && obj.values[i] !== undefined) {
|
|
239
|
-
return {
|
|
240
|
-
values: [obj.values[i]],
|
|
241
|
-
dynamic: false,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return obj.values.length
|
|
247
|
-
? { values: obj.values, dynamic: true }
|
|
248
|
-
: { values: [], dynamic: true };
|
|
180
|
+
return { values: [], dynamic: true };
|
|
249
181
|
}
|
|
250
182
|
|
|
251
|
-
//
|
|
252
|
-
if (ts.
|
|
183
|
+
// CALL → unwrap args
|
|
184
|
+
if (ts.isCallExpression(node)) {
|
|
253
185
|
const values: string[] = [];
|
|
254
186
|
|
|
255
|
-
for (const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const name = prop.name.getText();
|
|
259
|
-
|
|
260
|
-
if (["event", "name", "type"].includes(name)) {
|
|
261
|
-
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
262
|
-
values.push(...res.values);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
values: [...new Set(values)],
|
|
268
|
-
dynamic: true
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// BINARY ("btn_" + name)
|
|
273
|
-
if (ts.isBinaryExpression(node)) {
|
|
274
|
-
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
275
|
-
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
276
|
-
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
277
|
-
|
|
278
|
-
if (!left.values.length) {
|
|
279
|
-
return { values: [], dynamic: true };
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
values: left.values.flatMap(l =>
|
|
284
|
-
right.values.length
|
|
285
|
-
? right.values.map(r => l + r)
|
|
286
|
-
: [l + "*"]
|
|
287
|
-
),
|
|
288
|
-
dynamic: true,
|
|
289
|
-
};
|
|
187
|
+
for (const arg of node.arguments) {
|
|
188
|
+
const res = resolveNodeValue(arg, checker, paramMap, seen);
|
|
189
|
+
values.push(...res.values);
|
|
290
190
|
}
|
|
291
|
-
}
|
|
292
191
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
192
|
+
return values.length
|
|
193
|
+
? { values, dynamic: true }
|
|
194
|
+
: { values: [], dynamic: true };
|
|
296
195
|
}
|
|
297
196
|
|
|
298
197
|
return { values: [], dynamic: true };
|