@eventra_dev/eventra-cli 0.1.21 → 0.1.23
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 +18 -10
- package/dist/engine/resolver.js +97 -21
- package/dist/utils/processFile.js +1 -1
- package/dist/utils/{parsers/universal.js → universal.js} +68 -24
- package/package.json +1 -1
- package/src/engine/functionAnalyzer.ts +22 -12
- package/src/engine/resolver.ts +126 -31
- package/src/utils/processFile.ts +1 -1
- package/src/utils/{parsers/universal.ts → universal.ts} +86 -27
- package/dist/utils/parsers/router.js +0 -7
- package/dist/utils/parsers/utils.js +0 -21
- package/src/utils/parsers/router.ts +0 -6
- package/src/utils/parsers/utils.ts +0 -18
|
@@ -15,8 +15,20 @@ function findTrackParamIndex(fn) {
|
|
|
15
15
|
let result = null;
|
|
16
16
|
function visit(node) {
|
|
17
17
|
if (typescript_1.default.isCallExpression(node)) {
|
|
18
|
-
|
|
19
|
-
if (
|
|
18
|
+
let isTrackCall = false;
|
|
19
|
+
if (typescript_1.default.isPropertyAccessExpression(node.expression)) {
|
|
20
|
+
const method = node.expression.name.text;
|
|
21
|
+
if (method === "track") {
|
|
22
|
+
isTrackCall = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// fallback
|
|
26
|
+
if (typescript_1.default.isIdentifier(node.expression)) {
|
|
27
|
+
if (node.expression.text === "track") {
|
|
28
|
+
isTrackCall = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (isTrackCall) {
|
|
20
32
|
for (const arg of node.arguments) {
|
|
21
33
|
if (typescript_1.default.isIdentifier(arg)) {
|
|
22
34
|
const idx = paramIndex.get(arg.text);
|
|
@@ -25,6 +37,10 @@ function findTrackParamIndex(fn) {
|
|
|
25
37
|
return;
|
|
26
38
|
}
|
|
27
39
|
}
|
|
40
|
+
if (typescript_1.default.isStringLiteral(arg)) {
|
|
41
|
+
result = 0;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
28
44
|
}
|
|
29
45
|
}
|
|
30
46
|
}
|
|
@@ -35,11 +51,3 @@ function findTrackParamIndex(fn) {
|
|
|
35
51
|
}
|
|
36
52
|
return result;
|
|
37
53
|
}
|
|
38
|
-
function getCallName(expr) {
|
|
39
|
-
if (typescript_1.default.isIdentifier(expr))
|
|
40
|
-
return expr.text;
|
|
41
|
-
if (typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
42
|
-
return expr.name.text;
|
|
43
|
-
}
|
|
44
|
-
return "";
|
|
45
|
-
}
|
package/dist/engine/resolver.js
CHANGED
|
@@ -11,42 +11,60 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
11
11
|
return { values: [], dynamic: true };
|
|
12
12
|
}
|
|
13
13
|
seen.add(node);
|
|
14
|
+
// param mapping
|
|
14
15
|
if (typescript_1.default.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
15
16
|
return resolveNodeValue(paramMap.get(node.text), checker, paramMap, seen);
|
|
16
17
|
}
|
|
18
|
+
// STRING
|
|
17
19
|
if (typescript_1.default.isStringLiteral(node)) {
|
|
18
20
|
return { values: [node.text], dynamic: false };
|
|
19
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 { values, dynamic: true };
|
|
32
|
+
}
|
|
33
|
+
// TEMPLATE
|
|
20
34
|
if (typescript_1.default.isTemplateExpression(node)) {
|
|
21
|
-
let
|
|
35
|
+
let results = [node.head.text];
|
|
22
36
|
for (const span of node.templateSpans) {
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
const res = resolveNodeValue(span.expression, checker, paramMap, seen);
|
|
38
|
+
if (res.values.length) {
|
|
39
|
+
results = results.flatMap(r => res.values.map(v => r + v));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
results = results.map(r => r + "*");
|
|
43
|
+
}
|
|
44
|
+
results = results.map(r => r + span.literal.text);
|
|
25
45
|
}
|
|
26
|
-
return { values:
|
|
46
|
+
return { values: results, dynamic: true };
|
|
27
47
|
}
|
|
48
|
+
// CONDITIONAL
|
|
28
49
|
if (typescript_1.default.isConditionalExpression(node)) {
|
|
29
50
|
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
30
51
|
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
31
52
|
return {
|
|
32
|
-
values: [...new Set([...
|
|
53
|
+
values: [...new Set([...a.values, ...b.values])],
|
|
33
54
|
dynamic: true,
|
|
34
55
|
};
|
|
35
56
|
}
|
|
57
|
+
// IDENTIFIER
|
|
36
58
|
if (typescript_1.default.isIdentifier(node)) {
|
|
37
59
|
let symbol = checker.getSymbolAtLocation(node);
|
|
38
60
|
if (!symbol)
|
|
39
61
|
return { values: [], dynamic: true };
|
|
40
|
-
|
|
41
|
-
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
42
|
-
return resolveNodeValue(decl.initializer, checker, paramMap, seen);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const original = symbol;
|
|
62
|
+
// unwrap export
|
|
46
63
|
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
47
|
-
if (symbol
|
|
64
|
+
if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
48
65
|
symbol = checker.getAliasedSymbol(symbol);
|
|
49
66
|
}
|
|
67
|
+
// inline variable
|
|
50
68
|
for (const decl of symbol.getDeclarations() ?? []) {
|
|
51
69
|
if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
|
|
52
70
|
return resolveNodeValue(decl.initializer, checker, paramMap, seen);
|
|
@@ -60,12 +78,30 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
}
|
|
81
|
+
// UNION TYPES
|
|
82
|
+
const type = checker.getTypeAtLocation(node);
|
|
83
|
+
if (type.isUnion()) {
|
|
84
|
+
const values = [];
|
|
85
|
+
for (const t of type.types) {
|
|
86
|
+
if (t.isStringLiteral?.()) {
|
|
87
|
+
values.push(t.value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (values.length) {
|
|
91
|
+
return { values, dynamic: false };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// TYPE FALLBACK (as const)
|
|
95
|
+
if (type.isStringLiteral?.()) {
|
|
96
|
+
return { values: [type.value], dynamic: false };
|
|
97
|
+
}
|
|
63
98
|
return { values: [], dynamic: true };
|
|
64
99
|
}
|
|
100
|
+
// PROPERTY ACCESS (obj.a.b)
|
|
65
101
|
if (typescript_1.default.isPropertyAccessExpression(node)) {
|
|
66
102
|
const symbol = checker.getSymbolAtLocation(node);
|
|
67
103
|
if (symbol) {
|
|
68
|
-
let s = symbol;
|
|
104
|
+
let s = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
69
105
|
if (s.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
70
106
|
s = checker.getAliasedSymbol(s);
|
|
71
107
|
}
|
|
@@ -80,6 +116,20 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
80
116
|
}
|
|
81
117
|
}
|
|
82
118
|
}
|
|
119
|
+
// constant enum / readonly
|
|
120
|
+
const constantValue = checker.getConstantValue(node);
|
|
121
|
+
if (typeof constantValue === "string") {
|
|
122
|
+
return { values: [constantValue], dynamic: false };
|
|
123
|
+
}
|
|
124
|
+
// type fallback
|
|
125
|
+
const type = checker.getTypeAtLocation(node);
|
|
126
|
+
if (type.isStringLiteral?.()) {
|
|
127
|
+
return {
|
|
128
|
+
values: [type.value],
|
|
129
|
+
dynamic: false,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// object literal resolve
|
|
83
133
|
if (typescript_1.default.isIdentifier(node.expression)) {
|
|
84
134
|
const objSymbol = checker.getSymbolAtLocation(node.expression);
|
|
85
135
|
if (objSymbol) {
|
|
@@ -101,33 +151,59 @@ function resolveNodeValue(node, checker, paramMap, seen = new Set()) {
|
|
|
101
151
|
}
|
|
102
152
|
return { values: [], dynamic: true };
|
|
103
153
|
}
|
|
154
|
+
// ARRAY ACCESS (arr[0])
|
|
155
|
+
if (typescript_1.default.isElementAccessExpression(node)) {
|
|
156
|
+
const obj = resolveNodeValue(node.expression, checker, paramMap, seen);
|
|
157
|
+
if (!node.argumentExpression) {
|
|
158
|
+
return { values: obj.values, dynamic: true };
|
|
159
|
+
}
|
|
160
|
+
const index = resolveNodeValue(node.argumentExpression, checker, paramMap, seen);
|
|
161
|
+
// if index known
|
|
162
|
+
if (index.values.length === 1) {
|
|
163
|
+
const i = Number(index.values[0]);
|
|
164
|
+
if (!Number.isNaN(i) && obj.values[i] !== undefined) {
|
|
165
|
+
return {
|
|
166
|
+
values: [obj.values[i]],
|
|
167
|
+
dynamic: false,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// fallback → dynamic
|
|
172
|
+
return obj.values.length
|
|
173
|
+
? { values: obj.values, dynamic: true }
|
|
174
|
+
: { values: [], dynamic: true };
|
|
175
|
+
}
|
|
176
|
+
// OBJECT
|
|
104
177
|
if (typescript_1.default.isObjectLiteralExpression(node)) {
|
|
105
178
|
const values = [];
|
|
106
179
|
for (const prop of node.properties) {
|
|
107
180
|
if (!typescript_1.default.isPropertyAssignment(prop))
|
|
108
181
|
continue;
|
|
109
182
|
const name = prop.name.getText();
|
|
110
|
-
if (name
|
|
183
|
+
if (["event", "name", "type"].includes(name)) {
|
|
111
184
|
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
112
185
|
values.push(...res.values);
|
|
113
186
|
}
|
|
114
187
|
}
|
|
115
188
|
return { values, dynamic: true };
|
|
116
189
|
}
|
|
190
|
+
// BINARY ("btn_" + name)
|
|
117
191
|
if (typescript_1.default.isBinaryExpression(node)) {
|
|
118
192
|
if (node.operatorToken.kind === typescript_1.default.SyntaxKind.PlusToken) {
|
|
119
193
|
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
120
194
|
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
121
|
-
if (left
|
|
122
|
-
return {
|
|
123
|
-
values: left.values.flatMap(l => right.values.length
|
|
124
|
-
? right.values.map(r => l + r)
|
|
125
|
-
: [l + "*"]),
|
|
126
|
-
dynamic: true,
|
|
127
|
-
};
|
|
195
|
+
if (!left.values.length) {
|
|
196
|
+
return { values: [], dynamic: true };
|
|
128
197
|
}
|
|
198
|
+
return {
|
|
199
|
+
values: left.values.flatMap(l => right.values.length
|
|
200
|
+
? right.values.map(r => l + r)
|
|
201
|
+
: [l + "*"]),
|
|
202
|
+
dynamic: true,
|
|
203
|
+
};
|
|
129
204
|
}
|
|
130
205
|
}
|
|
206
|
+
// CALL → dynamic
|
|
131
207
|
if (typescript_1.default.isCallExpression(node)) {
|
|
132
208
|
return { values: [], dynamic: true };
|
|
133
209
|
}
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.processFile = processFile;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const universal_1 = require("./
|
|
8
|
+
const universal_1 = require("./universal");
|
|
9
9
|
function processFile(file, content) {
|
|
10
10
|
const { code, deps } = (0, universal_1.parseUniversal)(content, file);
|
|
11
11
|
const ext = path_1.default.extname(file);
|
|
@@ -5,6 +5,18 @@ 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
|
+
/**
|
|
9
|
+
* Strip comments (HTML + JS)
|
|
10
|
+
*/
|
|
11
|
+
function stripComments(input) {
|
|
12
|
+
return input
|
|
13
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
14
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
15
|
+
.replace(/\/\/.*$/gm, "");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract JS expressions safely
|
|
19
|
+
*/
|
|
8
20
|
function extractJS(input) {
|
|
9
21
|
const result = [];
|
|
10
22
|
let i = 0;
|
|
@@ -29,7 +41,7 @@ function extractJS(input) {
|
|
|
29
41
|
i++;
|
|
30
42
|
continue;
|
|
31
43
|
}
|
|
32
|
-
//
|
|
44
|
+
// { ... }
|
|
33
45
|
if (ch === "{") {
|
|
34
46
|
let depth = 1;
|
|
35
47
|
let j = i + 1;
|
|
@@ -60,13 +72,18 @@ function extractJS(input) {
|
|
|
60
72
|
}
|
|
61
73
|
if (depth === 0) {
|
|
62
74
|
const expr = input.slice(i + 1, j - 1).trim();
|
|
63
|
-
if (expr
|
|
75
|
+
if (expr &&
|
|
76
|
+
(expr.includes("(") ||
|
|
77
|
+
expr.includes("=") ||
|
|
78
|
+
expr.includes(".") ||
|
|
79
|
+
expr.includes("track"))) {
|
|
64
80
|
result.push(expr);
|
|
81
|
+
}
|
|
65
82
|
i = j;
|
|
66
83
|
continue;
|
|
67
84
|
}
|
|
68
85
|
}
|
|
69
|
-
//
|
|
86
|
+
// attr="..."
|
|
70
87
|
if (ch === "=" && (input[i + 1] === '"' || input[i + 1] === "'")) {
|
|
71
88
|
const quote = input[i + 1];
|
|
72
89
|
let j = i + 2;
|
|
@@ -83,14 +100,18 @@ function extractJS(input) {
|
|
|
83
100
|
val += c;
|
|
84
101
|
j++;
|
|
85
102
|
}
|
|
86
|
-
if (val.trim()
|
|
103
|
+
if (val.trim() &&
|
|
104
|
+
(val.includes("(") ||
|
|
105
|
+
val.includes("=>") ||
|
|
106
|
+
val.includes("track"))) {
|
|
87
107
|
result.push(val.trim());
|
|
108
|
+
}
|
|
88
109
|
i = j + 1;
|
|
89
110
|
continue;
|
|
90
111
|
}
|
|
91
112
|
i++;
|
|
92
113
|
}
|
|
93
|
-
// Vue / Svelte handlers
|
|
114
|
+
// Vue / Svelte / JSX handlers
|
|
94
115
|
const extra = [
|
|
95
116
|
/@[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
96
117
|
/v-on:[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
@@ -108,21 +129,41 @@ function extractJS(input) {
|
|
|
108
129
|
return result;
|
|
109
130
|
}
|
|
110
131
|
/**
|
|
111
|
-
*
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
* Extract loose JS but less noisy
|
|
133
|
+
*/
|
|
134
|
+
function extractLooseJS(input) {
|
|
135
|
+
const result = [];
|
|
136
|
+
const lines = input.split("\n");
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
const l = line.trim();
|
|
139
|
+
if (!l)
|
|
140
|
+
continue;
|
|
141
|
+
if (l.startsWith("<"))
|
|
142
|
+
continue;
|
|
143
|
+
// strict filter
|
|
144
|
+
if (l.includes("track") ||
|
|
145
|
+
l.includes(".track(") ||
|
|
146
|
+
l.includes("=>") ||
|
|
147
|
+
l.startsWith("function") ||
|
|
148
|
+
l.includes("(")) {
|
|
149
|
+
result.push(l);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Universal parser
|
|
116
156
|
*/
|
|
117
157
|
function parseUniversal(content, file) {
|
|
118
158
|
const parts = [];
|
|
119
159
|
const deps = [];
|
|
120
|
-
|
|
121
|
-
|
|
160
|
+
const clean = stripComments(content);
|
|
161
|
+
// Astro frontmatter
|
|
162
|
+
const fm = clean.match(/^---([\s\S]*?)---/);
|
|
122
163
|
if (fm?.[1])
|
|
123
164
|
parts.push(fm[1]);
|
|
124
165
|
// <script src="">
|
|
125
|
-
const scriptSrc =
|
|
166
|
+
const scriptSrc = clean.matchAll(/<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/g);
|
|
126
167
|
for (const s of scriptSrc) {
|
|
127
168
|
const src = s[1];
|
|
128
169
|
const resolved = src.startsWith("/")
|
|
@@ -131,20 +172,23 @@ function parseUniversal(content, file) {
|
|
|
131
172
|
deps.push(resolved);
|
|
132
173
|
}
|
|
133
174
|
// inline <script>
|
|
134
|
-
const scripts =
|
|
175
|
+
const scripts = clean.matchAll(/<script\b([^>]*)>([\s\S]*?)<\/script>/g);
|
|
135
176
|
for (const m of scripts) {
|
|
136
|
-
|
|
137
|
-
|
|
177
|
+
const attrs = m[1];
|
|
178
|
+
// skip JSON / data
|
|
179
|
+
if (/type=["']application\/json/.test(attrs))
|
|
180
|
+
continue;
|
|
181
|
+
if (m[2].trim()) {
|
|
182
|
+
parts.push(m[2]);
|
|
183
|
+
}
|
|
138
184
|
}
|
|
139
|
-
//
|
|
140
|
-
const noScripts =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (noHtml.trim())
|
|
145
|
-
parts.push(noHtml);
|
|
185
|
+
// remove scripts
|
|
186
|
+
const noScripts = clean.replace(/<script[\s\S]*?<\/script>/g, "");
|
|
187
|
+
// loose JS
|
|
188
|
+
const loose = extractLooseJS(noScripts);
|
|
189
|
+
parts.push(...loose);
|
|
146
190
|
// expressions
|
|
147
|
-
const extracted = extractJS(
|
|
191
|
+
const extracted = extractJS(clean);
|
|
148
192
|
for (const e of extracted) {
|
|
149
193
|
parts.push(e + ";");
|
|
150
194
|
}
|
package/package.json
CHANGED
|
@@ -15,9 +15,24 @@ export function findTrackParamIndex(
|
|
|
15
15
|
|
|
16
16
|
function visit(node: ts.Node) {
|
|
17
17
|
if (ts.isCallExpression(node)) {
|
|
18
|
-
|
|
18
|
+
let isTrackCall = false;
|
|
19
19
|
|
|
20
|
-
if (
|
|
20
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
21
|
+
const method = node.expression.name.text;
|
|
22
|
+
|
|
23
|
+
if (method === "track") {
|
|
24
|
+
isTrackCall = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// fallback
|
|
29
|
+
if (ts.isIdentifier(node.expression)) {
|
|
30
|
+
if (node.expression.text === "track") {
|
|
31
|
+
isTrackCall = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (isTrackCall) {
|
|
21
36
|
for (const arg of node.arguments) {
|
|
22
37
|
if (ts.isIdentifier(arg)) {
|
|
23
38
|
const idx = paramIndex.get(arg.text);
|
|
@@ -26,6 +41,11 @@ export function findTrackParamIndex(
|
|
|
26
41
|
return;
|
|
27
42
|
}
|
|
28
43
|
}
|
|
44
|
+
|
|
45
|
+
if (ts.isStringLiteral(arg)) {
|
|
46
|
+
result = 0;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
29
49
|
}
|
|
30
50
|
}
|
|
31
51
|
}
|
|
@@ -39,13 +59,3 @@ export function findTrackParamIndex(
|
|
|
39
59
|
|
|
40
60
|
return result;
|
|
41
61
|
}
|
|
42
|
-
|
|
43
|
-
function getCallName(expr: ts.Expression): string {
|
|
44
|
-
if (ts.isIdentifier(expr)) return expr.text;
|
|
45
|
-
|
|
46
|
-
if (ts.isPropertyAccessExpression(expr)) {
|
|
47
|
-
return expr.name.text;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return "";
|
|
51
|
-
}
|
package/src/engine/resolver.ts
CHANGED
|
@@ -12,12 +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
|
|
|
20
|
+
// param mapping
|
|
21
21
|
if (ts.isIdentifier(node) && paramMap?.has(node.text)) {
|
|
22
22
|
return resolveNodeValue(
|
|
23
23
|
paramMap.get(node.text)!,
|
|
@@ -27,54 +27,70 @@ export function resolveNodeValue(
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// STRING
|
|
30
31
|
if (ts.isStringLiteral(node)) {
|
|
31
32
|
return { values: [node.text], dynamic: false };
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// ARRAY
|
|
36
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
37
|
+
const values: string[] = [];
|
|
38
|
+
|
|
39
|
+
for (const el of node.elements) {
|
|
40
|
+
const res = resolveNodeValue(el, checker, paramMap, seen);
|
|
41
|
+
if (res.values.length) {
|
|
42
|
+
values.push(...res.values);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { values, dynamic: true };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// TEMPLATE
|
|
34
50
|
if (ts.isTemplateExpression(node)) {
|
|
35
|
-
let
|
|
51
|
+
let results = [node.head.text];
|
|
36
52
|
|
|
37
53
|
for (const span of node.templateSpans) {
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
const res = resolveNodeValue(span.expression, checker, paramMap, seen);
|
|
55
|
+
|
|
56
|
+
if (res.values.length) {
|
|
57
|
+
results = results.flatMap(r =>
|
|
58
|
+
res.values.map(v => r + v)
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
results = results.map(r => r + "*");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
results = results.map(r => r + span.literal.text);
|
|
40
65
|
}
|
|
41
66
|
|
|
42
|
-
return { values:
|
|
67
|
+
return { values: results, dynamic: true };
|
|
43
68
|
}
|
|
44
69
|
|
|
70
|
+
// CONDITIONAL
|
|
45
71
|
if (ts.isConditionalExpression(node)) {
|
|
46
72
|
const a = resolveNodeValue(node.whenTrue, checker, paramMap, seen);
|
|
47
73
|
const b = resolveNodeValue(node.whenFalse, checker, paramMap, seen);
|
|
48
74
|
|
|
49
75
|
return {
|
|
50
|
-
values: [...new Set([...
|
|
76
|
+
values: [...new Set([...a.values, ...b.values])],
|
|
51
77
|
dynamic: true,
|
|
52
78
|
};
|
|
53
79
|
}
|
|
54
80
|
|
|
81
|
+
// IDENTIFIER
|
|
55
82
|
if (ts.isIdentifier(node)) {
|
|
56
83
|
let symbol = checker.getSymbolAtLocation(node);
|
|
57
84
|
if (!symbol) return { values: [], dynamic: true };
|
|
58
85
|
|
|
59
|
-
|
|
60
|
-
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
61
|
-
return resolveNodeValue(
|
|
62
|
-
decl.initializer,
|
|
63
|
-
checker,
|
|
64
|
-
paramMap,
|
|
65
|
-
seen
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const original = symbol;
|
|
71
|
-
|
|
86
|
+
// unwrap export
|
|
72
87
|
symbol = resolveExportedSymbol(symbol, checker) ?? symbol;
|
|
73
88
|
|
|
74
|
-
if (symbol
|
|
89
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
75
90
|
symbol = checker.getAliasedSymbol(symbol);
|
|
76
91
|
}
|
|
77
92
|
|
|
93
|
+
// inline variable
|
|
78
94
|
for (const decl of symbol.getDeclarations() ?? []) {
|
|
79
95
|
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
80
96
|
return resolveNodeValue(
|
|
@@ -95,14 +111,38 @@ export function resolveNodeValue(
|
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
113
|
|
|
114
|
+
// UNION TYPES
|
|
115
|
+
const type = checker.getTypeAtLocation(node);
|
|
116
|
+
|
|
117
|
+
if (type.isUnion()) {
|
|
118
|
+
const values: string[] = [];
|
|
119
|
+
|
|
120
|
+
for (const t of type.types) {
|
|
121
|
+
if ((t as any).isStringLiteral?.()) {
|
|
122
|
+
values.push((t as any).value);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (values.length) {
|
|
127
|
+
return { values, dynamic: false };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// TYPE FALLBACK (as const)
|
|
132
|
+
if ((type as any).isStringLiteral?.()) {
|
|
133
|
+
return { values: [(type as any).value], dynamic: false };
|
|
134
|
+
}
|
|
135
|
+
|
|
98
136
|
return { values: [], dynamic: true };
|
|
99
137
|
}
|
|
100
138
|
|
|
139
|
+
// PROPERTY ACCESS (obj.a.b)
|
|
101
140
|
if (ts.isPropertyAccessExpression(node)) {
|
|
141
|
+
|
|
102
142
|
const symbol = checker.getSymbolAtLocation(node);
|
|
103
143
|
|
|
104
144
|
if (symbol) {
|
|
105
|
-
let s = symbol;
|
|
145
|
+
let s = resolveExportedSymbol(symbol, checker) ?? symbol;
|
|
106
146
|
|
|
107
147
|
if (s.flags & ts.SymbolFlags.Alias) {
|
|
108
148
|
s = checker.getAliasedSymbol(s);
|
|
@@ -120,6 +160,23 @@ export function resolveNodeValue(
|
|
|
120
160
|
}
|
|
121
161
|
}
|
|
122
162
|
|
|
163
|
+
// constant enum / readonly
|
|
164
|
+
const constantValue = checker.getConstantValue(node as any);
|
|
165
|
+
if (typeof constantValue === "string") {
|
|
166
|
+
return { values: [constantValue], dynamic: false };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// type fallback
|
|
170
|
+
const type = checker.getTypeAtLocation(node);
|
|
171
|
+
|
|
172
|
+
if ((type as any).isStringLiteral?.()) {
|
|
173
|
+
return {
|
|
174
|
+
values: [(type as any).value],
|
|
175
|
+
dynamic: false,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// object literal resolve
|
|
123
180
|
if (ts.isIdentifier(node.expression)) {
|
|
124
181
|
const objSymbol = checker.getSymbolAtLocation(node.expression);
|
|
125
182
|
|
|
@@ -152,6 +209,40 @@ export function resolveNodeValue(
|
|
|
152
209
|
return { values: [], dynamic: true };
|
|
153
210
|
}
|
|
154
211
|
|
|
212
|
+
// ARRAY ACCESS (arr[0])
|
|
213
|
+
if (ts.isElementAccessExpression(node)) {
|
|
214
|
+
const obj = resolveNodeValue(node.expression, checker, paramMap, seen);
|
|
215
|
+
|
|
216
|
+
if (!node.argumentExpression) {
|
|
217
|
+
return { values: obj.values, dynamic: true };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const index = resolveNodeValue(
|
|
221
|
+
node.argumentExpression,
|
|
222
|
+
checker,
|
|
223
|
+
paramMap,
|
|
224
|
+
seen
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// if index known
|
|
228
|
+
if (index.values.length === 1) {
|
|
229
|
+
const i = Number(index.values[0]);
|
|
230
|
+
|
|
231
|
+
if (!Number.isNaN(i) && obj.values[i] !== undefined) {
|
|
232
|
+
return {
|
|
233
|
+
values: [obj.values[i]],
|
|
234
|
+
dynamic: false,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// fallback → dynamic
|
|
240
|
+
return obj.values.length
|
|
241
|
+
? { values: obj.values, dynamic: true }
|
|
242
|
+
: { values: [], dynamic: true };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// OBJECT
|
|
155
246
|
if (ts.isObjectLiteralExpression(node)) {
|
|
156
247
|
const values: string[] = [];
|
|
157
248
|
|
|
@@ -160,7 +251,7 @@ export function resolveNodeValue(
|
|
|
160
251
|
|
|
161
252
|
const name = prop.name.getText();
|
|
162
253
|
|
|
163
|
-
if (name
|
|
254
|
+
if (["event", "name", "type"].includes(name)) {
|
|
164
255
|
const res = resolveNodeValue(prop.initializer, checker, paramMap, seen);
|
|
165
256
|
values.push(...res.values);
|
|
166
257
|
}
|
|
@@ -169,24 +260,28 @@ export function resolveNodeValue(
|
|
|
169
260
|
return { values, dynamic: true };
|
|
170
261
|
}
|
|
171
262
|
|
|
263
|
+
// BINARY ("btn_" + name)
|
|
172
264
|
if (ts.isBinaryExpression(node)) {
|
|
173
265
|
if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
174
266
|
const left = resolveNodeValue(node.left, checker, paramMap, seen);
|
|
175
267
|
const right = resolveNodeValue(node.right, checker, paramMap, seen);
|
|
176
268
|
|
|
177
|
-
if (left
|
|
178
|
-
return {
|
|
179
|
-
values: left.values.flatMap(l =>
|
|
180
|
-
right.values.length
|
|
181
|
-
? right.values.map(r => l + r)
|
|
182
|
-
: [l + "*"]
|
|
183
|
-
),
|
|
184
|
-
dynamic: true,
|
|
185
|
-
};
|
|
269
|
+
if (!left.values.length) {
|
|
270
|
+
return { values: [], dynamic: true };
|
|
186
271
|
}
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
values: left.values.flatMap(l =>
|
|
275
|
+
right.values.length
|
|
276
|
+
? right.values.map(r => l + r)
|
|
277
|
+
: [l + "*"]
|
|
278
|
+
),
|
|
279
|
+
dynamic: true,
|
|
280
|
+
};
|
|
187
281
|
}
|
|
188
282
|
}
|
|
189
283
|
|
|
284
|
+
// CALL → dynamic
|
|
190
285
|
if (ts.isCallExpression(node)) {
|
|
191
286
|
return { values: [], dynamic: true };
|
|
192
287
|
}
|
package/src/utils/processFile.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { ParseResult } from "
|
|
2
|
+
import { ParseResult } from "../types";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Strip comments (HTML + JS)
|
|
6
|
+
*/
|
|
7
|
+
function stripComments(input: string): string {
|
|
8
|
+
return input
|
|
9
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
10
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
11
|
+
.replace(/\/\/.*$/gm, "");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract JS expressions safely
|
|
16
|
+
*/
|
|
4
17
|
function extractJS(input: string): string[] {
|
|
5
18
|
const result: string[] = [];
|
|
6
19
|
|
|
@@ -18,7 +31,6 @@ function extractJS(input: string): string[] {
|
|
|
18
31
|
if (escape) escape = false;
|
|
19
32
|
else if (ch === "\\") escape = true;
|
|
20
33
|
else if (ch === inString) inString = null;
|
|
21
|
-
|
|
22
34
|
i++;
|
|
23
35
|
continue;
|
|
24
36
|
}
|
|
@@ -29,7 +41,7 @@ function extractJS(input: string): string[] {
|
|
|
29
41
|
continue;
|
|
30
42
|
}
|
|
31
43
|
|
|
32
|
-
//
|
|
44
|
+
// { ... }
|
|
33
45
|
if (ch === "{") {
|
|
34
46
|
let depth = 1;
|
|
35
47
|
let j = i + 1;
|
|
@@ -62,13 +74,23 @@ function extractJS(input: string): string[] {
|
|
|
62
74
|
|
|
63
75
|
if (depth === 0) {
|
|
64
76
|
const expr = input.slice(i + 1, j - 1).trim();
|
|
65
|
-
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
expr &&
|
|
80
|
+
(expr.includes("(") ||
|
|
81
|
+
expr.includes("=") ||
|
|
82
|
+
expr.includes(".") ||
|
|
83
|
+
expr.includes("track"))
|
|
84
|
+
) {
|
|
85
|
+
result.push(expr);
|
|
86
|
+
}
|
|
87
|
+
|
|
66
88
|
i = j;
|
|
67
89
|
continue;
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
//
|
|
93
|
+
// attr="..."
|
|
72
94
|
if (ch === "=" && (input[i + 1] === '"' || input[i + 1] === "'")) {
|
|
73
95
|
const quote = input[i + 1];
|
|
74
96
|
let j = i + 2;
|
|
@@ -89,7 +111,14 @@ function extractJS(input: string): string[] {
|
|
|
89
111
|
j++;
|
|
90
112
|
}
|
|
91
113
|
|
|
92
|
-
if (
|
|
114
|
+
if (
|
|
115
|
+
val.trim() &&
|
|
116
|
+
(val.includes("(") ||
|
|
117
|
+
val.includes("=>") ||
|
|
118
|
+
val.includes("track"))
|
|
119
|
+
) {
|
|
120
|
+
result.push(val.trim());
|
|
121
|
+
}
|
|
93
122
|
|
|
94
123
|
i = j + 1;
|
|
95
124
|
continue;
|
|
@@ -98,7 +127,7 @@ function extractJS(input: string): string[] {
|
|
|
98
127
|
i++;
|
|
99
128
|
}
|
|
100
129
|
|
|
101
|
-
// Vue / Svelte handlers
|
|
130
|
+
// Vue / Svelte / JSX handlers
|
|
102
131
|
const extra = [
|
|
103
132
|
/@[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
104
133
|
/v-on:[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
@@ -118,13 +147,37 @@ function extractJS(input: string): string[] {
|
|
|
118
147
|
}
|
|
119
148
|
|
|
120
149
|
/**
|
|
121
|
-
*
|
|
122
|
-
* - Vue
|
|
123
|
-
* - Svelte
|
|
124
|
-
* - HTML + JS
|
|
125
|
-
* - SSR / mixed files
|
|
150
|
+
* Extract loose JS but less noisy
|
|
126
151
|
*/
|
|
152
|
+
function extractLooseJS(input: string): string[] {
|
|
153
|
+
const result: string[] = [];
|
|
154
|
+
|
|
155
|
+
const lines = input.split("\n");
|
|
156
|
+
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const l = line.trim();
|
|
159
|
+
|
|
160
|
+
if (!l) continue;
|
|
161
|
+
if (l.startsWith("<")) continue;
|
|
127
162
|
|
|
163
|
+
// strict filter
|
|
164
|
+
if (
|
|
165
|
+
l.includes("track") ||
|
|
166
|
+
l.includes(".track(") ||
|
|
167
|
+
l.includes("=>") ||
|
|
168
|
+
l.startsWith("function") ||
|
|
169
|
+
l.includes("(")
|
|
170
|
+
) {
|
|
171
|
+
result.push(l);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Universal parser
|
|
180
|
+
*/
|
|
128
181
|
export function parseUniversal(
|
|
129
182
|
content: string,
|
|
130
183
|
file: string
|
|
@@ -132,12 +185,14 @@ export function parseUniversal(
|
|
|
132
185
|
const parts: string[] = [];
|
|
133
186
|
const deps: string[] = [];
|
|
134
187
|
|
|
135
|
-
|
|
136
|
-
|
|
188
|
+
const clean = stripComments(content);
|
|
189
|
+
|
|
190
|
+
// Astro frontmatter
|
|
191
|
+
const fm = clean.match(/^---([\s\S]*?)---/);
|
|
137
192
|
if (fm?.[1]) parts.push(fm[1]);
|
|
138
193
|
|
|
139
194
|
// <script src="">
|
|
140
|
-
const scriptSrc =
|
|
195
|
+
const scriptSrc = clean.matchAll(
|
|
141
196
|
/<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/g
|
|
142
197
|
);
|
|
143
198
|
|
|
@@ -152,29 +207,33 @@ export function parseUniversal(
|
|
|
152
207
|
}
|
|
153
208
|
|
|
154
209
|
// inline <script>
|
|
155
|
-
const scripts =
|
|
156
|
-
/<script\b[^>]
|
|
210
|
+
const scripts = clean.matchAll(
|
|
211
|
+
/<script\b([^>]*)>([\s\S]*?)<\/script>/g
|
|
157
212
|
);
|
|
158
213
|
|
|
159
214
|
for (const m of scripts) {
|
|
160
|
-
|
|
215
|
+
const attrs = m[1];
|
|
216
|
+
|
|
217
|
+
// skip JSON / data
|
|
218
|
+
if (/type=["']application\/json/.test(attrs)) continue;
|
|
219
|
+
|
|
220
|
+
if (m[2].trim()) {
|
|
221
|
+
parts.push(m[2]);
|
|
222
|
+
}
|
|
161
223
|
}
|
|
162
224
|
|
|
163
|
-
//
|
|
164
|
-
const noScripts =
|
|
225
|
+
// remove scripts
|
|
226
|
+
const noScripts = clean.replace(
|
|
165
227
|
/<script[\s\S]*?<\/script>/g,
|
|
166
228
|
""
|
|
167
229
|
);
|
|
168
230
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (noHtml.trim()) parts.push(noHtml);
|
|
231
|
+
// loose JS
|
|
232
|
+
const loose = extractLooseJS(noScripts);
|
|
233
|
+
parts.push(...loose);
|
|
174
234
|
|
|
175
235
|
// expressions
|
|
176
|
-
const extracted = extractJS(
|
|
177
|
-
|
|
236
|
+
const extracted = extractJS(clean);
|
|
178
237
|
for (const e of extracted) {
|
|
179
238
|
parts.push(e + ";");
|
|
180
239
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveImport = resolveImport;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
function resolveImport(from, src) {
|
|
10
|
-
if (!src.startsWith("."))
|
|
11
|
-
return null;
|
|
12
|
-
const base = path_1.default.resolve(path_1.default.dirname(from), src);
|
|
13
|
-
const exts = [".ts", ".tsx", ".js", ".jsx"];
|
|
14
|
-
for (const ext of exts) {
|
|
15
|
-
if (fs_1.default.existsSync(base + ext))
|
|
16
|
-
return base + ext;
|
|
17
|
-
}
|
|
18
|
-
if (fs_1.default.existsSync(base))
|
|
19
|
-
return base;
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
export function resolveImport(from: string, src: string): string | null {
|
|
5
|
-
if (!src.startsWith(".")) return null;
|
|
6
|
-
|
|
7
|
-
const base = path.resolve(path.dirname(from), src);
|
|
8
|
-
|
|
9
|
-
const exts = [".ts", ".tsx", ".js", ".jsx"];
|
|
10
|
-
|
|
11
|
-
for (const ext of exts) {
|
|
12
|
-
if (fs.existsSync(base + ext)) return base + ext;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (fs.existsSync(base)) return base;
|
|
16
|
-
|
|
17
|
-
return null;
|
|
18
|
-
}
|