@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.
- package/dist/commands/sync.js +11 -1
- package/dist/engine/languageService.js +0 -3
- package/dist/engine/scanner.js +50 -23
- package/package.json +1 -1
- package/src/commands/sync.ts +13 -2
- package/src/engine/languageService.ts +0 -4
- package/src/engine/scanner.ts +72 -22
package/dist/commands/sync.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/dist/engine/scanner.js
CHANGED
|
@@ -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) &&
|
|
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
|
-
|
|
68
|
-
|
|
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 &&
|
|
72
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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 (
|
|
90
|
-
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(
|
|
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
|
-
|
|
110
|
-
|
|
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) ||
|
|
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
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
package/src/commands/sync.ts
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/src/engine/scanner.ts
CHANGED
|
@@ -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))
|
|
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) &&
|
|
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
|
-
|
|
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
|
-
|
|
124
|
+
resolvedFn && findTrackParamIndex(resolvedFn) !== null;
|
|
98
125
|
|
|
99
126
|
const isTracking =
|
|
100
127
|
isKnownWrapper ||
|
|
101
128
|
TRACKING_NAMES.has(name) ||
|
|
102
|
-
(isAutoWrapper &&
|
|
129
|
+
(isAutoWrapper &&
|
|
130
|
+
resolvedFn &&
|
|
131
|
+
!isExternalFile(resolvedFn.getSourceFile().fileName));
|
|
103
132
|
|
|
104
|
-
if (!isTracking)
|
|
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
|
-
|
|
142
|
+
// paramMap (chain support)
|
|
143
|
+
const paramMap = new Map(inheritedParamMap ?? []);
|
|
111
144
|
|
|
112
|
-
if (
|
|
113
|
-
|
|
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 (
|
|
126
|
-
const idx = findTrackParamIndex(
|
|
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
|
-
|
|
154
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
|
280
|
+
const paramMap = new Map<string, ts.Expression>();
|
|
281
|
+
paramMap.set(prop, expr);
|
|
239
282
|
|
|
240
|
-
res
|
|
241
|
-
|
|
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
|
}
|