@eventra_dev/eventra-cli 0.1.24 → 0.1.26
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 +40 -16
- package/dist/commands/sync.js +40 -38
- package/dist/commands/watch.js +90 -36
- package/dist/engine/callResolver.js +52 -4
- package/dist/engine/engine.js +66 -32
- package/dist/engine/exportResolver.js +16 -4
- package/dist/engine/functionAnalyzer.js +66 -13
- package/dist/engine/graph.js +40 -4
- package/dist/engine/languageService.js +83 -20
- package/dist/engine/resolver.js +29 -21
- package/dist/engine/scanner.js +57 -46
- package/dist/engine/trackingDetector.js +68 -0
- package/dist/utils/getVirtualFile.js +8 -0
- package/dist/utils/processFile.js +0 -9
- package/dist/utils/universal.js +25 -87
- package/package.json +1 -1
- package/src/commands/check.ts +49 -29
- package/src/commands/sync.ts +48 -47
- package/src/commands/watch.ts +105 -37
- package/src/engine/callResolver.ts +77 -6
- package/src/engine/engine.ts +80 -33
- package/src/engine/exportResolver.ts +22 -5
- package/src/engine/functionAnalyzer.ts +76 -16
- package/src/engine/graph.ts +45 -6
- package/src/engine/languageService.ts +100 -29
- package/src/engine/resolver.ts +36 -27
- package/src/engine/scanner.ts +72 -50
- package/src/engine/trackingDetector.ts +94 -0
- package/src/types.ts +0 -3
- package/src/utils/getVirtualFile.ts +5 -0
- package/src/utils/processFile.ts +0 -9
- package/src/utils/universal.ts +26 -102
package/src/commands/sync.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fg from "fast-glob";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import path from "path";
|
|
4
5
|
|
|
5
6
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
6
7
|
import { EventraEngine } from "../engine/engine";
|
|
7
8
|
import { processFile } from "../utils/processFile";
|
|
9
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
8
10
|
|
|
9
11
|
export async function sync() {
|
|
10
12
|
const config = await loadConfig();
|
|
@@ -14,6 +16,7 @@ export async function sync() {
|
|
|
14
16
|
|
|
15
17
|
const files = await fg(config.sync.include, {
|
|
16
18
|
ignore: config.sync.exclude,
|
|
19
|
+
absolute: true,
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
if (!files.length) {
|
|
@@ -23,76 +26,86 @@ export async function sync() {
|
|
|
23
26
|
|
|
24
27
|
const engine = new EventraEngine(process.cwd());
|
|
25
28
|
|
|
26
|
-
const
|
|
27
|
-
const detectedCmp = new Map<string, string>();
|
|
29
|
+
const toScan = new Set<string>();
|
|
28
30
|
|
|
29
|
-
const
|
|
31
|
+
const cache = new Map<
|
|
32
|
+
string,
|
|
33
|
+
{ content: string; deps: string[] }
|
|
34
|
+
>();
|
|
30
35
|
|
|
36
|
+
// COLLECT + CACHE
|
|
31
37
|
for (const file of files) {
|
|
32
38
|
try {
|
|
33
39
|
const raw = await fs.readFile(file, "utf-8");
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
if (raw.length > 2_000_000) continue;
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
fileCache.push({ file: virtualFile, content });
|
|
43
|
+
const parsed = processFile(file, raw);
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const depRaw = await fs.readFile(dep, "utf-8");
|
|
45
|
+
cache.set(file, parsed);
|
|
46
|
+
toScan.add(file);
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} = processFile(dep, depRaw);
|
|
48
|
+
for (const dep of parsed.deps) {
|
|
49
|
+
toScan.add(path.resolve(dep));
|
|
50
|
+
}
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
// PRELOAD
|
|
56
|
+
for (const file of toScan) {
|
|
57
|
+
try {
|
|
58
|
+
let parsed = cache.get(file);
|
|
59
|
+
|
|
60
|
+
if (!parsed) {
|
|
61
|
+
const raw = await fs.readFile(file, "utf-8");
|
|
62
|
+
parsed = processFile(file, raw);
|
|
63
|
+
cache.set(file, parsed);
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
const virtual = getVirtualFile(file);
|
|
67
|
+
engine.preloadFile(virtual, parsed.content);
|
|
68
|
+
|
|
56
69
|
} catch {}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
|
|
72
|
+
// SCAN
|
|
73
|
+
const detectedFn = new Set<string>();
|
|
74
|
+
const detectedCmp = new Map<string, string>();
|
|
75
|
+
|
|
76
|
+
for (const file of toScan) {
|
|
60
77
|
try {
|
|
61
|
-
const
|
|
78
|
+
const parsed = cache.get(file);
|
|
79
|
+
if (!parsed) continue;
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
const virtual = getVirtualFile(file);
|
|
82
|
+
|
|
83
|
+
const res = engine.scanFile(virtual, parsed.content, config);
|
|
84
|
+
|
|
85
|
+
res.detectedFunctionWrappers.forEach(w =>
|
|
86
|
+
detectedFn.add(w)
|
|
87
|
+
);
|
|
66
88
|
|
|
67
89
|
for (const [k, v] of res.detectedComponentWrappers) {
|
|
68
90
|
detectedCmp.set(k, v);
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
} catch
|
|
72
|
-
console.log(chalk.
|
|
73
|
-
|
|
74
|
-
if (err instanceof Error) {
|
|
75
|
-
console.log(chalk.gray(err.message));
|
|
76
|
-
}
|
|
93
|
+
} catch {
|
|
94
|
+
console.log(chalk.gray(`skip: ${file}`));
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
//
|
|
98
|
+
// RESULT
|
|
81
99
|
const events = engine.getAllEvents().sort();
|
|
82
100
|
|
|
83
|
-
|
|
84
|
-
events.forEach(e => mergedEvents.add(e));
|
|
101
|
+
config.events = events;
|
|
85
102
|
|
|
86
|
-
config.events = [...mergedEvents].sort();
|
|
87
|
-
|
|
88
|
-
// FUNCTION WRAPPERS
|
|
89
103
|
config.functionWrappers = mergeUnique(
|
|
90
104
|
config.functionWrappers ?? [],
|
|
91
105
|
[...detectedFn].map(name => ({ name })),
|
|
92
106
|
w => w.name
|
|
93
107
|
);
|
|
94
108
|
|
|
95
|
-
// COMPONENT WRAPPERS
|
|
96
109
|
config.wrappers = mergeUnique(
|
|
97
110
|
config.wrappers ?? [],
|
|
98
111
|
[...detectedCmp.entries()].map(([name, prop]) => ({
|
|
@@ -105,18 +118,6 @@ export async function sync() {
|
|
|
105
118
|
await saveConfig(config);
|
|
106
119
|
|
|
107
120
|
console.log(chalk.green(`Found ${events.length} events`));
|
|
108
|
-
|
|
109
|
-
if (detectedFn.size) {
|
|
110
|
-
console.log(chalk.blue("\nDetected function wrappers:"));
|
|
111
|
-
detectedFn.forEach(w => console.log(chalk.gray(`• ${w}`)));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (detectedCmp.size) {
|
|
115
|
-
console.log(chalk.blue("\nDetected component wrappers:"));
|
|
116
|
-
detectedCmp.forEach((prop, name) =>
|
|
117
|
-
console.log(chalk.gray(`• ${name} (${prop})`))
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// HELPERS
|
package/src/commands/watch.ts
CHANGED
|
@@ -7,17 +7,15 @@ import path from "path";
|
|
|
7
7
|
import { loadConfig, saveConfig } from "../utils/config";
|
|
8
8
|
import { EventraEngine } from "../engine/engine";
|
|
9
9
|
import { processFile } from "../utils/processFile";
|
|
10
|
+
import { getVirtualFile } from "../utils/getVirtualFile";
|
|
10
11
|
|
|
11
12
|
type QueueItem = {
|
|
12
13
|
real: string;
|
|
13
14
|
virtual: string;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return /\.(vue|svelte|astro|html)$/i.test(file)
|
|
19
|
-
? file + ".tsx"
|
|
20
|
-
: file;
|
|
17
|
+
function normalize(file: string) {
|
|
18
|
+
return path.resolve(file).replace(/\\/g, "/");
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export async function watch() {
|
|
@@ -28,26 +26,34 @@ export async function watch() {
|
|
|
28
26
|
|
|
29
27
|
const files = await fg(config.sync.include, {
|
|
30
28
|
ignore: config.sync.exclude,
|
|
29
|
+
absolute: true,
|
|
31
30
|
});
|
|
32
31
|
|
|
33
32
|
const engine = new EventraEngine(process.cwd());
|
|
34
|
-
|
|
35
33
|
const watchedDeps = new Set<string>();
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
const cache = new Map<
|
|
36
|
+
string,
|
|
37
|
+
{ content: string; deps: string[] }
|
|
38
|
+
>();
|
|
39
|
+
|
|
40
|
+
const toScan = new Set<string>();
|
|
41
|
+
|
|
42
|
+
// COLLECT + CACHE
|
|
38
43
|
for (const file of files) {
|
|
39
44
|
try {
|
|
40
|
-
const abs =
|
|
45
|
+
const abs = normalize(file);
|
|
41
46
|
const raw = await fs.readFile(abs, "utf-8");
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
if (raw.length > 2_000_000) continue;
|
|
49
|
+
|
|
50
|
+
const parsed = processFile(abs, raw);
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
cache.set(abs, parsed);
|
|
53
|
+
toScan.add(abs);
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
watchedDeps.add(path.resolve(dep));
|
|
55
|
+
for (const dep of parsed.deps) {
|
|
56
|
+
toScan.add(normalize(dep));
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
} catch {
|
|
@@ -55,6 +61,44 @@ export async function watch() {
|
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
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
|
+
}
|
|
98
|
+
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
|
|
58
102
|
config.events = engine.getAllEvents().sort();
|
|
59
103
|
await saveConfig(config);
|
|
60
104
|
|
|
@@ -64,15 +108,11 @@ export async function watch() {
|
|
|
64
108
|
|
|
65
109
|
// QUEUE
|
|
66
110
|
let locked = false;
|
|
67
|
-
|
|
111
|
+
const queued = new Map<string, QueueItem>();
|
|
68
112
|
let timer: NodeJS.Timeout | null = null;
|
|
69
113
|
|
|
70
114
|
const run = async () => {
|
|
71
|
-
if (locked)
|
|
72
|
-
timer = setTimeout(run, 100);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
115
|
+
if (locked) return;
|
|
76
116
|
locked = true;
|
|
77
117
|
|
|
78
118
|
const batch = [...queued.values()];
|
|
@@ -82,26 +122,47 @@ export async function watch() {
|
|
|
82
122
|
try {
|
|
83
123
|
const raw = await fs.readFile(item.real, "utf-8");
|
|
84
124
|
|
|
85
|
-
|
|
125
|
+
if (raw.length > 2_000_000) continue;
|
|
126
|
+
|
|
127
|
+
const parsed = processFile(item.real, raw);
|
|
128
|
+
cache.set(item.real, parsed);
|
|
86
129
|
|
|
87
|
-
engine.updateFile(item.virtual, content, config);
|
|
130
|
+
engine.updateFile(item.virtual, parsed.content, config);
|
|
88
131
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
const absDep = path.resolve(dep);
|
|
132
|
+
// DEPENDENCY DIFF
|
|
133
|
+
const nextDeps = new Set<string>();
|
|
92
134
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
135
|
+
for (const dep of parsed.deps) {
|
|
136
|
+
nextDeps.add(normalize(dep));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// add new deps
|
|
140
|
+
for (const dep of nextDeps) {
|
|
141
|
+
if (!watchedDeps.has(dep)) {
|
|
142
|
+
watcher.add(dep);
|
|
143
|
+
watchedDeps.add(dep);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// remove old deps
|
|
148
|
+
for (const dep of [...watchedDeps]) {
|
|
149
|
+
if (!nextDeps.has(dep)) {
|
|
150
|
+
watcher.unwatch(dep);
|
|
151
|
+
watchedDeps.delete(dep);
|
|
96
152
|
}
|
|
153
|
+
}
|
|
97
154
|
|
|
155
|
+
// preload deps
|
|
156
|
+
for (const dep of nextDeps) {
|
|
98
157
|
try {
|
|
99
|
-
const
|
|
100
|
-
const
|
|
158
|
+
const raw = await fs.readFile(dep, "utf-8");
|
|
159
|
+
const parsedDep = processFile(dep, raw);
|
|
101
160
|
|
|
102
|
-
|
|
161
|
+
cache.set(dep, parsedDep);
|
|
103
162
|
|
|
104
|
-
|
|
163
|
+
const virtual = getVirtualFile(dep);
|
|
164
|
+
|
|
165
|
+
engine.updateFile(virtual, parsedDep.content, config);
|
|
105
166
|
} catch {}
|
|
106
167
|
}
|
|
107
168
|
|
|
@@ -110,7 +171,7 @@ export async function watch() {
|
|
|
110
171
|
}
|
|
111
172
|
}
|
|
112
173
|
|
|
113
|
-
// EVENTS
|
|
174
|
+
// DIFF EVENTS
|
|
114
175
|
const next = engine.getAllEvents();
|
|
115
176
|
|
|
116
177
|
const added = next.filter(e => !config.events.includes(e));
|
|
@@ -133,26 +194,29 @@ export async function watch() {
|
|
|
133
194
|
|
|
134
195
|
// SCHEDULER
|
|
135
196
|
const schedule = (file: string) => {
|
|
136
|
-
const abs =
|
|
197
|
+
const abs = normalize(file);
|
|
137
198
|
const virtual = getVirtualFile(abs);
|
|
138
199
|
|
|
200
|
+
console.log(chalk.gray(`change: ${file}`));
|
|
201
|
+
|
|
139
202
|
queued.set(abs, { real: abs, virtual });
|
|
140
203
|
|
|
141
204
|
if (timer) clearTimeout(timer);
|
|
142
|
-
timer = setTimeout(run,
|
|
205
|
+
timer = setTimeout(run, 200);
|
|
143
206
|
};
|
|
144
207
|
|
|
145
208
|
// WATCHER
|
|
146
209
|
const watcher = chokidar.watch(config.sync.include, {
|
|
147
210
|
ignored: config.sync.exclude,
|
|
148
211
|
ignoreInitial: true,
|
|
212
|
+
persistent: true,
|
|
149
213
|
});
|
|
150
214
|
|
|
151
215
|
watcher
|
|
152
216
|
.on("add", schedule)
|
|
153
217
|
.on("change", schedule)
|
|
154
218
|
.on("unlink", async (file) => {
|
|
155
|
-
const abs =
|
|
219
|
+
const abs = normalize(file);
|
|
156
220
|
|
|
157
221
|
console.log(chalk.gray(`remove: ${file}`));
|
|
158
222
|
|
|
@@ -160,6 +224,7 @@ export async function watch() {
|
|
|
160
224
|
|
|
161
225
|
engine.removeFile(virtual, config);
|
|
162
226
|
|
|
227
|
+
watcher.unwatch(abs);
|
|
163
228
|
watchedDeps.delete(abs);
|
|
164
229
|
|
|
165
230
|
const next = engine.getAllEvents();
|
|
@@ -170,9 +235,12 @@ export async function watch() {
|
|
|
170
235
|
console.log(
|
|
171
236
|
chalk.gray(`Total: ${config.events.length} events\n`)
|
|
172
237
|
);
|
|
238
|
+
})
|
|
239
|
+
.on("error", (err) => {
|
|
240
|
+
console.error(chalk.red("Watcher error:"), err);
|
|
173
241
|
});
|
|
174
242
|
|
|
175
|
-
// WATCH DEPS
|
|
243
|
+
// WATCH DEPS INIT
|
|
176
244
|
for (const dep of watchedDeps) {
|
|
177
245
|
watcher.add(dep);
|
|
178
246
|
}
|
|
@@ -29,18 +29,48 @@ function resolveFromSymbol(
|
|
|
29
29
|
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// re-export support
|
|
33
|
+
if ((symbol as any).exports) {
|
|
34
|
+
for (const exp of (symbol as any).exports.values()) {
|
|
35
|
+
const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
|
|
36
|
+
if (resolved) return resolved;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
const decls = symbol.getDeclarations() ?? [];
|
|
33
41
|
|
|
34
42
|
for (const decl of decls) {
|
|
43
|
+
// direct function
|
|
35
44
|
if (isFunctionLike(decl)) {
|
|
36
45
|
return decl;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
//
|
|
48
|
+
// VARIABLE → FUNCTION
|
|
49
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
50
|
+
if (isFunctionLike(decl.initializer)) {
|
|
51
|
+
return decl.initializer;
|
|
52
|
+
}
|
|
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
|
+
// MethodSignature (interface / type)
|
|
40
67
|
if (ts.isMethodSignature(decl)) {
|
|
41
68
|
const parent = decl.parent;
|
|
42
69
|
|
|
43
|
-
if (
|
|
70
|
+
if (
|
|
71
|
+
ts.isInterfaceDeclaration(parent) ||
|
|
72
|
+
ts.isTypeLiteralNode(parent)
|
|
73
|
+
) {
|
|
44
74
|
const name = decl.name.getText();
|
|
45
75
|
|
|
46
76
|
const type = checker.getTypeAtLocation(parent);
|
|
@@ -49,16 +79,26 @@ function resolveFromSymbol(
|
|
|
49
79
|
for (const prop of props) {
|
|
50
80
|
if (prop.name !== name) continue;
|
|
51
81
|
|
|
52
|
-
const resolved = resolveFromSymbol(
|
|
82
|
+
const resolved = resolveFromSymbol(
|
|
83
|
+
prop,
|
|
84
|
+
checker,
|
|
85
|
+
visited,
|
|
86
|
+
depth + 1
|
|
87
|
+
);
|
|
53
88
|
if (resolved) return resolved;
|
|
54
89
|
}
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
92
|
|
|
58
|
-
//
|
|
93
|
+
// fallback recursion
|
|
59
94
|
const s = checker.getSymbolAtLocation(decl);
|
|
60
95
|
if (s && s !== symbol) {
|
|
61
|
-
const resolved = resolveFromSymbol(
|
|
96
|
+
const resolved = resolveFromSymbol(
|
|
97
|
+
s,
|
|
98
|
+
checker,
|
|
99
|
+
visited,
|
|
100
|
+
depth + 1
|
|
101
|
+
);
|
|
62
102
|
if (resolved) return resolved;
|
|
63
103
|
}
|
|
64
104
|
}
|
|
@@ -70,6 +110,32 @@ export function resolveFunctionFromCall(
|
|
|
70
110
|
expr: ts.Expression,
|
|
71
111
|
checker: ts.TypeChecker
|
|
72
112
|
): ts.FunctionLikeDeclaration | null {
|
|
113
|
+
|
|
114
|
+
// tracker.track
|
|
115
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
116
|
+
let symbol = checker.getSymbolAtLocation(expr.name);
|
|
117
|
+
|
|
118
|
+
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
119
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (symbol) {
|
|
123
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
124
|
+
if (resolved) return resolved;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// tracker["track"]
|
|
129
|
+
if (ts.isElementAccessExpression(expr)) {
|
|
130
|
+
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
131
|
+
|
|
132
|
+
if (symbol) {
|
|
133
|
+
const resolved = resolveFromSymbol(symbol, checker);
|
|
134
|
+
if (resolved) return resolved;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// normal call signatures
|
|
73
139
|
const type = checker.getTypeAtLocation(expr);
|
|
74
140
|
const signatures = type.getCallSignatures();
|
|
75
141
|
|
|
@@ -81,7 +147,6 @@ export function resolveFunctionFromCall(
|
|
|
81
147
|
return decl;
|
|
82
148
|
}
|
|
83
149
|
|
|
84
|
-
// interface method
|
|
85
150
|
if (ts.isMethodSignature(decl)) {
|
|
86
151
|
const symbol = checker.getSymbolAtLocation(decl.name);
|
|
87
152
|
|
|
@@ -92,5 +157,11 @@ export function resolveFunctionFromCall(
|
|
|
92
157
|
}
|
|
93
158
|
}
|
|
94
159
|
|
|
160
|
+
// fallback
|
|
161
|
+
const symbol = checker.getSymbolAtLocation(expr);
|
|
162
|
+
if (symbol) {
|
|
163
|
+
return resolveFromSymbol(symbol, checker);
|
|
164
|
+
}
|
|
165
|
+
|
|
95
166
|
return null;
|
|
96
167
|
}
|