@eventra_dev/eventra-cli 0.3.11 → 0.3.13
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/cli/watch.js
CHANGED
|
@@ -41,10 +41,12 @@ async function watch() {
|
|
|
41
41
|
try {
|
|
42
42
|
const abs = normalize(file);
|
|
43
43
|
const raw = await promises_1.default.readFile(abs, "utf-8");
|
|
44
|
+
// processFile extracts import specifiers for chokidar tracking;
|
|
45
|
+
// the engine itself receives raw file content.
|
|
44
46
|
const parsed = await (0, processFile_1.processFile)(abs, raw);
|
|
45
47
|
cache.set(abs, parsed);
|
|
46
48
|
trackedFiles.add(abs);
|
|
47
|
-
await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(abs),
|
|
49
|
+
await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(abs), raw);
|
|
48
50
|
fileDeps.set(abs, new Set(parsed.dependencies
|
|
49
51
|
.filter((dep) => dep.startsWith(".") || dep.startsWith("/"))
|
|
50
52
|
.map((dep) => normalize(path_1.default.resolve(path_1.default.dirname(abs), dep)))));
|
|
@@ -68,10 +70,13 @@ async function watch() {
|
|
|
68
70
|
for (const file of batch) {
|
|
69
71
|
try {
|
|
70
72
|
const raw = await promises_1.default.readFile(file, "utf-8");
|
|
73
|
+
// processFile here is used ONLY to extract import specifiers for
|
|
74
|
+
// chokidar's dependency tracking. The engine receives raw content
|
|
75
|
+
// and parses it itself.
|
|
71
76
|
const parsed = await (0, processFile_1.processFile)(file, raw);
|
|
72
77
|
cache.set(file, parsed);
|
|
73
78
|
trackedFiles.add(file);
|
|
74
|
-
await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(file),
|
|
79
|
+
await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(file), raw, config);
|
|
75
80
|
const abs = normalize(file);
|
|
76
81
|
const prevDeps = fileDeps.get(abs) ?? new Set();
|
|
77
82
|
const nextDeps = new Set(parsed.dependencies
|
|
@@ -8,7 +8,6 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const typescript_1 = __importDefault(require("typescript"));
|
|
10
10
|
const documentRegistry_1 = require("./documentRegistry");
|
|
11
|
-
const processFile_1 = require("../filesystem/processFile");
|
|
12
11
|
class CompilerContext {
|
|
13
12
|
rootDir;
|
|
14
13
|
registry = new documentRegistry_1.DocumentRegistry();
|
|
@@ -137,10 +136,10 @@ class CompilerContext {
|
|
|
137
136
|
flushUpdates() {
|
|
138
137
|
this.rebuildProgram();
|
|
139
138
|
}
|
|
140
|
-
|
|
139
|
+
// accept content as-is; the engine layer is responsible for parsing via processFile
|
|
140
|
+
updateFile(fileName, content) {
|
|
141
141
|
const normalized = this.normalize(fileName);
|
|
142
|
-
|
|
143
|
-
this.stageFile(processed.fileName, processed.content);
|
|
142
|
+
this.stageFile(normalized, content);
|
|
144
143
|
this.rebuildProgram();
|
|
145
144
|
}
|
|
146
145
|
removeFile(fileName) {
|
|
@@ -13,7 +13,6 @@ const evaluationCache_1 = require("../analysis/cache/evaluationCache");
|
|
|
13
13
|
const resolvedCallCache_1 = require("../analysis/cache/resolvedCallCache");
|
|
14
14
|
const resolvedExportCache_1 = require("../analysis/cache/resolvedExportCache");
|
|
15
15
|
const returnPropagationCache_1 = require("../analysis/cache/returnPropagationCache");
|
|
16
|
-
const symbolInvalidation_1 = require("../analysis/cache/symbolInvalidation");
|
|
17
16
|
const analyzeFile_1 = require("../analysis/engine/analyzeFile");
|
|
18
17
|
const wrapperRegistry_1 = require("../analysis/symbols/wrapperRegistry");
|
|
19
18
|
const processFile_1 = require("../filesystem/processFile");
|
|
@@ -46,22 +45,52 @@ class EventraEngine {
|
|
|
46
45
|
this.compiler = new compilerContext_1.CompilerContext(rootDir);
|
|
47
46
|
this.wrapperRegistry = new wrapperRegistry_1.WrapperRegistry(this.compiler.getChecker(), this.resolvedExportCache);
|
|
48
47
|
this.scheduler = new scheduler_1.Scheduler(async (updates) => {
|
|
49
|
-
const
|
|
48
|
+
const changedFiles = new Set();
|
|
50
49
|
for (const [file, content] of updates) {
|
|
51
|
-
const existing = this.compiler.getSourceFile(file);
|
|
52
|
-
if (existing) {
|
|
53
|
-
(0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache, this.returnPropagationCache, this.wrapperRegistry);
|
|
54
|
-
}
|
|
55
50
|
await this.compiler.updateFile(file, content);
|
|
56
51
|
this.updateImportGraph(file);
|
|
52
|
+
changedFiles.add(file);
|
|
53
|
+
}
|
|
54
|
+
// Program was rebuilt → every ts.Symbol from the previous program is now
|
|
55
|
+
// dead. WeakMap-keyed caches (incl. WrapperRegistry) must be dropped before
|
|
56
|
+
// we touch any NEW symbols, otherwise cross-file wrapper lookups miss.
|
|
57
|
+
this.resetCaches();
|
|
58
|
+
this.refreshChecker();
|
|
59
|
+
// Full re-index: WrapperRegistry must contain entries keyed by NEW
|
|
60
|
+
// symbols for every file, otherwise affected files cannot resolve
|
|
61
|
+
// wrappers defined in unaffected files (this is the watch-corruption bug).
|
|
62
|
+
this.reindexAll(this.lastConfig);
|
|
63
|
+
// Incremental extract: only re-extract changed files + their dependents.
|
|
64
|
+
// Unaffected files keep their previously-computed Set<string> events,
|
|
65
|
+
// which is safe (just strings, no AST refs).
|
|
66
|
+
const affected = new Set();
|
|
67
|
+
for (const file of changedFiles) {
|
|
57
68
|
affected.add(file);
|
|
58
69
|
for (const dependent of this.importGraph.collectDependents(file)) {
|
|
59
70
|
affected.add(dependent);
|
|
60
71
|
}
|
|
61
72
|
}
|
|
62
|
-
|
|
73
|
+
for (const file of affected) {
|
|
74
|
+
this.extractFile(file, this.lastConfig);
|
|
75
|
+
}
|
|
63
76
|
});
|
|
64
77
|
}
|
|
78
|
+
// drop every symbol-keyed cache; call after a program rebuild
|
|
79
|
+
resetCaches() {
|
|
80
|
+
this.evaluationCache.clear();
|
|
81
|
+
this.resolvedCallCache.clear();
|
|
82
|
+
this.resolvedExportCache.clear();
|
|
83
|
+
this.returnPropagationCache.clear();
|
|
84
|
+
this.wrapperRegistry.clear();
|
|
85
|
+
}
|
|
86
|
+
// re-index every source file in the current program (skips .d.ts)
|
|
87
|
+
reindexAll(config) {
|
|
88
|
+
const sources = this.compiler.getAllSourceFiles();
|
|
89
|
+
this.fileIndices.clear();
|
|
90
|
+
for (const sf of sources) {
|
|
91
|
+
this.indexFile(sf.fileName, config);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
65
94
|
beginPreload() {
|
|
66
95
|
this.isPreloading = true;
|
|
67
96
|
}
|
|
@@ -137,11 +166,13 @@ class EventraEngine {
|
|
|
137
166
|
}
|
|
138
167
|
async reanalyzeFiles(fileNames, config) {
|
|
139
168
|
this.lastConfig = config;
|
|
169
|
+
// Same correctness contract as the scheduler flush: the program may have
|
|
170
|
+
// just been rebuilt (e.g. via removeFile), so clear symbol caches and
|
|
171
|
+
// re-index every file before re-extracting the requested slice.
|
|
172
|
+
this.resetCaches();
|
|
140
173
|
this.refreshChecker();
|
|
174
|
+
this.reindexAll(config);
|
|
141
175
|
const normalized = fileNames.map((f) => this.normalize(f));
|
|
142
|
-
for (const file of normalized) {
|
|
143
|
-
this.indexFile(file, config);
|
|
144
|
-
}
|
|
145
176
|
for (const file of normalized) {
|
|
146
177
|
this.extractFile(file, config);
|
|
147
178
|
}
|
|
@@ -175,10 +206,6 @@ class EventraEngine {
|
|
|
175
206
|
}
|
|
176
207
|
async removeFile(fileName, config) {
|
|
177
208
|
const normalized = this.normalize(fileName);
|
|
178
|
-
const existing = this.compiler.getSourceFile(normalized);
|
|
179
|
-
if (existing) {
|
|
180
|
-
(0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache, this.returnPropagationCache, this.wrapperRegistry);
|
|
181
|
-
}
|
|
182
209
|
const affected = this.importGraph.collectDependents(normalized);
|
|
183
210
|
this.importGraph.removeFile(normalized);
|
|
184
211
|
this.compiler.removeFile(normalized);
|
|
@@ -188,9 +215,17 @@ class EventraEngine {
|
|
|
188
215
|
this.normalizedPathCache.delete(normalized);
|
|
189
216
|
const nextConfig = config ?? this.lastConfig;
|
|
190
217
|
const toReanalyze = [...affected].filter((f) => f !== normalized);
|
|
218
|
+
// reanalyzeFiles already resets caches + reindexes all sources,
|
|
219
|
+
// which is what we need after compiler.removeFile rebuilds the program.
|
|
191
220
|
if (toReanalyze.length > 0) {
|
|
192
221
|
await this.reanalyzeFiles(toReanalyze, nextConfig);
|
|
193
222
|
}
|
|
223
|
+
else {
|
|
224
|
+
// Even without dependents, the program changed → caches must be reset
|
|
225
|
+
// so the next call (e.g. runFullAnalysis) sees a clean slate.
|
|
226
|
+
this.resetCaches();
|
|
227
|
+
this.refreshChecker();
|
|
228
|
+
}
|
|
194
229
|
}
|
|
195
230
|
getAllEvents() {
|
|
196
231
|
const events = new Set();
|