driftdetect-core 0.4.1 → 0.4.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 (237) hide show
  1. package/dist/boundaries/boundary-scanner.d.ts +76 -0
  2. package/dist/boundaries/boundary-scanner.d.ts.map +1 -0
  3. package/dist/boundaries/boundary-scanner.js +801 -0
  4. package/dist/boundaries/boundary-scanner.js.map +1 -0
  5. package/dist/boundaries/data-access-learner.d.ts +126 -0
  6. package/dist/boundaries/data-access-learner.d.ts.map +1 -0
  7. package/dist/boundaries/data-access-learner.js +486 -0
  8. package/dist/boundaries/data-access-learner.js.map +1 -0
  9. package/dist/boundaries/index.d.ts +6 -0
  10. package/dist/boundaries/index.d.ts.map +1 -1
  11. package/dist/boundaries/index.js +6 -0
  12. package/dist/boundaries/index.js.map +1 -1
  13. package/dist/boundaries/security-prioritizer.d.ts +118 -0
  14. package/dist/boundaries/security-prioritizer.d.ts.map +1 -0
  15. package/dist/boundaries/security-prioritizer.js +316 -0
  16. package/dist/boundaries/security-prioritizer.js.map +1 -0
  17. package/dist/call-graph/analysis/coverage-analyzer.d.ts +201 -0
  18. package/dist/call-graph/analysis/coverage-analyzer.d.ts.map +1 -0
  19. package/dist/call-graph/analysis/coverage-analyzer.js +553 -0
  20. package/dist/call-graph/analysis/coverage-analyzer.js.map +1 -0
  21. package/dist/call-graph/analysis/dead-code-detector.d.ts +145 -0
  22. package/dist/call-graph/analysis/dead-code-detector.d.ts.map +1 -0
  23. package/dist/call-graph/analysis/dead-code-detector.js +391 -0
  24. package/dist/call-graph/analysis/dead-code-detector.js.map +1 -0
  25. package/dist/call-graph/analysis/graph-builder.d.ts +142 -0
  26. package/dist/call-graph/analysis/graph-builder.d.ts.map +1 -0
  27. package/dist/call-graph/analysis/graph-builder.js +624 -0
  28. package/dist/call-graph/analysis/graph-builder.js.map +1 -0
  29. package/dist/call-graph/analysis/impact-analyzer.d.ts +150 -0
  30. package/dist/call-graph/analysis/impact-analyzer.d.ts.map +1 -0
  31. package/dist/call-graph/analysis/impact-analyzer.js +329 -0
  32. package/dist/call-graph/analysis/impact-analyzer.js.map +1 -0
  33. package/dist/call-graph/analysis/index.d.ts +11 -0
  34. package/dist/call-graph/analysis/index.d.ts.map +1 -0
  35. package/dist/call-graph/analysis/index.js +9 -0
  36. package/dist/call-graph/analysis/index.js.map +1 -0
  37. package/dist/call-graph/analysis/path-finder.d.ts +117 -0
  38. package/dist/call-graph/analysis/path-finder.d.ts.map +1 -0
  39. package/dist/call-graph/analysis/path-finder.js +360 -0
  40. package/dist/call-graph/analysis/path-finder.js.map +1 -0
  41. package/dist/call-graph/analysis/reachability.d.ts +56 -0
  42. package/dist/call-graph/analysis/reachability.d.ts.map +1 -0
  43. package/dist/call-graph/analysis/reachability.js +357 -0
  44. package/dist/call-graph/analysis/reachability.js.map +1 -0
  45. package/dist/call-graph/demo.d.ts +11 -0
  46. package/dist/call-graph/demo.d.ts.map +1 -0
  47. package/dist/call-graph/demo.js +339 -0
  48. package/dist/call-graph/demo.js.map +1 -0
  49. package/dist/call-graph/enrichment/enrichment-engine.d.ts +126 -0
  50. package/dist/call-graph/enrichment/enrichment-engine.d.ts.map +1 -0
  51. package/dist/call-graph/enrichment/enrichment-engine.js +760 -0
  52. package/dist/call-graph/enrichment/enrichment-engine.js.map +1 -0
  53. package/dist/call-graph/enrichment/impact-scorer.d.ts +59 -0
  54. package/dist/call-graph/enrichment/impact-scorer.d.ts.map +1 -0
  55. package/dist/call-graph/enrichment/impact-scorer.js +328 -0
  56. package/dist/call-graph/enrichment/impact-scorer.js.map +1 -0
  57. package/dist/call-graph/enrichment/index.d.ts +12 -0
  58. package/dist/call-graph/enrichment/index.d.ts.map +1 -0
  59. package/dist/call-graph/enrichment/index.js +15 -0
  60. package/dist/call-graph/enrichment/index.js.map +1 -0
  61. package/dist/call-graph/enrichment/remediation-generator.d.ts +41 -0
  62. package/dist/call-graph/enrichment/remediation-generator.d.ts.map +1 -0
  63. package/dist/call-graph/enrichment/remediation-generator.js +609 -0
  64. package/dist/call-graph/enrichment/remediation-generator.js.map +1 -0
  65. package/dist/call-graph/enrichment/sensitivity-classifier.d.ts +71 -0
  66. package/dist/call-graph/enrichment/sensitivity-classifier.d.ts.map +1 -0
  67. package/dist/call-graph/enrichment/sensitivity-classifier.js +454 -0
  68. package/dist/call-graph/enrichment/sensitivity-classifier.js.map +1 -0
  69. package/dist/call-graph/enrichment/types.d.ts +402 -0
  70. package/dist/call-graph/enrichment/types.d.ts.map +1 -0
  71. package/dist/call-graph/enrichment/types.js +9 -0
  72. package/dist/call-graph/enrichment/types.js.map +1 -0
  73. package/dist/call-graph/extractors/base-extractor.d.ts +112 -0
  74. package/dist/call-graph/extractors/base-extractor.d.ts.map +1 -0
  75. package/dist/call-graph/extractors/base-extractor.js +140 -0
  76. package/dist/call-graph/extractors/base-extractor.js.map +1 -0
  77. package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts +76 -0
  78. package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts.map +1 -0
  79. package/dist/call-graph/extractors/csharp-data-access-extractor.js +387 -0
  80. package/dist/call-graph/extractors/csharp-data-access-extractor.js.map +1 -0
  81. package/dist/call-graph/extractors/csharp-extractor.d.ts +87 -0
  82. package/dist/call-graph/extractors/csharp-extractor.d.ts.map +1 -0
  83. package/dist/call-graph/extractors/csharp-extractor.js +470 -0
  84. package/dist/call-graph/extractors/csharp-extractor.js.map +1 -0
  85. package/dist/call-graph/extractors/data-access-extractor.d.ts +76 -0
  86. package/dist/call-graph/extractors/data-access-extractor.d.ts.map +1 -0
  87. package/dist/call-graph/extractors/data-access-extractor.js +234 -0
  88. package/dist/call-graph/extractors/data-access-extractor.js.map +1 -0
  89. package/dist/call-graph/extractors/index.d.ts +26 -0
  90. package/dist/call-graph/extractors/index.d.ts.map +1 -0
  91. package/dist/call-graph/extractors/index.js +36 -0
  92. package/dist/call-graph/extractors/index.js.map +1 -0
  93. package/dist/call-graph/extractors/java-data-access-extractor.d.ts +101 -0
  94. package/dist/call-graph/extractors/java-data-access-extractor.d.ts.map +1 -0
  95. package/dist/call-graph/extractors/java-data-access-extractor.js +611 -0
  96. package/dist/call-graph/extractors/java-data-access-extractor.js.map +1 -0
  97. package/dist/call-graph/extractors/java-extractor.d.ts +87 -0
  98. package/dist/call-graph/extractors/java-extractor.d.ts.map +1 -0
  99. package/dist/call-graph/extractors/java-extractor.js +510 -0
  100. package/dist/call-graph/extractors/java-extractor.js.map +1 -0
  101. package/dist/call-graph/extractors/php-data-access-extractor.d.ts +93 -0
  102. package/dist/call-graph/extractors/php-data-access-extractor.d.ts.map +1 -0
  103. package/dist/call-graph/extractors/php-data-access-extractor.js +589 -0
  104. package/dist/call-graph/extractors/php-data-access-extractor.js.map +1 -0
  105. package/dist/call-graph/extractors/php-extractor.d.ts +104 -0
  106. package/dist/call-graph/extractors/php-extractor.d.ts.map +1 -0
  107. package/dist/call-graph/extractors/php-extractor.js +619 -0
  108. package/dist/call-graph/extractors/php-extractor.js.map +1 -0
  109. package/dist/call-graph/extractors/python-data-access-extractor.d.ts +90 -0
  110. package/dist/call-graph/extractors/python-data-access-extractor.d.ts.map +1 -0
  111. package/dist/call-graph/extractors/python-data-access-extractor.js +537 -0
  112. package/dist/call-graph/extractors/python-data-access-extractor.js.map +1 -0
  113. package/dist/call-graph/extractors/python-extractor.d.ts +98 -0
  114. package/dist/call-graph/extractors/python-extractor.d.ts.map +1 -0
  115. package/dist/call-graph/extractors/python-extractor.js +681 -0
  116. package/dist/call-graph/extractors/python-extractor.js.map +1 -0
  117. package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts +91 -0
  118. package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts.map +1 -0
  119. package/dist/call-graph/extractors/semantic-data-access-scanner.js +498 -0
  120. package/dist/call-graph/extractors/semantic-data-access-scanner.js.map +1 -0
  121. package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts +122 -0
  122. package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts.map +1 -0
  123. package/dist/call-graph/extractors/typescript-data-access-extractor.js +788 -0
  124. package/dist/call-graph/extractors/typescript-data-access-extractor.js.map +1 -0
  125. package/dist/call-graph/extractors/typescript-extractor.d.ts +145 -0
  126. package/dist/call-graph/extractors/typescript-extractor.d.ts.map +1 -0
  127. package/dist/call-graph/extractors/typescript-extractor.js +904 -0
  128. package/dist/call-graph/extractors/typescript-extractor.js.map +1 -0
  129. package/dist/call-graph/index.d.ts +127 -0
  130. package/dist/call-graph/index.d.ts.map +1 -0
  131. package/dist/call-graph/index.js +247 -0
  132. package/dist/call-graph/index.js.map +1 -0
  133. package/dist/call-graph/store/call-graph-store.d.ts +70 -0
  134. package/dist/call-graph/store/call-graph-store.d.ts.map +1 -0
  135. package/dist/call-graph/store/call-graph-store.js +210 -0
  136. package/dist/call-graph/store/call-graph-store.js.map +1 -0
  137. package/dist/call-graph/store/index.d.ts +7 -0
  138. package/dist/call-graph/store/index.d.ts.map +1 -0
  139. package/dist/call-graph/store/index.js +7 -0
  140. package/dist/call-graph/store/index.js.map +1 -0
  141. package/dist/call-graph/types.d.ts +376 -0
  142. package/dist/call-graph/types.d.ts.map +1 -0
  143. package/dist/call-graph/types.js +8 -0
  144. package/dist/call-graph/types.js.map +1 -0
  145. package/dist/index.d.ts +8 -0
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +12 -0
  148. package/dist/index.js.map +1 -1
  149. package/dist/lake/callgraph-shard-store.d.ts +168 -0
  150. package/dist/lake/callgraph-shard-store.d.ts.map +1 -0
  151. package/dist/lake/callgraph-shard-store.js +466 -0
  152. package/dist/lake/callgraph-shard-store.js.map +1 -0
  153. package/dist/lake/examples-store.d.ts +127 -0
  154. package/dist/lake/examples-store.d.ts.map +1 -0
  155. package/dist/lake/examples-store.js +389 -0
  156. package/dist/lake/examples-store.js.map +1 -0
  157. package/dist/lake/index-store.d.ts +82 -0
  158. package/dist/lake/index-store.d.ts.map +1 -0
  159. package/dist/lake/index-store.js +359 -0
  160. package/dist/lake/index-store.js.map +1 -0
  161. package/dist/lake/index.d.ts +93 -0
  162. package/dist/lake/index.d.ts.map +1 -0
  163. package/dist/lake/index.js +138 -0
  164. package/dist/lake/index.js.map +1 -0
  165. package/dist/lake/lake.bak/index-store.d.ts +82 -0
  166. package/dist/lake/lake.bak/index-store.d.ts.map +1 -0
  167. package/dist/lake/lake.bak/index-store.js +357 -0
  168. package/dist/lake/lake.bak/index-store.js.map +1 -0
  169. package/dist/lake/lake.bak/index.d.ts +81 -0
  170. package/dist/lake/lake.bak/index.d.ts.map +1 -0
  171. package/dist/lake/lake.bak/index.js +114 -0
  172. package/dist/lake/lake.bak/index.js.map +1 -0
  173. package/dist/lake/lake.bak/manifest-store.d.ts +51 -0
  174. package/dist/lake/lake.bak/manifest-store.d.ts.map +1 -0
  175. package/dist/lake/lake.bak/manifest-store.js +347 -0
  176. package/dist/lake/lake.bak/manifest-store.js.map +1 -0
  177. package/dist/lake/lake.bak/query-engine.d.ts +112 -0
  178. package/dist/lake/lake.bak/query-engine.d.ts.map +1 -0
  179. package/dist/lake/lake.bak/query-engine.js +370 -0
  180. package/dist/lake/lake.bak/query-engine.js.map +1 -0
  181. package/dist/lake/lake.bak/types.d.ts +428 -0
  182. package/dist/lake/lake.bak/types.d.ts.map +1 -0
  183. package/dist/lake/lake.bak/types.js +46 -0
  184. package/dist/lake/lake.bak/types.js.map +1 -0
  185. package/dist/lake/lake.bak/view-materializer.d.ts +70 -0
  186. package/dist/lake/lake.bak/view-materializer.d.ts.map +1 -0
  187. package/dist/lake/lake.bak/view-materializer.js +314 -0
  188. package/dist/lake/lake.bak/view-materializer.js.map +1 -0
  189. package/dist/lake/lake.bak/view-store.d.ts +57 -0
  190. package/dist/lake/lake.bak/view-store.d.ts.map +1 -0
  191. package/dist/lake/lake.bak/view-store.js +348 -0
  192. package/dist/lake/lake.bak/view-store.js.map +1 -0
  193. package/dist/lake/manifest-store.d.ts +51 -0
  194. package/dist/lake/manifest-store.d.ts.map +1 -0
  195. package/dist/lake/manifest-store.js +348 -0
  196. package/dist/lake/manifest-store.js.map +1 -0
  197. package/dist/lake/pattern-shard-store.d.ts +87 -0
  198. package/dist/lake/pattern-shard-store.d.ts.map +1 -0
  199. package/dist/lake/pattern-shard-store.js +347 -0
  200. package/dist/lake/pattern-shard-store.js.map +1 -0
  201. package/dist/lake/query-engine.d.ts +124 -0
  202. package/dist/lake/query-engine.d.ts.map +1 -0
  203. package/dist/lake/query-engine.js +453 -0
  204. package/dist/lake/query-engine.js.map +1 -0
  205. package/dist/lake/security-shard-store.d.ts +156 -0
  206. package/dist/lake/security-shard-store.d.ts.map +1 -0
  207. package/dist/lake/security-shard-store.js +498 -0
  208. package/dist/lake/security-shard-store.js.map +1 -0
  209. package/dist/lake/types.d.ts +428 -0
  210. package/dist/lake/types.d.ts.map +1 -0
  211. package/dist/lake/types.js +46 -0
  212. package/dist/lake/types.js.map +1 -0
  213. package/dist/lake/view-materializer.d.ts +70 -0
  214. package/dist/lake/view-materializer.d.ts.map +1 -0
  215. package/dist/lake/view-materializer.js +314 -0
  216. package/dist/lake/view-materializer.js.map +1 -0
  217. package/dist/lake/view-store.d.ts +57 -0
  218. package/dist/lake/view-store.d.ts.map +1 -0
  219. package/dist/lake/view-store.js +348 -0
  220. package/dist/lake/view-store.js.map +1 -0
  221. package/dist/parsers/tree-sitter/index.d.ts +1 -0
  222. package/dist/parsers/tree-sitter/index.d.ts.map +1 -1
  223. package/dist/parsers/tree-sitter/index.js +4 -0
  224. package/dist/parsers/tree-sitter/index.js.map +1 -1
  225. package/dist/parsers/tree-sitter/typescript-loader.d.ts +58 -0
  226. package/dist/parsers/tree-sitter/typescript-loader.d.ts.map +1 -0
  227. package/dist/parsers/tree-sitter/typescript-loader.js +250 -0
  228. package/dist/parsers/tree-sitter/typescript-loader.js.map +1 -0
  229. package/dist/store/project-config.d.ts +154 -0
  230. package/dist/store/project-config.d.ts.map +1 -0
  231. package/dist/store/project-config.js +235 -0
  232. package/dist/store/project-config.js.map +1 -0
  233. package/dist/store/project-registry.d.ts +241 -0
  234. package/dist/store/project-registry.d.ts.map +1 -0
  235. package/dist/store/project-registry.js +557 -0
  236. package/dist/store/project-registry.js.map +1 -0
  237. package/package.json +4 -2
