@eventra_dev/eventra-cli 0.3.6 → 0.3.10

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.
@@ -0,0 +1,171 @@
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.isEventraSdkTrackCall = isEventraSdkTrackCall;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const eventraSdk_1 = require("./eventraSdk");
9
+ function isTrackCallee(expr) {
10
+ if (typescript_1.default.isPropertyAccessExpression(expr) || typescript_1.default.isPropertyAccessChain(expr)) {
11
+ return expr.name.text === "track";
12
+ }
13
+ if (typescript_1.default.isElementAccessExpression(expr) &&
14
+ expr.argumentExpression &&
15
+ typescript_1.default.isStringLiteral(expr.argumentExpression) &&
16
+ expr.argumentExpression.text === "track") {
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+ function fileImportsEventraSdk(sourceFile) {
22
+ for (const stmt of sourceFile.statements) {
23
+ if (!typescript_1.default.isImportDeclaration(stmt) || !stmt.moduleSpecifier) {
24
+ continue;
25
+ }
26
+ if (!typescript_1.default.isStringLiteral(stmt.moduleSpecifier)) {
27
+ continue;
28
+ }
29
+ if ((0, eventraSdk_1.isEventraSdkModuleSpecifier)(stmt.moduleSpecifier.text)) {
30
+ return true;
31
+ }
32
+ }
33
+ return false;
34
+ }
35
+ function getImportDeclarationForSpecifier(decl) {
36
+ let current = decl;
37
+ while (current) {
38
+ if (typescript_1.default.isImportDeclaration(current)) {
39
+ return current;
40
+ }
41
+ current = current.parent;
42
+ }
43
+ return null;
44
+ }
45
+ function isEventraImportDeclaration(decl) {
46
+ if (typescript_1.default.isImportSpecifier(decl) && decl.name.text === "Eventra") {
47
+ const importDecl = getImportDeclarationForSpecifier(decl);
48
+ if (importDecl?.moduleSpecifier && typescript_1.default.isStringLiteral(importDecl.moduleSpecifier)) {
49
+ return (0, eventraSdk_1.isEventraSdkModuleSpecifier)(importDecl.moduleSpecifier.text);
50
+ }
51
+ }
52
+ if (typescript_1.default.isImportClause(decl)) {
53
+ const parent = decl.parent;
54
+ if (parent && typescript_1.default.isImportDeclaration(parent) && parent.moduleSpecifier && typescript_1.default.isStringLiteral(parent.moduleSpecifier)) {
55
+ return (0, eventraSdk_1.isEventraSdkModuleSpecifier)(parent.moduleSpecifier.text);
56
+ }
57
+ }
58
+ return false;
59
+ }
60
+ function isEventraClassDeclaration(decl) {
61
+ if (!typescript_1.default.isClassDeclaration(decl) || decl.name?.text !== "Eventra") {
62
+ return false;
63
+ }
64
+ const file = decl.getSourceFile().fileName.replace(/\\/g, "/");
65
+ return file.includes("eventra-sdk") || file.includes("@eventra_dev");
66
+ }
67
+ function isEventraSymbol(symbol, checker, depth = 0) {
68
+ if (depth > 10) {
69
+ return false;
70
+ }
71
+ if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
72
+ return isEventraSymbol(checker.getAliasedSymbol(symbol), checker, depth + 1);
73
+ }
74
+ for (const decl of symbol.getDeclarations() ?? []) {
75
+ if (isEventraImportDeclaration(decl) || isEventraClassDeclaration(decl)) {
76
+ return true;
77
+ }
78
+ }
79
+ return false;
80
+ }
81
+ function isEventraNewExpression(node, checker) {
82
+ const expr = node.expression;
83
+ if (!typescript_1.default.isIdentifier(expr)) {
84
+ return false;
85
+ }
86
+ const symbol = checker.getSymbolAtLocation(expr);
87
+ return symbol ? isEventraSymbol(symbol, checker) : false;
88
+ }
89
+ function typeLooksLikeEventra(type, checker, depth = 0) {
90
+ if (depth > 8) {
91
+ return false;
92
+ }
93
+ const symbol = type.getSymbol();
94
+ if (symbol && isEventraSymbol(symbol, checker)) {
95
+ return true;
96
+ }
97
+ const typeStr = checker.typeToString(type);
98
+ if (typeStr === "Eventra" || /^Eventra<.*>$/.test(typeStr)) {
99
+ return true;
100
+ }
101
+ if (type.isUnionOrIntersection()) {
102
+ return type.types.some((t) => typeLooksLikeEventra(t, checker, depth + 1));
103
+ }
104
+ return false;
105
+ }
106
+ function isEventraReceiverExpression(receiver, checker) {
107
+ if (typescript_1.default.isNewExpression(receiver)) {
108
+ return isEventraNewExpression(receiver, checker);
109
+ }
110
+ if (typescript_1.default.isCallExpression(receiver) && typescript_1.default.isNewExpression(receiver.expression)) {
111
+ return isEventraNewExpression(receiver.expression, checker);
112
+ }
113
+ const type = checker.getTypeAtLocation(receiver);
114
+ if (typeLooksLikeEventra(type, checker)) {
115
+ return true;
116
+ }
117
+ if (typescript_1.default.isIdentifier(receiver)) {
118
+ const symbol = checker.getSymbolAtLocation(receiver);
119
+ if (!symbol) {
120
+ return false;
121
+ }
122
+ for (const decl of symbol.getDeclarations() ?? []) {
123
+ if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
124
+ if (typescript_1.default.isNewExpression(decl.initializer) && isEventraNewExpression(decl.initializer, checker)) {
125
+ return true;
126
+ }
127
+ if (typescript_1.default.isCallExpression(decl.initializer) &&
128
+ typescript_1.default.isNewExpression(decl.initializer.expression) &&
129
+ isEventraNewExpression(decl.initializer.expression, checker)) {
130
+ return true;
131
+ }
132
+ }
133
+ if (typescript_1.default.isParameter(decl) && decl.type) {
134
+ const paramType = checker.getTypeFromTypeNode(decl.type);
135
+ if (typeLooksLikeEventra(paramType, checker)) {
136
+ return true;
137
+ }
138
+ }
139
+ if (typescript_1.default.isPropertyDeclaration(decl) && decl.type) {
140
+ const propType = checker.getTypeFromTypeNode(decl.type);
141
+ if (typeLooksLikeEventra(propType, checker)) {
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ if (typescript_1.default.isPropertyAccessExpression(receiver) || typescript_1.default.isPropertyAccessChain(receiver)) {
148
+ return isEventraReceiverExpression(receiver.expression, checker);
149
+ }
150
+ return false;
151
+ }
152
+ /** Only `instance.track(...)` where instance is Eventra from @eventra_dev/eventra-sdk. */
153
+ function isEventraSdkTrackCall(call, checker) {
154
+ const expr = call.expression;
155
+ if (!isTrackCallee(expr)) {
156
+ return false;
157
+ }
158
+ const sourceFile = call.getSourceFile();
159
+ if (!fileImportsEventraSdk(sourceFile)) {
160
+ return false;
161
+ }
162
+ const receiver = typescript_1.default.isPropertyAccessExpression(expr) ||
163
+ typescript_1.default.isPropertyAccessChain(expr) ||
164
+ typescript_1.default.isElementAccessExpression(expr)
165
+ ? expr.expression
166
+ : undefined;
167
+ if (!receiver) {
168
+ return false;
169
+ }
170
+ return isEventraReceiverExpression(receiver, checker);
171
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_EVENT_NAME_LENGTH = exports.normalizeEventName = exports.isValidEventName = void 0;
4
+ var eventraSdk_1 = require("../sdk/eventraSdk");
5
+ Object.defineProperty(exports, "isValidEventName", { enumerable: true, get: function () { return eventraSdk_1.isValidEventName; } });
6
+ Object.defineProperty(exports, "normalizeEventName", { enumerable: true, get: function () { return eventraSdk_1.normalizeEventName; } });
7
+ Object.defineProperty(exports, "MAX_EVENT_NAME_LENGTH", { enumerable: true, get: function () { return eventraSdk_1.MAX_EVENT_NAME_LENGTH; } });
@@ -1,13 +1,10 @@
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.WrapperRegistry = void 0;
7
- const typescript_1 = __importDefault(require("typescript"));
8
4
  const exportResolver_1 = require("../resolver/exportResolver");
5
+ const symbolUtils_1 = require("./symbolUtils");
9
6
  function isSymbol(value) {
10
- return "flags" in value;
7
+ return typeof value.getDeclarations === "function";
11
8
  }
12
9
  class WrapperRegistry {
13
10
  checker;
@@ -17,6 +14,12 @@ class WrapperRegistry {
17
14
  this.checker = checker;
18
15
  this.exportCache = exportCache;
19
16
  }
17
+ setChecker(checker) {
18
+ this.checker = checker;
19
+ }
20
+ getChecker() {
21
+ return this.checker;
22
+ }
20
23
  normalize(symbol) {
21
24
  return ((0, exportResolver_1.resolveExportedSymbol)(symbol, this.checker, this.exportCache) ?? symbol);
22
25
  }
@@ -27,16 +30,7 @@ class WrapperRegistry {
27
30
  if (isSymbol(target)) {
28
31
  return this.wrappers.get(this.normalize(target));
29
32
  }
30
- let symbol;
31
- if (typescript_1.default.isFunctionDeclaration(target) && target.name) {
32
- symbol = this.checker.getSymbolAtLocation(target.name);
33
- }
34
- else if (typescript_1.default.isMethodDeclaration(target) && typescript_1.default.isIdentifier(target.name)) {
35
- symbol = this.checker.getSymbolAtLocation(target.name);
36
- }
37
- else if (target.parent && typescript_1.default.isVariableDeclaration(target.parent) && typescript_1.default.isIdentifier(target.parent.name)) {
38
- symbol = this.checker.getSymbolAtLocation(target.parent.name);
39
- }
33
+ const symbol = (0, symbolUtils_1.getFunctionSymbol)(target, this.checker);
40
34
  if (!symbol) {
41
35
  return undefined;
42
36
  }
package/dist/cli/check.js CHANGED
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -40,31 +7,46 @@ exports.check = check;
40
7
  const chalk_1 = __importDefault(require("chalk"));
41
8
  const config_1 = require("../config/config");
42
9
  const projectScanner_1 = require("../core/projectScanner");
10
+ const scanResults_1 = require("../core/scanResults");
11
+ function diffSets(found, known) {
12
+ return {
13
+ added: [...found].filter((e) => !known.has(e)),
14
+ removed: [...known].filter((e) => !found.has(e)),
15
+ };
16
+ }
43
17
  async function check({ fix = false }) {
44
18
  const config = await (0, config_1.loadConfig)();
45
19
  if (!config) {
46
20
  console.log(chalk_1.default.red("eventra.json not found"));
47
21
  process.exit(1);
48
22
  }
49
- console.log(chalk_1.default.blue("Checking events..."));
23
+ console.log(chalk_1.default.blue("Checking events and function wrappers..."));
50
24
  const { engine } = await (0, projectScanner_1.scanProject)(config);
51
- const found = new Set(engine.getAllEvents());
52
- const known = new Set(config.events ?? []);
53
- const added = [...found].filter(e => !known.has(e));
54
- const removed = [...known].filter(e => !found.has(e));
25
+ const foundEvents = new Set(engine.getAllEvents());
26
+ const knownEvents = new Set(config.events ?? []);
27
+ const eventDiff = diffSets(foundEvents, knownEvents);
28
+ const foundWrappers = new Set(engine.getAllFunctionWrappers());
29
+ const knownWrappers = new Set(config.functionWrappers?.map((w) => w.name) ?? []);
30
+ const wrapperDiff = diffSets(foundWrappers, knownWrappers);
55
31
  if (fix) {
56
- const nextConfig = {
57
- ...config,
58
- events: [...found].sort(),
59
- };
60
- const { saveConfig } = await Promise.resolve().then(() => __importStar(require("../config/config")));
61
- await saveConfig(nextConfig);
32
+ await (0, config_1.saveConfig)((0, scanResults_1.buildConfigFromScan)(config, engine));
62
33
  console.log(chalk_1.default.green("Synced"));
63
34
  return;
64
35
  }
65
- if (added.length || removed.length) {
66
- added.forEach(e => console.log(chalk_1.default.red(`+ ${e}`)));
67
- removed.forEach(e => console.log(chalk_1.default.yellow(`- ${e}`)));
36
+ let hasDiff = false;
37
+ if (eventDiff.added.length || eventDiff.removed.length) {
38
+ hasDiff = true;
39
+ console.log(chalk_1.default.blue("\nEvents:"));
40
+ eventDiff.added.forEach((e) => console.log(chalk_1.default.red(`+ ${e}`)));
41
+ eventDiff.removed.forEach((e) => console.log(chalk_1.default.yellow(`- ${e}`)));
42
+ }
43
+ if (wrapperDiff.added.length || wrapperDiff.removed.length) {
44
+ hasDiff = true;
45
+ console.log(chalk_1.default.blue("\nFunction wrappers:"));
46
+ wrapperDiff.added.forEach((w) => console.log(chalk_1.default.red(`+ ${w}`)));
47
+ wrapperDiff.removed.forEach((w) => console.log(chalk_1.default.yellow(`- ${w}`)));
48
+ }
49
+ if (hasDiff) {
68
50
  process.exit(1);
69
51
  }
70
52
  console.log(chalk_1.default.green("All good"));
package/dist/cli/init.js CHANGED
@@ -23,17 +23,16 @@ async function init() {
23
23
  },
24
24
  ]);
25
25
  console.log("");
26
- console.log(chalk_1.default.gray("Eventra automatically detects:"));
27
- console.log(chalk_1.default.gray("• track('event')"));
28
- console.log(chalk_1.default.gray("• analytics.track('event')"));
29
- console.log(chalk_1.default.gray("• semantic parameter propagation"));
30
- console.log(chalk_1.default.gray("• cross-file wrappers"));
31
- console.log(chalk_1.default.gray("• parameter propagation"));
26
+ console.log(chalk_1.default.gray("Eventra detects @eventra_dev/eventra-sdk usage:"));
27
+ console.log(chalk_1.default.gray('• tracker.track("event.name")'));
28
+ console.log(chalk_1.default.gray("• function wrappers → Eventra.track()"));
29
+ console.log(chalk_1.default.gray("• cross-file propagation chains"));
32
30
  console.log("");
33
31
  const config = {
34
32
  apiKey,
35
33
  endpoint,
36
34
  events: [],
35
+ functionWrappers: [],
37
36
  sync: {
38
37
  include: [
39
38
  "**/*.{ts,tsx,js,jsx}",
package/dist/cli/sync.js CHANGED
@@ -7,6 +7,7 @@ exports.sync = sync;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const config_1 = require("../config/config");
9
9
  const projectScanner_1 = require("../core/projectScanner");
10
+ const scanResults_1 = require("../core/scanResults");
10
11
  async function sync() {
11
12
  const config = await (0, config_1.loadConfig)();
12
13
  if (!config) {
@@ -15,7 +16,8 @@ async function sync() {
15
16
  }
16
17
  console.log(chalk_1.default.blue("Scanning..."));
17
18
  const { engine } = await (0, projectScanner_1.scanProject)(config);
18
- const events = engine.getAllEvents().sort();
19
- await (0, config_1.saveConfig)({ ...config, events });
20
- console.log(chalk_1.default.green(`Found ${events.length} events`));
19
+ await (0, scanResults_1.persistScanResults)(config, engine);
20
+ const events = engine.getAllEvents();
21
+ const wrappers = engine.getAllFunctionWrappers();
22
+ console.log(chalk_1.default.green(`Found ${events.length} events, ${wrappers.length} function wrappers`));
21
23
  }
package/dist/cli/watch.js CHANGED
@@ -10,7 +10,10 @@ const promises_1 = __importDefault(require("fs/promises"));
10
10
  const fast_glob_1 = __importDefault(require("fast-glob"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const config_1 = require("../config/config");
13
+ const eventraSdk_1 = require("../analysis/sdk/eventraSdk");
13
14
  const EventraEngine_1 = require("../core/EventraEngine");
15
+ const scanResults_1 = require("../core/scanResults");
16
+ const SDK_TYPES_FILE = "__eventra_sdk_types__.d.ts";
14
17
  const processFile_1 = require("../filesystem/processFile");
15
18
  const getVirtualFile_1 = require("../filesystem/getVirtualFile");
16
19
  async function watch() {
@@ -24,38 +27,39 @@ async function watch() {
24
27
  const fileDeps = new Map();
25
28
  const cache = new Map();
26
29
  const watchedDeps = new Set();
30
+ const trackedFiles = new Set();
27
31
  const normalize = (file) => {
28
32
  return path_1.default.resolve(file).replace(/\\/g, "/");
29
33
  };
30
- // INITIAL LOAD
31
34
  const files = await (0, fast_glob_1.default)(config.sync.include, {
32
35
  ignore: config.sync.exclude,
33
36
  absolute: true,
34
37
  });
35
38
  engine.beginPreload();
39
+ await engine.preloadFile(SDK_TYPES_FILE, eventraSdk_1.EVENTRA_SDK_SHIM);
36
40
  for (const file of files) {
37
41
  try {
38
42
  const abs = normalize(file);
39
43
  const raw = await promises_1.default.readFile(abs, "utf-8");
40
44
  const parsed = await (0, processFile_1.processFile)(abs, raw);
41
45
  cache.set(abs, parsed);
46
+ trackedFiles.add(abs);
42
47
  await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(abs), parsed.content);
43
- await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(abs), parsed.content, config);
44
- fileDeps.set(abs, new Set(parsed.dependencies.map(dep => normalize(dep))));
48
+ fileDeps.set(abs, new Set(parsed.dependencies
49
+ .filter((dep) => dep.startsWith(".") || dep.startsWith("/"))
50
+ .map((dep) => normalize(path_1.default.resolve(path_1.default.dirname(abs), dep)))));
45
51
  }
46
52
  catch {
47
53
  // ignore invalid files
48
54
  }
49
55
  }
50
56
  engine.endPreload();
51
- const initialEvents = engine.getAllEvents().sort();
52
- await (0, config_1.saveConfig)({
53
- ...config,
54
- events: initialEvents,
55
- });
56
- console.log(chalk_1.default.gray(`Initial: ${initialEvents.length}\n`));
57
- // WATCHER
58
- const watcher = chokidar_1.default.watch([...files.map(normalize), ...watchedDeps], { ignoreInitial: true });
57
+ await engine.runFullAnalysis([SDK_TYPES_FILE, ...trackedFiles].map((f) => (0, getVirtualFile_1.getVirtualFile)(f)), config);
58
+ await (0, scanResults_1.persistScanResults)(config, engine);
59
+ const initialEvents = engine.getAllEvents();
60
+ const initialWrappers = engine.getAllFunctionWrappers();
61
+ console.log(chalk_1.default.gray(`Initial: ${initialEvents.length} events, ${initialWrappers.length} wrappers\n`));
62
+ const watcher = chokidar_1.default.watch([...trackedFiles, ...watchedDeps], { ignoreInitial: true });
59
63
  const queue = new Map();
60
64
  let timer = null;
61
65
  const run = async () => {
@@ -66,24 +70,22 @@ async function watch() {
66
70
  const raw = await promises_1.default.readFile(file, "utf-8");
67
71
  const parsed = await (0, processFile_1.processFile)(file, raw);
68
72
  cache.set(file, parsed);
73
+ trackedFiles.add(file);
69
74
  await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(file), parsed.content, config);
70
- // dependency diff
71
75
  const abs = normalize(file);
72
76
  const prevDeps = fileDeps.get(abs) ?? new Set();
73
- const nextDeps = new Set(parsed.dependencies.map(dep => normalize(dep)));
74
- // add deps
77
+ const nextDeps = new Set(parsed.dependencies
78
+ .filter((dep) => dep.startsWith(".") || dep.startsWith("/"))
79
+ .map((dep) => normalize(path_1.default.resolve(path_1.default.dirname(abs), dep))));
75
80
  for (const dep of nextDeps) {
76
- if (prevDeps.has(dep)) {
81
+ if (prevDeps.has(dep))
77
82
  continue;
78
- }
79
83
  watcher.add(dep);
80
84
  watchedDeps.add(dep);
81
85
  }
82
- // remove deps
83
86
  for (const dep of prevDeps) {
84
- if (nextDeps.has(dep)) {
87
+ if (nextDeps.has(dep))
85
88
  continue;
86
- }
87
89
  watcher.unwatch(dep);
88
90
  watchedDeps.delete(dep);
89
91
  }
@@ -93,19 +95,14 @@ async function watch() {
93
95
  console.log(chalk_1.default.gray(`skip: ${file}`));
94
96
  }
95
97
  }
96
- const nextEvents = engine.getAllEvents().sort();
97
- await (0, config_1.saveConfig)({
98
- ...config,
99
- events: nextEvents,
100
- });
101
- console.log(chalk_1.default.green(`Updated: ${nextEvents.length}`));
98
+ await (0, scanResults_1.persistScanResults)(config, engine);
99
+ console.log(chalk_1.default.green(`Updated: ${engine.getAllEvents().length} events, ${engine.getAllFunctionWrappers().length} wrappers`));
102
100
  };
103
101
  const schedule = (file) => {
104
102
  const abs = normalize(file);
105
103
  queue.set(abs, true);
106
- if (timer) {
104
+ if (timer)
107
105
  clearTimeout(timer);
108
- }
109
106
  timer = setTimeout(run, 200);
110
107
  };
111
108
  watcher
@@ -113,13 +110,10 @@ async function watch() {
113
110
  .on("add", schedule)
114
111
  .on("unlink", async (file) => {
115
112
  const abs = normalize(file);
113
+ trackedFiles.delete(abs);
116
114
  await engine.removeFile((0, getVirtualFile_1.getVirtualFile)(abs), config);
117
- const nextEvents = engine.getAllEvents().sort();
118
- await (0, config_1.saveConfig)({
119
- ...config,
120
- events: nextEvents,
121
- });
122
- console.log(chalk_1.default.red(`Removed → ${nextEvents.length}`));
115
+ await (0, scanResults_1.persistScanResults)(config, engine);
116
+ console.log(chalk_1.default.red(`Removed → ${engine.getAllEvents().length} events, ${engine.getAllFunctionWrappers().length} wrappers`));
123
117
  });
124
118
  process.on("SIGINT", async () => {
125
119
  await watcher.close();