@eventra_dev/eventra-cli 0.1.8 → 0.1.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.
@@ -25,11 +25,21 @@ async function sync() {
25
25
  const engine = new engine_1.EventraEngine(process.cwd());
26
26
  const detectedFn = new Set();
27
27
  const detectedCmp = new Map();
28
+ const fileCache = [];
28
29
  for (const file of files) {
29
30
  try {
30
31
  const raw = await promises_1.default.readFile(file, "utf-8");
31
32
  const { content, virtualFile } = (0, processFile_1.processFile)(file, raw);
32
- const res = engine.scanFile(virtualFile, content, config);
33
+ fileCache.push({ file: virtualFile, content });
34
+ engine["ts"].updateFile(virtualFile, content);
35
+ }
36
+ catch {
37
+ // ignore
38
+ }
39
+ }
40
+ for (const { file, content } of fileCache) {
41
+ try {
42
+ const res = engine.scanFile(file, content, config);
33
43
  for (const w of res.detectedFunctionWrappers) {
34
44
  detectedFn.add(w);
35
45
  }
@@ -48,9 +48,6 @@ class TSService {
48
48
  },
49
49
  getScriptVersion: (file) => this.files.get(file)?.version.toString() ?? "0",
50
50
  getScriptSnapshot: (file) => {
51
- if (!this.fileNames.has(file)) {
52
- return undefined;
53
- }
54
51
  const f = this.files.get(file);
55
52
  if (f) {
56
53
  return typescript_1.default.ScriptSnapshot.fromString(f.content);
@@ -37,11 +37,14 @@ function isPropUsedInTracking(fn, prop) {
37
37
  function visit(node) {
38
38
  if (typescript_1.default.isCallExpression(node)) {
39
39
  const name = getCallName(node.expression);
40
- if (!TRACKING_NAMES.has(name))
40
+ if (!TRACKING_NAMES.has(name)) {
41
+ typescript_1.default.forEachChild(node, visit);
41
42
  return;
43
+ }
42
44
  for (const arg of node.arguments) {
43
45
  if ((typescript_1.default.isIdentifier(arg) && arg.text === prop) ||
44
- (typescript_1.default.isPropertyAccessExpression(arg) && arg.name.text === prop)) {
46
+ (typescript_1.default.isPropertyAccessExpression(arg) &&
47
+ arg.name.text === prop)) {
45
48
  found = true;
46
49
  }
47
50
  }
@@ -51,7 +54,7 @@ function isPropUsedInTracking(fn, prop) {
51
54
  visit(fn);
52
55
  return found;
53
56
  }
54
- function scanSource(source, checker, config, visited = new Set(), depth = 0) {
57
+ function scanSource(source, checker, config, visited = new Set(), depth = 0, inheritedParamMap) {
55
58
  const events = new Set();
56
59
  const detectedFunctionWrappers = new Set();
57
60
  const detectedComponentWrappers = new Map();
@@ -64,19 +67,40 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
64
67
  if (typescript_1.default.isCallExpression(node)) {
65
68
  const name = getCallName(node.expression);
66
69
  const isKnownWrapper = config.functionWrappers.some(w => w.name === name);
67
- const fn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
68
- const isAutoWrapper = fn && (0, functionAnalyzer_1.findTrackParamIndex)(fn) !== null;
70
+ let resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
71
+ // fallback: tracker.track(...)
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
+ }
87
+ const isAutoWrapper = resolvedFn && (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn) !== null;
69
88
  const isTracking = isKnownWrapper ||
70
89
  TRACKING_NAMES.has(name) ||
71
- (isAutoWrapper && fn && !(0, boundary_1.isExternalFile)(fn.getSourceFile().fileName));
72
- if (!isTracking)
90
+ (isAutoWrapper &&
91
+ resolvedFn &&
92
+ !(0, boundary_1.isExternalFile)(resolvedFn.getSourceFile().fileName));
93
+ if (!isTracking) {
94
+ typescript_1.default.forEachChild(node, visit);
73
95
  return;
96
+ }
74
97
  if (!isKnownWrapper && isAutoWrapper && !TRACKING_NAMES.has(name)) {
75
98
  detectedFunctionWrappers.add(name);
76
99
  }
77
- const paramMap = new Map();
78
- if (fn && typescript_1.default.isFunctionLike(fn)) {
79
- fn.parameters.forEach((param, i) => {
100
+ // paramMap (chain support)
101
+ const paramMap = new Map(inheritedParamMap ?? []);
102
+ if (resolvedFn && typescript_1.default.isFunctionLike(resolvedFn)) {
103
+ resolvedFn.parameters.forEach((param, i) => {
80
104
  const arg = node.arguments[i];
81
105
  if (!arg)
82
106
  return;
@@ -86,8 +110,8 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
86
110
  });
87
111
  }
88
112
  let handled = false;
89
- if (fn) {
90
- const idx = (0, functionAnalyzer_1.findTrackParamIndex)(fn);
113
+ if (resolvedFn) {
114
+ const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
91
115
  if (idx !== null && node.arguments[idx]) {
92
116
  const res = (0, resolver_1.resolveNodeValue)(node.arguments[idx], checker, paramMap);
93
117
  res?.values.forEach(v => {
@@ -106,15 +130,16 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
106
130
  });
107
131
  }
108
132
  }
109
- if (fn) {
110
- const sf = fn.getSourceFile();
133
+ // cross-file with paramMap
134
+ if (resolvedFn) {
135
+ const sf = resolvedFn.getSourceFile();
111
136
  if (!visited.has(sf.fileName)) {
112
- const res = scanSource(sf, checker, config, visited, depth + 1);
137
+ const res = scanSource(sf, checker, config, visited, depth + 1, paramMap);
113
138
  res.events.forEach(e => events.add(e));
114
139
  }
115
140
  }
116
141
  }
117
- // JSX
142
+ // JSX COMPONENTS
118
143
  if (typescript_1.default.isJsxSelfClosingElement(node) ||
119
144
  typescript_1.default.isJsxOpeningElement(node)) {
120
145
  const tagNode = typescript_1.default.isPropertyAccessExpression(node.tagName)
@@ -127,8 +152,10 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
127
152
  if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
128
153
  symbol = checker.getAliasedSymbol(symbol);
129
154
  }
130
- if (!symbol)
155
+ if (!symbol) {
156
+ typescript_1.default.forEachChild(node, visit);
131
157
  return;
158
+ }
132
159
  const tagName = typescript_1.default.isPropertyAccessExpression(node.tagName)
133
160
  ? node.tagName.name.getText()
134
161
  : node.tagName.getText();
@@ -138,7 +165,8 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
138
165
  fn = decl;
139
166
  if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
140
167
  const init = decl.initializer;
141
- if (typescript_1.default.isArrowFunction(init) || typescript_1.default.isFunctionExpression(init)) {
168
+ if (typescript_1.default.isArrowFunction(init) ||
169
+ typescript_1.default.isFunctionExpression(init)) {
142
170
  fn = init;
143
171
  }
144
172
  }
@@ -165,11 +193,10 @@ function scanSource(source, checker, config, visited = new Set(), depth = 0) {
165
193
  : attr.initializer;
166
194
  if (!expr)
167
195
  continue;
168
- const res = (0, resolver_1.resolveNodeValue)(expr, checker);
169
- res?.values.forEach(v => {
170
- if (isValidEvent(v))
171
- events.add(v);
172
- });
196
+ const paramMap = new Map();
197
+ paramMap.set(prop, expr);
198
+ const res = scanSource(fn.getSourceFile(), checker, config, visited, depth + 1, paramMap);
199
+ res.events.forEach(e => events.add(e));
173
200
  detectedComponentWrappers.set(tagName, prop);
174
201
  }
175
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Eventra CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -26,13 +26,24 @@ export async function sync() {
26
26
  const detectedFn = new Set<string>();
27
27
  const detectedCmp = new Map<string, string>();
28
28
 
29
+ const fileCache: { file: string; content: string }[] = [];
30
+
29
31
  for (const file of files) {
30
32
  try {
31
33
  const raw = await fs.readFile(file, "utf-8");
32
-
33
34
  const { content, virtualFile } = processFile(file, raw);
34
35
 
35
- const res = engine.scanFile(virtualFile, content, config);
36
+ fileCache.push({ file: virtualFile, content });
37
+
38
+ engine["ts"].updateFile(virtualFile, content);
39
+ } catch {
40
+ // ignore
41
+ }
42
+ }
43
+
44
+ for (const { file, content } of fileCache) {
45
+ try {
46
+ const res = engine.scanFile(file, content, config);
36
47
 
37
48
  for (const w of res.detectedFunctionWrappers) {
38
49
  detectedFn.add(w);
@@ -62,10 +62,6 @@ export class TSService {
62
62
  this.files.get(file)?.version.toString() ?? "0",
63
63
 
64
64
  getScriptSnapshot: (file) => {
65
- if (!this.fileNames.has(file)) {
66
- return undefined;
67
- }
68
-
69
65
  const f = this.files.get(file);
70
66
 
71
67
  if (f) {
@@ -40,12 +40,16 @@ function isPropUsedInTracking(
40
40
  if (ts.isCallExpression(node)) {
41
41
  const name = getCallName(node.expression);
42
42
 
43
- if (!TRACKING_NAMES.has(name)) return;
43
+ if (!TRACKING_NAMES.has(name)) {
44
+ ts.forEachChild(node, visit);
45
+ return;
46
+ }
44
47
 
45
48
  for (const arg of node.arguments) {
46
49
  if (
47
50
  (ts.isIdentifier(arg) && arg.text === prop) ||
48
- (ts.isPropertyAccessExpression(arg) && arg.name.text === prop)
51
+ (ts.isPropertyAccessExpression(arg) &&
52
+ arg.name.text === prop)
49
53
  ) {
50
54
  found = true;
51
55
  }
@@ -70,7 +74,8 @@ export function scanSource(
70
74
  checker: ts.TypeChecker,
71
75
  config: EventraConfig,
72
76
  visited = new Set<string>(),
73
- depth = 0
77
+ depth = 0,
78
+ inheritedParamMap?: Map<string, ts.Expression>
74
79
  ): ScanResult {
75
80
  const events = new Set<string>();
76
81
  const detectedFunctionWrappers = new Set<string>();
@@ -91,26 +96,54 @@ export function scanSource(
91
96
  w => w.name === name
92
97
  );
93
98
 
94
- const fn = resolveFunctionFromCall(node.expression, checker);
99
+ let resolvedFn = resolveFunctionFromCall(node.expression, checker);
100
+
101
+ // fallback: tracker.track(...)
102
+ if (!resolvedFn && ts.isPropertyAccessExpression(node.expression)) {
103
+ const symbol = checker.getSymbolAtLocation(node.expression.name);
104
+
105
+ if (symbol) {
106
+ const decls = symbol.getDeclarations();
107
+
108
+ if (decls?.length) {
109
+ const d = decls[0];
110
+
111
+ if (
112
+ ts.isMethodDeclaration(d) ||
113
+ ts.isFunctionDeclaration(d) ||
114
+ ts.isFunctionExpression(d) ||
115
+ ts.isArrowFunction(d)
116
+ ) {
117
+ resolvedFn = d;
118
+ }
119
+ }
120
+ }
121
+ }
95
122
 
96
123
  const isAutoWrapper =
97
- fn && findTrackParamIndex(fn) !== null;
124
+ resolvedFn && findTrackParamIndex(resolvedFn) !== null;
98
125
 
99
126
  const isTracking =
100
127
  isKnownWrapper ||
101
128
  TRACKING_NAMES.has(name) ||
102
- (isAutoWrapper && fn && !isExternalFile(fn.getSourceFile().fileName));
129
+ (isAutoWrapper &&
130
+ resolvedFn &&
131
+ !isExternalFile(resolvedFn.getSourceFile().fileName));
103
132
 
104
- if (!isTracking) return;
133
+ if (!isTracking) {
134
+ ts.forEachChild(node, visit);
135
+ return;
136
+ }
105
137
 
106
138
  if (!isKnownWrapper && isAutoWrapper && !TRACKING_NAMES.has(name)) {
107
139
  detectedFunctionWrappers.add(name);
108
140
  }
109
141
 
110
- const paramMap = new Map<string, ts.Expression>();
142
+ // paramMap (chain support)
143
+ const paramMap = new Map(inheritedParamMap ?? []);
111
144
 
112
- if (fn && ts.isFunctionLike(fn)) {
113
- fn.parameters.forEach((param, i) => {
145
+ if (resolvedFn && ts.isFunctionLike(resolvedFn)) {
146
+ resolvedFn.parameters.forEach((param, i) => {
114
147
  const arg = node.arguments[i];
115
148
  if (!arg) return;
116
149
 
@@ -122,8 +155,8 @@ export function scanSource(
122
155
 
123
156
  let handled = false;
124
157
 
125
- if (fn) {
126
- const idx = findTrackParamIndex(fn);
158
+ if (resolvedFn) {
159
+ const idx = findTrackParamIndex(resolvedFn);
127
160
 
128
161
  if (idx !== null && node.arguments[idx]) {
129
162
  const res = resolveNodeValue(
@@ -150,8 +183,9 @@ export function scanSource(
150
183
  }
151
184
  }
152
185
 
153
- if (fn) {
154
- const sf = fn.getSourceFile();
186
+ // cross-file with paramMap
187
+ if (resolvedFn) {
188
+ const sf = resolvedFn.getSourceFile();
155
189
 
156
190
  if (!visited.has(sf.fileName)) {
157
191
  const res = scanSource(
@@ -159,7 +193,8 @@ export function scanSource(
159
193
  checker,
160
194
  config,
161
195
  visited,
162
- depth + 1
196
+ depth + 1,
197
+ paramMap
163
198
  );
164
199
 
165
200
  res.events.forEach(e => events.add(e));
@@ -167,7 +202,7 @@ export function scanSource(
167
202
  }
168
203
  }
169
204
 
170
- // JSX
205
+ // JSX COMPONENTS
171
206
  if (
172
207
  ts.isJsxSelfClosingElement(node) ||
173
208
  ts.isJsxOpeningElement(node)
@@ -187,7 +222,10 @@ export function scanSource(
187
222
  symbol = checker.getAliasedSymbol(symbol);
188
223
  }
189
224
 
190
- if (!symbol) return;
225
+ if (!symbol) {
226
+ ts.forEachChild(node, visit);
227
+ return;
228
+ }
191
229
 
192
230
  const tagName =
193
231
  ts.isPropertyAccessExpression(node.tagName)
@@ -201,7 +239,11 @@ export function scanSource(
201
239
 
202
240
  if (ts.isVariableDeclaration(decl) && decl.initializer) {
203
241
  const init = decl.initializer;
204
- if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) {
242
+
243
+ if (
244
+ ts.isArrowFunction(init) ||
245
+ ts.isFunctionExpression(init)
246
+ ) {
205
247
  fn = init;
206
248
  }
207
249
  }
@@ -235,11 +277,19 @@ export function scanSource(
235
277
 
236
278
  if (!expr) continue;
237
279
 
238
- const res = resolveNodeValue(expr, checker);
280
+ const paramMap = new Map<string, ts.Expression>();
281
+ paramMap.set(prop, expr);
239
282
 
240
- res?.values.forEach(v => {
241
- if (isValidEvent(v)) events.add(v);
242
- });
283
+ const res = scanSource(
284
+ fn.getSourceFile(),
285
+ checker,
286
+ config,
287
+ visited,
288
+ depth + 1,
289
+ paramMap
290
+ );
291
+
292
+ res.events.forEach(e => events.add(e));
243
293
 
244
294
  detectedComponentWrappers.set(tagName, prop);
245
295
  }