@eventra_dev/eventra-cli 0.1.32 → 0.1.34
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/ARCHITECTURE.md +88 -0
- package/dist/engine/callResolver.js +32 -131
- package/dist/engine/languageService.js +7 -38
- package/dist/engine/resolver.js +88 -77
- package/dist/engine/scanner.js +123 -201
- package/package.json +1 -1
- package/src/engine/callResolver.ts +24 -159
- package/src/engine/languageService.ts +22 -52
- package/src/engine/resolver.ts +99 -120
- package/src/engine/scanner.ts +128 -262
package/dist/engine/scanner.js
CHANGED
|
@@ -7,23 +7,13 @@ exports.scanSource = scanSource;
|
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
8
|
const resolver_1 = require("./resolver");
|
|
9
9
|
const callResolver_1 = require("./callResolver");
|
|
10
|
-
const functionAnalyzer_1 = require("./functionAnalyzer");
|
|
11
10
|
const exportResolver_1 = require("./exportResolver");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return "";
|
|
19
|
-
}
|
|
20
|
-
function getJsxTagName(tag) {
|
|
21
|
-
if (typescript_1.default.isIdentifier(tag))
|
|
22
|
-
return tag.text;
|
|
23
|
-
if (typescript_1.default.isPropertyAccessExpression(tag)) {
|
|
24
|
-
return tag.name.text;
|
|
25
|
-
}
|
|
26
|
-
return "";
|
|
11
|
+
function isValidEvent(v) {
|
|
12
|
+
return (v &&
|
|
13
|
+
v.length > 1 &&
|
|
14
|
+
v.length < 120 &&
|
|
15
|
+
!v.includes(" ") &&
|
|
16
|
+
/^[a-zA-Z0-9._:-]+$/.test(v));
|
|
27
17
|
}
|
|
28
18
|
function getCallName(expr) {
|
|
29
19
|
if (typescript_1.default.isIdentifier(expr))
|
|
@@ -32,163 +22,123 @@ function getCallName(expr) {
|
|
|
32
22
|
return expr.name.text;
|
|
33
23
|
return "";
|
|
34
24
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
function getJsxTagName(tag) {
|
|
26
|
+
if (typescript_1.default.isIdentifier(tag))
|
|
27
|
+
return tag.text;
|
|
28
|
+
if (typescript_1.default.isPropertyAccessExpression(tag))
|
|
29
|
+
return tag.name.text;
|
|
30
|
+
return tag.getText();
|
|
41
31
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
32
|
+
function getJsxAttrName(name) {
|
|
33
|
+
if (typescript_1.default.isIdentifier(name))
|
|
34
|
+
return name.text;
|
|
35
|
+
if (typescript_1.default.isJsxNamespacedName(name))
|
|
36
|
+
return name.name.text;
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap, wrapperCache = new Map()) {
|
|
40
|
+
const events = new Set();
|
|
41
|
+
const detectedFunctionWrappers = new Set();
|
|
42
|
+
const detectedComponentWrappers = new Map();
|
|
43
|
+
if (visited.has(source.fileName)) {
|
|
44
|
+
return { events, detectedFunctionWrappers, detectedComponentWrappers };
|
|
45
|
+
}
|
|
46
|
+
visited.add(source.fileName);
|
|
47
|
+
function extractTrackCall(node, paramMap) {
|
|
48
|
+
const expr = node.expression;
|
|
49
|
+
if (typescript_1.default.isPropertyAccessExpression(expr) && expr.name.text === "track") {
|
|
50
|
+
const arg = node.arguments[0];
|
|
51
|
+
if (!arg)
|
|
52
|
+
return;
|
|
53
|
+
const res = (0, resolver_1.resolveNodeValue)(arg, checker, paramMap);
|
|
54
|
+
for (const v of res.values) {
|
|
55
|
+
if (isValidEvent(v))
|
|
56
|
+
events.add(v);
|
|
57
|
+
}
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
function isWrapperFunction(fn) {
|
|
61
|
+
let found = false;
|
|
62
|
+
function walk(n) {
|
|
63
|
+
if (found)
|
|
64
|
+
return;
|
|
65
|
+
if (typescript_1.default.isCallExpression(n)) {
|
|
66
|
+
const expr = n.expression;
|
|
67
|
+
if (typescript_1.default.isPropertyAccessExpression(expr) && expr.name.text === "track") {
|
|
68
|
+
found = true;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
typescript_1.default.forEachChild(n, walk);
|
|
73
|
+
}
|
|
74
|
+
if (fn.body)
|
|
75
|
+
walk(fn.body);
|
|
76
|
+
return found;
|
|
60
77
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!symbol)
|
|
78
|
+
function detectComponentWrapper(tag) {
|
|
79
|
+
const tagName = getJsxTagName(tag);
|
|
80
|
+
if (wrapperCache.has(tagName))
|
|
81
|
+
return wrapperCache.get(tagName);
|
|
82
|
+
let symbol = checker.getSymbolAtLocation(tag);
|
|
83
|
+
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
84
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
85
|
+
}
|
|
86
|
+
if (!symbol) {
|
|
87
|
+
wrapperCache.set(tagName, null);
|
|
72
88
|
return null;
|
|
89
|
+
}
|
|
90
|
+
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
73
91
|
for (const decl of symbol.getDeclarations() ?? []) {
|
|
74
|
-
|
|
75
|
-
if (typescript_1.default.
|
|
76
|
-
decl
|
|
77
|
-
typescript_1.default.isPropertyAccessExpression(decl.initializer)) {
|
|
78
|
-
return decl.initializer.name.text;
|
|
79
|
-
}
|
|
80
|
-
// const e = event
|
|
92
|
+
let fn = null;
|
|
93
|
+
if (typescript_1.default.isFunctionDeclaration(decl))
|
|
94
|
+
fn = decl;
|
|
81
95
|
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
82
96
|
decl.initializer &&
|
|
83
|
-
typescript_1.default.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return inner;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
function visit(node) {
|
|
92
|
-
if (result)
|
|
93
|
-
return;
|
|
94
|
-
if (typescript_1.default.isCallExpression(node)) {
|
|
95
|
-
const expr = node.expression;
|
|
96
|
-
const isTrack = (typescript_1.default.isIdentifier(expr) && expr.text === "track") ||
|
|
97
|
-
(typescript_1.default.isPropertyAccessExpression(expr) &&
|
|
98
|
-
expr.name.text === "track");
|
|
99
|
-
if (!isTrack) {
|
|
100
|
-
typescript_1.default.forEachChild(node, visit);
|
|
101
|
-
return;
|
|
97
|
+
(typescript_1.default.isArrowFunction(decl.initializer) ||
|
|
98
|
+
typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
99
|
+
fn = decl.initializer;
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
while (typescript_1.default.isPropertyAccessExpression(current)) {
|
|
116
|
-
result = current.name.text;
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// track(fn())
|
|
121
|
-
if (typescript_1.default.isCallExpression(arg)) {
|
|
122
|
-
const inner = (0, callResolver_1.resolveFunctionFromCall)(arg.expression, checker);
|
|
123
|
-
if (inner?.body) {
|
|
124
|
-
visit(inner.body);
|
|
125
|
-
if (result)
|
|
101
|
+
if (!fn || !fn.body)
|
|
102
|
+
continue;
|
|
103
|
+
let prop = null;
|
|
104
|
+
function walk(n) {
|
|
105
|
+
if (prop)
|
|
106
|
+
return;
|
|
107
|
+
if (typescript_1.default.isCallExpression(n)) {
|
|
108
|
+
const expr = n.expression;
|
|
109
|
+
if (typescript_1.default.isPropertyAccessExpression(expr) && expr.name.text === "track") {
|
|
110
|
+
const arg = n.arguments[0];
|
|
111
|
+
if (typescript_1.default.isPropertyAccessExpression(arg)) {
|
|
112
|
+
prop = arg.name.text;
|
|
126
113
|
return;
|
|
114
|
+
}
|
|
127
115
|
}
|
|
128
116
|
}
|
|
117
|
+
typescript_1.default.forEachChild(n, walk);
|
|
118
|
+
}
|
|
119
|
+
walk(fn.body);
|
|
120
|
+
wrapperCache.set(tagName, prop);
|
|
121
|
+
if (prop) {
|
|
122
|
+
detectedComponentWrappers.set(tagName, prop);
|
|
123
|
+
return prop;
|
|
129
124
|
}
|
|
130
125
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (fn.body)
|
|
134
|
-
visit(fn.body);
|
|
135
|
-
return result;
|
|
136
|
-
}
|
|
137
|
-
// MAIN SCANNER
|
|
138
|
-
function scanSource(source, checker, config, visited = new Set(), inheritedParamMap, wrapperCache = new Map()) {
|
|
139
|
-
const events = new Set();
|
|
140
|
-
const detectedFunctionWrappers = new Set();
|
|
141
|
-
const detectedComponentWrappers = new Map();
|
|
142
|
-
if (visited.has(source.fileName) || visited.size > 200) {
|
|
143
|
-
return { events, detectedFunctionWrappers, detectedComponentWrappers };
|
|
126
|
+
wrapperCache.set(tagName, null);
|
|
127
|
+
return null;
|
|
144
128
|
}
|
|
145
|
-
visited.add(source.fileName);
|
|
146
129
|
function visit(node) {
|
|
147
|
-
// JSX
|
|
130
|
+
// JSX
|
|
148
131
|
if (typescript_1.default.isJsxSelfClosingElement(node) || typescript_1.default.isJsxOpeningElement(node)) {
|
|
149
132
|
const tagName = getJsxTagName(node.tagName);
|
|
150
|
-
// skip html
|
|
151
133
|
if (tagName[0] === tagName[0].toLowerCase()) {
|
|
152
134
|
typescript_1.default.forEachChild(node, visit);
|
|
153
135
|
return;
|
|
154
136
|
}
|
|
155
137
|
let wrapper = config.wrappers.find(w => w.name === tagName);
|
|
156
|
-
// AUTO DETECT
|
|
157
138
|
if (!wrapper) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
wrapper = { name: tagName, prop: cached };
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
let symbol = checker.getSymbolAtLocation(node.tagName);
|
|
165
|
-
if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
|
|
166
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
167
|
-
}
|
|
168
|
-
if (symbol) {
|
|
169
|
-
symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker) ?? symbol;
|
|
170
|
-
for (const decl of symbol.getDeclarations() ?? []) {
|
|
171
|
-
let fn = null;
|
|
172
|
-
if (typescript_1.default.isFunctionDeclaration(decl))
|
|
173
|
-
fn = decl;
|
|
174
|
-
if (typescript_1.default.isVariableDeclaration(decl) &&
|
|
175
|
-
decl.initializer &&
|
|
176
|
-
(typescript_1.default.isArrowFunction(decl.initializer) ||
|
|
177
|
-
typescript_1.default.isFunctionExpression(decl.initializer))) {
|
|
178
|
-
fn = decl.initializer;
|
|
179
|
-
}
|
|
180
|
-
if (!fn)
|
|
181
|
-
continue;
|
|
182
|
-
const prop = findTrackedPropName(fn, checker);
|
|
183
|
-
wrapperCache.set(tagName, prop ?? null);
|
|
184
|
-
if (prop) {
|
|
185
|
-
wrapper = { name: tagName, prop };
|
|
186
|
-
detectedComponentWrappers.set(tagName, prop);
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
139
|
+
const prop = detectComponentWrapper(node.tagName);
|
|
140
|
+
if (prop)
|
|
141
|
+
wrapper = { name: tagName, prop };
|
|
192
142
|
}
|
|
193
143
|
if (wrapper) {
|
|
194
144
|
for (const attr of node.attributes.properties) {
|
|
@@ -200,15 +150,15 @@ function scanSource(source, checker, config, visited = new Set(), inheritedParam
|
|
|
200
150
|
if (!attr.initializer)
|
|
201
151
|
continue;
|
|
202
152
|
if (typescript_1.default.isStringLiteral(attr.initializer)) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
153
|
+
if (isValidEvent(attr.initializer.text)) {
|
|
154
|
+
events.add(attr.initializer.text);
|
|
155
|
+
}
|
|
206
156
|
}
|
|
207
157
|
if (typescript_1.default.isJsxExpression(attr.initializer)) {
|
|
208
158
|
const expr = attr.initializer.expression;
|
|
209
159
|
if (!expr)
|
|
210
160
|
continue;
|
|
211
|
-
const res = (0, resolver_1.resolveNodeValue)(expr, checker
|
|
161
|
+
const res = (0, resolver_1.resolveNodeValue)(expr, checker);
|
|
212
162
|
res.values.forEach(v => {
|
|
213
163
|
if (isValidEvent(v))
|
|
214
164
|
events.add(v);
|
|
@@ -219,65 +169,37 @@ function scanSource(source, checker, config, visited = new Set(), inheritedParam
|
|
|
219
169
|
typescript_1.default.forEachChild(node, visit);
|
|
220
170
|
return;
|
|
221
171
|
}
|
|
222
|
-
//
|
|
172
|
+
// CALL
|
|
223
173
|
if (typescript_1.default.isCallExpression(node)) {
|
|
224
|
-
let trackParamIndex = null;
|
|
225
|
-
let isTracking = false;
|
|
226
|
-
const callName = getCallName(node.expression);
|
|
227
|
-
// direct .track()
|
|
228
|
-
if (typescript_1.default.isPropertyAccessExpression(node.expression) &&
|
|
229
|
-
node.expression.name.text === "track") {
|
|
230
|
-
isTracking = true;
|
|
231
|
-
trackParamIndex = 0;
|
|
232
|
-
}
|
|
233
|
-
// config wrapper
|
|
234
|
-
const manualWrapper = config.functionWrappers.find(w => w.name === callName);
|
|
235
|
-
if (manualWrapper) {
|
|
236
|
-
isTracking = true;
|
|
237
|
-
trackParamIndex = 0;
|
|
238
|
-
}
|
|
239
|
-
// resolve function → check body
|
|
240
174
|
const resolvedFn = (0, callResolver_1.resolveFunctionFromCall)(node.expression, checker);
|
|
241
|
-
if (resolvedFn) {
|
|
242
|
-
const idx = (0, functionAnalyzer_1.findTrackParamIndex)(resolvedFn);
|
|
243
|
-
if (idx !== null) {
|
|
244
|
-
isTracking = true;
|
|
245
|
-
trackParamIndex = idx;
|
|
246
|
-
if (!manualWrapper && callName !== "track") {
|
|
247
|
-
detectedFunctionWrappers.add(callName);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (!isTracking) {
|
|
252
|
-
typescript_1.default.forEachChild(node, visit);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
175
|
const paramMap = new Map(inheritedParamMap ?? []);
|
|
256
|
-
if (resolvedFn
|
|
257
|
-
resolvedFn.parameters.forEach((
|
|
176
|
+
if (resolvedFn) {
|
|
177
|
+
resolvedFn.parameters.forEach((p, i) => {
|
|
258
178
|
const arg = node.arguments[i];
|
|
259
179
|
if (!arg)
|
|
260
180
|
return;
|
|
261
|
-
if (typescript_1.default.isIdentifier(
|
|
262
|
-
paramMap.set(
|
|
181
|
+
if (typescript_1.default.isIdentifier(p.name)) {
|
|
182
|
+
paramMap.set(p.name.text, arg);
|
|
263
183
|
}
|
|
264
184
|
});
|
|
265
185
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (!visited.has(sf.fileName)) {
|
|
277
|
-
const res = scanSource(sf, checker, config, visited, paramMap, wrapperCache);
|
|
278
|
-
res.events.forEach(e => events.add(e));
|
|
186
|
+
// direct track
|
|
187
|
+
extractTrackCall(node, paramMap);
|
|
188
|
+
// wrapper
|
|
189
|
+
if (resolvedFn && isWrapperFunction(resolvedFn)) {
|
|
190
|
+
const name = getCallName(node.expression);
|
|
191
|
+
if (name && name !== "track") {
|
|
192
|
+
detectedFunctionWrappers.add(name);
|
|
193
|
+
}
|
|
194
|
+
if (resolvedFn.body) {
|
|
195
|
+
visit(resolvedFn.body);
|
|
279
196
|
}
|
|
280
197
|
}
|
|
198
|
+
for (const arg of node.arguments) {
|
|
199
|
+
visit(arg);
|
|
200
|
+
}
|
|
201
|
+
visit(node.expression);
|
|
202
|
+
return;
|
|
281
203
|
}
|
|
282
204
|
typescript_1.default.forEachChild(node, visit);
|
|
283
205
|
}
|
package/package.json
CHANGED
|
@@ -1,200 +1,65 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
function isFunctionLike(
|
|
5
|
-
node: ts.Node
|
|
6
|
-
): node is ts.FunctionLikeDeclaration {
|
|
3
|
+
function isFn(node: ts.Node): node is ts.FunctionLikeDeclaration {
|
|
7
4
|
return (
|
|
8
5
|
ts.isFunctionDeclaration(node) ||
|
|
9
6
|
ts.isMethodDeclaration(node) ||
|
|
10
|
-
ts.isConstructorDeclaration(node) ||
|
|
11
7
|
ts.isFunctionExpression(node) ||
|
|
12
8
|
ts.isArrowFunction(node)
|
|
13
9
|
);
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
// symbol → function
|
|
17
12
|
function resolveFromSymbol(
|
|
18
13
|
symbol: ts.Symbol,
|
|
19
14
|
checker: ts.TypeChecker,
|
|
20
|
-
|
|
21
|
-
depth = 0
|
|
15
|
+
seen = new Set<ts.Symbol>()
|
|
22
16
|
): ts.FunctionLikeDeclaration | null {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
if (depth > 10) return null;
|
|
17
|
+
if (seen.has(symbol)) return null;
|
|
18
|
+
seen.add(symbol);
|
|
26
19
|
|
|
27
|
-
visited.add(symbol);
|
|
28
|
-
|
|
29
|
-
// Alias unwrap (import/export)
|
|
30
20
|
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
31
|
-
|
|
32
|
-
const aliased = checker.getAliasedSymbol(symbol);
|
|
33
|
-
return resolveFromSymbol(aliased, checker, visited, depth + 1);
|
|
34
|
-
} catch {}
|
|
21
|
+
return resolveFromSymbol(checker.getAliasedSymbol(symbol), checker, seen);
|
|
35
22
|
}
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
for (const d of symbol.getDeclarations() ?? []) {
|
|
25
|
+
if (isFn(d)) return d;
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* direct function
|
|
43
|
-
*/
|
|
44
|
-
if (isFunctionLike(decl)) {
|
|
45
|
-
return decl;
|
|
46
|
-
}
|
|
27
|
+
if (ts.isVariableDeclaration(d) && d.initializer) {
|
|
28
|
+
if (isFn(d.initializer)) return d.initializer;
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return
|
|
30
|
+
const s = checker.getSymbolAtLocation(d.initializer);
|
|
31
|
+
if (s) {
|
|
32
|
+
const r = resolveFromSymbol(s, checker, seen);
|
|
33
|
+
if (r) return r;
|
|
52
34
|
}
|
|
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
|
-
// CLASS METHOD
|
|
67
|
-
if (ts.isMethodDeclaration(decl)) {
|
|
68
|
-
return decl;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// interface / type method
|
|
72
|
-
if (ts.isMethodSignature(decl)) {
|
|
73
|
-
const parent = decl.parent;
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
ts.isInterfaceDeclaration(parent) ||
|
|
77
|
-
ts.isTypeLiteralNode(parent)
|
|
78
|
-
) {
|
|
79
|
-
const type = checker.getTypeAtLocation(parent);
|
|
80
|
-
const props = type.getProperties();
|
|
81
|
-
|
|
82
|
-
for (const prop of props) {
|
|
83
|
-
const resolved = resolveFromSymbol(
|
|
84
|
-
prop,
|
|
85
|
-
checker,
|
|
86
|
-
visited,
|
|
87
|
-
depth + 1
|
|
88
|
-
);
|
|
89
|
-
if (resolved) return resolved;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// fallback recursion
|
|
95
|
-
const inner = checker.getSymbolAtLocation(decl);
|
|
96
|
-
if (inner && inner !== symbol) {
|
|
97
|
-
const resolved = resolveFromSymbol(
|
|
98
|
-
inner,
|
|
99
|
-
checker,
|
|
100
|
-
visited,
|
|
101
|
-
depth + 1
|
|
102
|
-
);
|
|
103
|
-
if (resolved) return resolved;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Exports (re-export)
|
|
108
|
-
const exports = (symbol as any).exports;
|
|
109
|
-
if (exports) {
|
|
110
|
-
for (const exp of exports.values()) {
|
|
111
|
-
const resolved = resolveFromSymbol(
|
|
112
|
-
exp,
|
|
113
|
-
checker,
|
|
114
|
-
visited,
|
|
115
|
-
depth + 1
|
|
116
|
-
);
|
|
117
|
-
if (resolved) return resolved;
|
|
118
35
|
}
|
|
119
36
|
}
|
|
120
37
|
|
|
121
38
|
return null;
|
|
122
39
|
}
|
|
123
40
|
|
|
124
|
-
// MAIN FUNCTION
|
|
125
41
|
export function resolveFunctionFromCall(
|
|
126
42
|
expr: ts.Expression,
|
|
127
43
|
checker: ts.TypeChecker
|
|
128
44
|
): ts.FunctionLikeDeclaration | null {
|
|
129
|
-
//
|
|
45
|
+
// foo.bar()
|
|
130
46
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
136
|
-
} catch {}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (symbol) {
|
|
140
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
141
|
-
if (resolved) return resolved;
|
|
47
|
+
const s = checker.getSymbolAtLocation(expr.name);
|
|
48
|
+
if (s) {
|
|
49
|
+
const r = resolveFromSymbol(s, checker);
|
|
50
|
+
if (r) return r;
|
|
142
51
|
}
|
|
143
52
|
}
|
|
144
53
|
|
|
145
|
-
//
|
|
146
|
-
if (ts.isElementAccessExpression(expr)) {
|
|
147
|
-
const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
|
|
148
|
-
|
|
149
|
-
if (symbol) {
|
|
150
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
151
|
-
if (resolved) return resolved;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Call signatures
|
|
54
|
+
// direct call
|
|
156
55
|
const type = checker.getTypeAtLocation(expr);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const decl = sig.getDeclaration();
|
|
161
|
-
if (!decl) continue;
|
|
162
|
-
|
|
163
|
-
if (isFunctionLike(decl)) {
|
|
164
|
-
return decl;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (ts.isMethodSignature(decl)) {
|
|
168
|
-
const symbol = checker.getSymbolAtLocation(decl.name);
|
|
169
|
-
|
|
170
|
-
if (symbol) {
|
|
171
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
172
|
-
if (resolved) return resolved;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
56
|
+
for (const sig of type.getCallSignatures()) {
|
|
57
|
+
const d = sig.getDeclaration();
|
|
58
|
+
if (d && isFn(d)) return d;
|
|
175
59
|
}
|
|
176
60
|
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
let symbol = checker.getSymbolAtLocation(expr);
|
|
180
|
-
|
|
181
|
-
if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
|
|
182
|
-
try {
|
|
183
|
-
symbol = checker.getAliasedSymbol(symbol);
|
|
184
|
-
} catch {}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (symbol) {
|
|
188
|
-
const resolved = resolveFromSymbol(symbol, checker);
|
|
189
|
-
if (resolved) return resolved;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// LAST fallback
|
|
194
|
-
const symbol = checker.getSymbolAtLocation(expr);
|
|
195
|
-
if (symbol) {
|
|
196
|
-
return resolveFromSymbol(symbol, checker);
|
|
197
|
-
}
|
|
61
|
+
const s = checker.getSymbolAtLocation(expr);
|
|
62
|
+
if (s) return resolveFromSymbol(s, checker);
|
|
198
63
|
|
|
199
64
|
return null;
|
|
200
65
|
}
|