@eventra_dev/eventra-cli 0.2.4 → 0.3.2

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.
Files changed (89) hide show
  1. package/README.md +143 -32
  2. package/dist/analysis/cache/evaluationCache.js +21 -0
  3. package/dist/analysis/cache/resolvedCallCache.js +21 -0
  4. package/dist/analysis/cache/resolvedExportCache.js +21 -0
  5. package/dist/analysis/cache/semanticDependencyGraph.js +25 -0
  6. package/dist/analysis/cache/symbolInvalidation.js +20 -0
  7. package/dist/analysis/extractor/extractor.js +47 -0
  8. package/dist/analysis/extractor/propagationExtractor.js +52 -0
  9. package/dist/analysis/pipeline/semanticPass.js +2 -0
  10. package/dist/analysis/resolver/callResolver.js +125 -0
  11. package/dist/analysis/resolver/exportResolver.js +75 -0
  12. package/dist/analysis/resolver/resolver.js +133 -0
  13. package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +193 -0
  14. package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +105 -0
  15. package/dist/analysis/scanner/scanner.js +117 -0
  16. package/dist/analysis/scanner/sinkDetector.js +91 -0
  17. package/dist/analysis/shared/evaluationContext.js +9 -0
  18. package/dist/analysis/shared/propagation.js +3 -0
  19. package/dist/analysis/shared/returnPropagation.js +2 -0
  20. package/dist/analysis/shared/types.js +2 -0
  21. package/dist/analysis/shared/utils.js +62 -0
  22. package/dist/analysis/symbols/symbolUtils.js +68 -0
  23. package/dist/analysis/symbols/wrapperRegistry.js +55 -0
  24. package/dist/cli/check.js +73 -0
  25. package/dist/{commands → cli}/init.js +1 -1
  26. package/dist/{commands → cli}/send.js +1 -1
  27. package/dist/cli/sync.js +56 -0
  28. package/dist/{commands → cli}/watch.js +41 -25
  29. package/dist/compiler/compilerContext.js +204 -0
  30. package/dist/compiler/documentRegistry.js +78 -0
  31. package/dist/compiler/importGraph.js +83 -0
  32. package/dist/compiler/scheduler.js +29 -0
  33. package/dist/core/EventraEngine.js +234 -0
  34. package/dist/core/projectScanner.js +73 -0
  35. package/dist/{utils → filesystem}/getVirtualFile.js +1 -3
  36. package/dist/filesystem/processFile.js +38 -0
  37. package/dist/index.js +5 -5
  38. package/dist/shared/buildSyntheticTsx.js +20 -0
  39. package/dist/shared/templateExpressionExtractor.js +22 -0
  40. package/package.json +32 -9
  41. package/.github/workflows/release.yml +0 -43
  42. package/ARCHITECTURE.md +0 -88
  43. package/assets/eventra-icon-animated.svg +0 -19
  44. package/dist/commands/check.js +0 -78
  45. package/dist/commands/sync.js +0 -79
  46. package/dist/engine/boundary.js +0 -14
  47. package/dist/engine/callResolver.js +0 -58
  48. package/dist/engine/engine.js +0 -154
  49. package/dist/engine/exportResolver.js +0 -46
  50. package/dist/engine/functionAnalyzer.js +0 -71
  51. package/dist/engine/graph.js +0 -168
  52. package/dist/engine/languageService.js +0 -152
  53. package/dist/engine/resolver.js +0 -149
  54. package/dist/engine/scanner.js +0 -230
  55. package/dist/utils/processFile.js +0 -28
  56. package/dist/utils/universal.js +0 -190
  57. package/src/commands/check.ts +0 -91
  58. package/src/commands/init.ts +0 -174
  59. package/src/commands/send.ts +0 -213
  60. package/src/commands/sync.ts +0 -106
  61. package/src/commands/watch.ts +0 -144
  62. package/src/engine/boundary.ts +0 -12
  63. package/src/engine/callResolver.ts +0 -65
  64. package/src/engine/engine.ts +0 -227
  65. package/src/engine/exportResolver.ts +0 -56
  66. package/src/engine/functionAnalyzer.ts +0 -81
  67. package/src/engine/graph.ts +0 -193
  68. package/src/engine/hash.ts +0 -5
  69. package/src/engine/languageService.ts +0 -197
  70. package/src/engine/resolver.ts +0 -177
  71. package/src/engine/scanner.ts +0 -298
  72. package/src/index.ts +0 -49
  73. package/src/types.ts +0 -35
  74. package/src/utils/config.ts +0 -53
  75. package/src/utils/getVirtualFile.ts +0 -5
  76. package/src/utils/processFile.ts +0 -29
  77. package/src/utils/universal.ts +0 -249
  78. package/tests/fixtures/backend/express/app.ts +0 -233
  79. package/tests/fixtures/backend/nest/service.ts +0 -70
  80. package/tests/fixtures/backend/node/index.ts +0 -63
  81. package/tests/fixtures/frontend/next/page.tsx +0 -101
  82. package/tests/fixtures/frontend/react/App.tsx +0 -104
  83. package/tests/fixtures/frontend/vue/App.vue +0 -83
  84. package/tests/fixtures/wrappers/component/test.tsx +0 -62
  85. package/tests/fixtures/wrappers/function/test.ts +0 -60
  86. package/tests/run.ts +0 -106
  87. package/tsconfig.json +0 -14
  88. /package/dist/{utils → config}/config.js +0 -0
  89. /package/dist/{engine → shared}/hash.js +0 -0
