@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/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/watch.ts
CHANGED
|
@@ -14,6 +14,10 @@ type QueueItem = {
|
|
|
14
14
|
virtual: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
function normalize(file: string) {
|
|
18
|
+
return path.resolve(file).replace(/\\/g, "/");
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
export async function watch() {
|
|
18
22
|
const config = await loadConfig();
|
|
19
23
|
if (!config) return;
|
|
@@ -27,6 +31,7 @@ export async function watch() {
|
|
|
27
31
|
|
|
28
32
|
const engine = new EventraEngine(process.cwd());
|
|
29
33
|
const watchedDeps = new Set<string>();
|
|
34
|
+
|
|
30
35
|
const cache = new Map<
|
|
31
36
|
string,
|
|
32
37
|
{ content: string; deps: string[] }
|
|
@@ -37,18 +42,18 @@ export async function watch() {
|
|
|
37
42
|
// COLLECT + CACHE
|
|
38
43
|
for (const file of files) {
|
|
39
44
|
try {
|
|
40
|
-
const
|
|
45
|
+
const abs = normalize(file);
|
|
46
|
+
const raw = await fs.readFile(abs, "utf-8");
|
|
41
47
|
|
|
42
|
-
if (raw.length > 2_000_000) continue;
|
|
48
|
+
if (raw.length > 2_000_000) continue;
|
|
43
49
|
|
|
44
|
-
const parsed = processFile(
|
|
50
|
+
const parsed = processFile(abs, raw);
|
|
45
51
|
|
|
46
|
-
cache.set(
|
|
47
|
-
toScan.add(
|
|
52
|
+
cache.set(abs, parsed);
|
|
53
|
+
toScan.add(abs);
|
|
48
54
|
|
|
49
55
|
for (const dep of parsed.deps) {
|
|
50
|
-
|
|
51
|
-
toScan.add(absDep);
|
|
56
|
+
toScan.add(normalize(dep));
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
} catch {
|
|
@@ -56,7 +61,7 @@ export async function watch() {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
// PRELOAD
|
|
64
|
+
// PRELOAD
|
|
60
65
|
for (const file of toScan) {
|
|
61
66
|
try {
|
|
62
67
|
let parsed = cache.get(file);
|
|
@@ -64,10 +69,10 @@ export async function watch() {
|
|
|
64
69
|
if (!parsed) {
|
|
65
70
|
const raw = await fs.readFile(file, "utf-8");
|
|
66
71
|
parsed = processFile(file, raw);
|
|
72
|
+
cache.set(file, parsed);
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
const virtual = getVirtualFile(file);
|
|
70
|
-
|
|
71
76
|
engine.preloadFile?.(virtual, parsed.content);
|
|
72
77
|
|
|
73
78
|
} catch {}
|
|
@@ -88,7 +93,7 @@ export async function watch() {
|
|
|
88
93
|
engine.scanFile(virtual, parsed.content, config);
|
|
89
94
|
|
|
90
95
|
for (const dep of parsed.deps) {
|
|
91
|
-
watchedDeps.add(
|
|
96
|
+
watchedDeps.add(normalize(dep));
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
} catch {}
|
|
@@ -120,29 +125,44 @@ export async function watch() {
|
|
|
120
125
|
if (raw.length > 2_000_000) continue;
|
|
121
126
|
|
|
122
127
|
const parsed = processFile(item.real, raw);
|
|
123
|
-
|
|
124
128
|
cache.set(item.real, parsed);
|
|
125
129
|
|
|
126
130
|
engine.updateFile(item.virtual, parsed.content, config);
|
|
127
131
|
|
|
128
|
-
//
|
|
132
|
+
// DEPENDENCY DIFF
|
|
133
|
+
const nextDeps = new Set<string>();
|
|
134
|
+
|
|
129
135
|
for (const dep of parsed.deps) {
|
|
130
|
-
|
|
136
|
+
nextDeps.add(normalize(dep));
|
|
137
|
+
}
|
|
131
138
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
139
|
+
// add new deps
|
|
140
|
+
for (const dep of nextDeps) {
|
|
141
|
+
if (!watchedDeps.has(dep)) {
|
|
142
|
+
watcher.add(dep);
|
|
143
|
+
watchedDeps.add(dep);
|
|
135
144
|
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// remove old deps
|
|
148
|
+
for (const dep of [...watchedDeps]) {
|
|
149
|
+
if (!nextDeps.has(dep)) {
|
|
150
|
+
watcher.unwatch(dep);
|
|
151
|
+
watchedDeps.delete(dep);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
136
154
|
|
|
155
|
+
// preload deps
|
|
156
|
+
for (const dep of nextDeps) {
|
|
137
157
|
try {
|
|
138
|
-
const
|
|
139
|
-
const
|
|
158
|
+
const raw = await fs.readFile(dep, "utf-8");
|
|
159
|
+
const parsedDep = processFile(dep, raw);
|
|
140
160
|
|
|
141
|
-
cache.set(
|
|
161
|
+
cache.set(dep, parsedDep);
|
|
142
162
|
|
|
143
|
-
const
|
|
163
|
+
const virtual = getVirtualFile(dep);
|
|
144
164
|
|
|
145
|
-
engine.updateFile(
|
|
165
|
+
engine.updateFile(virtual, parsedDep.content, config);
|
|
146
166
|
} catch {}
|
|
147
167
|
}
|
|
148
168
|
|
|
@@ -174,7 +194,7 @@ export async function watch() {
|
|
|
174
194
|
|
|
175
195
|
// SCHEDULER
|
|
176
196
|
const schedule = (file: string) => {
|
|
177
|
-
const abs =
|
|
197
|
+
const abs = normalize(file);
|
|
178
198
|
const virtual = getVirtualFile(abs);
|
|
179
199
|
|
|
180
200
|
console.log(chalk.gray(`change: ${file}`));
|
|
@@ -182,7 +202,7 @@ export async function watch() {
|
|
|
182
202
|
queued.set(abs, { real: abs, virtual });
|
|
183
203
|
|
|
184
204
|
if (timer) clearTimeout(timer);
|
|
185
|
-
timer = setTimeout(run,
|
|
205
|
+
timer = setTimeout(run, 200);
|
|
186
206
|
};
|
|
187
207
|
|
|
188
208
|
// WATCHER
|
|
@@ -196,7 +216,7 @@ export async function watch() {
|
|
|
196
216
|
.on("add", schedule)
|
|
197
217
|
.on("change", schedule)
|
|
198
218
|
.on("unlink", async (file) => {
|
|
199
|
-
const abs =
|
|
219
|
+
const abs = normalize(file);
|
|
200
220
|
|
|
201
221
|
console.log(chalk.gray(`remove: ${file}`));
|
|
202
222
|
|
|
@@ -220,7 +240,7 @@ export async function watch() {
|
|
|
220
240
|
console.error(chalk.red("Watcher error:"), err);
|
|
221
241
|
});
|
|
222
242
|
|
|
223
|
-
// WATCH DEPS
|
|
243
|
+
// WATCH DEPS INIT
|
|
224
244
|
for (const dep of watchedDeps) {
|
|
225
245
|
watcher.add(dep);
|
|
226
246
|
}
|
|
@@ -29,18 +29,48 @@ function resolveFromSymbol(
|
|
|
29
29
|
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// re-export support
|
|
33
|
+
if ((symbol as any).exports) {
|
|
34
|
+
for (const exp of (symbol as any).exports.values()) {
|
|
35
|
+
const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
|
|
36
|
+
if (resolved) return resolved;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
const decls = symbol.getDeclarations() ?? [];
|
|
33
41
|
|
|
34
42
|
for (const decl of decls) {
|
|
43
|
+
// direct function
|
|
35
44
|
if (isFunctionLike(decl)) {
|
|
36
45
|
return decl;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
//
|
|
48
|
+
// VARIABLE → FUNCTION
|
|
49
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
50
|
+
if (isFunctionLike(decl.initializer)) {
|
|
51
|
+
return decl.initializer;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const innerSymbol = checker.getSymbolAtLocation(decl.initializer);
|
|
55
|
+
if (innerSymbol && innerSymbol !== symbol) {
|
|
56
|
+
const resolved = resolveFromSymbol(
|
|
57
|
+
innerSymbol,
|
|
58
|
+
checker,
|
|
59
|
+
visited,
|
|
60
|
+
depth + 1
|
|
61
|
+
);
|
|
62
|
+
if (resolved) return resolved;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// MethodSignature (interface / type)
|
|
40
67
|
if (ts.isMethodSignature(decl)) {
|
|
41
68
|
const parent = decl.parent;
|
|
42
69
|
|
|
43
|
-
if (
|
|
70
|
+
if (
|
|
71
|
+
ts.isInterfaceDeclaration(parent) ||
|
|
72
|
+
ts.isTypeLiteralNode(parent)
|
|
73
|
+
) {
|
|
44
74
|
const name = decl.name.getText();
|
|
45
75
|
|
|
46
76
|
const type = checker.getTypeAtLocation(parent);
|
|
@@ -49,16 +79,26 @@ function resolveFromSymbol(
|
|
|
49
79
|
for (const prop of props) {
|
|
50
80
|
if (prop.name !== name) continue;
|
|
51
81
|
|
|
52
|
-
const resolved = resolveFromSymbol(
|
|
82
|
+
const resolved = resolveFromSymbol(
|
|
83
|
+
prop,
|
|
84
|
+
checker,
|
|
85
|
+
visited,
|
|
86
|
+
depth + 1
|
|
87
|
+
);
|
|
53
88
|
if (resolved) return resolved;
|
|
54
89
|
}
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
92
|
|
|
58
|
-
//
|
|
93
|
+
// fallback recursion
|
|
59
94
|
const s = checker.getSymbolAtLocation(decl);
|
|
60
95
|
if (s && s !== symbol) {
|
|
61
|
-
const resolved = resolveFromSymbol(
|
|
96
|
+
const resolved = resolveFromSymbol(
|
|
97
|
+
s,
|
|
98
|
+
checker,
|
|
99
|
+
visited,
|
|
100
|
+
depth + 1
|
|
101
|
+
);
|
|
62
102
|
if (resolved) return resolved;
|
|
63
103
|
}
|
|
64
104
|
}
|
|
@@ -70,6 +110,32 @@ export function resolveFunctionFromCall(
|
|
|
70
110
|
expr: ts.Expression,
|
|
71
111
|
checker: ts.TypeChecker
|
|
72
112
|
): ts.FunctionLikeDeclaration | null {
|
|
113
|
+
|
|
114
|
+
// tracker.track
|
|
115
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
116
|
+
let symbol = checker.getSymbolAtLocation(expr.name);
|
|
117
|
+
|
|
118
|
+
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
119
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (symbol) {
|
|
123
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
124
|
+
if (resolved) return resolved;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// tracker["track"]
|
|
129
|
+
if (ts.isElementAccessExpression(expr)) {
|
|
130
|
+
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
131
|
+
|
|
132
|
+
if (symbol) {
|
|
133
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
134
|
+
if (resolved) return resolved;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// normal call signatures
|
|
73
139
|
const type = checker.getTypeAtLocation(expr);
|
|
74
140
|
const signatures = type.getCallSignatures();
|
|
75
141
|
|
|
@@ -81,7 +147,6 @@ export function resolveFunctionFromCall(
|
|
|
81
147
|
return decl;
|
|
82
148
|
}
|
|
83
149
|
|
|
84
|
-
// interface method
|
|
85
150
|
if (ts.isMethodSignature(decl)) {
|
|
86
151
|
const symbol = checker.getSymbolAtLocation(decl.name);
|
|
87
152
|
|
|
@@ -92,5 +157,11 @@ export function resolveFunctionFromCall(
|
|
|
92
157
|
}
|
|
93
158
|
}
|
|
94
159
|
|
|
160
|
+
// fallback
|
|
161
|
+
const symbol = checker.getSymbolAtLocation(expr);
|
|
162
|
+
if (symbol) {
|
|
163
|
+
return resolveFromSymbol(symbol, checker);
|
|
164
|
+
}
|
|
165
|
+
|
|
95
166
|
return null;
|
|
96
167
|
}
|
package/src/engine/engine.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
|
|
1
4
|
import { TSService } from "./languageService";
|
|
2
5
|
import { ScanResult, scanSource } from "./scanner";
|
|
3
6
|
import { DependencyGraph } from "./graph";
|
|
@@ -18,8 +21,29 @@ export class EventraEngine {
|
|
|
18
21
|
const { baseUrl, paths } = this.ts.getModuleResolutionConfig();
|
|
19
22
|
this.graph = new DependencyGraph(baseUrl, paths);
|
|
20
23
|
}
|
|
24
|
+
|
|
25
|
+
private normalize(file: string) {
|
|
26
|
+
return path.resolve(file).replace(/\\/g, "/");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private empty(): ScanResult {
|
|
30
|
+
return {
|
|
31
|
+
events: new Set(),
|
|
32
|
+
detectedFunctionWrappers: new Set(),
|
|
33
|
+
detectedComponentWrappers: new Map(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// PRELOAD
|
|
21
38
|
preloadFile(file: string, content: string) {
|
|
39
|
+
file = this.normalize(file);
|
|
40
|
+
|
|
22
41
|
this.ts.updateFile(file, content);
|
|
42
|
+
|
|
43
|
+
const source = this.ts.getSourceFile(file);
|
|
44
|
+
if (source) {
|
|
45
|
+
this.graph.update(file, source);
|
|
46
|
+
}
|
|
23
47
|
}
|
|
24
48
|
|
|
25
49
|
// SCAN FILE
|
|
@@ -28,33 +52,26 @@ export class EventraEngine {
|
|
|
28
52
|
content: string,
|
|
29
53
|
config: EventraConfig
|
|
30
54
|
): ScanResult {
|
|
55
|
+
file = this.normalize(file);
|
|
56
|
+
|
|
31
57
|
const h = hash(content);
|
|
32
58
|
|
|
33
|
-
// skip unchanged
|
|
34
59
|
if (this.fileHash.get(file) === h) {
|
|
35
|
-
return (
|
|
36
|
-
this.fileResults.get(file) ?? {
|
|
37
|
-
events: new Set(),
|
|
38
|
-
detectedFunctionWrappers: new Set(),
|
|
39
|
-
detectedComponentWrappers: new Map(),
|
|
40
|
-
}
|
|
41
|
-
);
|
|
60
|
+
return this.fileResults.get(file) ?? this.empty();
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
this.fileHash.set(file, h);
|
|
64
|
+
|
|
45
65
|
this.ts.updateFile(file, content);
|
|
46
66
|
|
|
47
67
|
const source = this.ts.getSourceFile(file);
|
|
48
68
|
const checker = this.ts.getChecker();
|
|
49
69
|
|
|
50
70
|
if (!source || !checker) {
|
|
51
|
-
return
|
|
52
|
-
events: new Set(),
|
|
53
|
-
detectedFunctionWrappers: new Set(),
|
|
54
|
-
detectedComponentWrappers: new Map(),
|
|
55
|
-
};
|
|
71
|
+
return this.empty();
|
|
56
72
|
}
|
|
57
73
|
|
|
74
|
+
// обновляем graph
|
|
58
75
|
this.graph.update(file, source);
|
|
59
76
|
|
|
60
77
|
const res = scanSource(source, checker, config);
|
|
@@ -69,32 +86,46 @@ export class EventraEngine {
|
|
|
69
86
|
content: string,
|
|
70
87
|
config: EventraConfig
|
|
71
88
|
) {
|
|
89
|
+
file = this.normalize(file);
|
|
90
|
+
|
|
72
91
|
const res = this.scanFile(file, content, config);
|
|
73
92
|
|
|
74
93
|
const dependents = this.graph.getAllDependentsDeep(file);
|
|
75
94
|
|
|
76
95
|
for (const dep of dependents) {
|
|
77
|
-
|
|
96
|
+
const abs = this.normalize(dep);
|
|
78
97
|
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
if (isExternalFile(abs)) continue;
|
|
99
|
+
|
|
100
|
+
let depContent = this.ts.getFileContent(abs);
|
|
101
|
+
|
|
102
|
+
if (!depContent) {
|
|
103
|
+
try {
|
|
104
|
+
depContent = fs.readFileSync(abs, "utf-8");
|
|
105
|
+
} catch {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
81
109
|
|
|
82
110
|
const h = hash(depContent);
|
|
83
111
|
|
|
84
|
-
if (this.fileHash.get(
|
|
112
|
+
if (this.fileHash.get(abs) === h) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.fileHash.set(abs, h);
|
|
85
117
|
|
|
86
|
-
this.
|
|
87
|
-
this.ts.updateFile(dep, depContent);
|
|
118
|
+
this.ts.updateFile(abs, depContent);
|
|
88
119
|
|
|
89
|
-
const source = this.ts.getSourceFile(
|
|
120
|
+
const source = this.ts.getSourceFile(abs);
|
|
90
121
|
const checker = this.ts.getChecker();
|
|
91
122
|
|
|
92
123
|
if (!source || !checker) continue;
|
|
93
124
|
|
|
94
|
-
this.graph.update(
|
|
125
|
+
this.graph.update(abs, source);
|
|
95
126
|
|
|
96
127
|
const r = scanSource(source, checker, config);
|
|
97
|
-
this.fileResults.set(
|
|
128
|
+
this.fileResults.set(abs, r);
|
|
98
129
|
}
|
|
99
130
|
|
|
100
131
|
return res;
|
|
@@ -113,31 +144,45 @@ export class EventraEngine {
|
|
|
113
144
|
|
|
114
145
|
// REMOVE FILE
|
|
115
146
|
removeFile(file: string, config?: EventraConfig) {
|
|
147
|
+
file = this.normalize(file);
|
|
148
|
+
|
|
149
|
+
const dependents = this.graph.getAllDependentsDeep(file);
|
|
150
|
+
|
|
116
151
|
this.ts.removeFile(file);
|
|
117
152
|
this.fileResults.delete(file);
|
|
118
153
|
this.fileHash.delete(file);
|
|
119
154
|
this.graph.remove(file);
|
|
120
155
|
|
|
121
|
-
const dependents = this.graph.getAllDependentsDeep(file);
|
|
122
|
-
|
|
123
156
|
for (const dep of dependents) {
|
|
124
|
-
|
|
157
|
+
const abs = this.normalize(dep);
|
|
125
158
|
|
|
126
|
-
|
|
127
|
-
|
|
159
|
+
if (isExternalFile(abs)) continue;
|
|
160
|
+
|
|
161
|
+
let content = this.ts.getFileContent(abs);
|
|
162
|
+
|
|
163
|
+
if (!content) {
|
|
164
|
+
try {
|
|
165
|
+
content = fs.readFileSync(abs, "utf-8");
|
|
166
|
+
} catch {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
128
170
|
|
|
129
171
|
const h = hash(content);
|
|
130
172
|
|
|
131
|
-
if (this.fileHash.get(
|
|
173
|
+
if (this.fileHash.get(abs) === h) continue;
|
|
174
|
+
|
|
175
|
+
this.fileHash.set(abs, h);
|
|
132
176
|
|
|
133
|
-
this.
|
|
134
|
-
this.ts.updateFile(dep, content);
|
|
177
|
+
this.ts.updateFile(abs, content);
|
|
135
178
|
|
|
136
|
-
const source = this.ts.getSourceFile(
|
|
179
|
+
const source = this.ts.getSourceFile(abs);
|
|
137
180
|
const checker = this.ts.getChecker();
|
|
138
181
|
|
|
139
182
|
if (!source || !checker) continue;
|
|
140
183
|
|
|
184
|
+
this.graph.update(abs, source);
|
|
185
|
+
|
|
141
186
|
const r = scanSource(
|
|
142
187
|
source,
|
|
143
188
|
checker,
|
|
@@ -149,7 +194,7 @@ export class EventraEngine {
|
|
|
149
194
|
}
|
|
150
195
|
);
|
|
151
196
|
|
|
152
|
-
this.fileResults.set(
|
|
197
|
+
this.fileResults.set(abs, r);
|
|
153
198
|
}
|
|
154
199
|
}
|
|
155
200
|
}
|