@eventra_dev/eventra-cli 0.1.25 → 0.1.27

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/README.md CHANGED
@@ -9,23 +9,29 @@
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 feature usage events in your codebase and syncs them with Eventra.
12
+ Eventra CLI automatically discovers and tracks event usage across your codebase — even through abstractions like wrappers, variables, and cross-file calls.
13
13
 
14
14
  ---
15
15
 
16
- # Installation
16
+ ## Overview
17
17
 
18
- ```bash
19
- npm install -D @eventra_dev/eventra-cli
20
- ```
18
+ Eventra scans your codebase using a TypeScript-powered engine and extracts event usage — even through abstractions like wrappers, variables, and cross-file calls.
19
+
20
+ It requires no runtime instrumentation and fits naturally into CI/CD workflows.
21
+
22
+ ---
23
+
24
+ ## Installation
21
25
 
22
26
  ```bash
27
+ npm install -D @eventra_dev/eventra-cli
28
+ # or
23
29
  pnpm add -D @eventra_dev/eventra-cli
24
30
  ```
25
31
 
26
32
  ---
27
33
 
28
- # Quick Start
34
+ ## Quick Start
29
35
 
30
36
  ```bash
31
37
  eventra init
@@ -36,22 +42,43 @@ eventra send
36
42
 
37
43
  ---
38
44
 
39
- # Commands
45
+ ## How it works
40
46
 
41
- ## eventra init
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
42
51
 
43
- Creates `eventra.json` configuration file.
52
+ No runtime required. No SDK needed.
44
53
 
45
54
  ---
46
55
 
47
- ## eventra sync
56
+ ## Why Eventra
57
+
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
62
+
63
+ ---
48
64
 
49
- Scans your project and discovers events automatically.
65
+ ## Commands
50
66
 
51
- ### Supports:
67
+ ### `eventra init`
68
+ Creates `eventra.json` configuration file.
69
+
70
+ ---
52
71
 
53
- - track("event")
54
- - analytics.track("event")
72
+ ### `eventra sync`
73
+ Scans your project and updates the list of detected events.
74
+
75
+ #### Supports
76
+
77
+ - Direct calls
78
+ ```ts
79
+ track("event")
80
+ analytics.track("event")
81
+ ```
55
82
 
56
83
  - Variables
57
84
  ```ts
@@ -74,7 +101,7 @@ track(`click_${type}`)
74
101
  track(condition ? "a" : "b")
75
102
  ```
76
103
 
77
- - Object format
104
+ - Object payloads
78
105
  ```ts
79
106
  track({ event: "click" })
80
107
  ```
@@ -88,36 +115,27 @@ const trackFeature = (name: string) => {
88
115
  trackFeature("click")
89
116
  ```
90
117
 
91
- - Multi-argument wrappers
92
- ```ts
93
- trackFeature("test", "click")
94
- ```
95
-
96
- - Cross-file tracking
97
-
98
- - Vue / Svelte / Astro support
118
+ - Cross-file event resolution
119
+ - Framework templates (Vue, Svelte, Astro)
99
120
 
100
121
  ---
101
122
 
102
- ## eventra check
103
-
104
- Validate events without modifying config.
123
+ ### `eventra check`
124
+ Validates detected events against config.
105
125
 
106
126
  ```bash
107
127
  eventra check
108
128
  ```
109
129
 
110
- ### Detects:
111
-
130
+ Detects:
112
131
  - New events
113
132
  - Removed events
114
- - Dynamic values
133
+ - Inconsistent tracking usage
115
134
 
116
135
  ---
117
136
 
118
- ## eventra check --fix
119
-
120
- Auto-fix mode.
137
+ ### `eventra check --fix`
138
+ Automatically updates config to match detected events.
121
139
 
122
140
  ```bash
123
141
  eventra check --fix
@@ -125,9 +143,8 @@ eventra check --fix
125
143
 
126
144
  ---
127
145
 
128
- ## eventra watch
129
-
130
- Real-time event detection.
146
+ ### `eventra watch`
147
+ Runs in watch mode and updates events in real time.
131
148
 
132
149
  ```bash
