@eventra_dev/eventra-cli 0.1.25 → 0.1.26
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/commands/watch.js +41 -23
- package/dist/engine/callResolver.js +52 -4
- package/dist/engine/engine.js +63 -31
- package/dist/engine/exportResolver.js +16 -4
- package/dist/engine/functionAnalyzer.js +66 -13
- package/dist/engine/graph.js +40 -4
- package/dist/engine/languageService.js +83 -20
- package/dist/engine/resolver.js +29 -21
- package/dist/engine/scanner.js +57 -46
- package/dist/engine/trackingDetector.js +68 -0
- package/dist/utils/processFile.js +0 -9
- package/dist/utils/universal.js +25 -87
- package/package.json +1 -1
- package/src/commands/watch.ts +45 -25
- package/src/engine/callResolver.ts +77 -6
- package/src/engine/engine.ts +77 -32
- package/src/engine/exportResolver.ts +22 -5
- package/src/engine/functionAnalyzer.ts +76 -16
- package/src/engine/graph.ts +45 -6
- package/src/engine/languageService.ts +100 -29
- package/src/engine/resolver.ts +36 -27
- package/src/engine/scanner.ts +72 -50
- package/src/engine/trackingDetector.ts +94 -0
- package/src/types.ts +0 -3
- package/src/utils/processFile.ts +0 -9
- package/src/utils/universal.ts +26 -102
package/src/engine/scanner.ts
CHANGED
|
@@ -5,8 +5,7 @@ import { resolveExportedSymbol } from "./exportResolver";
|
|
|
5
5
|
import { findTrackParamIndex } from "./functionAnalyzer";
|
|
6
6
|
import { EventraConfig } from "../types";
|
|
7
7
|
import { isExternalFile } from "./boundary";
|
|
8
|
-
|
|
9
|
-
const TRACKING_NAMES = new Set(["track", "event", "capture", "send"]);
|
|
8
|
+
import { isTrackingCall } from "./trackingDetector";
|
|
10
9
|
|
|
11
10
|
function getCallName(expr: ts.Expression): string {
|
|
12
11
|
if (ts.isIdentifier(expr)) return expr.text;
|
|
@@ -20,6 +19,19 @@ function getJsxAttrName(name: ts.JsxAttributeName): string | null {
|
|
|
20
19
|
return null;
|
|
21
20
|
}
|
|
22
21
|
|
|
22
|
+
// unwrap
|
|
23
|
+
function unwrap(expr: ts.Expression): ts.Expression {
|
|
24
|
+
while (
|
|
25
|
+
ts.isAsExpression(expr) ||
|
|
26
|
+
ts.isParenthesizedExpression(expr) ||
|
|
27
|
+
ts.isTypeAssertionExpression(expr) ||
|
|
28
|
+
ts.isNonNullExpression(expr)
|
|
29
|
+
) {
|
|
30
|
+
expr = expr.expression;
|
|
31
|
+
}
|
|
32
|
+
return expr;
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
function isValidEvent(v: string) {
|
|
24
36
|
return (
|
|
25
37
|
v &&
|
|
@@ -32,24 +44,25 @@ function isValidEvent(v: string) {
|
|
|
32
44
|
|
|
33
45
|
function isPropUsedInTracking(
|
|
34
46
|
fn: ts.FunctionLikeDeclaration,
|
|
47
|
+
checker: ts.TypeChecker,
|
|
35
48
|
prop: string
|
|
36
49
|
) {
|
|
37
50
|
let found = false;
|
|
38
51
|
|
|
39
52
|
function visit(node: ts.Node) {
|
|
40
53
|
if (ts.isCallExpression(node)) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!TRACKING_NAMES.has(name)) {
|
|
54
|
+
if (!isTrackingCall(node, checker)) {
|
|
44
55
|
ts.forEachChild(node, visit);
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
for (const arg of node.arguments) {
|
|
60
|
+
const a = unwrap(arg as ts.Expression);
|
|
61
|
+
|
|
49
62
|
if (
|
|
50
|
-
(ts.isIdentifier(
|
|
51
|
-
(ts.isPropertyAccessExpression(
|
|
52
|
-
|
|
63
|
+
(ts.isIdentifier(a) && a.text === prop) ||
|
|
64
|
+
(ts.isPropertyAccessExpression(a) &&
|
|
65
|
+
a.name.text === prop)
|
|
53
66
|
) {
|
|
54
67
|
found = true;
|
|
55
68
|
}
|
|
@@ -59,7 +72,10 @@ function isPropUsedInTracking(
|
|
|
59
72
|
ts.forEachChild(node, visit);
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
if (fn.body) {
|
|
76
|
+
visit(fn.body);
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
return found;
|
|
64
80
|
}
|
|
65
81
|
|
|
@@ -81,7 +97,11 @@ export function scanSource(
|
|
|
81
97
|
const detectedFunctionWrappers = new Set<string>();
|
|
82
98
|
const detectedComponentWrappers = new Map<string, string>();
|
|
83
99
|
|
|
84
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
visited.has(source.fileName) ||
|
|
102
|
+
depth > 10 ||
|
|
103
|
+
visited.size > 200
|
|
104
|
+
) {
|
|
85
105
|
return { events, detectedFunctionWrappers, detectedComponentWrappers };
|
|
86
106
|
}
|
|
87
107
|
|
|
@@ -90,53 +110,55 @@ export function scanSource(
|
|
|
90
110
|
function visit(node: ts.Node) {
|
|
91
111
|
// CALL EXPRESSIONS
|
|
92
112
|
if (ts.isCallExpression(node)) {
|
|
93
|
-
const
|
|
113
|
+
const expr = unwrap(node.expression);
|
|
114
|
+
const name = getCallName(expr);
|
|
94
115
|
|
|
95
116
|
const isKnownWrapper = config.functionWrappers.some(
|
|
96
117
|
w => w.name === name
|
|
97
118
|
);
|
|
98
119
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// fallback: tracker.track(...)
|
|
102
|
-
if (!resolvedFn && ts.isPropertyAccessExpression(node.expression)) {
|
|
103
|
-
const symbol = checker.getSymbolAtLocation(node.expression.name);
|
|
104
|
-
|
|
105
|
-
if (symbol) {
|
|
106
|
-
const decls = symbol.getDeclarations();
|
|
120
|
+
const isTrackingByType = isTrackingCall(node, checker);
|
|
107
121
|
|
|
108
|
-
|
|
109
|
-
const d = decls[0];
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
ts.isMethodDeclaration(d) ||
|
|
113
|
-
ts.isFunctionDeclaration(d) ||
|
|
114
|
-
ts.isFunctionExpression(d) ||
|
|
115
|
-
ts.isArrowFunction(d)
|
|
116
|
-
) {
|
|
117
|
-
resolvedFn = d;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
+
let resolvedFn = resolveFunctionFromCall(expr, checker);
|
|
122
123
|
|
|
123
124
|
const isAutoWrapper =
|
|
124
125
|
resolvedFn && findTrackParamIndex(resolvedFn) !== null;
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
let isTracking =
|
|
127
128
|
isKnownWrapper ||
|
|
128
|
-
|
|
129
|
-
name.startsWith("$") ||
|
|
129
|
+
isTrackingByType ||
|
|
130
|
+
name.startsWith("$") ||
|
|
130
131
|
(isAutoWrapper &&
|
|
131
132
|
resolvedFn &&
|
|
132
133
|
!isExternalFile(resolvedFn.getSourceFile().fileName));
|
|
133
134
|
|
|
135
|
+
// fallback
|
|
136
|
+
if (!isTracking) {
|
|
137
|
+
if (
|
|
138
|
+
ts.isPropertyAccessExpression(expr) &&
|
|
139
|
+
expr.name.text === "track"
|
|
140
|
+
) {
|
|
141
|
+
isTracking = true;
|
|
142
|
+
} else if (
|
|
143
|
+
ts.isIdentifier(expr) &&
|
|
144
|
+
expr.text === "track"
|
|
145
|
+
) {
|
|
146
|
+
isTracking = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
134
150
|
if (!isTracking) {
|
|
135
151
|
ts.forEachChild(node, visit);
|
|
136
152
|
return;
|
|
137
153
|
}
|
|
138
154
|
|
|
139
|
-
|
|
155
|
+
// detect wrapper
|
|
156
|
+
if (
|
|
157
|
+
!isKnownWrapper &&
|
|
158
|
+
isAutoWrapper &&
|
|
159
|
+
!isTrackingByType &&
|
|
160
|
+
!name.startsWith("track")
|
|
161
|
+
) {
|
|
140
162
|
detectedFunctionWrappers.add(name);
|
|
141
163
|
}
|
|
142
164
|
|
|
@@ -161,16 +183,14 @@ export function scanSource(
|
|
|
161
183
|
|
|
162
184
|
if (idx !== null && node.arguments[idx]) {
|
|
163
185
|
const res = resolveNodeValue(
|
|
164
|
-
node.arguments[idx],
|
|
186
|
+
unwrap(node.arguments[idx]),
|
|
165
187
|
checker,
|
|
166
188
|
paramMap
|
|
167
189
|
);
|
|
168
190
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
}
|
|
191
|
+
res.values.forEach(v => {
|
|
192
|
+
if (isValidEvent(v)) events.add(v);
|
|
193
|
+
});
|
|
174
194
|
|
|
175
195
|
handled = true;
|
|
176
196
|
}
|
|
@@ -179,13 +199,15 @@ export function scanSource(
|
|
|
179
199
|
// FALLBACK
|
|
180
200
|
if (!handled) {
|
|
181
201
|
for (const arg of node.arguments) {
|
|
182
|
-
const res = resolveNodeValue(
|
|
202
|
+
const res = resolveNodeValue(
|
|
203
|
+
unwrap(arg),
|
|
204
|
+
checker,
|
|
205
|
+
paramMap
|
|
206
|
+
);
|
|
183
207
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
}
|
|
208
|
+
res.values.forEach(v => {
|
|
209
|
+
if (isValidEvent(v)) events.add(v);
|
|
210
|
+
});
|
|
189
211
|
}
|
|
190
212
|
}
|
|
191
213
|
|
|
@@ -263,7 +285,7 @@ export function scanSource(
|
|
|
263
285
|
const prop = el.name.getText();
|
|
264
286
|
|
|
265
287
|
const isEvent =
|
|
266
|
-
isPropUsedInTracking(fn, prop) ||
|
|
288
|
+
isPropUsedInTracking(fn, checker, prop) ||
|
|
267
289
|
config.wrappers.some(
|
|
268
290
|
w => w.name === tagName && w.prop === prop
|
|
269
291
|
);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
function isStringLike(type: ts.Type) {
|
|
4
|
+
return (
|
|
5
|
+
(type.flags & ts.TypeFlags.StringLike) !== 0 ||
|
|
6
|
+
(type.flags & ts.TypeFlags.StringLiteral) !== 0
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function hasTrackLikeSignature(
|
|
11
|
+
type: ts.Type,
|
|
12
|
+
checker: ts.TypeChecker
|
|
13
|
+
): boolean {
|
|
14
|
+
const sigs = type.getCallSignatures();
|
|
15
|
+
|
|
16
|
+
for (const sig of sigs) {
|
|
17
|
+
const params = sig.getParameters();
|
|
18
|
+
if (!params.length) continue;
|
|
19
|
+
|
|
20
|
+
const t = checker.getTypeOfSymbolAtLocation(
|
|
21
|
+
params[0],
|
|
22
|
+
params[0].valueDeclaration!
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (isStringLike(t)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isEventraLikeType(
|
|
34
|
+
type: ts.Type,
|
|
35
|
+
checker: ts.TypeChecker
|
|
36
|
+
): boolean {
|
|
37
|
+
const props = type.getProperties();
|
|
38
|
+
|
|
39
|
+
for (const prop of props) {
|
|
40
|
+
const decl = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
41
|
+
if (!decl) continue;
|
|
42
|
+
|
|
43
|
+
const t = checker.getTypeOfSymbolAtLocation(prop, decl);
|
|
44
|
+
|
|
45
|
+
if (hasTrackLikeSignature(t, checker)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isTrackingCall(
|
|
54
|
+
node: ts.CallExpression,
|
|
55
|
+
checker: ts.TypeChecker
|
|
56
|
+
): boolean {
|
|
57
|
+
const expr = node.expression;
|
|
58
|
+
|
|
59
|
+
// fn(...)
|
|
60
|
+
if (ts.isIdentifier(expr)) {
|
|
61
|
+
const type = checker.getTypeAtLocation(expr);
|
|
62
|
+
if (hasTrackLikeSignature(type, checker)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// obj.method(...)
|
|
68
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
69
|
+
const objType = checker.getTypeAtLocation(expr.expression);
|
|
70
|
+
|
|
71
|
+
// объект похож на tracker
|
|
72
|
+
if (isEventraLikeType(objType, checker)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// fallback
|
|
77
|
+
const methodType = checker.getTypeAtLocation(expr.name);
|
|
78
|
+
|
|
79
|
+
if (hasTrackLikeSignature(methodType, checker)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// obj["track"](...)
|
|
85
|
+
if (ts.isElementAccessExpression(expr)) {
|
|
86
|
+
const objType = checker.getTypeAtLocation(expr.expression);
|
|
87
|
+
|
|
88
|
+
if (isEventraLikeType(objType, checker)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
}
|
package/src/types.ts
CHANGED
package/src/utils/processFile.ts
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
|
-
import path from "path";
|
|
2
1
|
import { parseUniversal } from "./universal";
|
|
3
2
|
|
|
4
3
|
export function processFile(file: string, content: string) {
|
|
5
4
|
const { code, deps } = parseUniversal(content, file);
|
|
6
5
|
|
|
7
|
-
const ext = path.extname(file);
|
|
8
|
-
|
|
9
|
-
const virtualFile =
|
|
10
|
-
/\.(vue|svelte|astro|html)$/i.test(file)
|
|
11
|
-
? file.replace(ext, "") + ".tsx"
|
|
12
|
-
: file;
|
|
13
|
-
|
|
14
6
|
return {
|
|
15
7
|
content: code || "",
|
|
16
|
-
virtualFile,
|
|
17
8
|
deps: [...new Set(deps.filter(Boolean))],
|
|
18
9
|
};
|
|
19
10
|
}
|
package/src/utils/universal.ts
CHANGED
|
@@ -12,7 +12,7 @@ function stripComments(input: string): string {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Extract JS expressions
|
|
15
|
+
* Extract JS expressions
|
|
16
16
|
*/
|
|
17
17
|
function extractJS(input: string): string[] {
|
|
18
18
|
const result: string[] = [];
|
|
@@ -26,7 +26,6 @@ function extractJS(input: string): string[] {
|
|
|
26
26
|
while (i < len) {
|
|
27
27
|
const ch = input[i];
|
|
28
28
|
|
|
29
|
-
// STRING
|
|
30
29
|
if (inString) {
|
|
31
30
|
if (escape) escape = false;
|
|
32
31
|
else if (ch === "\\") escape = true;
|
|
@@ -41,47 +40,20 @@ function extractJS(input: string): string[] {
|
|
|
41
40
|
continue;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
// { ... }
|
|
45
43
|
if (ch === "{") {
|
|
46
44
|
let depth = 1;
|
|
47
45
|
let j = i + 1;
|
|
48
46
|
|
|
49
|
-
let innerStr: '"' | "'" | "`" | null = null;
|
|
50
|
-
let innerEsc = false;
|
|
51
|
-
|
|
52
47
|
while (j < len && depth > 0) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (innerStr) {
|
|
56
|
-
if (innerEsc) innerEsc = false;
|
|
57
|
-
else if (c === "\\") innerEsc = true;
|
|
58
|
-
else if (c === innerStr) innerStr = null;
|
|
59
|
-
j++;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
64
|
-
innerStr = c;
|
|
65
|
-
j++;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (c === "{") depth++;
|
|
70
|
-
else if (c === "}") depth--;
|
|
71
|
-
|
|
48
|
+
if (input[j] === "{") depth++;
|
|
49
|
+
else if (input[j] === "}") depth--;
|
|
72
50
|
j++;
|
|
73
51
|
}
|
|
74
52
|
|
|
75
53
|
if (depth === 0) {
|
|
76
54
|
const expr = input.slice(i + 1, j - 1).trim();
|
|
77
55
|
|
|
78
|
-
if (
|
|
79
|
-
expr &&
|
|
80
|
-
(expr.includes("(") ||
|
|
81
|
-
expr.includes("=") ||
|
|
82
|
-
expr.includes(".") ||
|
|
83
|
-
expr.includes("track"))
|
|
84
|
-
) {
|
|
56
|
+
if (expr && /[a-zA-Z0-9_$]\s*\(/.test(expr)) {
|
|
85
57
|
result.push(expr);
|
|
86
58
|
}
|
|
87
59
|
|
|
@@ -90,64 +62,14 @@ function extractJS(input: string): string[] {
|
|
|
90
62
|
}
|
|
91
63
|
}
|
|
92
64
|
|
|
93
|
-
// attr="..."
|
|
94
|
-
if (ch === "=" && (input[i + 1] === '"' || input[i + 1] === "'")) {
|
|
95
|
-
const quote = input[i + 1];
|
|
96
|
-
let j = i + 2;
|
|
97
|
-
let val = "";
|
|
98
|
-
|
|
99
|
-
while (j < len) {
|
|
100
|
-
const c = input[j];
|
|
101
|
-
|
|
102
|
-
if (c === "\\" && input[j + 1]) {
|
|
103
|
-
val += c + input[j + 1];
|
|
104
|
-
j += 2;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (c === quote) break;
|
|
109
|
-
|
|
110
|
-
val += c;
|
|
111
|
-
j++;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
val.trim() &&
|
|
116
|
-
(val.includes("(") ||
|
|
117
|
-
val.includes("=>") ||
|
|
118
|
-
val.includes("track"))
|
|
119
|
-
) {
|
|
120
|
-
result.push(val.trim());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
i = j + 1;
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
65
|
i++;
|
|
128
66
|
}
|
|
129
67
|
|
|
130
|
-
// Vue / Svelte / JSX handlers
|
|
131
|
-
const extra = [
|
|
132
|
-
/@[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
133
|
-
/v-on:[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
134
|
-
/on:[\w-]+\s*=\s*{([\s\S]*?)}/g,
|
|
135
|
-
/on\w+\s*=\s*{([\s\S]*?)}/g,
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
for (const r of extra) {
|
|
139
|
-
const m = input.matchAll(r);
|
|
140
|
-
for (const x of m) {
|
|
141
|
-
const v = x[1]?.trim();
|
|
142
|
-
if (v) result.push(v);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
68
|
return result;
|
|
147
69
|
}
|
|
148
70
|
|
|
149
71
|
/**
|
|
150
|
-
*
|
|
72
|
+
* Loose JS extractor
|
|
151
73
|
*/
|
|
152
74
|
function extractLooseJS(input: string): string[] {
|
|
153
75
|
const result: string[] = [];
|
|
@@ -160,14 +82,7 @@ function extractLooseJS(input: string): string[] {
|
|
|
160
82
|
if (!l) continue;
|
|
161
83
|
if (l.startsWith("<")) continue;
|
|
162
84
|
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
l.includes("track") ||
|
|
166
|
-
l.includes(".track(") ||
|
|
167
|
-
l.includes("=>") ||
|
|
168
|
-
l.startsWith("function") ||
|
|
169
|
-
l.includes("(")
|
|
170
|
-
) {
|
|
85
|
+
if (/[a-zA-Z0-9_$]\s*\(/.test(l)) {
|
|
171
86
|
result.push(l);
|
|
172
87
|
}
|
|
173
88
|
}
|
|
@@ -176,7 +91,14 @@ function extractLooseJS(input: string): string[] {
|
|
|
176
91
|
}
|
|
177
92
|
|
|
178
93
|
/**
|
|
179
|
-
*
|
|
94
|
+
* UNIVERSAL PARSER
|
|
95
|
+
* Support:
|
|
96
|
+
* - React / JSX
|
|
97
|
+
* - Vue
|
|
98
|
+
* - Svelte
|
|
99
|
+
* - Astro
|
|
100
|
+
* - HTML + JS
|
|
101
|
+
* - SSR
|
|
180
102
|
*/
|
|
181
103
|
export function parseUniversal(
|
|
182
104
|
content: string,
|
|
@@ -187,9 +109,11 @@ export function parseUniversal(
|
|
|
187
109
|
|
|
188
110
|
const clean = stripComments(content);
|
|
189
111
|
|
|
190
|
-
//
|
|
112
|
+
// ASTRO FRONTMATTER
|
|
191
113
|
const fm = clean.match(/^---([\s\S]*?)---/);
|
|
192
|
-
if (fm?.[1])
|
|
114
|
+
if (fm?.[1]) {
|
|
115
|
+
parts.push(fm[1]);
|
|
116
|
+
}
|
|
193
117
|
|
|
194
118
|
// <script src="">
|
|
195
119
|
const scriptSrc = clean.matchAll(
|
|
@@ -206,7 +130,7 @@ export function parseUniversal(
|
|
|
206
130
|
deps.push(resolved);
|
|
207
131
|
}
|
|
208
132
|
|
|
209
|
-
//
|
|
133
|
+
// INLINE <script>
|
|
210
134
|
const scripts = clean.matchAll(
|
|
211
135
|
/<script\b([^>]*)>([\s\S]*?)<\/script>/g
|
|
212
136
|
);
|
|
@@ -214,7 +138,7 @@ export function parseUniversal(
|
|
|
214
138
|
for (const m of scripts) {
|
|
215
139
|
const attrs = m[1];
|
|
216
140
|
|
|
217
|
-
// skip JSON
|
|
141
|
+
// skip JSON
|
|
218
142
|
if (/type=["']application\/json/.test(attrs)) continue;
|
|
219
143
|
|
|
220
144
|
if (m[2].trim()) {
|
|
@@ -222,24 +146,24 @@ export function parseUniversal(
|
|
|
222
146
|
}
|
|
223
147
|
}
|
|
224
148
|
|
|
225
|
-
//
|
|
149
|
+
// REMOVE SCRIPTS → TEMPLATE PART
|
|
226
150
|
const noScripts = clean.replace(
|
|
227
151
|
/<script[\s\S]*?<\/script>/g,
|
|
228
152
|
""
|
|
229
153
|
);
|
|
230
154
|
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
parts.push(...loose);
|
|
155
|
+
// LOOSE JS
|
|
156
|
+
parts.push(...extractLooseJS(noScripts));
|
|
234
157
|
|
|
235
|
-
//
|
|
158
|
+
// EXPRESSIONS
|
|
236
159
|
const extracted = extractJS(clean);
|
|
237
160
|
for (const e of extracted) {
|
|
238
161
|
parts.push(e + ";");
|
|
239
162
|
}
|
|
240
163
|
|
|
164
|
+
// RESULT
|
|
241
165
|
return {
|
|
242
166
|
code: [...new Set(parts)].join("\n"),
|
|
243
|
-
deps,
|
|
167
|
+
deps: [...new Set(deps.filter(Boolean))],
|
|
244
168
|
};
|
|
245
169
|
}
|