@eventra_dev/eventra-cli 0.1.24 → 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/check.js +40 -16
- package/dist/commands/sync.js +40 -38
- package/dist/commands/watch.js +90 -36
- package/dist/engine/callResolver.js +52 -4
- package/dist/engine/engine.js +66 -32
- 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/getVirtualFile.js +8 -0
- package/dist/utils/processFile.js +0 -9
- package/dist/utils/universal.js +25 -87
- package/package.json +1 -1
- package/src/commands/check.ts +49 -29
- package/src/commands/sync.ts +48 -47
- package/src/commands/watch.ts +105 -37
- package/src/engine/callResolver.ts +77 -6
- package/src/engine/engine.ts +80 -33
- 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/getVirtualFile.ts +5 -0
- package/src/utils/processFile.ts +0 -9
- package/src/utils/universal.ts +26 -102
package/dist/engine/scanner.js
CHANGED
|
@@ -10,7 +10,7 @@ const callResolver_1 = require("./callResolver");
|
|
|
10
10
|
const exportResolver_1 = require("./exportResolver");
|
|
11
11
|
const functionAnalyzer_1 = require("./functionAnalyzer");
|
|
12
12
|
const boundary_1 = require("./boundary");
|
|
13
|
-
const
|
|
13
|
+
const trackingDetector_1 = require("./trackingDetector");
|
|
14
14
|
function getCallName(expr) {
|
|
15
15
|
if (typescript_1.default.isIdentifier(expr))
|
|
16
16
|
return expr.text;
|
|
@@ -25,6 +25,16 @@ function getJsxAttrName(name) {
|
|
|
25
25
|
return name.name.text;
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
|
+
// unwrap
|
|
29
|
+
function unwrap(expr) {
|
|
30
|
+
while (typescript_1.default.isAsExpression(expr) ||
|
|
31
|
+
typescript_1.default.isParenthesizedExpression(expr) ||
|
|
32
|
+
typescript_1.default.isTypeAssertionExpression(expr) ||
|
|
33
|
+
typescript_1.default.isNonNullExpression(expr)) {
|
|
34
|
+
expr = expr.expression;
|
|
35
|
+
}
|
|
36
|
+
return expr;
|
|
37
|
+
}
|
|
28
38
|
function isValidEvent(v) {
|
|
29
39
|
return (v &&
|
|
30
40
|
v.length > 1 &&
|
|
@@ -32,70 +42,75 @@ function isValidEvent(v) {
|
|
|
32
42
|
!v.includes(" ") &&
|
|
33
43
|
/^[a-zA-Z0-9._:-]+$/.test(v));
|
|
34
44
|
}
|
|
35
|
-
function isPropUsedInTracking(fn, prop) {
|
|
45
|
+
function isPropUsedInTracking(fn, checker, prop) {
|
|
36
46
|
let found = false;
|
|
37
47
|
function visit(node) {
|
|
38
48
|
if (typescript_1.default.isCallExpression(node)) {
|
|
39
|
-
|
|
40
|
-
if (!TRACKING_NAMES.has(name)) {
|
|
49
|
+
if (!(0, trackingDetector_1.isTrackingCall)(node, checker)) {
|
|
41
50
|
typescript_1.default.forEachChild(node, visit);
|
|
42
51
|
return;
|
|
43
52
|
}
|
|
44
53
|
for (const arg of node.arguments) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
const a = unwrap(arg);
|
|
55
|
+
if ((typescript_1.default.isIdentifier(a) && a.text === prop) ||
|
|
56
|
+
(typescript_1.default.isPropertyAccessExpression(a) &&
|
|
57
|
+
a.name.text === prop)) {
|
|
48
58
|
found = true;
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
typescript_1.default.forEachChild(node, visit);
|
|
53
63
|
}
|
|
54
|
-
|
|
64
|
+
if (fn.body) {
|
|
65
|
+
visit(fn.body);
|
|
66
|
+
}
|
|
55
67
|
return found;
|
|
56
68
|
}
|
|
57
69
|
function scanSource(source, checker, config, visited = new Set(), depth = 0, inheritedParamMap) {
|
|
58
70
|
const events = new Set();
|
|
59
71
|
const detectedFunctionWrappers = new Set();
|
|
60
72
|
const detectedComponentWrappers = new Map();
|
|
61
|
-
if (
|
|
73
|
+
if (visited.has(source.fileName) ||
|
|
74
|
+
depth > 10 ||
|
|
75
|
+
visited.size > 200) {
|
|
62
76
|
return { events, detectedFunctionWrappers, detectedComponentWrappers };
|
|
63
77
|
}
|
|
64
78
|
visited.add(source.fileName);
|
|
65
79
|
function visit(node) {
|
|
66
80
|
// CALL EXPRESSIONS
|
|
67
81
|
if (typescript_1.default.isCallExpression(node)) {
|
|
68
|
-
const
|
|
82
|
+
const expr = unwrap(node.expression);
|
|
83
|
+
const name = getCallName(expr);
|
|
69
84
|
const isKnownWrapper = config.functionWrappers.some(w => w.name === name);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (!resolvedFn && typescript_1.default.isPropertyAccessExpression(node.expression)) {
|
|
73
|
-
const symbol = checker.getSymbolAtLocation(node.expression.name);
|
|
74
|
-
if (symbol) {
|
|
75
|
-
const decls = symbol.getDeclarations();
|
|
76
|
-
if (decls?.length) {
|
|
77
|
-
const d = decls[0];
|
|
78
|
-
if (typescript_1.default.isMethodDeclaration(d) ||
|
|
79
|
-
typescript_1.default.isFunctionDeclaration(d) ||
|
|
80
|
-
typescript_1.default.isFunctionExpression(d) ||
|
|
81
|
-
typescript_1.default.isArrowFunction(d)) {
|
|
82
|
-
resolvedFn = d;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
85
|
+
const isTrackingByType = (0, trackingDetector_1.isTrackingCall)(node, checker);
|
|
86
|
+
let resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(expr, checker);
|
|
87
87
|
const isAutoWrapper = resolvedFn && (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn) !== null;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
name.startsWith("$") ||
|
|
88
|
+
let isTracking = isKnownWrapper ||
|
|
89
|
+
isTrackingByType ||
|
|
90
|
+
name.startsWith("$") ||
|
|
91
91
|
(isAutoWrapper &&
|
|
92
92
|
resolvedFn &&
|
|
93
93
|
!(0, boundary_1.isExternalFile)(resolvedFn.getSourceFile().fileName));
|
|
94
|
+
// fallback
|
|
95
|
+
if (!isTracking) {
|
|
96
|
+
if (typescript_1.default.isPropertyAccessExpression(expr) &&
|
|
97
|
+
expr.name.text === "track") {
|
|
98
|
+
isTracking = true;
|
|
99
|
+
}
|
|
100
|
+
else if (typescript_1.default.isIdentifier(expr) &&
|
|
101
|
+
expr.text === "track") {
|
|
102
|
+
isTracking = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
94
105
|
if (!isTracking) {
|
|
95
106
|
typescript_1.default.forEachChild(node, visit);
|
|
96
107
|
return;
|
|
97
108
|
}
|
|
98
|
-
|
|
109
|
+
// detect wrapper
|
|
110
|
+
if (!isKnownWrapper &&
|
|
111
|
+
isAutoWrapper &&
|
|
112
|
+
!isTrackingByType &&
|
|
113
|
+
!name.startsWith("track")) {
|
|
99
114
|
detectedFunctionWrappers.add(name);
|
|
100
115
|
}
|
|
101
116
|
const paramMap = new Map(inheritedParamMap ?? []);
|
|
@@ -114,26 +129,22 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
114
129
|
if (resolvedFn) {
|
|
115
130
|
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
|
|
116
131
|
if (idx !== null && node.arguments[idx]) {
|
|
117
|
-
const res = (0, resolver_1.resolveNodeValue)(node.arguments[idx], checker, paramMap);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
}
|
|
132
|
+
const res = (0, resolver_1.resolveNodeValue)(unwrap(node.arguments[idx]), checker, paramMap);
|
|
133
|
+
res.values.forEach(v => {
|
|
134
|
+
if (isValidEvent(v))
|
|
135
|
+
events.add(v);
|
|
136
|
+
});
|
|
124
137
|
handled = true;
|
|
125
138
|
}
|
|
126
139
|
}
|
|
127
140
|
// FALLBACK
|
|
128
141
|
if (!handled) {
|
|
129
142
|
for (const arg of node.arguments) {
|
|
130
|
-
const res = (0, resolver_1.resolveNodeValue)(arg, checker, paramMap);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
143
|
+
const res = (0, resolver_1.resolveNodeValue)(unwrap(arg), checker, paramMap);
|
|
144
|
+
res.values.forEach(v => {
|
|
145
|
+
if (isValidEvent(v))
|
|
146
|
+
events.add(v);
|
|
147
|
+
});
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
150
|
// CROSS FILE
|
|
@@ -183,7 +194,7 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0, inh
|
|
|
183
194
|
continue;
|
|
184
195
|
for (const el of param.name.elements) {
|
|
185
196
|
const prop = el.name.getText();
|
|
186
|
-
const isEvent = isPropUsedInTracking(fn, prop) ||
|
|
197
|
+
const isEvent = isPropUsedInTracking(fn, checker, prop) ||
|
|
187
198
|
config.wrappers.some(w => w.name === tagName && w.prop === prop);
|
|
188
199
|
if (!isEvent)
|
|
189
200
|
continue;
|
|
@@ -0,0 +1,68 @@
|
|
|
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.isTrackingCall = isTrackingCall;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
function isStringLike(type) {
|
|
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) {
|
|
39
|
+
const expr = node.expression;
|
|
40
|
+
// fn(...)
|
|
41
|
+
if (typescript_1.default.isIdentifier(expr)) {
|
|
42
|
+
const type = checker.getTypeAtLocation(expr);
|
|
43
|
+
if (hasTrackLikeSignature(type, checker)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// obj.method(...)
|
|
48
|
+
if (typescript_1.default.isPropertyAccessExpression(expr)) {
|
|
49
|
+
const objType = checker.getTypeAtLocation(expr.expression);
|
|
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
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.processFile = processFile;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
4
|
const universal_1 = require("./universal");
|
|
9
5
|
function processFile(file, content) {
|
|
10
6
|
const { code, deps } = (0, universal_1.parseUniversal)(content, file);
|
|
11
|
-
const ext = path_1.default.extname(file);
|
|
12
|
-
const virtualFile = /\.(vue|svelte|astro|html)$/i.test(file)
|
|
13
|
-
? file.replace(ext, "") + ".tsx"
|
|
14
|
-
: file;
|
|
15
7
|
return {
|
|
16
8
|
content: code || "",
|
|
17
|
-
virtualFile,
|
|
18
9
|
deps: [...new Set(deps.filter(Boolean))],
|
|
19
10
|
};
|
|
20
11
|
}
|
package/dist/utils/universal.js
CHANGED
|
@@ -15,7 +15,7 @@ function stripComments(input) {
|
|
|
15
15
|
.replace(/\/\/.*$/gm, "");
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Extract JS expressions
|
|
18
|
+
* Extract JS expressions
|
|
19
19
|
*/
|
|
20
20
|
function extractJS(input) {
|
|
21
21
|
const result = [];
|
|
@@ -25,7 +25,6 @@ function extractJS(input) {
|
|
|
25
25
|
let escape = false;
|
|
26
26
|
while (i < len) {
|
|
27
27
|
const ch = input[i];
|
|
28
|
-
// STRING
|
|
29
28
|
if (inString) {
|
|
30
29
|
if (escape)
|
|
31
30
|
escape = false;
|
|
@@ -41,95 +40,31 @@ function extractJS(input) {
|
|
|
41
40
|
i++;
|
|
42
41
|
continue;
|
|
43
42
|
}
|
|
44
|
-
// { ... }
|
|
45
43
|
if (ch === "{") {
|
|
46
44
|
let depth = 1;
|
|
47
45
|
let j = i + 1;
|
|
48
|
-
let innerStr = null;
|
|
49
|
-
let innerEsc = false;
|
|
50
46
|
while (j < len && depth > 0) {
|
|
51
|
-
|
|
52
|
-
if (innerStr) {
|
|
53
|
-
if (innerEsc)
|
|
54
|
-
innerEsc = false;
|
|
55
|
-
else if (c === "\\")
|
|
56
|
-
innerEsc = true;
|
|
57
|
-
else if (c === innerStr)
|
|
58
|
-
innerStr = null;
|
|
59
|
-
j++;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
63
|
-
innerStr = c;
|
|
64
|
-
j++;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (c === "{")
|
|
47
|
+
if (input[j] === "{")
|
|
68
48
|
depth++;
|
|
69
|
-
else if (
|
|
49
|
+
else if (input[j] === "}")
|
|
70
50
|
depth--;
|
|
71
51
|
j++;
|
|
72
52
|
}
|
|
73
53
|
if (depth === 0) {
|
|
74
54
|
const expr = input.slice(i + 1, j - 1).trim();
|
|
75
|
-
if (expr &&
|
|
76
|
-
(expr.includes("(") ||
|
|
77
|
-
expr.includes("=") ||
|
|
78
|
-
expr.includes(".") ||
|
|
79
|
-
expr.includes("track"))) {
|
|
55
|
+
if (expr && /[a-zA-Z0-9_$]\s*\(/.test(expr)) {
|
|
80
56
|
result.push(expr);
|
|
81
57
|
}
|
|
82
58
|
i = j;
|
|
83
59
|
continue;
|
|
84
60
|
}
|
|
85
61
|
}
|
|
86
|
-
// attr="..."
|
|
87
|
-
if (ch === "=" && (input[i + 1] === '"' || input[i + 1] === "'")) {
|
|
88
|
-
const quote = input[i + 1];
|
|
89
|
-
let j = i + 2;
|
|
90
|
-
let val = "";
|
|
91
|
-
while (j < len) {
|
|
92
|
-
const c = input[j];
|
|
93
|
-
if (c === "\\" && input[j + 1]) {
|
|
94
|
-
val += c + input[j + 1];
|
|
95
|
-
j += 2;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (c === quote)
|
|
99
|
-
break;
|
|
100
|
-
val += c;
|
|
101
|
-
j++;
|
|
102
|
-
}
|
|
103
|
-
if (val.trim() &&
|
|
104
|
-
(val.includes("(") ||
|
|
105
|
-
val.includes("=>") ||
|
|
106
|
-
val.includes("track"))) {
|
|
107
|
-
result.push(val.trim());
|
|
108
|
-
}
|
|
109
|
-
i = j + 1;
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
62
|
i++;
|
|
113
63
|
}
|
|
114
|
-
// Vue / Svelte / JSX handlers
|
|
115
|
-
const extra = [
|
|
116
|
-
/@[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
117
|
-
/v-on:[\w-]+\s*=\s*["']([\s\S]*?)["']/g,
|
|
118
|
-
/on:[\w-]+\s*=\s*{([\s\S]*?)}/g,
|
|
119
|
-
/on\w+\s*=\s*{([\s\S]*?)}/g,
|
|
120
|
-
];
|
|
121
|
-
for (const r of extra) {
|
|
122
|
-
const m = input.matchAll(r);
|
|
123
|
-
for (const x of m) {
|
|
124
|
-
const v = x[1]?.trim();
|
|
125
|
-
if (v)
|
|
126
|
-
result.push(v);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
64
|
return result;
|
|
130
65
|
}
|
|
131
66
|
/**
|
|
132
|
-
*
|
|
67
|
+
* Loose JS extractor
|
|
133
68
|
*/
|
|
134
69
|
function extractLooseJS(input) {
|
|
135
70
|
const result = [];
|
|
@@ -140,28 +75,31 @@ function extractLooseJS(input) {
|
|
|
140
75
|
continue;
|
|
141
76
|
if (l.startsWith("<"))
|
|
142
77
|
continue;
|
|
143
|
-
|
|
144
|
-
if (l.includes("track") ||
|
|
145
|
-
l.includes(".track(") ||
|
|
146
|
-
l.includes("=>") ||
|
|
147
|
-
l.startsWith("function") ||
|
|
148
|
-
l.includes("(")) {
|
|
78
|
+
if (/[a-zA-Z0-9_$]\s*\(/.test(l)) {
|
|
149
79
|
result.push(l);
|
|
150
80
|
}
|
|
151
81
|
}
|
|
152
82
|
return result;
|
|
153
83
|
}
|
|
154
84
|
/**
|
|
155
|
-
*
|
|
85
|
+
* UNIVERSAL PARSER
|
|
86
|
+
* Support:
|
|
87
|
+
* - React / JSX
|
|
88
|
+
* - Vue
|
|
89
|
+
* - Svelte
|
|
90
|
+
* - Astro
|
|
91
|
+
* - HTML + JS
|
|
92
|
+
* - SSR
|
|
156
93
|
*/
|
|
157
94
|
function parseUniversal(content, file) {
|
|
158
95
|
const parts = [];
|
|
159
96
|
const deps = [];
|
|
160
97
|
const clean = stripComments(content);
|
|
161
|
-
//
|
|
98
|
+
// ASTRO FRONTMATTER
|
|
162
99
|
const fm = clean.match(/^---([\s\S]*?)---/);
|
|
163
|
-
if (fm?.[1])
|
|
100
|
+
if (fm?.[1]) {
|
|
164
101
|
parts.push(fm[1]);
|
|
102
|
+
}
|
|
165
103
|
// <script src="">
|
|
166
104
|
const scriptSrc = clean.matchAll(/<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/g);
|
|
167
105
|
for (const s of scriptSrc) {
|
|
@@ -171,29 +109,29 @@ function parseUniversal(content, file) {
|
|
|
171
109
|
: path_1.default.resolve(path_1.default.dirname(file), src);
|
|
172
110
|
deps.push(resolved);
|
|
173
111
|
}
|
|
174
|
-
//
|
|
112
|
+
// INLINE <script>
|
|
175
113
|
const scripts = clean.matchAll(/<script\b([^>]*)>([\s\S]*?)<\/script>/g);
|
|
176
114
|
for (const m of scripts) {
|
|
177
115
|
const attrs = m[1];
|
|
178
|
-
// skip JSON
|
|
116
|
+
// skip JSON
|
|
179
117
|
if (/type=["']application\/json/.test(attrs))
|
|
180
118
|
continue;
|
|
181
119
|
if (m[2].trim()) {
|
|
182
120
|
parts.push(m[2]);
|
|
183
121
|
}
|
|
184
122
|
}
|
|
185
|
-
//
|
|
123
|
+
// REMOVE SCRIPTS → TEMPLATE PART
|
|
186
124
|
const noScripts = clean.replace(/<script[\s\S]*?<\/script>/g, "");
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// expressions
|
|
125
|
+
// LOOSE JS
|
|
126
|
+
parts.push(...extractLooseJS(noScripts));
|
|
127
|
+
// EXPRESSIONS
|
|
191
128
|
const extracted = extractJS(clean);
|
|
192
129
|
for (const e of extracted) {
|
|
193
130
|
parts.push(e + ";");
|
|
194
131
|
}
|
|
132
|
+
// RESULT
|
|
195
133
|
return {
|
|
196
134
|
code: [...new Set(parts)].join("\n"),
|
|
197
|
-
deps,
|
|
135
|
+
deps: [...new Set(deps.filter(Boolean))],
|
|
198
136
|
};
|
|
199
137
|
}
|
package/package.json
CHANGED
package/src/commands/check.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import fg from "fast-glob";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
4
5
|
|
|
5
6
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
6
7
|
import { EventraEngine } from "../engine/engine";
|
|
7
8
|
import { processFile } from "../utils/processFile";
|
|
9
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
8
10
|
|
|
9
11
|
export async function check({ fix = false }: { fix?: boolean }) {
|
|
10
12
|
const config = await loadConfig();
|
|
@@ -18,36 +20,68 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
18
20
|
|
|
19
21
|
const files = await fg(config.sync.include, {
|
|
20
22
|
ignore: config.sync.exclude,
|
|
23
|
+
absolute: true,
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
const engine = new EventraEngine(process.cwd());
|
|
24
27
|
|
|
28
|
+
const toScan = new Set<string>();
|
|
29
|
+
|
|
30
|
+
const cache = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{ content: string; deps: string[] }
|
|
33
|
+
>();
|
|
34
|
+
|
|
35
|
+
// COLLECT + CACHE
|
|
25
36
|
for (const file of files) {
|
|
26
37
|
try {
|
|
27
38
|
const raw = await fs.readFile(file, "utf-8");
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
if (raw.length > 2_000_000) continue;
|
|
41
|
+
|
|
42
|
+
const parsed = processFile(file, raw);
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
cache.set(file, parsed);
|
|
45
|
+
toScan.add(file);
|
|
32
46
|
|
|
33
|
-
for (const dep of deps) {
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
for (const dep of parsed.deps) {
|
|
48
|
+
toScan.add(path.resolve(dep));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
// PRELOAD
|
|
55
|
+
for (const file of toScan) {
|
|
56
|
+
try {
|
|
57
|
+
let parsed = cache.get(file);
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
if (!parsed) {
|
|
60
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
61
|
+
parsed = processFile(file, raw);
|
|
62
|
+
cache.set(file, parsed);
|
|
44
63
|
}
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
const virtual = getVirtualFile(file);
|
|
66
|
+
engine.preloadFile(virtual, parsed.content);
|
|
67
|
+
|
|
68
|
+
} catch {}
|
|
49
69
|
}
|
|
50
70
|
|
|
71
|
+
// SCAN
|
|
72
|
+
for (const file of toScan) {
|
|
73
|
+
try {
|
|
74
|
+
const parsed = cache.get(file);
|
|
75
|
+
if (!parsed) continue;
|
|
76
|
+
|
|
77
|
+
const virtual = getVirtualFile(file);
|
|
78
|
+
|
|
79
|
+
engine.scanFile(virtual, parsed.content, config);
|
|
80
|
+
|
|
81
|
+
} catch {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// DIFF
|
|
51
85
|
const found = new Set(engine.getAllEvents());
|
|
52
86
|
const known = new Set(config.events ?? []);
|
|
53
87
|
|
|
@@ -56,26 +90,12 @@ export async function check({ fix = false }: { fix?: boolean }) {
|
|
|
56
90
|
|
|
57
91
|
// FIX MODE
|
|
58
92
|
if (fix) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
found.forEach(e => next.add(e));
|
|
62
|
-
|
|
63
|
-
newEvents.forEach(e =>
|
|
64
|
-
console.log(chalk.green(`+ ${e}`))
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
removed.forEach(e =>
|
|
68
|
-
console.log(chalk.red(`- ${e}`))
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
config.events = [...next].sort();
|
|
72
|
-
|
|
93
|
+
config.events = [...found].sort();
|
|
73
94
|
await saveConfig(config);
|
|
74
95
|
|
|
75
96
|
console.log(
|
|
76
97
|
chalk.green(`\nSynced (${config.events.length} events)`)
|
|
77
98
|
);
|
|
78
|
-
|
|
79
99
|
return;
|
|
80
100
|
}
|
|
81
101
|
|