@eventra_dev/eventra-cli 0.1.33 → 0.2.1
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 +14 -30
- package/dist/commands/sync.js +9 -27
- package/dist/commands/watch.js +38 -121
- package/dist/engine/callResolver.js +32 -131
- package/dist/engine/engine.js +14 -9
- package/dist/engine/languageService.js +7 -38
- package/dist/engine/resolver.js +88 -77
- package/dist/engine/scanner.js +131 -196
- package/dist/utils/universal.js +1 -1
- package/package.json +1 -1
- package/src/commands/check.ts +17 -48
- package/src/commands/sync.ts +14 -44
- package/src/commands/watch.ts +48 -158
- package/src/engine/callResolver.ts +24 -159
- package/src/engine/engine.ts +20 -12
- package/src/engine/languageService.ts +22 -52
- package/src/engine/resolver.ts +99 -120
- package/src/engine/scanner.ts +149 -243
- package/src/utils/universal.ts +1 -1
package/src/commands/watch.ts
CHANGED
|
@@ -9,92 +9,39 @@ import { EventraEngine } from "../engine/engine";
|
|
|
9
9
|
import { processFile } from "../utils/processFile";
|
|
10
10
|
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
11
11
|
|
|
12
|
-
type QueueItem = {
|
|
13
|
-
real: string;
|
|
14
|
-
virtual: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function normalize(file: string) {
|
|
18
|
-
return path.resolve(file).replace(/\\/g, "/");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
export async function watch() {
|
|
22
13
|
const config = await loadConfig();
|
|
23
14
|
if (!config) return;
|
|
24
15
|
|
|
25
16
|
console.log(chalk.blue("Watching...\n"));
|
|
26
17
|
|
|
27
|
-
const files = await fg(config.sync.include, {
|
|
28
|
-
ignore: config.sync.exclude,
|
|
29
|
-
absolute: true,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
18
|
const engine = new EventraEngine(process.cwd());
|
|
19
|
+
const fileDeps = new Map<string, Set<string>>();
|
|
20
|
+
const cache = new Map<string, { content: string; deps: string[] }>();
|
|
33
21
|
const watchedDeps = new Set<string>();
|
|
34
22
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
{ content: string; deps: string[] }
|
|
38
|
-
>();
|
|
23
|
+
const normalize = (f: string) =>
|
|
24
|
+
path.resolve(f).replace(/\\/g, "/");
|
|
39
25
|
|
|
40
|
-
|
|
26
|
+
// INITIAL LOAD
|
|
27
|
+
const files = await fg(config.sync.include, {
|
|
28
|
+
ignore: config.sync.exclude,
|
|
29
|
+
absolute: true,
|
|
30
|
+
});
|
|
41
31
|
|
|
42
|
-
// COLLECT + CACHE
|
|
43
32
|
for (const file of files) {
|
|
44
33
|
try {
|
|
45
34
|
const abs = normalize(file);
|
|
46
35
|
const raw = await fs.readFile(abs, "utf-8");
|
|
47
36
|
|
|
48
|
-
if (raw.length > 2_000_000) continue;
|
|
49
|
-
|
|
50
37
|
const parsed = processFile(abs, raw);
|
|
51
38
|
|
|
52
39
|
cache.set(abs, parsed);
|
|
53
|
-
toScan.add(abs);
|
|
54
40
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
41
|
+
engine.preloadFile(getVirtualFile(abs), parsed.content);
|
|
42
|
+
engine.updateFile(getVirtualFile(abs), parsed.content, config);
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
console.log(chalk.gray(`skip: ${file}`));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// PRELOAD
|
|
65
|
-
for (const file of toScan) {
|
|
66
|
-
try {
|
|
67
|
-
let parsed = cache.get(file);
|
|
68
|
-
|
|
69
|
-
if (!parsed) {
|
|
70
|
-
const raw = await fs.readFile(file, "utf-8");
|
|
71
|
-
parsed = processFile(file, raw);
|
|
72
|
-
cache.set(file, parsed);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const virtual = getVirtualFile(file);
|
|
76
|
-
engine.preloadFile?.(virtual, parsed.content);
|
|
77
|
-
|
|
78
|
-
} catch {}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// INITIAL SCAN
|
|
82
|
-
for (const file of toScan) {
|
|
83
|
-
try {
|
|
84
|
-
let parsed = cache.get(file);
|
|
85
|
-
|
|
86
|
-
if (!parsed) {
|
|
87
|
-
const raw = await fs.readFile(file, "utf-8");
|
|
88
|
-
parsed = processFile(file, raw);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const virtual = getVirtualFile(file);
|
|
92
|
-
|
|
93
|
-
engine.scanFile(virtual, parsed.content, config);
|
|
94
|
-
|
|
95
|
-
for (const dep of parsed.deps) {
|
|
96
|
-
watchedDeps.add(normalize(dep));
|
|
97
|
-
}
|
|
44
|
+
fileDeps.set(abs, new Set(parsed.deps.map(d => normalize(d))));
|
|
98
45
|
|
|
99
46
|
} catch {}
|
|
100
47
|
}
|
|
@@ -102,152 +49,95 @@ export async function watch() {
|
|
|
102
49
|
config.events = engine.getAllEvents().sort();
|
|
103
50
|
await saveConfig(config);
|
|
104
51
|
|
|
105
|
-
console.log(
|
|
106
|
-
|
|
52
|
+
console.log(chalk.gray(`Initial: ${config.events.length}\n`));
|
|
53
|
+
|
|
54
|
+
// WATCHER
|
|
55
|
+
const watcher = chokidar.watch(
|
|
56
|
+
[...files.map(normalize), ...watchedDeps],
|
|
57
|
+
{ ignoreInitial: true }
|
|
107
58
|
);
|
|
108
59
|
|
|
109
|
-
|
|
110
|
-
let locked = false;
|
|
111
|
-
const queued = new Map<string, QueueItem>();
|
|
60
|
+
const queue = new Map<string, boolean>();
|
|
112
61
|
let timer: NodeJS.Timeout | null = null;
|
|
113
62
|
|
|
114
63
|
const run = async () => {
|
|
115
|
-
|
|
116
|
-
|
|
64
|
+
const batch = [...queue.keys()];
|
|
65
|
+
queue.clear();
|
|
117
66
|
|
|
118
|
-
const
|
|
119
|
-
queued.clear();
|
|
120
|
-
|
|
121
|
-
for (const item of batch) {
|
|
67
|
+
for (const file of batch) {
|
|
122
68
|
try {
|
|
123
|
-
const raw = await fs.readFile(
|
|
124
|
-
|
|
125
|
-
if (raw.length > 2_000_000) continue;
|
|
69
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
70
|
+
const parsed = processFile(file, raw);
|
|
126
71
|
|
|
127
|
-
|
|
128
|
-
cache.set(item.real, parsed);
|
|
72
|
+
cache.set(file, parsed);
|
|
129
73
|
|
|
130
|
-
engine.updateFile(
|
|
74
|
+
engine.updateFile(
|
|
75
|
+
getVirtualFile(file),
|
|
76
|
+
parsed.content,
|
|
77
|
+
config
|
|
78
|
+
);
|
|
131
79
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
80
|
+
//dependency diff
|
|
81
|
+
const abs = normalize(file);
|
|
134
82
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
83
|
+
const prevDeps = fileDeps.get(abs) ?? new Set();
|
|
84
|
+
const nextDeps = new Set(parsed.deps.map(d => normalize(d)));
|
|
138
85
|
|
|
139
86
|
// add new deps
|
|
140
87
|
for (const dep of nextDeps) {
|
|
141
|
-
if (!
|
|
88
|
+
if (!prevDeps.has(dep)) {
|
|
142
89
|
watcher.add(dep);
|
|
143
90
|
watchedDeps.add(dep);
|
|
144
91
|
}
|
|
145
92
|
}
|
|
146
93
|
|
|
147
94
|
// remove old deps
|
|
148
|
-
for (const dep of
|
|
95
|
+
for (const dep of prevDeps) {
|
|
149
96
|
if (!nextDeps.has(dep)) {
|
|
150
97
|
watcher.unwatch(dep);
|
|
151
98
|
watchedDeps.delete(dep);
|
|
152
99
|
}
|
|
153
100
|
}
|
|
154
101
|
|
|
155
|
-
|
|
156
|
-
for (const dep of nextDeps) {
|
|
157
|
-
try {
|
|
158
|
-
const raw = await fs.readFile(dep, "utf-8");
|
|
159
|
-
const parsedDep = processFile(dep, raw);
|
|
160
|
-
|
|
161
|
-
cache.set(dep, parsedDep);
|
|
162
|
-
|
|
163
|
-
const virtual = getVirtualFile(dep);
|
|
164
|
-
|
|
165
|
-
engine.updateFile(virtual, parsedDep.content, config);
|
|
166
|
-
} catch {}
|
|
167
|
-
}
|
|
102
|
+
fileDeps.set(abs, nextDeps);
|
|
168
103
|
|
|
169
104
|
} catch {
|
|
170
|
-
console.log(chalk.gray(`skip: ${
|
|
105
|
+
console.log(chalk.gray(`skip: ${file}`));
|
|
171
106
|
}
|
|
172
107
|
}
|
|
173
108
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const added = next.filter(e => !config.events.includes(e));
|
|
178
|
-
const removed = config.events.filter(e => !next.includes(e));
|
|
179
|
-
|
|
180
|
-
if (added.length || removed.length) {
|
|
181
|
-
added.forEach(e => console.log(chalk.green(`+ ${e}`)));
|
|
182
|
-
removed.forEach(e => console.log(chalk.red(`- ${e}`)));
|
|
109
|
+
const next = engine.getAllEvents().sort();
|
|
110
|
+
config.events = next;
|
|
183
111
|
|
|
184
|
-
|
|
185
|
-
await saveConfig(config);
|
|
186
|
-
|
|
187
|
-
console.log(
|
|
188
|
-
chalk.gray(`Total: ${config.events.length} events\n`)
|
|
189
|
-
);
|
|
190
|
-
}
|
|
112
|
+
await saveConfig(config);
|
|
191
113
|
|
|
192
|
-
|
|
114
|
+
console.log(chalk.green(`Updated: ${next.length}`));
|
|
193
115
|
};
|
|
194
116
|
|
|
195
|
-
// SCHEDULER
|
|
196
117
|
const schedule = (file: string) => {
|
|
197
118
|
const abs = normalize(file);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
console.log(chalk.gray(`change: ${file}`));
|
|
201
|
-
|
|
202
|
-
queued.set(abs, { real: abs, virtual });
|
|
119
|
+
queue.set(abs, true);
|
|
203
120
|
|
|
204
121
|
if (timer) clearTimeout(timer);
|
|
205
122
|
timer = setTimeout(run, 200);
|
|
206
123
|
};
|
|
207
124
|
|
|
208
|
-
// WATCHER
|
|
209
|
-
const watcher = chokidar.watch(config.sync.include, {
|
|
210
|
-
ignored: config.sync.exclude,
|
|
211
|
-
ignoreInitial: true,
|
|
212
|
-
persistent: true,
|
|
213
|
-
});
|
|
214
|
-
|
|
215
125
|
watcher
|
|
216
|
-
.on("add", schedule)
|
|
217
126
|
.on("change", schedule)
|
|
127
|
+
.on("add", schedule)
|
|
218
128
|
.on("unlink", async (file) => {
|
|
219
129
|
const abs = normalize(file);
|
|
220
130
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const virtual = getVirtualFile(abs);
|
|
224
|
-
|
|
225
|
-
engine.removeFile(virtual, config);
|
|
131
|
+
engine.removeFile(getVirtualFile(abs), config);
|
|
226
132
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const next = engine.getAllEvents();
|
|
231
|
-
|
|
232
|
-
config.events = next.sort();
|
|
133
|
+
const next = engine.getAllEvents().sort();
|
|
134
|
+
config.events = next;
|
|
233
135
|
await saveConfig(config);
|
|
234
136
|
|
|
235
|
-
console.log(
|
|
236
|
-
chalk.gray(`Total: ${config.events.length} events\n`)
|
|
237
|
-
);
|
|
238
|
-
})
|
|
239
|
-
.on("error", (err) => {
|
|
240
|
-
console.error(chalk.red("Watcher error:"), err);
|
|
137
|
+
console.log(chalk.red(`Removed → ${next.length}`));
|
|
241
138
|
});
|
|
242
139
|
|
|
243
|
-
// WATCH DEPS INIT
|
|
244
|
-
for (const dep of watchedDeps) {
|
|
245
|
-
watcher.add(dep);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// EXIT
|
|
249
140
|
process.on("SIGINT", async () => {
|
|
250
|
-
console.log(chalk.yellow("\nStopping watcher..."));
|
|
251
141
|
await watcher.close();
|
|
252
142
|
process.exit(0);
|
|
253
143
|
});
|
|
@@ -1,200 +1,65 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
function isFunctionLike(
|
|
5
|
-
node: ts.Node
|
|
6
|
-
): node is ts.FunctionLikeDeclaration {
|
|
3
|
+
function isFn(node: ts.Node): node is ts.FunctionLikeDeclaration {
|
|
7
4
|
return (
|
|
8
5
|
ts.isFunctionDeclaration(node) ||
|
|
9
6
|
ts.isMethodDeclaration(node) ||
|
|
10
|
-
ts.isConstructorDeclaration(node) ||
|
|
11
7
|
ts.isFunctionExpression(node) ||
|
|
12
8
|
ts.isArrowFunction(node)
|
|
13
9
|
);
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
// symbol → function
|
|
17
12
|
function resolveFromSymbol(
|
|
18
13
|
symbol: ts.Symbol,
|
|
19
14
|
checker: ts.TypeChecker,
|
|
20
|
-
|
|
21
|
-
depth = 0
|
|
15
|
+
seen = new Set<ts.Symbol>()
|
|
22
16
|
): ts.FunctionLikeDeclaration | null {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
if (depth > 10) return null;
|
|
17
|
+
if (seen.has(symbol)) return null;
|
|
18
|
+
seen.add(symbol);
|
|
26
19
|
|
|
27
|
-
visited.add(symbol);
|
|
28
|
-
|
|
29
|
-
// Alias unwrap (import/export)
|
|
30
20
|
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
31
|
-
|
|
32
|
-
const aliased = checker.getAliasedSymbol(symbol);
|
|
33
|
-
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
34
|
-
} catch {}
|
|
21
|
+
return resolveFromSymbol(checker.getAliasedSymbol(symbol), checker, seen);
|
|
35
22
|
}
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
for (const d of symbol.getDeclarations() ?? []) {
|
|
25
|
+
if (isFn(d)) return d;
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* direct function
|
|
43
|
-
*/
|
|
44
|
-
if (isFunctionLike(decl)) {
|
|
45
|
-
return decl;
|
|
46
|
-
}
|
|
27
|
+
if (ts.isVariableDeclaration(d) && d.initializer) {
|
|
28
|
+
if (isFn(d.initializer)) return d.initializer;
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
30
|
+
const s = checker.getSymbolAtLocation(d.initializer);
|
|
31
|
+
if (s) {
|
|
32
|
+
const r = resolveFromSymbol(s, checker, seen);
|
|
33
|
+
if (r) return r;
|
|
52
34
|
}
|
|
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
|
-
// CLASS METHOD
|
|
67
|
-
if (ts.isMethodDeclaration(decl)) {
|
|
68
|
-
return decl;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// interface / type method
|
|
72
|
-
if (ts.isMethodSignature(decl)) {
|
|
73
|
-
const parent = decl.parent;
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
ts.isInterfaceDeclaration(parent) ||
|
|
77
|
-
ts.isTypeLiteralNode(parent)
|
|
78
|
-
) {
|
|
79
|
-
const type = checker.getTypeAtLocation(parent);
|
|
80
|
-
const props = type.getProperties();
|
|
81
|
-
|
|
82
|
-
for (const prop of props) {
|
|
83
|
-
const resolved = resolveFromSymbol(
|
|
84
|
-
prop,
|
|
85
|
-
checker,
|
|
86
|
-
visited,
|
|
87
|
-
depth + 1
|
|
88
|
-
);
|
|
89
|
-
if (resolved) return resolved;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// fallback recursion
|
|
95
|
-
const inner = checker.getSymbolAtLocation(decl);
|
|
96
|
-
if (inner && inner !== symbol) {
|
|
97
|
-
const resolved = resolveFromSymbol(
|
|
98
|
-
inner,
|
|
99
|
-
checker,
|
|
100
|
-
visited,
|
|
101
|
-
depth + 1
|
|
102
|
-
);
|
|
103
|
-
if (resolved) return resolved;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Exports (re-export)
|
|
108
|
-
const exports = (symbol as any).exports;
|
|
109
|
-
if (exports) {
|
|
110
|
-
for (const exp of exports.values()) {
|
|
111
|
-
const resolved = resolveFromSymbol(
|
|
112
|
-
exp,
|
|
113
|
-
checker,
|
|
114
|
-
visited,
|
|
115
|
-
depth + 1
|
|
116
|
-
);
|
|
117
|
-
if (resolved) return resolved;
|
|
118
35
|
}
|
|
119
36
|
}
|
|
120
37
|
|
|
121
38
|
return null;
|
|
122
39
|
}
|
|
123
40
|
|
|
124
|
-
// MAIN FUNCTION
|
|
125
41
|
export function resolveFunctionFromCall(
|
|
126
42
|
expr: ts.Expression,
|
|
127
43
|
checker: ts.TypeChecker
|
|
128
44
|
): ts.FunctionLikeDeclaration | null {
|
|
129
|
-
//
|
|
45
|
+
// foo.bar()
|
|
130
46
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
136
|
-
} catch {}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (symbol) {
|
|
140
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
141
|
-
if (resolved) return resolved;
|
|
47
|
+
const s = checker.getSymbolAtLocation(expr.name);
|
|
48
|
+
if (s) {
|
|
49
|
+
const r = resolveFromSymbol(s, checker);
|
|
50
|
+
if (r) return r;
|
|
142
51
|
}
|
|
143
52
|
}
|
|
144
53
|
|
|
145
|
-
//
|
|
146
|
-
if (ts.isElementAccessExpression(expr)) {
|
|
147
|
-
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
148
|
-
|
|
149
|
-
if (symbol) {
|
|
150
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
151
|
-
if (resolved) return resolved;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Call signatures
|
|
54
|
+
// direct call
|
|
156
55
|
const type = checker.getTypeAtLocation(expr);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const decl = sig.getDeclaration();
|
|
161
|
-
if (!decl) continue;
|
|
162
|
-
|
|
163
|
-
if (isFunctionLike(decl)) {
|
|
164
|
-
return decl;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (ts.isMethodSignature(decl)) {
|
|
168
|
-
const symbol = checker.getSymbolAtLocation(decl.name);
|
|
169
|
-
|
|
170
|
-
if (symbol) {
|
|
171
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
172
|
-
if (resolved) return resolved;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
56
|
+
for (const sig of type.getCallSignatures()) {
|
|
57
|
+
const d = sig.getDeclaration();
|
|
58
|
+
if (d && isFn(d)) return d;
|
|
175
59
|
}
|
|
176
60
|
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
let symbol = checker.getSymbolAtLocation(expr);
|
|
180
|
-
|
|
181
|
-
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
182
|
-
try {
|
|
183
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
184
|
-
} catch {}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (symbol) {
|
|
188
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
189
|
-
if (resolved) return resolved;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// LAST fallback
|
|
194
|
-
const symbol = checker.getSymbolAtLocation(expr);
|
|
195
|
-
if (symbol) {
|
|
196
|
-
return resolveFromSymbol(symbol, checker);
|
|
197
|
-
}
|
|
61
|
+
const s = checker.getSymbolAtLocation(expr);
|
|
62
|
+
if (s) return resolveFromSymbol(s, checker);
|
|
198
63
|
|
|
199
64
|
return null;
|
|
200
65
|
}
|
package/src/engine/engine.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { TSService } from "./languageService";
|
|
|
5
5
|
import { scanSource } from "./scanner";
|
|
6
6
|
import { DependencyGraph } from "./graph";
|
|
7
7
|
import { hash } from "./hash";
|
|
8
|
-
import {EventraConfig, ScanResult} from "../types";
|
|
8
|
+
import { EventraConfig, ScanResult } from "../types";
|
|
9
9
|
import { isExternalFile } from "./boundary";
|
|
10
10
|
|
|
11
11
|
export class EventraEngine {
|
|
@@ -46,7 +46,7 @@ export class EventraEngine {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// SCAN FILE
|
|
49
|
+
// SCAN FILE (cached)
|
|
50
50
|
scanFile(
|
|
51
51
|
file: string,
|
|
52
52
|
content: string,
|
|
@@ -71,7 +71,6 @@ export class EventraEngine {
|
|
|
71
71
|
return this.empty();
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// update graph
|
|
75
74
|
this.graph.update(file, source);
|
|
76
75
|
|
|
77
76
|
const res = scanSource(
|
|
@@ -82,6 +81,7 @@ export class EventraEngine {
|
|
|
82
81
|
undefined,
|
|
83
82
|
this.wrapperCache
|
|
84
83
|
);
|
|
84
|
+
|
|
85
85
|
this.fileResults.set(file, res);
|
|
86
86
|
|
|
87
87
|
return res;
|
|
@@ -92,10 +92,17 @@ export class EventraEngine {
|
|
|
92
92
|
file: string,
|
|
93
93
|
content: string,
|
|
94
94
|
config: EventraConfig
|
|
95
|
-
) {
|
|
96
|
-
this.wrapperCache.clear();
|
|
95
|
+
): ScanResult {
|
|
97
96
|
file = this.normalize(file);
|
|
98
97
|
|
|
98
|
+
// EARLY EXIT
|
|
99
|
+
if (this.fileHash.get(file) === hash(content)) {
|
|
100
|
+
return this.fileResults.get(file) ?? this.empty();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// invalidate wrapper cache
|
|
104
|
+
this.wrapperCache.clear();
|
|
105
|
+
|
|
99
106
|
const res = this.scanFile(file, content, config);
|
|
100
107
|
|
|
101
108
|
const dependents = this.graph.getAllDependentsDeep(file);
|
|
@@ -103,6 +110,7 @@ export class EventraEngine {
|
|
|
103
110
|
for (const dep of dependents) {
|
|
104
111
|
const abs = this.normalize(dep);
|
|
105
112
|
|
|
113
|
+
if (abs === file) continue; // self-skip
|
|
106
114
|
if (isExternalFile(abs)) continue;
|
|
107
115
|
|
|
108
116
|
let depContent = this.ts.getFileContent(abs);
|
|
@@ -140,6 +148,7 @@ export class EventraEngine {
|
|
|
140
148
|
undefined,
|
|
141
149
|
this.wrapperCache
|
|
142
150
|
);
|
|
151
|
+
|
|
143
152
|
this.fileResults.set(abs, r);
|
|
144
153
|
}
|
|
145
154
|
|
|
@@ -159,19 +168,22 @@ export class EventraEngine {
|
|
|
159
168
|
|
|
160
169
|
// REMOVE FILE
|
|
161
170
|
removeFile(file: string, config?: EventraConfig) {
|
|
162
|
-
this.wrapperCache.clear();
|
|
163
171
|
file = this.normalize(file);
|
|
164
172
|
|
|
173
|
+
this.wrapperCache.clear();
|
|
174
|
+
|
|
165
175
|
const dependents = this.graph.getAllDependentsDeep(file);
|
|
166
176
|
|
|
177
|
+
this.graph.remove(file);
|
|
178
|
+
|
|
167
179
|
this.ts.removeFile(file);
|
|
168
180
|
this.fileResults.delete(file);
|
|
169
181
|
this.fileHash.delete(file);
|
|
170
|
-
this.graph.remove(file);
|
|
171
182
|
|
|
172
183
|
for (const dep of dependents) {
|
|
173
184
|
const abs = this.normalize(dep);
|
|
174
185
|
|
|
186
|
+
if (abs === file) continue;
|
|
175
187
|
if (isExternalFile(abs)) continue;
|
|
176
188
|
|
|
177
189
|
let content = this.ts.getFileContent(abs);
|
|
@@ -184,11 +196,7 @@ export class EventraEngine {
|
|
|
184
196
|
}
|
|
185
197
|
}
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (this.fileHash.get(abs) === h) continue;
|
|
190
|
-
|
|
191
|
-
this.fileHash.set(abs, h);
|
|
199
|
+
this.fileHash.delete(abs);
|
|
192
200
|
|
|
193
201
|
this.ts.updateFile(abs, content);
|
|
194
202
|
|