package/README.md CHANGED
@@ -9,15 +9,17 @@
9
9
  [![TypeScript](https://img.shields.io/badge/typescript-ready-blue.svg)](https://www.typescriptlang.org/)
10
10
  [![License](https://img.shields.io/npm/l/@eventra_dev/eventra-cli)]()
11
11
 
12
- Eventra CLI automatically discovers and tracks event usage across your codebase — even through abstractions like wrappers, variables, and cross-file calls.
12
+ Eventra CLI automatically discovers and tracks event usage across your codebase — even through abstractions like wrappers, propagation chains, variables, and cross-file calls.
13
13
 
14
14
  ---
15
15
 
16
16
  ## Overview
17
17
 
18
- Eventra scans your codebase using a TypeScript-powered engine and extracts event usage — even through abstractions like wrappers, variables, and cross-file calls.
18
+ Eventra scans your JavaScript and TypeScript codebase using a TypeScript-powered semantic engine and extracts analytics event usage statically.
19
19
 
20
- It requires no runtime instrumentation and fits naturally into CI/CD workflows.
20
+ It supports wrapper propagation, return propagation, cross-file resolution, and incremental dependency-aware analysis.
21
+
22
+ No runtime instrumentation required.
21
23
 
22
24
  ---
23
25
 
@@ -44,83 +46,135 @@ eventra send
44
46
 
45
47
  ## How it works
46
48
 
47
- 1. Parses your project using the TypeScript AST
48
- 2. Resolves function calls, imports, and dependencies
49
- 3. Tracks event propagation through variables and wrappers
50
- 4. Extracts event names and aggregates them
49
+ 1. Parses your project using the TypeScript compiler API
50
+ 2. Builds semantic indexes from AST nodes
51
+ 3. Resolves wrappers, imports, exports, and propagation chains
52
+ 4. Extracts concrete analytics events statically
53
+ 5. Incrementally re-analyzes only affected files
51
54
 
52
- No runtime required. No SDK needed.
55
+ No runtime SDK hooks.
56
+ No Babel transforms.
57
+ No instrumentation.
53
58
 
54
59
  ---
55
60
 
56
61
  ## Why Eventra
57
62
 
58
- - Prevent missing or inconsistent event tracking
59
- - Keep your analytics schema in sync with code
60
- - Detect dead or unused events
61
- - Integrate validation into CI pipelines
63
+ * Prevent missing or inconsistent event tracking
64
+ * Keep analytics schemas synchronized with source code
65
+ * Detect dead or unused events
66
+ * Analyze wrapper-heavy codebases statically
67
+ * Integrate validation into CI/CD pipelines
68
+ * Avoid runtime analytics overhead
62
69
 
63
70
  ---
64
71
 
65
72
  ## Commands
66
73
 
67
74
  ### `eventra init`
75
+
68
76
  Creates `eventra.json` configuration file.
69
77
 
70
78
  ---
71
79
 
72
80
  ### `eventra sync`
81
+
73
82
  Scans your project and updates the list of detected events.
74
83
 
75
84
  #### Supports
76
85
 
77
- - Direct calls
86
+ ##### Direct calls
87
+
78
88
  ```ts
79
89
  track("event")
80
90
  analytics.track("event")
81
91
  ```
82
92
 
83
- - Variables
93
+ ##### Variables
94
+
84
95
  ```ts
85
96
  const EVENT = "click"
86
97
  track(EVENT)
87
98
  ```
88
99
 
89
- - Enums
100
+ ##### Enums
101
+
90
102
  ```ts
91
103
  track(EVENTS.CLICK)
92
104
  ```
93
105
 
94
- - Template strings
106
+ ##### Template strings
107
+
95
108
  ```ts
96
109
  track(`click_${type}`)
97
110
  ```
98
111
 
99
- - Conditionals
112
+ ##### Conditional expressions
113
+
100
114
  ```ts
101
115
  track(condition ? "a" : "b")
102
116
  ```
103
117
 
104
- - Object payloads
118
+ ##### Arrays
119
+
105
120
  ```ts
106
- track({ event: "click" })
121
+ track(["a", "b"])
107
122
  ```
108
123
 
109
- - Function wrappers
124
+ ##### Object payloads
125
+
110
126
  ```ts
111
- const trackFeature = (name: string) => {
127
+ track({
128
+ event: "click"
129
+ })
130
+ ```
131
+
132
+ ##### Function wrappers
133
+
134
+ ```ts
135
+ function trackFeature(name: string) {
112
136
  track(name)
113
137
  }
114
138
 
115
139
  trackFeature("click")
116
140
  ```
117
141
 
118
- - Cross-file event resolution
119
- - Framework templates (Vue, Svelte, Astro)
142
+ ##### Wrapper propagation
143
+
144
+ ```ts
145
+ function trackEvent(props: {
146
+ event: string
147
+ }) {
148
+ track(props.event)
149
+ }
150
+
151
+ trackEvent({
152
+ event: "purchase"
153
+ })
154
+ ```
155
+
156
+ ##### Return propagation
157
+
158
+ ```ts
159
+ function buildEvent(name: string) {
160
+ return name
161
+ }
162
+
163
+ track(buildEvent("signup"))
164
+ ```
165
+
166
+ ##### Cross-file resolution
167
+
168
+ ```ts
169
+ import { trackFeature } from "./tracker"
170
+
171
+ trackFeature("purchase")
172
+ ```
120
173
 
121
174
  ---
122
175
 
123
176
  ### `eventra check`
177
+
124
178
  Validates detected events against config.
125
179
 
126
180
  ```bash
@@ -128,13 +182,15 @@ eventra check
128
182
  ```
129
183
 
130
184
  Detects:
131
- - New events
132
- - Removed events
133
- - Inconsistent tracking usage
185
+
186
+ * New events
187
+ * Removed events
188
+ * Invalid or inconsistent tracking usage
134
189
 
135
190
  ---
136
191
 
137
192
  ### `eventra check --fix`
193
+
138
194
  Automatically updates config to match detected events.
139
195
 
140
196
  ```bash
@@ -144,15 +200,19 @@ eventra check --fix
144
200
  ---
145
201
 
146
202
  ### `eventra watch`
147
- Runs in watch mode and updates events in real time.
203
+
204
+ Runs in watch mode with incremental semantic analysis.
148
205
 
149
206
  ```bash
150
207
  eventra watch
151
208
  ```
152
209
 
210
+ Only affected files are re-analyzed after changes.
211
+
153
212
  ---
154
213
 
155
214
  ### `eventra send`
215
+
156
216
  Sends events to Eventra backend.
157
217
 
158
218
  ```bash
@@ -171,25 +231,76 @@ eventra send
171
231
  "wrappers": [],
172
232
  "functionWrappers": [],
173
233
  "sync": {
174
- "include": ["**/*.{ts,tsx,js,jsx,vue,svelte,astro}"],
175
- "exclude": ["node_modules", "dist", ".next", ".git"]
234
+ "include": ["**/*.{ts,tsx,js,jsx}"],
235
+ "exclude": [
236
+ "node_modules",
237
+ "dist",
238
+ ".next",
239
+ ".git"
240
+ ]
176
241
  }
177
242
  }
178
243
  ```
179
244
 
180
245
  ---
181
246
 
247
+ ## Architecture Highlights
248
+
249
+ ### Semantic propagation analysis
250
+
251
+ Eventra resolves event propagation through wrappers and returned values.
252
+
253
+ ```ts
254
+ function createEvent(name: string) {
255
+ return name
256
+ }
257
+
258
+ function trackEvent(name: string) {
259
+ track(createEvent(name))
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ### Incremental analysis engine
266
+
267
+ Eventra does NOT rescan the whole project after every update.
268
+
269
+ Instead it:
270
+
271
+ 1. Updates changed files
272
+ 2. Invalidates affected caches
273
+ 3. Rebuilds TypeScript incrementally
274
+ 4. Traverses dependency graph
275
+ 5. Re-analyzes affected files only
276
+
277
+ ---
278
+
279
+ ### Static extraction
280
+
281
+ Eventra works entirely statically.
282
+
283
+ No runtime execution.
284
+ No monkey patching.
285
+ No SDK instrumentation.
286
+
287
+ ---
288
+
182
289
  ## Supported Environments
183
290
 
184
- Eventra works with **any JavaScript or TypeScript codebase**.
291
+ Eventra works with:
185
292
 
186
- No framework limitations.
293
+ * JavaScript
294
+ * TypeScript
295
+ * Node.js projects
296
+ * Browser applications
297
+ * Monorepos
187
298
 
188
299
  ---
189
300
 
190
301
  ## Requirements
191
302
 
192
- - Node.js 18+
303
+ * Node.js 18+
193
304
 
194
305
  ---
195
306
 
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // src/analysis/cache/evaluationCache.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.EvaluationCache = void 0;
5
+ class EvaluationCache {
6
+ cache = new WeakMap();
7
+ get(symbol) {
8
+ return this.cache.get(symbol);
9
+ }
10
+ set(symbol, result) {
11
+ this.cache.set(symbol, result);
12
+ }
13
+ delete(symbol) {
14
+ this.cache.delete(symbol);
15
+ }
16
+ clear() {
17
+ this.cache =
18
+ new WeakMap();
19
+ }
20
+ }
21
+ exports.EvaluationCache = EvaluationCache;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // src/analysis/cache/resolvedCallCache.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ResolvedCallCache = void 0;
5
+ class ResolvedCallCache {
6
+ cache = new WeakMap();
7
+ get(symbol) {
8
+ return this.cache.get(symbol);
9
+ }
10
+ set(symbol, value) {
11
+ this.cache.set(symbol, value);
12
+ }
13
+ delete(symbol) {
14
+ this.cache.delete(symbol);
15
+ }
16
+ clear() {
17
+ this.cache =
18
+ new WeakMap();
19
+ }
20
+ }
21
+ exports.ResolvedCallCache = ResolvedCallCache;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // src/analysis/cache/resolvedExportCache.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ResolvedExportCache = void 0;
5
+ class ResolvedExportCache {
6
+ cache = new WeakMap();
7
+ get(symbol) {
8
+ return this.cache.get(symbol);
9
+ }
10
+ set(symbol, resolved) {
11
+ this.cache.set(symbol, resolved);
12
+ }
13
+ delete(symbol) {
14
+ this.cache.delete(symbol);
15
+ }
16
+ clear() {
17
+ this.cache =
18
+ new WeakMap();
19
+ }
20
+ }
21
+ exports.ResolvedExportCache = ResolvedExportCache;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SemanticDependencyGraph = void 0;
4
+ class SemanticDependencyGraph {
5
+ dependencies = new Map();
6
+ addDependency(from, to) {
7
+ let deps = this.dependencies.get(from);
8
+ if (!deps) {
9
+ deps =
10
+ new Set();
11
+ this.dependencies.set(from, deps);
12
+ }
13
+ deps.add(to);
14
+ }
15
+ getDependents(file) {
16
+ const result = new Set();
17
+ for (const [from, deps,] of this.dependencies) {
18
+ if (deps.has(file)) {
19
+ result.add(from);
20
+ }
21
+ }
22
+ return result;
23
+ }
24
+ }
25
+ exports.SemanticDependencyGraph = SemanticDependencyGraph;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ // src/analysis/cache/symbolInvalidation.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.invalidateSourceFileSymbols = invalidateSourceFileSymbols;
8
+ const typescript_1 = __importDefault(require("typescript"));
9
+ function invalidateSourceFileSymbols(sourceFile, checker, evaluationCache, resolvedCallCache, resolvedExportCache) {
10
+ function visit(node) {
11
+ const symbol = checker.getSymbolAtLocation(node);
12
+ if (symbol) {
13
+ evaluationCache.delete(symbol);
14
+ resolvedCallCache.delete(symbol);
15
+ resolvedExportCache.delete(symbol);
16
+ }
17
+ typescript_1.default.forEachChild(node, visit);
18
+ }
19
+ visit(sourceFile);
20
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ // src/analysis/extractor/extractor.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.extractEvents = extractEvents;
8
+ const typescript_1 = __importDefault(require("typescript"));
9
+ const resolver_1 = require("../resolver/resolver");
10
+ const evaluationContext_1 = require("../shared/evaluationContext");
11
+ const propagationExtractor_1 = require("./propagationExtractor");
12
+ function isValidEvent(value) {
13
+ return (value.length > 0
14
+ && value.length < 160
15
+ && /^[a-zA-Z0-9:_./-]+$/.test(value));
16
+ }
17
+ function extractEvents(index, checker, _config, evaluationCache, exportCache, resolvedCallCache, wrapperRegistry) {
18
+ const events = new Set();
19
+ //
20
+ // direct track sinks
21
+ //
22
+ for (const call of index.trackCalls) {
23
+ for (const argument of call.trackedArguments) {
24
+ const resolved = (0, resolver_1.resolveNodeValue)(argument, checker, (0, evaluationContext_1.createEvaluationContext)(), new Set(), evaluationCache, resolvedCallCache, exportCache);
25
+ for (const value of resolved.values) {
26
+ if (isValidEvent(value)) {
27
+ events.add(value);
28
+ }
29
+ }
30
+ }
31
+ }
32
+ //
33
+ // wrapper propagation
34
+ //
35
+ function visit(node) {
36
+ if (typescript_1.default.isCallExpression(node)) {
37
+ (0, propagationExtractor_1.extractPropagationEvents)(node, checker, wrapperRegistry, resolvedCallCache, evaluationCache, exportCache, events, new Set());
38
+ }
39
+ typescript_1.default.forEachChild(node, visit);
40
+ }
41
+ visit(index.sourceFile);
42
+ return {
43
+ events,
44
+ detectedFunctionWrappers: new Set(index.wrappers.map(wrapper => wrapper.symbol.getName())),
45
+ detectedComponentWrappers: new Map(),
46
+ };
47
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // src/analysis/extractor/propagationExtractor.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.extractPropagationEvents = extractPropagationEvents;
5
+ const callResolver_1 = require("../resolver/callResolver");
6
+ const resolver_1 = require("../resolver/resolver");
7
+ const evaluationContext_1 = require("../shared/evaluationContext");
8
+ function isValidEvent(value) {
9
+ return (value.length > 0
10
+ && value.length < 160
11
+ && /^[a-zA-Z0-9:_./-]+$/.test(value));
12
+ }
13
+ function extractPropagationEvents(call, checker, wrapperRegistry, resolvedCallCache, evaluationCache, exportCache, events, visited) {
14
+ const resolved = (0, callResolver_1.resolveFunctionFromCall)(call.expression, checker, resolvedCallCache, exportCache);
15
+ if (!resolved) {
16
+ return;
17
+ }
18
+ const semantic = wrapperRegistry.get(resolved, checker, exportCache);
19
+ if (!semantic) {
20
+ return;
21
+ }
22
+ const signature = checker.getSignatureFromDeclaration(resolved);
23
+ if (!signature) {
24
+ return;
25
+ }
26
+ if (visited.has(signature)) {
27
+ return;
28
+ }
29
+ visited.add(signature);
30
+ for (const propagation of semantic.propagations) {
31
+ extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, exportCache, events);
32
+ }
33
+ }
34
+ function extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, exportCache, events) {
35
+ const argument = call.arguments[propagation.sourceParameterIndex];
36
+ if (!argument) {
37
+ return;
38
+ }
39
+ const context = (0, evaluationContext_1.createEvaluationContext)();
40
+ const parameterSymbol = checker.getSymbolAtLocation(propagation
41
+ .sourceParameter
42
+ .name);
43
+ if (parameterSymbol) {
44
+ context.parameterBindings.set(parameterSymbol, argument);
45
+ }
46
+ const resolved = (0, resolver_1.resolveNodeValue)(propagation.targetNode, checker, context, new Set(), evaluationCache, resolvedCallCache, exportCache);
47
+ for (const value of resolved.values) {
48
+ if (isValidEvent(value)) {
49
+ events.add(value);
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ // src/analysis/resolver/callResolver.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.resolveFunctionFromCall = resolveFunctionFromCall;
8
+ const typescript_1 = __importDefault(require("typescript"));
9
+ const exportResolver_1 = require("./exportResolver");
10
+ function isFunctionLike(node) {
11
+ return (typescript_1.default.isFunctionDeclaration(node)
12
+ || typescript_1.default.isMethodDeclaration(node)
13
+ || typescript_1.default.isArrowFunction(node)
14
+ || typescript_1.default.isFunctionExpression(node));
15
+ }
16
+ function resolveDeclaration(declaration) {
17
+ //
18
+ // function foo() {}
19
+ //
20
+ if (isFunctionLike(declaration)) {
21
+ return declaration;
22
+ }
23
+ //
24
+ // const foo = () => {}
25
+ //
26
+ if (typescript_1.default.isVariableDeclaration(declaration)
27
+ && declaration.initializer
28
+ && isFunctionLike(declaration.initializer)) {
29
+ return declaration.initializer;
30
+ }
31
+ //
32
+ // class field = () => {}
33
+ //
34
+ if (typescript_1.default.isPropertyDeclaration(declaration)
35
+ && declaration.initializer
36
+ && isFunctionLike(declaration.initializer)) {
37
+ return declaration.initializer;
38
+ }
39
+ return null;
40
+ }
41
+ function getExpressionSymbol(expression, checker) {
42
+ //
43
+ // analytics.track()
44
+ //
45
+ if (typescript_1.default.isPropertyAccessExpression(expression)) {
46
+ return checker.getSymbolAtLocation(expression.name);
47
+ }
48
+ //
49
+ // analytics?.track()
50
+ //
51
+ if (typescript_1.default.isPropertyAccessChain(expression)) {
52
+ return checker.getSymbolAtLocation(expression.name);
53
+ }
54
+ //
55
+ // analytics["track"]()
56
+ //
57
+ if (typescript_1.default.isElementAccessExpression(expression)
58
+ && expression.argumentExpression) {
59
+ return checker.getSymbolAtLocation(expression.argumentExpression);
60
+ }
61
+ //
62
+ // track()
63
+ //
64
+ return checker.getSymbolAtLocation(expression);
65
+ }
66
+ function resolveFunctionFromCall(expression, checker, callCache, exportCache) {
67
+ let symbol = getExpressionSymbol(expression, checker);
68
+ if (!symbol) {
69
+ return null;
70
+ }
71
+ //
72
+ // export unwrap
73
+ //
74
+ symbol =
75
+ (0, exportResolver_1.resolveExportedSymbol)(symbol, checker, exportCache)
76
+ ?? symbol;
77
+ //
78
+ // alias unwrap
79
+ //
80
+ if (symbol.flags
81
+ & typescript_1.default.SymbolFlags.Alias) {
82
+ symbol =
83
+ checker.getAliasedSymbol(symbol);
84
+ }
85
+ //
86
+ // cache
87
+ //
88
+ const cached = callCache.get(symbol);
89
+ if (cached !== undefined) {
90
+ return cached;
91
+ }
92
+ //
93
+ // declaration resolution
94
+ //
95
+ const declarations = symbol.getDeclarations()
96
+ ?? [];
97
+ for (const declaration of declarations) {
98
+ const resolved = resolveDeclaration(declaration);
99
+ if (!resolved) {
100
+ continue;
101
+ }
102
+ callCache.set(symbol, resolved);
103
+ return resolved;
104
+ }
105
+ //
106
+ // fallback:
107
+ // type signatures
108
+ //
109
+ const type = checker.getTypeOfSymbolAtLocation(symbol, declarations[0]
110
+ ?? expression);
111
+ const signatures = checker.getSignaturesOfType(type, typescript_1.default.SignatureKind.Call);
112
+ for (const signature of signatures) {
113
+ const declaration = signature.declaration;
114
+ if (declaration
115
+ && isFunctionLike(declaration)) {
116
+ callCache.set(symbol, declaration);
117
+ return declaration;
118
+ }
119
+ }
120
+ //
121
+ // unresolved
122
+ //
123
+ callCache.set(symbol, null);
124
+ return null;
125
+ }