133
150
  eventra watch
@@ -135,9 +152,8 @@ eventra watch
135
152
 
136
153
  ---
137
154
 
138
- ## eventra send
139
-
140
- Send events to backend.
155
+ ### `eventra send`
156
+ Sends events to Eventra backend.
141
157
 
142
158
  ```bash
143
159
  eventra send
@@ -145,11 +161,12 @@ eventra send
145
161
 
146
162
  ---
147
163
 
148
- # Configuration
164
+ ## Configuration
149
165
 
150
166
  ```json
151
167
  {
152
168
  "apiKey": "",
169
+ "endpoint": "",
153
170
  "events": [],
154
171
  "wrappers": [],
155
172
  "functionWrappers": [],
@@ -163,25 +180,20 @@ eventra send
163
180
 
164
181
  ---
165
182
 
166
- # Supported Frameworks
183
+ ## Supported Environments
184
+
185
+ Eventra works with **any JavaScript or TypeScript codebase**.
167
186
 
168
- - React
169
- - Vue
170
- - Svelte
171
- - Astro
172
- - Node.js
173
- - Express
174
- - NestJS
175
- - Fastify
187
+ No framework limitations.
176
188
 
177
189
  ---
178
190
 
179
- # Requirements
191
+ ## Requirements
180
192
 
181
193
  - Node.js 18+
182
194
 
183
195
  ---
184
196
 
185
- # License
197
+ ## License
186
198
 
187
199
  MIT
@@ -13,6 +13,9 @@ const config_1 = require("../utils/config");
13
13
  const engine_1 = require("../engine/engine");
14
14
  const processFile_1 = require("../utils/processFile");
15
15
  const getVirtualFile_1 = require("../utils/getVirtualFile");
16
+ function normalize(file) {
17
+ return path_1.default.resolve(file).replace(/\\/g, "/");
18
+ }
16
19
  async function watch() {
17
20
  const config = await (0, config_1.loadConfig)();
18
21
  if (!config)
@@ -29,28 +32,29 @@ async function watch() {
29
32
  // COLLECT + CACHE
30
33
  for (const file of files) {
31
34
  try {
32
- const raw = await promises_1.default.readFile(file, "utf-8");
35
+ const abs = normalize(file);
36
+ const raw = await promises_1.default.readFile(abs, "utf-8");
33
37
  if (raw.length > 2_000_000)
34
- continue; // safeguard
35
- const parsed = (0, processFile_1.processFile)(file, raw);
36
- cache.set(file, parsed);
37
- toScan.add(file);
38
+ continue;
39
+ const parsed = (0, processFile_1.processFile)(abs, raw);
40
+ cache.set(abs, parsed);
41
+ toScan.add(abs);
38
42
  for (const dep of parsed.deps) {
39
- const absDep = path_1.default.resolve(dep);
40
- toScan.add(absDep);
43
+ toScan.add(normalize(dep));
41
44
  }
42
45
  }
43
46
  catch {
44
47
  console.log(chalk_1.default.gray(`skip: ${file}`));
45
48
  }
46
49
  }
47
- // PRELOAD (TS PROGRAM)
50
+ // PRELOAD
48
51
  for (const file of toScan) {
49
52
  try {
50
53
  let parsed = cache.get(file);
51
54
  if (!parsed) {
52
55
  const raw = await promises_1.default.readFile(file, "utf-8");
53
56
  parsed = (0, processFile_1.processFile)(file, raw);
57
+ cache.set(file, parsed);
54
58
  }
55
59
  const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
56
60
  engine.preloadFile?.(virtual, parsed.content);
@@ -68,7 +72,7 @@ async function watch() {
68
72
  const virtual = (0, getVirtualFile_1.getVirtualFile)(file);
69
73
  engine.scanFile(virtual, parsed.content, config);
70
74
  for (const dep of parsed.deps) {
71
- watchedDeps.add(path_1.default.resolve(dep));
75
+ watchedDeps.add(normalize(dep));
72
76
  }
73
77
  }
74
78
  catch { }
@@ -94,19 +98,33 @@ async function watch() {
94
98
  const parsed = (0, processFile_1.processFile)(item.real, raw);
95
99
  cache.set(item.real, parsed);
96
100
  engine.updateFile(item.virtual, parsed.content, config);
97
- // DEPS
101
+ // DEPENDENCY DIFF
102
+ const nextDeps = new Set();
98
103
  for (const dep of parsed.deps) {
99
- const absDep = path_1.default.resolve(dep);
100
- if (!watchedDeps.has(absDep)) {
101
- watcher.add(absDep);
102
- watchedDeps.add(absDep);
104
+ nextDeps.add(normalize(dep));
105
+ }
106
+ // add new deps
107
+ for (const dep of nextDeps) {
108
+ if (!watchedDeps.has(dep)) {
109
+ watcher.add(dep);
110
+ watchedDeps.add(dep);
103
111
  }
112
+ }
113
+ // remove old deps
114
+ for (const dep of [...watchedDeps]) {
115
+ if (!nextDeps.has(dep)) {
116
+ watcher.unwatch(dep);
117
+ watchedDeps.delete(dep);
118
+ }
119
+ }
120
+ // preload deps
121
+ for (const dep of nextDeps) {
104
122
  try {
105
- const depRaw = await promises_1.default.readFile(absDep, "utf-8");
106
- const depParsed = (0, processFile_1.processFile)(absDep, depRaw);
107
- cache.set(absDep, depParsed);
108
- const depVirtual = (0, getVirtualFile_1.getVirtualFile)(absDep);
109
- engine.updateFile(depVirtual, depParsed.content, config);
123
+ const raw = await promises_1.default.readFile(dep, "utf-8");
124
+ const parsedDep = (0, processFile_1.processFile)(dep, raw);
125
+ cache.set(dep, parsedDep);
126
+ const virtual = (0, getVirtualFile_1.getVirtualFile)(dep);
127
+ engine.updateFile(virtual, parsedDep.content, config);
110
128
  }
111
129
  catch { }
112
130
  }
@@ -130,13 +148,13 @@ async function watch() {
130
148
  };
131
149
  // SCHEDULER
132
150
  const schedule = (file) => {
133
- const abs = path_1.default.resolve(file);
151
+ const abs = normalize(file);
134
152
  const virtual = (0, getVirtualFile_1.getVirtualFile)(abs);
135
153
  console.log(chalk_1.default.gray(`change: ${file}`));
136
154
  queued.set(abs, { real: abs, virtual });
137
155
  if (timer)
138
156
  clearTimeout(timer);
139
- timer = setTimeout(run, 250);
157
+ timer = setTimeout(run, 200);
140
158
  };
141
159
  // WATCHER
142
160
  const watcher = chokidar_1.default.watch(config.sync.include, {
@@ -148,7 +166,7 @@ async function watch() {
148
166
  .on("add", schedule)
149
167
  .on("change", schedule)
150
168
  .on("unlink", async (file) => {
151
- const abs = path_1.default.resolve(file);
169
+ const abs = normalize(file);
152
170
  console.log(chalk_1.default.gray(`remove: ${file}`));
153
171
  const virtual = (0, getVirtualFile_1.getVirtualFile)(abs);
154
172
  engine.removeFile(virtual, config);
@@ -162,7 +180,7 @@ async function watch() {
162
180
  .on("error", (err) => {
163
181
  console.error(chalk_1.default.red("Watcher error:"), err);
164
182
  });
165
- // WATCH DEPS
183
+ // WATCH DEPS INIT
166
184
  for (const dep of watchedDeps) {
167
185
  watcher.add(dep);
168
186
  }
@@ -23,15 +23,37 @@ function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
23
23
  const aliased = checker.getAliasedSymbol(symbol);
24
24
  return resolveFromSymbol(aliased, checker, visited, depth + 1);
25
25
  }
26
+ // re-export support
27
+ if (symbol.exports) {
28
+ for (const exp of symbol.exports.values()) {
29
+ const resolved = resolveFromSymbol(exp, checker, visited, depth + 1);
30
+ if (resolved)
31
+ return resolved;
32
+ }
33
+ }
26
34
  const decls = symbol.getDeclarations() ?? [];
27
35
  for (const decl of decls) {
36
+ // direct function
28
37
  if (isFunctionLike(decl)) {
29
38
  return decl;
30
39
  }
31
- // MethodSignature
40
+ // VARIABLE → FUNCTION
41
+ if (typescript_1.default.isVariableDeclaration(decl) && decl.initializer) {
42
+ if (isFunctionLike(decl.initializer)) {
43
+ return decl.initializer;
44
+ }
45
+ const innerSymbol = checker.getSymbolAtLocation(decl.initializer);
46
+ if (innerSymbol && innerSymbol !== symbol) {
47
+ const resolved = resolveFromSymbol(innerSymbol, checker, visited, depth + 1);
48
+ if (resolved)
49
+ return resolved;
50
+ }
51
+ }
52
+ // MethodSignature (interface / type)
32
53
  if (typescript_1.default.isMethodSignature(decl)) {
33
54
  const parent = decl.parent;
34
- if (typescript_1.default.isInterfaceDeclaration(parent) || typescript_1.default.isTypeLiteralNode(parent)) {
55
+ if (typescript_1.default.isInterfaceDeclaration(parent) ||
56
+ typescript_1.default.isTypeLiteralNode(parent)) {
35
57
  const name = decl.name.getText();
36
58
  const type = checker.getTypeAtLocation(parent);
37
59
  const props = type.getProperties();
@@ -44,7 +66,7 @@ function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
44
66
  }
45
67
  }
46
68
  }
47
- // SAFE fallback
69
+ // fallback recursion
48
70
  const s = checker.getSymbolAtLocation(decl);
49
71
  if (s && s !== symbol) {
50
72
  const resolved = resolveFromSymbol(s, checker, visited, depth + 1);
@@ -55,6 +77,28 @@ function resolveFromSymbol(symbol, checker, visited = new Set(), depth = 0) {
55
77
  return null;
56
78
  }
57
79
  function resolveFunctionFromCall(expr, checker) {
80
+ // tracker.track
81
+ if (typescript_1.default.isPropertyAccessExpression(expr)) {
82
+ let symbol = checker.getSymbolAtLocation(expr.name);
83
+ if (symbol && (symbol.flags & typescript_1.default.SymbolFlags.Alias)) {
84
+ symbol = checker.getAliasedSymbol(symbol);
85
+ }
86
+ if (symbol) {
87
+ const resolved = resolveFromSymbol(symbol, checker);
88
+ if (resolved)
89
+ return resolved;
90
+ }
91
+ }
92
+ // tracker["track"]
93
+ if (typescript_1.default.isElementAccessExpression(expr)) {
94
+ const symbol = checker.getSymbolAtLocation(expr.argumentExpression);
95
+ if (symbol) {
96
+ const resolved = resolveFromSymbol(symbol, checker);
97
+ if (resolved)
98
+ return resolved;
99
+ }
100
+ }
101
+ // normal call signatures
58
102
  const type = checker.getTypeAtLocation(expr);
59
103
  const signatures = type.getCallSignatures();
60
104
  for (const sig of signatures) {
@@ -64,7 +108,6 @@ function resolveFunctionFromCall(expr, checker) {
64
108
  if (isFunctionLike(decl)) {
65
109
  return decl;
66
110
  }
67
- // interface method
68
111
  if (typescript_1.default.isMethodSignature(decl)) {
69
112
  const symbol = checker.getSymbolAtLocation(decl.name);
70
113
  if (symbol) {
@@ -74,5 +117,10 @@ function resolveFunctionFromCall(expr, checker) {
74
117
  }
75
118
  }
76
119
  }
120
+ // fallback
121
+ const symbol = checker.getSymbolAtLocation(expr);
122
+ if (symbol) {
123
+ return resolveFromSymbol(symbol, checker);
124
+ }
77
125
  return null;
78
126
  }
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.EventraEngine = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
4
9
  const languageService_1 = require("./languageService");
5
10
  const scanner_1 = require("./scanner");
6
11
  const graph_1 = require("./graph");
@@ -16,31 +21,40 @@ class EventraEngine {
16
21
  const { baseUrl, paths } = this.ts.getModuleResolutionConfig();
17
22
  this.graph = new graph_1.DependencyGraph(baseUrl, paths);
18
23
  }
24
+ normalize(file) {
25
+ return path_1.default.resolve(file).replace(/\\/g, "/");
26
+ }
27
+ empty() {
28
+ return {
29
+ events: new Set(),
30
+ detectedFunctionWrappers: new Set(),
31
+ detectedComponentWrappers: new Map(),
32
+ };
33
+ }
34
+ // PRELOAD
19
35
  preloadFile(file, content) {
36
+ file = this.normalize(file);
20
37
  this.ts.updateFile(file, content);
38
+ const source = this.ts.getSourceFile(file);
39
+ if (source) {
40
+ this.graph.update(file, source);
41
+ }
21
42
  }
22
43
  // SCAN FILE
23
44
  scanFile(file, content, config) {
45
+ file = this.normalize(file);
24
46
  const h = (0, hash_1.hash)(content);
25
- // skip unchanged
26
47
  if (this.fileHash.get(file) === h) {
27
- return (this.fileResults.get(file) ?? {
28
- events: new Set(),
29
- detectedFunctionWrappers: new Set(),
30
- detectedComponentWrappers: new Map(),
31
- });
48
+ return this.fileResults.get(file) ?? this.empty();
32
49
  }
33
50
  this.fileHash.set(file, h);
34
51
  this.ts.updateFile(file, content);
35
52
  const source = this.ts.getSourceFile(file);
36
53
  const checker = this.ts.getChecker();
37
54
  if (!source || !checker) {
38
- return {
39
- events: new Set(),
40
- detectedFunctionWrappers: new Set(),
41
- detectedComponentWrappers: new Map(),
42
- };
55
+ return this.empty();
43
56
  }
57
+ // обновляем graph
44
58
  this.graph.update(file, source);
45
59
  const res = (0, scanner_1.scanSource)(source, checker, config);
46
60
  this.fileResults.set(file, res);
@@ -48,26 +62,35 @@ class EventraEngine {
48
62
  }
49
63
  // UPDATE FILE (incremental)
50
64
  updateFile(file, content, config) {
65
+ file = this.normalize(file);
51
66
  const res = this.scanFile(file, content, config);
52
67
  const dependents = this.graph.getAllDependentsDeep(file);
53
68
  for (const dep of dependents) {
54
- if ((0, boundary_1.isExternalFile)(dep))
55
- continue;
56
- const depContent = this.ts.getFileContent(dep);
57
- if (!depContent)
69
+ const abs = this.normalize(dep);
70
+ if ((0, boundary_1.isExternalFile)(abs))
58
71
  continue;
72
+ let depContent = this.ts.getFileContent(abs);
73
+ if (!depContent) {
74
+ try {
75
+ depContent = fs_1.default.readFileSync(abs, "utf-8");
76
+ }
77
+ catch {
78
+ continue;
79
+ }
80
+ }
59
81
  const h = (0, hash_1.hash)(depContent);
60
- if (this.fileHash.get(dep) === h)
82
+ if (this.fileHash.get(abs) === h) {
61
83
  continue;
62
- this.fileHash.set(dep, h);
63
- this.ts.updateFile(dep, depContent);
64
- const source = this.ts.getSourceFile(dep);
84
+ }
85
+ this.fileHash.set(abs, h);
86
+ this.ts.updateFile(abs, depContent);
87
+ const source = this.ts.getSourceFile(abs);
65
88
  const checker = this.ts.getChecker();
66
89
  if (!source || !checker)
67
90
  continue;
68
- this.graph.update(dep, source);
91
+ this.graph.update(abs, source);
69
92
  const r = (0, scanner_1.scanSource)(source, checker, config);
70
- this.fileResults.set(dep, r);
93
+ this.fileResults.set(abs, r);
71
94
  }
72
95
  return res;
73
96
  }
@@ -81,33 +104,42 @@ class EventraEngine {
81
104
  }
82
105
  // REMOVE FILE
83
106
  removeFile(file, config) {
107
+ file = this.normalize(file);
108
+ const dependents = this.graph.getAllDependentsDeep(file);
84
109
  this.ts.removeFile(file);
85
110
  this.fileResults.delete(file);
86
111
  this.fileHash.delete(file);
87
112
  this.graph.remove(file);
88
- const dependents = this.graph.getAllDependentsDeep(file);
89
113
  for (const dep of dependents) {
90
- if ((0, boundary_1.isExternalFile)(dep))
91
- continue;
92
- const content = this.ts.getFileContent(dep);
93
- if (!content)
114
+ const abs = this.normalize(dep);
115
+ if ((0, boundary_1.isExternalFile)(abs))
94
116
  continue;
117
+ let content = this.ts.getFileContent(abs);
118
+ if (!content) {
119
+ try {
120
+ content = fs_1.default.readFileSync(abs, "utf-8");
121
+ }
122
+ catch {
123
+ continue;
124
+ }
125
+ }
95
126
  const h = (0, hash_1.hash)(content);
96
- if (this.fileHash.get(dep) === h)
127
+ if (this.fileHash.get(abs) === h)
97
128
  continue;
98
- this.fileHash.set(dep, h);
99
- this.ts.updateFile(dep, content);
100
- const source = this.ts.getSourceFile(dep);
129
+ this.fileHash.set(abs, h);
130
+ this.ts.updateFile(abs, content);
131
+ const source = this.ts.getSourceFile(abs);
101
132
  const checker = this.ts.getChecker();
102
133
  if (!source || !checker)
103
134
  continue;
135
+ this.graph.update(abs, source);
104
136
  const r = (0, scanner_1.scanSource)(source, checker, config ?? {
105
137
  events: [],
106
138
  wrappers: [],
107
139
  functionWrappers: [],
108
140
  sync: { include: [], exclude: [] },
109
141
  });
110
- this.fileResults.set(dep, r);
142
+ this.fileResults.set(abs, r);
111
143
  }
112
144
  }
113
145
  }
@@ -5,21 +5,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.resolveExportedSymbol = resolveExportedSymbol;
7
7
  const typescript_1 = __importDefault(require("typescript"));
8
+ function isRealDeclaration(node) {
9
+ return (typescript_1.default.isFunctionDeclaration(node) ||
10
+ typescript_1.default.isVariableDeclaration(node) ||
11
+ typescript_1.default.isClassDeclaration(node));
12
+ }
8
13
  function resolveExportedSymbol(symbol, checker, visited = new Set()) {
9
14
  if (visited.has(symbol))
10
15
  return null;
11
16
  visited.add(symbol);
17
+ // unwrap alias
12
18
  if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
13
19
  const aliased = checker.getAliasedSymbol(symbol);
14
20
  return resolveExportedSymbol(aliased, checker, visited);
15
21
  }
16
- if (symbol.getDeclarations()?.length) {
22
+ // export symbol
23
+ const exportSymbol = checker.getExportSymbolOfSymbol(symbol);
24
+ if (exportSymbol && exportSymbol !== symbol) {
25
+ return resolveExportedSymbol(exportSymbol, checker, visited);
26
+ }
27
+ const decls = symbol.getDeclarations() ?? [];
28
+ if (decls.some(isRealDeclaration)) {
17
29
  return symbol;
18
30
  }
19
- const declarations = symbol.getDeclarations() ?? [];
20
- for (const decl of declarations) {
31
+ // exports (re-export)
32
+ for (const decl of decls) {
21
33
  const source = decl.getSourceFile();
22
- const moduleSymbol = checker.getSymbolAtLocation(source);
34
+ const moduleSymbol = source.symbol;
23
35
  if (!moduleSymbol)
24
36
  continue;
25
37
  const exports = checker.getExportsOfModule(moduleSymbol);