@@ -0,0 +1,904 @@
1
+ /**
2
+ * TypeScript/JavaScript Call Graph Extractor
3
+ *
4
+ * Extracts functions, calls, imports, and exports from TypeScript/JavaScript
5
+ * using the TypeScript Compiler API.
6
+ *
7
+ * Handles:
8
+ * - Function declarations, arrow functions, function expressions
9
+ * - Nested functions (closures)
10
+ * - Class methods and constructors
11
+ * - JSX component usage
12
+ * - Module-level calls (top-level code)
13
+ * - Callback patterns (functions passed as arguments)
14
+ * - IIFE patterns
15
+ */
16
+ import ts from 'typescript';
17
+ import { BaseCallGraphExtractor } from './base-extractor.js';
18
+ /**
19
+ * TypeScript/JavaScript call graph extractor
20
+ */
21
+ export class TypeScriptCallGraphExtractor extends BaseCallGraphExtractor {
22
+ language = 'typescript';
23
+ extensions = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
24
+ /**
25
+ * Extract call graph information from TypeScript/JavaScript source
26
+ */
27
+ extract(source, filePath) {
28
+ const result = this.createEmptyResult(filePath);
29
+ result.language = this.getLanguageFromPath(filePath);
30
+ try {
31
+ const scriptKind = this.getScriptKind(filePath);
32
+ const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, scriptKind);
33
+ // Extract all information in a single pass
34
+ this.visitNode(sourceFile, result, null, null);
35
+ // Extract module-level calls (top-level code not inside any function)
36
+ this.extractModuleLevelCalls(sourceFile, result);
37
+ }
38
+ catch (error) {
39
+ result.errors.push(error instanceof Error ? error.message : 'Unknown parse error');
40
+ }
41
+ return result;
42
+ }
43
+ /**
44
+ * Extract calls at module level (not inside any function)
45
+ * This catches patterns like: ReactDOM.render(<App />, ...) in main.tsx
46
+ */
47
+ extractModuleLevelCalls(sourceFile, result) {
48
+ const source = sourceFile.text;
49
+ // Create a synthetic "module" function to hold top-level calls
50
+ const moduleCalls = [];
51
+ // Visit only top-level statements
52
+ for (const statement of sourceFile.statements) {
53
+ // Expression statements at top level (like ReactDOM.render(...))
54
+ if (ts.isExpressionStatement(statement)) {
55
+ this.extractCallsFromNodeWithCallback(statement, moduleCalls, source);
56
+ }
57
+ // Variable declarations with calls (like const x = foo())
58
+ else if (ts.isVariableStatement(statement)) {
59
+ let hasFunction = false;
60
+ for (const decl of statement.declarationList.declarations) {
61
+ if (decl.initializer &&
62
+ (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
63
+ hasFunction = true;
64
+ }
65
+ }
66
+ if (!hasFunction) {
67
+ this.extractCallsFromNodeWithCallback(statement, moduleCalls, source);
68
+ }
69
+ }
70
+ }
71
+ // Add module-level calls to result
72
+ result.calls.push(...moduleCalls);
73
+ // If there are module-level calls, create a synthetic module function
74
+ if (moduleCalls.length > 0) {
75
+ const moduleFunc = this.createFunction({
76
+ name: '__module__',
77
+ startLine: 1,
78
+ endLine: sourceFile.getLineAndCharacterOfPosition(sourceFile.end).line + 1,
79
+ startColumn: 0,
80
+ endColumn: 0,
81
+ parameters: [],
82
+ isMethod: false,
83
+ isStatic: false,
84
+ isExported: false,
85
+ isConstructor: false,
86
+ isAsync: false,
87
+ decorators: [],
88
+ bodyStartLine: 1,
89
+ bodyEndLine: sourceFile.getLineAndCharacterOfPosition(sourceFile.end).line + 1,
90
+ });
91
+ result.functions.push(moduleFunc);
92
+ }
93
+ }
94
+ /**
95
+ * Visit a node and extract relevant information
96
+ * @param parentFunction - The name of the containing function (for nested functions)
97
+ */
98
+ visitNode(node, result, currentClass, parentFunction) {
99
+ const source = node.getSourceFile().text;
100
+ // Function declarations
101
+ if (ts.isFunctionDeclaration(node) && node.name) {
102
+ const func = this.extractFunctionDeclaration(node, source, currentClass, parentFunction);
103
+ if (func) {
104
+ result.functions.push(func);
105
+ // Extract calls within the function body
106
+ if (node.body) {
107
+ this.extractCallsFromNode(node.body, result, source);
108
+ // Visit nested functions
109
+ this.visitNestedFunctions(node.body, result, source, currentClass, func.name);
110
+ }
111
+ }
112
+ return; // Don't recurse into children - we've handled the body
113
+ }
114
+ // Arrow functions and function expressions assigned to variables
115
+ else if (ts.isVariableStatement(node)) {
116
+ let hasFunction = false;
117
+ for (const decl of node.declarationList.declarations) {
118
+ if (decl.initializer && ts.isIdentifier(decl.name)) {
119
+ if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
120
+ hasFunction = true;
121
+ const func = this.extractVariableFunction(node, decl, source, parentFunction);
122
+ if (func) {
123
+ result.functions.push(func);
124
+ // Extract calls within the function body
125
+ this.extractCallsFromNode(decl.initializer, result, source);
126
+ // Visit nested functions
127
+ this.visitNestedFunctions(decl.initializer.body, result, source, currentClass, func.name);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ if (hasFunction) {
133
+ return; // Don't recurse into children - we've handled the function body
134
+ }
135
+ }
136
+ // Class declarations
137
+ else if (ts.isClassDeclaration(node) && node.name) {
138
+ const classInfo = this.extractClassDeclaration(node, source);
139
+ result.classes.push(classInfo);
140
+ // Visit class members with class context
141
+ for (const member of node.members) {
142
+ this.visitNode(member, result, node.name.text, parentFunction);
143
+ }
144
+ return; // Don't recurse into children again
145
+ }
146
+ // Method declarations
147
+ else if (ts.isMethodDeclaration(node) && node.name && currentClass) {
148
+ const func = this.extractMethodDeclaration(node, source, currentClass);
149
+ if (func) {
150
+ result.functions.push(func);
151
+ if (node.body) {
152
+ this.extractCallsFromNode(node.body, result, source);
153
+ // Visit nested functions in methods
154
+ this.visitNestedFunctions(node.body, result, source, currentClass, func.qualifiedName);
155
+ }
156
+ }
157
+ return; // Don't recurse into children - we've handled the body
158
+ }
159
+ // Constructor
160
+ else if (ts.isConstructorDeclaration(node) && currentClass) {
161
+ const func = this.extractConstructor(node, source, currentClass);
162
+ result.functions.push(func);
163
+ if (node.body) {
164
+ this.extractCallsFromNode(node.body, result, source);
165
+ // Visit nested functions in constructor
166
+ this.visitNestedFunctions(node.body, result, source, currentClass, func.qualifiedName);
167
+ }
168
+ return; // Don't recurse into children - we've handled the body
169
+ }
170
+ // Import declarations
171
+ else if (ts.isImportDeclaration(node)) {
172
+ const importInfo = this.extractImportDeclaration(node, source);
173
+ if (importInfo) {
174
+ result.imports.push(importInfo);
175
+ }
176
+ }
177
+ // Export declarations
178
+ else if (ts.isExportDeclaration(node)) {
179
+ const exports = this.extractExportDeclaration(node, source);
180
+ result.exports.push(...exports);
181
+ }
182
+ // Exported declarations
183
+ else if (this.hasExportModifier(node)) {
184
+ const exportInfo = this.extractExportedDeclaration(node, source);
185
+ if (exportInfo) {
186
+ result.exports.push(exportInfo);
187
+ }
188
+ }
189
+ // Recurse into children
190
+ ts.forEachChild(node, (child) => this.visitNode(child, result, currentClass, parentFunction));
191
+ }
192
+ /**
193
+ * Visit nested functions within a function body
194
+ * This extracts arrow functions, function expressions, and named functions defined inside other functions
195
+ * Also handles anonymous callbacks passed to functions like useEffect, useCallback, etc.
196
+ */
197
+ visitNestedFunctions(body, result, source, currentClass, parentFunctionName) {
198
+ let callbackCounter = 0;
199
+ const visit = (node) => {
200
+ // Named function inside another function
201
+ if (ts.isFunctionDeclaration(node) && node.name) {
202
+ const func = this.extractFunctionDeclaration(node, source, currentClass, parentFunctionName);
203
+ if (func) {
204
+ result.functions.push(func);
205
+ if (node.body) {
206
+ this.extractCallsFromNode(node.body, result, source);
207
+ // Recursively visit nested functions
208
+ this.visitNestedFunctions(node.body, result, source, currentClass, func.name);
209
+ }
210
+ }
211
+ return; // Don't recurse into this function's children
212
+ }
213
+ // Arrow function or function expression assigned to a variable
214
+ if (ts.isVariableStatement(node)) {
215
+ for (const decl of node.declarationList.declarations) {
216
+ if (decl.initializer && ts.isIdentifier(decl.name)) {
217
+ if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
218
+ const func = this.extractNestedFunction(decl, source, parentFunctionName);
219
+ if (func) {
220
+ result.functions.push(func);
221
+ this.extractCallsFromNode(decl.initializer, result, source);
222
+ // Recursively visit nested functions
223
+ if (ts.isBlock(decl.initializer.body)) {
224
+ this.visitNestedFunctions(decl.initializer.body, result, source, currentClass, func.name);
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ return;
231
+ }
232
+ // Call expressions with anonymous callback arguments
233
+ // Handles: useEffect(() => {...}), setTimeout(() => {...}), arr.map((x) => {...}), etc.
234
+ if (ts.isCallExpression(node)) {
235
+ const processedArgs = new Set();
236
+ for (const arg of node.arguments) {
237
+ if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
238
+ processedArgs.add(arg);
239
+ // Create a synthetic function for the callback
240
+ const calleeName = this.getCallExpressionName(node);
241
+ callbackCounter++;
242
+ const syntheticName = `$${calleeName}$${callbackCounter}`;
243
+ const syntheticQualifiedName = `${parentFunctionName}.${syntheticName}`;
244
+ const startPos = this.getPosition(arg.getStart(), source);
245
+ const endPos = this.getPosition(arg.getEnd(), source);
246
+ const modifiers = ts.canHaveModifiers(arg) ? ts.getModifiers(arg) : undefined;
247
+ const syntheticFunc = this.createFunction({
248
+ name: syntheticName,
249
+ qualifiedName: syntheticQualifiedName,
250
+ startLine: startPos.row + 1,
251
+ endLine: endPos.row + 1,
252
+ startColumn: startPos.column,
253
+ endColumn: endPos.column,
254
+ parameters: this.extractParameters(arg.parameters),
255
+ returnType: arg.type?.getText(),
256
+ isMethod: false,
257
+ isStatic: false,
258
+ isExported: false,
259
+ isConstructor: false,
260
+ isAsync: modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false,
261
+ decorators: [],
262
+ bodyStartLine: this.getPosition(arg.body.getStart(), source).row + 1,
263
+ bodyEndLine: this.getPosition(arg.body.getEnd(), source).row + 1,
264
+ });
265
+ result.functions.push(syntheticFunc);
266
+ // Extract calls from the callback body
267
+ this.extractCallsFromNode(arg.body, result, source);
268
+ // Recursively visit nested functions in the callback
269
+ if (ts.isBlock(arg.body)) {
270
+ this.visitNestedFunctions(arg.body, result, source, currentClass, syntheticQualifiedName);
271
+ }
272
+ }
273
+ }
274
+ // Visit children but skip the callback arguments we already processed
275
+ ts.forEachChild(node, (child) => {
276
+ if (!processedArgs.has(child)) {
277
+ visit(child);
278
+ }
279
+ });
280
+ return;
281
+ }
282
+ ts.forEachChild(node, visit);
283
+ };
284
+ ts.forEachChild(body, visit);
285
+ }
286
+ /**
287
+ * Get the name of a call expression for synthetic function naming
288
+ */
289
+ getCallExpressionName(node) {
290
+ const expr = node.expression;
291
+ if (ts.isIdentifier(expr)) {
292
+ return expr.text;
293
+ }
294
+ else if (ts.isPropertyAccessExpression(expr)) {
295
+ return expr.name.text;
296
+ }
297
+ return 'callback';
298
+ }
299
+ /**
300
+ * Extract a nested function (arrow function or function expression inside another function)
301
+ */
302
+ extractNestedFunction(decl, source, parentFunctionName) {
303
+ if (!ts.isIdentifier(decl.name))
304
+ return null;
305
+ const func = decl.initializer;
306
+ const startPos = this.getPosition(decl.getStart(), source);
307
+ const endPos = this.getPosition(decl.getEnd(), source);
308
+ const modifiers = ts.canHaveModifiers(func) ? ts.getModifiers(func) : undefined;
309
+ const name = decl.name.text;
310
+ return this.createFunction({
311
+ name,
312
+ qualifiedName: `${parentFunctionName}.${name}`,
313
+ startLine: startPos.row + 1,
314
+ endLine: endPos.row + 1,
315
+ startColumn: startPos.column,
316
+ endColumn: endPos.column,
317
+ parameters: this.extractParameters(func.parameters),
318
+ returnType: func.type?.getText(),
319
+ isMethod: false,
320
+ isStatic: false,
321
+ isExported: false, // Nested functions are never exported
322
+ isConstructor: false,
323
+ isAsync: modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false,
324
+ decorators: [],
325
+ bodyStartLine: this.getPosition(func.body.getStart(), source).row + 1,
326
+ bodyEndLine: this.getPosition(func.body.getEnd(), source).row + 1,
327
+ });
328
+ }
329
+ /**
330
+ * Extract calls from a node (function body, etc.)
331
+ */
332
+ extractCallsFromNode(node, result, source) {
333
+ this.extractCallsFromNodeWithCallback(node, result.calls, source);
334
+ }
335
+ /**
336
+ * Extract calls from a node into a specific array
337
+ * Also detects callback patterns where functions are passed as arguments
338
+ */
339
+ extractCallsFromNodeWithCallback(node, calls, source) {
340
+ const visit = (n) => {
341
+ // Call expressions: foo(), obj.method(), new Foo()
342
+ if (ts.isCallExpression(n)) {
343
+ const call = this.extractCallExpression(n, source);
344
+ if (call) {
345
+ calls.push(call);
346
+ }
347
+ // Check for callback patterns - functions passed as arguments
348
+ this.extractCallbackReferences(n, calls, source);
349
+ }
350
+ // New expressions: new Foo()
351
+ else if (ts.isNewExpression(n)) {
352
+ const call = this.extractNewExpression(n, source);
353
+ if (call) {
354
+ calls.push(call);
355
+ }
356
+ }
357
+ // JSX elements: <Component /> or <Component>...</Component>
358
+ // These are effectively function calls to the component
359
+ else if (ts.isJsxSelfClosingElement(n)) {
360
+ const call = this.extractJsxElement(n.tagName, n, source);
361
+ if (call) {
362
+ calls.push(call);
363
+ }
364
+ // Extract function references from JSX attributes (onClick={handleClick}, etc.)
365
+ this.extractJsxAttributeReferences(n.attributes, calls, source);
366
+ }
367
+ else if (ts.isJsxOpeningElement(n)) {
368
+ const call = this.extractJsxElement(n.tagName, n, source);
369
+ if (call) {
370
+ calls.push(call);
371
+ }
372
+ // Extract function references from JSX attributes (onClick={handleClick}, etc.)
373
+ this.extractJsxAttributeReferences(n.attributes, calls, source);
374
+ }
375
+ ts.forEachChild(n, visit);
376
+ };
377
+ visit(node);
378
+ }
379
+ /**
380
+ * Extract function references from JSX attributes
381
+ * Handles patterns like: onClick={handleClick}, onSubmit={formHandler}, ref={myRef}
382
+ */
383
+ extractJsxAttributeReferences(attributes, calls, source) {
384
+ for (const attr of attributes.properties) {
385
+ if (ts.isJsxAttribute(attr) && attr.initializer) {
386
+ // Check if it's an expression container: onClick={...}
387
+ if (ts.isJsxExpression(attr.initializer) && attr.initializer.expression) {
388
+ const expr = attr.initializer.expression;
389
+ // Direct function reference: onClick={handleClick}
390
+ if (ts.isIdentifier(expr)) {
391
+ const pos = this.getPosition(expr.getStart(), source);
392
+ calls.push(this.createCall({
393
+ calleeName: expr.text,
394
+ fullExpression: expr.text,
395
+ line: pos.row + 1,
396
+ column: pos.column,
397
+ argumentCount: 0,
398
+ isMethodCall: false,
399
+ isConstructorCall: false,
400
+ }));
401
+ }
402
+ // Property access: onClick={this.handleClick} or onClick={handlers.click}
403
+ else if (ts.isPropertyAccessExpression(expr)) {
404
+ const pos = this.getPosition(expr.getStart(), source);
405
+ calls.push(this.createCall({
406
+ calleeName: expr.name.text,
407
+ receiver: expr.expression.getText(),
408
+ fullExpression: expr.getText(),
409
+ line: pos.row + 1,
410
+ column: pos.column,
411
+ argumentCount: 0,
412
+ isMethodCall: true,
413
+ isConstructorCall: false,
414
+ }));
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+ /**
421
+ * Extract callback references from function call arguments
422
+ * Handles patterns like: setTimeout(myFunc, 1000), arr.map(processItem), etc.
423
+ */
424
+ extractCallbackReferences(callExpr, calls, source) {
425
+ for (const arg of callExpr.arguments) {
426
+ // Direct function reference: setTimeout(myFunc, 1000)
427
+ if (ts.isIdentifier(arg)) {
428
+ const pos = this.getPosition(arg.getStart(), source);
429
+ calls.push(this.createCall({
430
+ calleeName: arg.text,
431
+ fullExpression: arg.text,
432
+ line: pos.row + 1,
433
+ column: pos.column,
434
+ argumentCount: 0,
435
+ isMethodCall: false,
436
+ isConstructorCall: false,
437
+ }));
438
+ }
439
+ // Property access: obj.method passed as callback
440
+ else if (ts.isPropertyAccessExpression(arg)) {
441
+ const pos = this.getPosition(arg.getStart(), source);
442
+ calls.push(this.createCall({
443
+ calleeName: arg.name.text,
444
+ receiver: arg.expression.getText(),
445
+ fullExpression: arg.getText(),
446
+ line: pos.row + 1,
447
+ column: pos.column,
448
+ argumentCount: 0,
449
+ isMethodCall: true,
450
+ isConstructorCall: false,
451
+ }));
452
+ }
453
+ }
454
+ }
455
+ /**
456
+ * Extract a call expression
457
+ */
458
+ extractCallExpression(node, source) {
459
+ const pos = this.getPosition(node.getStart(), source);
460
+ let calleeName;
461
+ let receiver;
462
+ let fullExpression;
463
+ const expr = node.expression;
464
+ // Method call: obj.method() or obj.prop.method()
465
+ if (ts.isPropertyAccessExpression(expr)) {
466
+ calleeName = expr.name.text;
467
+ receiver = expr.expression.getText();
468
+ fullExpression = expr.getText();
469
+ }
470
+ // Element access: obj['method']()
471
+ else if (ts.isElementAccessExpression(expr)) {
472
+ const arg = expr.argumentExpression;
473
+ calleeName = ts.isStringLiteral(arg) ? arg.text : arg.getText();
474
+ receiver = expr.expression.getText();
475
+ fullExpression = expr.getText();
476
+ }
477
+ // Direct call: foo()
478
+ else if (ts.isIdentifier(expr)) {
479
+ calleeName = expr.text;
480
+ fullExpression = expr.text;
481
+ }
482
+ // Other (call expression result, etc.)
483
+ else {
484
+ calleeName = expr.getText();
485
+ fullExpression = expr.getText();
486
+ }
487
+ return this.createCall({
488
+ calleeName,
489
+ receiver,
490
+ fullExpression,
491
+ line: pos.row + 1,
492
+ column: pos.column,
493
+ argumentCount: node.arguments.length,
494
+ isMethodCall: !!receiver,
495
+ isConstructorCall: false,
496
+ });
497
+ }
498
+ /**
499
+ * Extract a new expression
500
+ */
501
+ extractNewExpression(node, source) {
502
+ const pos = this.getPosition(node.getStart(), source);
503
+ let calleeName;
504
+ let receiver;
505
+ const expr = node.expression;
506
+ if (ts.isIdentifier(expr)) {
507
+ calleeName = expr.text;
508
+ }
509
+ else if (ts.isPropertyAccessExpression(expr)) {
510
+ calleeName = expr.name.text;
511
+ receiver = expr.expression.getText();
512
+ }
513
+ else {
514
+ calleeName = expr.getText();
515
+ }
516
+ return this.createCall({
517
+ calleeName,
518
+ receiver,
519
+ fullExpression: node.getText(),
520
+ line: pos.row + 1,
521
+ column: pos.column,
522
+ argumentCount: node.arguments?.length ?? 0,
523
+ isMethodCall: false,
524
+ isConstructorCall: true,
525
+ });
526
+ }
527
+ /**
528
+ * Extract a JSX element as a function call
529
+ * <Component prop={value} /> is effectively Component({ prop: value })
530
+ */
531
+ extractJsxElement(tagName, node, source) {
532
+ const pos = this.getPosition(node.getStart(), source);
533
+ let calleeName;
534
+ let receiver;
535
+ // <Component /> - identifier
536
+ if (ts.isIdentifier(tagName)) {
537
+ calleeName = tagName.text;
538
+ // Skip intrinsic HTML elements (lowercase) - only track component calls
539
+ // React components must start with uppercase
540
+ if (!calleeName || calleeName.charAt(0) === calleeName.charAt(0).toLowerCase()) {
541
+ return null;
542
+ }
543
+ }
544
+ // <Namespace.Component /> - property access
545
+ else if (ts.isPropertyAccessExpression(tagName)) {
546
+ calleeName = tagName.name.text;
547
+ receiver = tagName.expression.getText();
548
+ }
549
+ // <this.component /> or other
550
+ else {
551
+ calleeName = tagName.getText();
552
+ }
553
+ return this.createCall({
554
+ calleeName,
555
+ receiver,
556
+ fullExpression: `<${tagName.getText()} />`,
557
+ line: pos.row + 1,
558
+ column: pos.column,
559
+ argumentCount: 1, // JSX props are like a single object argument
560
+ isMethodCall: !!receiver,
561
+ isConstructorCall: false,
562
+ });
563
+ }
564
+ /**
565
+ * Extract a function declaration
566
+ */
567
+ extractFunctionDeclaration(node, source, currentClass, parentFunction) {
568
+ if (!node.name)
569
+ return null;
570
+ const startPos = this.getPosition(node.getStart(), source);
571
+ const endPos = this.getPosition(node.getEnd(), source);
572
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
573
+ const name = node.name.text;
574
+ // Build qualified name including parent function for nested functions
575
+ let qualifiedName = name;
576
+ if (parentFunction) {
577
+ qualifiedName = `${parentFunction}.${name}`;
578
+ }
579
+ else if (currentClass) {
580
+ qualifiedName = `${currentClass}.${name}`;
581
+ }
582
+ return this.createFunction({
583
+ name,
584
+ qualifiedName,
585
+ startLine: startPos.row + 1,
586
+ endLine: endPos.row + 1,
587
+ startColumn: startPos.column,
588
+ endColumn: endPos.column,
589
+ parameters: this.extractParameters(node.parameters),
590
+ returnType: node.type?.getText(),
591
+ isMethod: false,
592
+ isStatic: false,
593
+ isExported: parentFunction ? false : this.hasExportModifier(node), // Nested functions can't be exported
594
+ isConstructor: false,
595
+ isAsync: modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false,
596
+ className: currentClass ?? undefined,
597
+ decorators: this.extractDecorators(node),
598
+ bodyStartLine: node.body ? this.getPosition(node.body.getStart(), source).row + 1 : startPos.row + 1,
599
+ bodyEndLine: node.body ? this.getPosition(node.body.getEnd(), source).row + 1 : endPos.row + 1,
600
+ });
601
+ }
602
+ /**
603
+ * Extract a variable function (arrow function or function expression)
604
+ */
605
+ extractVariableFunction(statement, decl, source, parentFunction) {
606
+ if (!ts.isIdentifier(decl.name))
607
+ return null;
608
+ const func = decl.initializer;
609
+ const startPos = this.getPosition(statement.getStart(), source);
610
+ const endPos = this.getPosition(statement.getEnd(), source);
611
+ const modifiers = ts.canHaveModifiers(func) ? ts.getModifiers(func) : undefined;
612
+ const name = decl.name.text;
613
+ // Build qualified name including parent function for nested functions
614
+ const qualifiedName = parentFunction ? `${parentFunction}.${name}` : name;
615
+ return this.createFunction({
616
+ name,
617
+ qualifiedName,
618
+ startLine: startPos.row + 1,
619
+ endLine: endPos.row + 1,
620
+ startColumn: startPos.column,
621
+ endColumn: endPos.column,
622
+ parameters: this.extractParameters(func.parameters),
623
+ returnType: func.type?.getText(),
624
+ isMethod: false,
625
+ isStatic: false,
626
+ isExported: parentFunction ? false : this.hasExportModifier(statement), // Nested functions can't be exported
627
+ isConstructor: false,
628
+ isAsync: modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false,
629
+ decorators: [],
630
+ bodyStartLine: this.getPosition(func.body.getStart(), source).row + 1,
631
+ bodyEndLine: this.getPosition(func.body.getEnd(), source).row + 1,
632
+ });
633
+ }
634
+ /**
635
+ * Extract a method declaration
636
+ */
637
+ extractMethodDeclaration(node, source, className) {
638
+ const name = ts.isIdentifier(node.name) ? node.name.text : node.name.getText();
639
+ const startPos = this.getPosition(node.getStart(), source);
640
+ const endPos = this.getPosition(node.getEnd(), source);
641
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
642
+ return this.createFunction({
643
+ name,
644
+ startLine: startPos.row + 1,
645
+ endLine: endPos.row + 1,
646
+ startColumn: startPos.column,
647
+ endColumn: endPos.column,
648
+ parameters: this.extractParameters(node.parameters),
649
+ returnType: node.type?.getText(),
650
+ isMethod: true,
651
+ isStatic: modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword) ?? false,
652
+ isExported: false, // Methods inherit class export status
653
+ isConstructor: false,
654
+ isAsync: modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false,
655
+ className,
656
+ decorators: this.extractDecorators(node),
657
+ bodyStartLine: node.body ? this.getPosition(node.body.getStart(), source).row + 1 : startPos.row + 1,
658
+ bodyEndLine: node.body ? this.getPosition(node.body.getEnd(), source).row + 1 : endPos.row + 1,
659
+ });
660
+ }
661
+ /**
662
+ * Extract a constructor
663
+ */
664
+ extractConstructor(node, source, className) {
665
+ const startPos = this.getPosition(node.getStart(), source);
666
+ const endPos = this.getPosition(node.getEnd(), source);
667
+ return this.createFunction({
668
+ name: 'constructor',
669
+ startLine: startPos.row + 1,
670
+ endLine: endPos.row + 1,
671
+ startColumn: startPos.column,
672
+ endColumn: endPos.column,
673
+ parameters: this.extractParameters(node.parameters),
674
+ isMethod: true,
675
+ isStatic: false,
676
+ isExported: false,
677
+ isConstructor: true,
678
+ isAsync: false,
679
+ className,
680
+ decorators: [],
681
+ bodyStartLine: node.body ? this.getPosition(node.body.getStart(), source).row + 1 : startPos.row + 1,
682
+ bodyEndLine: node.body ? this.getPosition(node.body.getEnd(), source).row + 1 : endPos.row + 1,
683
+ });
684
+ }
685
+ /**
686
+ * Extract a class declaration
687
+ */
688
+ extractClassDeclaration(node, source) {
689
+ const name = node.name?.text ?? 'anonymous';
690
+ const startPos = this.getPosition(node.getStart(), source);
691
+ const endPos = this.getPosition(node.getEnd(), source);
692
+ const baseClasses = [];
693
+ if (node.heritageClauses) {
694
+ for (const clause of node.heritageClauses) {
695
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
696
+ for (const type of clause.types) {
697
+ if (ts.isIdentifier(type.expression)) {
698
+ baseClasses.push(type.expression.text);
699
+ }
700
+ }
701
+ }
702
+ }
703
+ }
704
+ const methods = [];
705
+ for (const member of node.members) {
706
+ if (ts.isMethodDeclaration(member) && member.name) {
707
+ methods.push(ts.isIdentifier(member.name) ? member.name.text : member.name.getText());
708
+ }
709
+ else if (ts.isConstructorDeclaration(member)) {
710
+ methods.push('constructor');
711
+ }
712
+ }
713
+ return this.createClass({
714
+ name,
715
+ startLine: startPos.row + 1,
716
+ endLine: endPos.row + 1,
717
+ baseClasses,
718
+ methods,
719
+ isExported: this.hasExportModifier(node),
720
+ });
721
+ }
722
+ /**
723
+ * Extract an import declaration
724
+ */
725
+ extractImportDeclaration(node, source) {
726
+ const moduleSpecifier = node.moduleSpecifier.text;
727
+ const pos = this.getPosition(node.getStart(), source);
728
+ const isTypeOnly = node.importClause?.isTypeOnly ?? false;
729
+ const names = [];
730
+ const importClause = node.importClause;
731
+ if (importClause) {
732
+ // Default import
733
+ if (importClause.name) {
734
+ names.push({
735
+ imported: 'default',
736
+ local: importClause.name.text,
737
+ isDefault: true,
738
+ isNamespace: false,
739
+ });
740
+ }
741
+ // Named or namespace imports
742
+ const namedBindings = importClause.namedBindings;
743
+ if (namedBindings) {
744
+ if (ts.isNamespaceImport(namedBindings)) {
745
+ names.push({
746
+ imported: '*',
747
+ local: namedBindings.name.text,
748
+ isDefault: false,
749
+ isNamespace: true,
750
+ });
751
+ }
752
+ else if (ts.isNamedImports(namedBindings)) {
753
+ for (const element of namedBindings.elements) {
754
+ names.push({
755
+ imported: element.propertyName?.text ?? element.name.text,
756
+ local: element.name.text,
757
+ isDefault: false,
758
+ isNamespace: false,
759
+ });
760
+ }
761
+ }
762
+ }
763
+ }
764
+ return this.createImport({
765
+ source: moduleSpecifier,
766
+ names,
767
+ line: pos.row + 1,
768
+ isTypeOnly,
769
+ });
770
+ }
771
+ /**
772
+ * Extract export declarations
773
+ */
774
+ extractExportDeclaration(node, source) {
775
+ const pos = this.getPosition(node.getStart(), source);
776
+ const moduleSpecifier = node.moduleSpecifier ? node.moduleSpecifier.text : undefined;
777
+ const exports = [];
778
+ if (!node.exportClause) {
779
+ // export * from './foo'
780
+ exports.push(this.createExport({
781
+ name: '*',
782
+ isReExport: true,
783
+ source: moduleSpecifier,
784
+ line: pos.row + 1,
785
+ }));
786
+ }
787
+ else if (ts.isNamedExports(node.exportClause)) {
788
+ for (const element of node.exportClause.elements) {
789
+ exports.push(this.createExport({
790
+ name: element.name.text,
791
+ isReExport: !!moduleSpecifier,
792
+ source: moduleSpecifier,
793
+ line: pos.row + 1,
794
+ }));
795
+ }
796
+ }
797
+ return exports;
798
+ }
799
+ /**
800
+ * Extract exported declaration
801
+ */
802
+ extractExportedDeclaration(node, source) {
803
+ const pos = this.getPosition(node.getStart(), source);
804
+ let name = null;
805
+ const isDefault = this.hasDefaultModifier(node);
806
+ if (ts.isFunctionDeclaration(node) && node.name) {
807
+ name = node.name.text;
808
+ }
809
+ else if (ts.isClassDeclaration(node) && node.name) {
810
+ name = node.name.text;
811
+ }
812
+ else if (ts.isVariableStatement(node)) {
813
+ const firstDecl = node.declarationList.declarations[0];
814
+ if (firstDecl && ts.isIdentifier(firstDecl.name)) {
815
+ name = firstDecl.name.text;
816
+ }
817
+ }
818
+ if (!name)
819
+ return null;
820
+ return this.createExport({
821
+ name,
822
+ isDefault,
823
+ line: pos.row + 1,
824
+ });
825
+ }
826
+ /**
827
+ * Extract parameters from a parameter list
828
+ */
829
+ extractParameters(params) {
830
+ return params.map((param) => {
831
+ const name = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();
832
+ return this.parseParameter(name, param.type?.getText(), param.initializer !== undefined, param.dotDotDotToken !== undefined);
833
+ });
834
+ }
835
+ /**
836
+ * Extract decorators from a node
837
+ */
838
+ extractDecorators(node) {
839
+ const decorators = [];
840
+ const modifiers = ts.canHaveDecorators(node) ? ts.getDecorators(node) : undefined;
841
+ if (modifiers) {
842
+ for (const decorator of modifiers) {
843
+ decorators.push(decorator.expression.getText());
844
+ }
845
+ }
846
+ return decorators;
847
+ }
848
+ /**
849
+ * Check if node has export modifier
850
+ */
851
+ hasExportModifier(node) {
852
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
853
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
854
+ }
855
+ /**
856
+ * Check if node has default modifier
857
+ */
858
+ hasDefaultModifier(node) {
859
+ const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
860
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
861
+ }
862
+ /**
863
+ * Get script kind from file path
864
+ */
865
+ getScriptKind(filePath) {
866
+ const ext = filePath.toLowerCase().split('.').pop();
867
+ switch (ext) {
868
+ case 'tsx': return ts.ScriptKind.TSX;
869
+ case 'jsx': return ts.ScriptKind.JSX;
870
+ case 'js':
871
+ case 'mjs':
872
+ case 'cjs': return ts.ScriptKind.JS;
873
+ default: return ts.ScriptKind.TS;
874
+ }
875
+ }
876
+ /**
877
+ * Get language from file path
878
+ */
879
+ getLanguageFromPath(filePath) {
880
+ const ext = filePath.toLowerCase().split('.').pop();
881
+ if (ext === 'js' || ext === 'jsx' || ext === 'mjs' || ext === 'cjs') {
882
+ return 'javascript';
883
+ }
884
+ return 'typescript';
885
+ }
886
+ /**
887
+ * Convert offset to position
888
+ */
889
+ getPosition(offset, source) {
890
+ let row = 0;
891
+ let column = 0;
892
+ for (let i = 0; i < offset && i < source.length; i++) {
893
+ if (source[i] === '\n') {
894
+ row++;
895
+ column = 0;
896
+ }
897
+ else {
898
+ column++;
899
+ }
900
+ }
901
+ return { row, column };
902
+ }
903
+ }
904
+ //# sourceMappingURL=typescript-extractor.js.map