@colbymchenry/codegraph 0.8.0 → 0.9.1

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 (381) hide show
  1. package/README.md +23 -7
  2. package/npm-shim.js +43 -0
  3. package/package.json +13 -51
  4. package/LICENSE +0 -21
  5. package/dist/bin/codegraph.d.ts +0 -21
  6. package/dist/bin/codegraph.d.ts.map +0 -1
  7. package/dist/bin/codegraph.js +0 -1257
  8. package/dist/bin/codegraph.js.map +0 -1
  9. package/dist/bin/node-version-check.d.ts +0 -20
  10. package/dist/bin/node-version-check.d.ts.map +0 -1
  11. package/dist/bin/node-version-check.js +0 -42
  12. package/dist/bin/node-version-check.js.map +0 -1
  13. package/dist/bin/uninstall.d.ts +0 -14
  14. package/dist/bin/uninstall.d.ts.map +0 -1
  15. package/dist/bin/uninstall.js +0 -36
  16. package/dist/bin/uninstall.js.map +0 -1
  17. package/dist/config.d.ts +0 -51
  18. package/dist/config.d.ts.map +0 -1
  19. package/dist/config.js +0 -321
  20. package/dist/config.js.map +0 -1
  21. package/dist/context/formatter.d.ts +0 -30
  22. package/dist/context/formatter.d.ts.map +0 -1
  23. package/dist/context/formatter.js +0 -244
  24. package/dist/context/formatter.js.map +0 -1
  25. package/dist/context/index.d.ts +0 -97
  26. package/dist/context/index.d.ts.map +0 -1
  27. package/dist/context/index.js +0 -1050
  28. package/dist/context/index.js.map +0 -1
  29. package/dist/db/index.d.ts +0 -72
  30. package/dist/db/index.d.ts.map +0 -1
  31. package/dist/db/index.js +0 -200
  32. package/dist/db/index.js.map +0 -1
  33. package/dist/db/migrations.d.ts +0 -44
  34. package/dist/db/migrations.d.ts.map +0 -1
  35. package/dist/db/migrations.js +0 -131
  36. package/dist/db/migrations.js.map +0 -1
  37. package/dist/db/queries.d.ts +0 -253
  38. package/dist/db/queries.d.ts.map +0 -1
  39. package/dist/db/queries.js +0 -1207
  40. package/dist/db/queries.js.map +0 -1
  41. package/dist/db/schema.sql +0 -151
  42. package/dist/db/sqlite-adapter.d.ts +0 -52
  43. package/dist/db/sqlite-adapter.d.ts.map +0 -1
  44. package/dist/db/sqlite-adapter.js +0 -237
  45. package/dist/db/sqlite-adapter.js.map +0 -1
  46. package/dist/directory.d.ts +0 -57
  47. package/dist/directory.d.ts.map +0 -1
  48. package/dist/directory.js +0 -264
  49. package/dist/directory.js.map +0 -1
  50. package/dist/errors.d.ts +0 -136
  51. package/dist/errors.d.ts.map +0 -1
  52. package/dist/errors.js +0 -219
  53. package/dist/errors.js.map +0 -1
  54. package/dist/extraction/dfm-extractor.d.ts +0 -31
  55. package/dist/extraction/dfm-extractor.d.ts.map +0 -1
  56. package/dist/extraction/dfm-extractor.js +0 -151
  57. package/dist/extraction/dfm-extractor.js.map +0 -1
  58. package/dist/extraction/grammars.d.ts +0 -78
  59. package/dist/extraction/grammars.d.ts.map +0 -1
  60. package/dist/extraction/grammars.js +0 -322
  61. package/dist/extraction/grammars.js.map +0 -1
  62. package/dist/extraction/index.d.ts +0 -130
  63. package/dist/extraction/index.d.ts.map +0 -1
  64. package/dist/extraction/index.js +0 -1305
  65. package/dist/extraction/index.js.map +0 -1
  66. package/dist/extraction/languages/c-cpp.d.ts +0 -4
  67. package/dist/extraction/languages/c-cpp.d.ts.map +0 -1
  68. package/dist/extraction/languages/c-cpp.js +0 -126
  69. package/dist/extraction/languages/c-cpp.js.map +0 -1
  70. package/dist/extraction/languages/csharp.d.ts +0 -3
  71. package/dist/extraction/languages/csharp.d.ts.map +0 -1
  72. package/dist/extraction/languages/csharp.js +0 -72
  73. package/dist/extraction/languages/csharp.js.map +0 -1
  74. package/dist/extraction/languages/dart.d.ts +0 -3
  75. package/dist/extraction/languages/dart.d.ts.map +0 -1
  76. package/dist/extraction/languages/dart.js +0 -192
  77. package/dist/extraction/languages/dart.js.map +0 -1
  78. package/dist/extraction/languages/go.d.ts +0 -3
  79. package/dist/extraction/languages/go.d.ts.map +0 -1
  80. package/dist/extraction/languages/go.js +0 -58
  81. package/dist/extraction/languages/go.js.map +0 -1
  82. package/dist/extraction/languages/index.d.ts +0 -10
  83. package/dist/extraction/languages/index.d.ts.map +0 -1
  84. package/dist/extraction/languages/index.js +0 -45
  85. package/dist/extraction/languages/index.js.map +0 -1
  86. package/dist/extraction/languages/java.d.ts +0 -3
  87. package/dist/extraction/languages/java.d.ts.map +0 -1
  88. package/dist/extraction/languages/java.js +0 -64
  89. package/dist/extraction/languages/java.js.map +0 -1
  90. package/dist/extraction/languages/javascript.d.ts +0 -3
  91. package/dist/extraction/languages/javascript.d.ts.map +0 -1
  92. package/dist/extraction/languages/javascript.js +0 -90
  93. package/dist/extraction/languages/javascript.js.map +0 -1
  94. package/dist/extraction/languages/kotlin.d.ts +0 -3
  95. package/dist/extraction/languages/kotlin.d.ts.map +0 -1
  96. package/dist/extraction/languages/kotlin.js +0 -253
  97. package/dist/extraction/languages/kotlin.js.map +0 -1
  98. package/dist/extraction/languages/pascal.d.ts +0 -3
  99. package/dist/extraction/languages/pascal.d.ts.map +0 -1
  100. package/dist/extraction/languages/pascal.js +0 -66
  101. package/dist/extraction/languages/pascal.js.map +0 -1
  102. package/dist/extraction/languages/php.d.ts +0 -3
  103. package/dist/extraction/languages/php.d.ts.map +0 -1
  104. package/dist/extraction/languages/php.js +0 -107
  105. package/dist/extraction/languages/php.js.map +0 -1
  106. package/dist/extraction/languages/python.d.ts +0 -3
  107. package/dist/extraction/languages/python.d.ts.map +0 -1
  108. package/dist/extraction/languages/python.js +0 -56
  109. package/dist/extraction/languages/python.js.map +0 -1
  110. package/dist/extraction/languages/ruby.d.ts +0 -3
  111. package/dist/extraction/languages/ruby.d.ts.map +0 -1
  112. package/dist/extraction/languages/ruby.js +0 -114
  113. package/dist/extraction/languages/ruby.js.map +0 -1
  114. package/dist/extraction/languages/rust.d.ts +0 -3
  115. package/dist/extraction/languages/rust.d.ts.map +0 -1
  116. package/dist/extraction/languages/rust.js +0 -109
  117. package/dist/extraction/languages/rust.js.map +0 -1
  118. package/dist/extraction/languages/scala.d.ts +0 -3
  119. package/dist/extraction/languages/scala.d.ts.map +0 -1
  120. package/dist/extraction/languages/scala.js +0 -139
  121. package/dist/extraction/languages/scala.js.map +0 -1
  122. package/dist/extraction/languages/swift.d.ts +0 -3
  123. package/dist/extraction/languages/swift.d.ts.map +0 -1
  124. package/dist/extraction/languages/swift.js +0 -91
  125. package/dist/extraction/languages/swift.js.map +0 -1
  126. package/dist/extraction/languages/typescript.d.ts +0 -3
  127. package/dist/extraction/languages/typescript.d.ts.map +0 -1
  128. package/dist/extraction/languages/typescript.js +0 -129
  129. package/dist/extraction/languages/typescript.js.map +0 -1
  130. package/dist/extraction/liquid-extractor.d.ts +0 -52
  131. package/dist/extraction/liquid-extractor.d.ts.map +0 -1
  132. package/dist/extraction/liquid-extractor.js +0 -313
  133. package/dist/extraction/liquid-extractor.js.map +0 -1
  134. package/dist/extraction/parse-worker.d.ts +0 -8
  135. package/dist/extraction/parse-worker.d.ts.map +0 -1
  136. package/dist/extraction/parse-worker.js +0 -94
  137. package/dist/extraction/parse-worker.js.map +0 -1
  138. package/dist/extraction/svelte-extractor.d.ts +0 -56
  139. package/dist/extraction/svelte-extractor.d.ts.map +0 -1
  140. package/dist/extraction/svelte-extractor.js +0 -272
  141. package/dist/extraction/svelte-extractor.js.map +0 -1
  142. package/dist/extraction/tree-sitter-helpers.d.ts +0 -28
  143. package/dist/extraction/tree-sitter-helpers.d.ts.map +0 -1
  144. package/dist/extraction/tree-sitter-helpers.js +0 -103
  145. package/dist/extraction/tree-sitter-helpers.js.map +0 -1
  146. package/dist/extraction/tree-sitter-types.d.ts +0 -179
  147. package/dist/extraction/tree-sitter-types.d.ts.map +0 -1
  148. package/dist/extraction/tree-sitter-types.js +0 -10
  149. package/dist/extraction/tree-sitter-types.js.map +0 -1
  150. package/dist/extraction/tree-sitter.d.ts +0 -233
  151. package/dist/extraction/tree-sitter.d.ts.map +0 -1
  152. package/dist/extraction/tree-sitter.js +0 -2393
  153. package/dist/extraction/tree-sitter.js.map +0 -1
  154. package/dist/extraction/vue-extractor.d.ts +0 -36
  155. package/dist/extraction/vue-extractor.d.ts.map +0 -1
  156. package/dist/extraction/vue-extractor.js +0 -163
  157. package/dist/extraction/vue-extractor.js.map +0 -1
  158. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  159. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  160. package/dist/graph/index.d.ts +0 -8
  161. package/dist/graph/index.d.ts.map +0 -1
  162. package/dist/graph/index.js +0 -13
  163. package/dist/graph/index.js.map +0 -1
  164. package/dist/graph/queries.d.ts +0 -106
  165. package/dist/graph/queries.d.ts.map +0 -1
  166. package/dist/graph/queries.js +0 -366
  167. package/dist/graph/queries.js.map +0 -1
  168. package/dist/graph/traversal.d.ts +0 -127
  169. package/dist/graph/traversal.d.ts.map +0 -1
  170. package/dist/graph/traversal.js +0 -493
  171. package/dist/graph/traversal.js.map +0 -1
  172. package/dist/index.d.ts +0 -447
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/index.js +0 -825
  175. package/dist/index.js.map +0 -1
  176. package/dist/installer/claude-md-template.d.ts +0 -14
  177. package/dist/installer/claude-md-template.d.ts.map +0 -1
  178. package/dist/installer/claude-md-template.js +0 -21
  179. package/dist/installer/claude-md-template.js.map +0 -1
  180. package/dist/installer/config-writer.d.ts +0 -29
  181. package/dist/installer/config-writer.d.ts.map +0 -1
  182. package/dist/installer/config-writer.js +0 -111
  183. package/dist/installer/config-writer.js.map +0 -1
  184. package/dist/installer/index.d.ts +0 -65
  185. package/dist/installer/index.d.ts.map +0 -1
  186. package/dist/installer/index.js +0 -406
  187. package/dist/installer/index.js.map +0 -1
  188. package/dist/installer/instructions-template.d.ts +0 -28
  189. package/dist/installer/instructions-template.d.ts.map +0 -1
  190. package/dist/installer/instructions-template.js +0 -64
  191. package/dist/installer/instructions-template.js.map +0 -1
  192. package/dist/installer/targets/claude.d.ts +0 -31
  193. package/dist/installer/targets/claude.d.ts.map +0 -1
  194. package/dist/installer/targets/claude.js +0 -308
  195. package/dist/installer/targets/claude.js.map +0 -1
  196. package/dist/installer/targets/codex.d.ts +0 -18
  197. package/dist/installer/targets/codex.d.ts.map +0 -1
  198. package/dist/installer/targets/codex.js +0 -185
  199. package/dist/installer/targets/codex.js.map +0 -1
  200. package/dist/installer/targets/cursor.d.ts +0 -35
  201. package/dist/installer/targets/cursor.d.ts.map +0 -1
  202. package/dist/installer/targets/cursor.js +0 -229
  203. package/dist/installer/targets/cursor.js.map +0 -1
  204. package/dist/installer/targets/opencode.d.ts +0 -30
  205. package/dist/installer/targets/opencode.d.ts.map +0 -1
  206. package/dist/installer/targets/opencode.js +0 -235
  207. package/dist/installer/targets/opencode.js.map +0 -1
  208. package/dist/installer/targets/registry.d.ts +0 -35
  209. package/dist/installer/targets/registry.d.ts.map +0 -1
  210. package/dist/installer/targets/registry.js +0 -83
  211. package/dist/installer/targets/registry.js.map +0 -1
  212. package/dist/installer/targets/shared.d.ts +0 -77
  213. package/dist/installer/targets/shared.d.ts.map +0 -1
  214. package/dist/installer/targets/shared.js +0 -246
  215. package/dist/installer/targets/shared.js.map +0 -1
  216. package/dist/installer/targets/toml.d.ts +0 -52
  217. package/dist/installer/targets/toml.d.ts.map +0 -1
  218. package/dist/installer/targets/toml.js +0 -147
  219. package/dist/installer/targets/toml.js.map +0 -1
  220. package/dist/installer/targets/types.d.ts +0 -116
  221. package/dist/installer/targets/types.d.ts.map +0 -1
  222. package/dist/installer/targets/types.js +0 -16
  223. package/dist/installer/targets/types.js.map +0 -1
  224. package/dist/mcp/index.d.ts +0 -94
  225. package/dist/mcp/index.d.ts.map +0 -1
  226. package/dist/mcp/index.js +0 -453
  227. package/dist/mcp/index.js.map +0 -1
  228. package/dist/mcp/server-instructions.d.ts +0 -19
  229. package/dist/mcp/server-instructions.d.ts.map +0 -1
  230. package/dist/mcp/server-instructions.js +0 -71
  231. package/dist/mcp/server-instructions.js.map +0 -1
  232. package/dist/mcp/tools.d.ts +0 -257
  233. package/dist/mcp/tools.d.ts.map +0 -1
  234. package/dist/mcp/tools.js +0 -1633
  235. package/dist/mcp/tools.js.map +0 -1
  236. package/dist/mcp/transport.d.ts +0 -106
  237. package/dist/mcp/transport.d.ts.map +0 -1
  238. package/dist/mcp/transport.js +0 -233
  239. package/dist/mcp/transport.js.map +0 -1
  240. package/dist/resolution/frameworks/cargo-workspace.d.ts +0 -18
  241. package/dist/resolution/frameworks/cargo-workspace.d.ts.map +0 -1
  242. package/dist/resolution/frameworks/cargo-workspace.js +0 -225
  243. package/dist/resolution/frameworks/cargo-workspace.js.map +0 -1
  244. package/dist/resolution/frameworks/csharp.d.ts +0 -8
  245. package/dist/resolution/frameworks/csharp.d.ts.map +0 -1
  246. package/dist/resolution/frameworks/csharp.js +0 -213
  247. package/dist/resolution/frameworks/csharp.js.map +0 -1
  248. package/dist/resolution/frameworks/express.d.ts +0 -8
  249. package/dist/resolution/frameworks/express.d.ts.map +0 -1
  250. package/dist/resolution/frameworks/express.js +0 -225
  251. package/dist/resolution/frameworks/express.js.map +0 -1
  252. package/dist/resolution/frameworks/go.d.ts +0 -8
  253. package/dist/resolution/frameworks/go.d.ts.map +0 -1
  254. package/dist/resolution/frameworks/go.js +0 -158
  255. package/dist/resolution/frameworks/go.js.map +0 -1
  256. package/dist/resolution/frameworks/index.d.ts +0 -42
  257. package/dist/resolution/frameworks/index.d.ts.map +0 -1
  258. package/dist/resolution/frameworks/index.js +0 -133
  259. package/dist/resolution/frameworks/index.js.map +0 -1
  260. package/dist/resolution/frameworks/java.d.ts +0 -8
  261. package/dist/resolution/frameworks/java.d.ts.map +0 -1
  262. package/dist/resolution/frameworks/java.js +0 -177
  263. package/dist/resolution/frameworks/java.js.map +0 -1
  264. package/dist/resolution/frameworks/laravel.d.ts +0 -13
  265. package/dist/resolution/frameworks/laravel.d.ts.map +0 -1
  266. package/dist/resolution/frameworks/laravel.js +0 -248
  267. package/dist/resolution/frameworks/laravel.js.map +0 -1
  268. package/dist/resolution/frameworks/nestjs.d.ts +0 -26
  269. package/dist/resolution/frameworks/nestjs.d.ts.map +0 -1
  270. package/dist/resolution/frameworks/nestjs.js +0 -374
  271. package/dist/resolution/frameworks/nestjs.js.map +0 -1
  272. package/dist/resolution/frameworks/python.d.ts +0 -10
  273. package/dist/resolution/frameworks/python.d.ts.map +0 -1
  274. package/dist/resolution/frameworks/python.js +0 -278
  275. package/dist/resolution/frameworks/python.js.map +0 -1
  276. package/dist/resolution/frameworks/react.d.ts +0 -8
  277. package/dist/resolution/frameworks/react.d.ts.map +0 -1
  278. package/dist/resolution/frameworks/react.js +0 -272
  279. package/dist/resolution/frameworks/react.js.map +0 -1
  280. package/dist/resolution/frameworks/ruby.d.ts +0 -8
  281. package/dist/resolution/frameworks/ruby.d.ts.map +0 -1
  282. package/dist/resolution/frameworks/ruby.js +0 -198
  283. package/dist/resolution/frameworks/ruby.js.map +0 -1
  284. package/dist/resolution/frameworks/rust.d.ts +0 -8
  285. package/dist/resolution/frameworks/rust.d.ts.map +0 -1
  286. package/dist/resolution/frameworks/rust.js +0 -207
  287. package/dist/resolution/frameworks/rust.js.map +0 -1
  288. package/dist/resolution/frameworks/svelte.d.ts +0 -9
  289. package/dist/resolution/frameworks/svelte.d.ts.map +0 -1
  290. package/dist/resolution/frameworks/svelte.js +0 -249
  291. package/dist/resolution/frameworks/svelte.js.map +0 -1
  292. package/dist/resolution/frameworks/swift.d.ts +0 -10
  293. package/dist/resolution/frameworks/swift.d.ts.map +0 -1
  294. package/dist/resolution/frameworks/swift.js +0 -376
  295. package/dist/resolution/frameworks/swift.js.map +0 -1
  296. package/dist/resolution/frameworks/vue.d.ts +0 -9
  297. package/dist/resolution/frameworks/vue.d.ts.map +0 -1
  298. package/dist/resolution/frameworks/vue.js +0 -306
  299. package/dist/resolution/frameworks/vue.js.map +0 -1
  300. package/dist/resolution/import-resolver.d.ts +0 -40
  301. package/dist/resolution/import-resolver.d.ts.map +0 -1
  302. package/dist/resolution/import-resolver.js +0 -663
  303. package/dist/resolution/import-resolver.js.map +0 -1
  304. package/dist/resolution/index.d.ts +0 -106
  305. package/dist/resolution/index.d.ts.map +0 -1
  306. package/dist/resolution/index.js +0 -709
  307. package/dist/resolution/index.js.map +0 -1
  308. package/dist/resolution/name-matcher.d.ts +0 -32
  309. package/dist/resolution/name-matcher.d.ts.map +0 -1
  310. package/dist/resolution/name-matcher.js +0 -384
  311. package/dist/resolution/name-matcher.js.map +0 -1
  312. package/dist/resolution/path-aliases.d.ts +0 -68
  313. package/dist/resolution/path-aliases.d.ts.map +0 -1
  314. package/dist/resolution/path-aliases.js +0 -238
  315. package/dist/resolution/path-aliases.js.map +0 -1
  316. package/dist/resolution/strip-comments.d.ts +0 -27
  317. package/dist/resolution/strip-comments.d.ts.map +0 -1
  318. package/dist/resolution/strip-comments.js +0 -441
  319. package/dist/resolution/strip-comments.js.map +0 -1
  320. package/dist/resolution/types.d.ts +0 -172
  321. package/dist/resolution/types.d.ts.map +0 -1
  322. package/dist/resolution/types.js +0 -8
  323. package/dist/resolution/types.js.map +0 -1
  324. package/dist/search/query-parser.d.ts +0 -57
  325. package/dist/search/query-parser.d.ts.map +0 -1
  326. package/dist/search/query-parser.js +0 -177
  327. package/dist/search/query-parser.js.map +0 -1
  328. package/dist/search/query-utils.d.ts +0 -53
  329. package/dist/search/query-utils.d.ts.map +0 -1
  330. package/dist/search/query-utils.js +0 -350
  331. package/dist/search/query-utils.js.map +0 -1
  332. package/dist/sync/git-hooks.d.ts +0 -45
  333. package/dist/sync/git-hooks.d.ts.map +0 -1
  334. package/dist/sync/git-hooks.js +0 -223
  335. package/dist/sync/git-hooks.js.map +0 -1
  336. package/dist/sync/index.d.ts +0 -17
  337. package/dist/sync/index.d.ts.map +0 -1
  338. package/dist/sync/index.js +0 -28
  339. package/dist/sync/index.js.map +0 -1
  340. package/dist/sync/watch-policy.d.ts +0 -48
  341. package/dist/sync/watch-policy.d.ts.map +0 -1
  342. package/dist/sync/watch-policy.js +0 -124
  343. package/dist/sync/watch-policy.js.map +0 -1
  344. package/dist/sync/watcher.d.ts +0 -81
  345. package/dist/sync/watcher.d.ts.map +0 -1
  346. package/dist/sync/watcher.js +0 -194
  347. package/dist/sync/watcher.js.map +0 -1
  348. package/dist/types.d.ts +0 -423
  349. package/dist/types.d.ts.map +0 -1
  350. package/dist/types.js +0 -256
  351. package/dist/types.js.map +0 -1
  352. package/dist/ui/glyphs.d.ts +0 -42
  353. package/dist/ui/glyphs.d.ts.map +0 -1
  354. package/dist/ui/glyphs.js +0 -78
  355. package/dist/ui/glyphs.js.map +0 -1
  356. package/dist/ui/shimmer-progress.d.ts +0 -11
  357. package/dist/ui/shimmer-progress.d.ts.map +0 -1
  358. package/dist/ui/shimmer-progress.js +0 -90
  359. package/dist/ui/shimmer-progress.js.map +0 -1
  360. package/dist/ui/shimmer-worker.d.ts +0 -2
  361. package/dist/ui/shimmer-worker.d.ts.map +0 -1
  362. package/dist/ui/shimmer-worker.js +0 -118
  363. package/dist/ui/shimmer-worker.js.map +0 -1
  364. package/dist/ui/types.d.ts +0 -17
  365. package/dist/ui/types.d.ts.map +0 -1
  366. package/dist/ui/types.js +0 -3
  367. package/dist/ui/types.js.map +0 -1
  368. package/dist/utils.d.ts +0 -205
  369. package/dist/utils.d.ts.map +0 -1
  370. package/dist/utils.js +0 -549
  371. package/dist/utils.js.map +0 -1
  372. package/scripts/agent-eval/audit.sh +0 -68
  373. package/scripts/agent-eval/itrun.sh +0 -107
  374. package/scripts/agent-eval/parse-run.mjs +0 -45
  375. package/scripts/agent-eval/parse-session.mjs +0 -93
  376. package/scripts/agent-eval/run-agent.sh +0 -34
  377. package/scripts/agent-eval/run-all.sh +0 -67
  378. package/scripts/extract-release-notes.mjs +0 -130
  379. package/scripts/local-install.sh +0 -41
  380. package/scripts/patch-tree-sitter-dart.js +0 -112
  381. package/scripts/release.sh +0 -68
@@ -1,2393 +0,0 @@
1
- "use strict";
2
- /**
3
- * Tree-sitter Parser Wrapper
4
- *
5
- * Handles parsing source code and extracting structural information.
6
- */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
- Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.TreeSitterExtractor = exports.generateNodeId = void 0;
42
- exports.extractFromSource = extractFromSource;
43
- const path = __importStar(require("path"));
44
- const grammars_1 = require("./grammars");
45
- const tree_sitter_helpers_1 = require("./tree-sitter-helpers");
46
- const languages_1 = require("./languages");
47
- const liquid_extractor_1 = require("./liquid-extractor");
48
- const svelte_extractor_1 = require("./svelte-extractor");
49
- const dfm_extractor_1 = require("./dfm-extractor");
50
- const vue_extractor_1 = require("./vue-extractor");
51
- const frameworks_1 = require("../resolution/frameworks");
52
- // Re-export for backward compatibility
53
- var tree_sitter_helpers_2 = require("./tree-sitter-helpers");
54
- Object.defineProperty(exports, "generateNodeId", { enumerable: true, get: function () { return tree_sitter_helpers_2.generateNodeId; } });
55
- /**
56
- * Extract the name from a node based on language
57
- */
58
- function extractName(node, source, extractor) {
59
- // Try field name first
60
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, extractor.nameField);
61
- if (nameNode) {
62
- // Unwrap pointer_declarator(s) for C/C++ pointer return types
63
- let resolved = nameNode;
64
- while (resolved.type === 'pointer_declarator') {
65
- const inner = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'declarator') || resolved.namedChild(0);
66
- if (!inner)
67
- break;
68
- resolved = inner;
69
- }
70
- // Handle complex declarators (C/C++)
71
- if (resolved.type === 'function_declarator' || resolved.type === 'declarator') {
72
- const innerName = (0, tree_sitter_helpers_1.getChildByField)(resolved, 'declarator') || resolved.namedChild(0);
73
- return innerName ? (0, tree_sitter_helpers_1.getNodeText)(innerName, source) : (0, tree_sitter_helpers_1.getNodeText)(resolved, source);
74
- }
75
- return (0, tree_sitter_helpers_1.getNodeText)(resolved, source);
76
- }
77
- // For Dart method_signature, look inside inner signature types
78
- if (node.type === 'method_signature') {
79
- for (let i = 0; i < node.namedChildCount; i++) {
80
- const child = node.namedChild(i);
81
- if (child && (child.type === 'function_signature' ||
82
- child.type === 'getter_signature' ||
83
- child.type === 'setter_signature' ||
84
- child.type === 'constructor_signature' ||
85
- child.type === 'factory_constructor_signature')) {
86
- // Find identifier inside the inner signature
87
- for (let j = 0; j < child.namedChildCount; j++) {
88
- const inner = child.namedChild(j);
89
- if (inner?.type === 'identifier') {
90
- return (0, tree_sitter_helpers_1.getNodeText)(inner, source);
91
- }
92
- }
93
- }
94
- }
95
- }
96
- // Arrow/function expressions get their name from the parent variable_declarator,
97
- // not from identifiers in their body. Without this, single-expression arrow
98
- // functions like `const fn = () => someIdentifier` get named "someIdentifier"
99
- // instead of "fn", because the fallback below finds the body identifier.
100
- if (node.type === 'arrow_function' || node.type === 'function_expression') {
101
- return '<anonymous>';
102
- }
103
- // Fall back to first identifier child
104
- for (let i = 0; i < node.namedChildCount; i++) {
105
- const child = node.namedChild(i);
106
- if (child &&
107
- (child.type === 'identifier' ||
108
- child.type === 'type_identifier' ||
109
- child.type === 'simple_identifier' ||
110
- child.type === 'constant')) {
111
- return (0, tree_sitter_helpers_1.getNodeText)(child, source);
112
- }
113
- }
114
- return '<anonymous>';
115
- }
116
- /**
117
- * Tree-sitter node kinds that represent constructor invocations
118
- * (`new Foo()` and friends). Used by extractInstantiation to emit
119
- * an `instantiates` reference targeting the class name.
120
- */
121
- const INSTANTIATION_KINDS = new Set([
122
- 'new_expression', // typescript / javascript / tsx / jsx
123
- 'object_creation_expression', // java / c#
124
- 'instance_creation_expression', // some grammars
125
- ]);
126
- /**
127
- * TreeSitterExtractor - Main extraction class
128
- */
129
- class TreeSitterExtractor {
130
- filePath;
131
- language;
132
- source;
133
- tree = null;
134
- nodes = [];
135
- edges = [];
136
- unresolvedReferences = [];
137
- errors = [];
138
- extractor = null;
139
- nodeStack = []; // Stack of parent node IDs
140
- methodIndex = null; // lookup key → node ID for Pascal defProc lookup
141
- constructor(filePath, source, language) {
142
- this.filePath = filePath;
143
- this.source = source;
144
- this.language = language || (0, grammars_1.detectLanguage)(filePath, source);
145
- this.extractor = languages_1.EXTRACTORS[this.language] || null;
146
- }
147
- /**
148
- * Parse and extract from the source code
149
- */
150
- extract() {
151
- const startTime = Date.now();
152
- if (!(0, grammars_1.isLanguageSupported)(this.language)) {
153
- return {
154
- nodes: [],
155
- edges: [],
156
- unresolvedReferences: [],
157
- errors: [
158
- {
159
- message: `Unsupported language: ${this.language}`,
160
- filePath: this.filePath,
161
- severity: 'error',
162
- code: 'unsupported_language',
163
- },
164
- ],
165
- durationMs: Date.now() - startTime,
166
- };
167
- }
168
- const parser = (0, grammars_1.getParser)(this.language);
169
- if (!parser) {
170
- return {
171
- nodes: [],
172
- edges: [],
173
- unresolvedReferences: [],
174
- errors: [
175
- {
176
- message: `Failed to get parser for language: ${this.language}`,
177
- filePath: this.filePath,
178
- severity: 'error',
179
- code: 'parser_error',
180
- },
181
- ],
182
- durationMs: Date.now() - startTime,
183
- };
184
- }
185
- try {
186
- this.tree = parser.parse(this.source) ?? null;
187
- if (!this.tree) {
188
- throw new Error('Parser returned null tree');
189
- }
190
- // Create file node representing the source file
191
- const fileNode = {
192
- id: `file:${this.filePath}`,
193
- kind: 'file',
194
- name: path.basename(this.filePath),
195
- qualifiedName: this.filePath,
196
- filePath: this.filePath,
197
- language: this.language,
198
- startLine: 1,
199
- endLine: this.source.split('\n').length,
200
- startColumn: 0,
201
- endColumn: 0,
202
- isExported: false,
203
- updatedAt: Date.now(),
204
- };
205
- this.nodes.push(fileNode);
206
- // Push file node onto stack so top-level declarations get contains edges
207
- this.nodeStack.push(fileNode.id);
208
- this.visitNode(this.tree.rootNode);
209
- this.nodeStack.pop();
210
- }
211
- catch (error) {
212
- const msg = error instanceof Error ? error.message : String(error);
213
- // WASM memory errors leave the module in a corrupted state — all subsequent
214
- // parses would also fail. Re-throw so the worker can detect and crash,
215
- // forcing a clean restart with a fresh heap.
216
- if (msg.includes('memory access out of bounds') || msg.includes('out of memory')) {
217
- throw error;
218
- }
219
- this.errors.push({
220
- message: `Parse error: ${msg}`,
221
- filePath: this.filePath,
222
- severity: 'error',
223
- code: 'parse_error',
224
- });
225
- }
226
- finally {
227
- // Free tree-sitter WASM memory immediately — trees hold native heap memory
228
- // invisible to V8's GC that accumulates across thousands of files.
229
- if (this.tree) {
230
- this.tree.delete();
231
- this.tree = null;
232
- }
233
- // Release source string to reduce GC pressure
234
- this.source = '';
235
- }
236
- return {
237
- nodes: this.nodes,
238
- edges: this.edges,
239
- unresolvedReferences: this.unresolvedReferences,
240
- errors: this.errors,
241
- durationMs: Date.now() - startTime,
242
- };
243
- }
244
- /**
245
- * Visit a node and extract information
246
- */
247
- visitNode(node) {
248
- if (!this.extractor)
249
- return;
250
- const nodeType = node.type;
251
- let skipChildren = false;
252
- // Language-specific custom visitor hook
253
- if (this.extractor.visitNode) {
254
- const ctx = this.makeExtractorContext();
255
- const handled = this.extractor.visitNode(node, ctx);
256
- if (handled)
257
- return;
258
- }
259
- // Pascal-specific AST handling
260
- if (this.language === 'pascal') {
261
- skipChildren = this.visitPascalNode(node);
262
- if (skipChildren)
263
- return;
264
- }
265
- // Check for function declarations
266
- // For Python/Ruby, function_definition inside a class should be treated as method
267
- if (this.extractor.functionTypes.includes(nodeType)) {
268
- if (this.isInsideClassLikeNode() && this.extractor.methodTypes.includes(nodeType)) {
269
- // Inside a class - treat as method
270
- this.extractMethod(node);
271
- skipChildren = true; // extractMethod visits children via visitFunctionBody
272
- }
273
- else {
274
- this.extractFunction(node);
275
- skipChildren = true; // extractFunction visits children via visitFunctionBody
276
- }
277
- }
278
- // Check for class declarations
279
- else if (this.extractor.classTypes.includes(nodeType)) {
280
- // Some languages reuse class_declaration for structs/enums (e.g. Swift)
281
- const classification = this.extractor.classifyClassNode?.(node) ?? 'class';
282
- if (classification === 'struct') {
283
- this.extractStruct(node);
284
- }
285
- else if (classification === 'enum') {
286
- this.extractEnum(node);
287
- }
288
- else if (classification === 'interface') {
289
- this.extractInterface(node);
290
- }
291
- else if (classification === 'trait') {
292
- this.extractClass(node, 'trait');
293
- }
294
- else {
295
- this.extractClass(node);
296
- }
297
- skipChildren = true; // extractClass visits body children
298
- }
299
- // Extra class node types (e.g. Dart mixin_declaration, extension_declaration)
300
- else if (this.extractor.extraClassNodeTypes?.includes(nodeType)) {
301
- this.extractClass(node);
302
- skipChildren = true;
303
- }
304
- // Check for method declarations (only if not already handled by functionTypes)
305
- else if (this.extractor.methodTypes.includes(nodeType)) {
306
- this.extractMethod(node);
307
- skipChildren = true; // extractMethod visits children via visitFunctionBody
308
- }
309
- // Check for interface/protocol/trait declarations
310
- else if (this.extractor.interfaceTypes.includes(nodeType)) {
311
- this.extractInterface(node);
312
- skipChildren = true; // extractInterface visits body children
313
- }
314
- // Check for struct declarations
315
- else if (this.extractor.structTypes.includes(nodeType)) {
316
- this.extractStruct(node);
317
- skipChildren = true; // extractStruct visits body children
318
- }
319
- // Check for enum declarations
320
- else if (this.extractor.enumTypes.includes(nodeType)) {
321
- this.extractEnum(node);
322
- skipChildren = true; // extractEnum visits body children
323
- }
324
- // Check for type alias declarations (e.g. `type X = ...` in TypeScript)
325
- // For Go, type_spec wraps struct/interface definitions — resolveTypeAliasKind
326
- // detects these and extractTypeAlias creates the correct node kind.
327
- else if (this.extractor.typeAliasTypes.includes(nodeType)) {
328
- skipChildren = this.extractTypeAlias(node);
329
- }
330
- // Check for class properties (e.g. C# property_declaration)
331
- else if (this.extractor.propertyTypes?.includes(nodeType) && this.isInsideClassLikeNode()) {
332
- this.extractProperty(node);
333
- skipChildren = true;
334
- }
335
- // Check for class fields (e.g. Java field_declaration, C# field_declaration)
336
- else if (this.extractor.fieldTypes?.includes(nodeType) && this.isInsideClassLikeNode()) {
337
- this.extractField(node);
338
- skipChildren = true;
339
- }
340
- // Check for variable declarations (const, let, var, etc.)
341
- // Only extract top-level variables (not inside functions/methods)
342
- else if (this.extractor.variableTypes.includes(nodeType) && !this.isInsideClassLikeNode()) {
343
- this.extractVariable(node);
344
- skipChildren = true; // extractVariable handles children
345
- }
346
- // `export_statement` itself is not extracted — the walker descends
347
- // into children, where the inner declaration (lexical_declaration,
348
- // function_declaration, class_declaration, etc.) is dispatched to
349
- // its own extractor. `isExported` walks the parent chain, so the
350
- // exported flag is preserved automatically.
351
- //
352
- // Calling extractExportedVariables here AND descending caused every
353
- // `export const X = ...` to produce two nodes for the same symbol —
354
- // one kind:'variable' from extractExportedVariables and one
355
- // kind:'constant' from extractVariable. The dedicated dispatch is
356
- // the correct one (it picks kind from isConst, captures the
357
- // initializer signature, and walks type annotations); the
358
- // export-statement helper was redundant.
359
- // Check for imports
360
- else if (this.extractor.importTypes.includes(nodeType)) {
361
- this.extractImport(node);
362
- }
363
- // Check for function calls
364
- else if (this.extractor.callTypes.includes(nodeType)) {
365
- this.extractCall(node);
366
- }
367
- // `new Foo(...)` / `Foo::new(...)` / object_creation_expression —
368
- // produce an `instantiates` reference. Children still walked so
369
- // nested calls inside the constructor args (`new Foo(bar())`) get
370
- // their own `calls` refs.
371
- else if (INSTANTIATION_KINDS.has(nodeType)) {
372
- this.extractInstantiation(node);
373
- }
374
- // (Decorator handling lives inside the symbol-creating extractors
375
- // — extractClass / extractFunction / extractProperty — because the
376
- // decorator node sits BEFORE the symbol in the AST and the walker
377
- // would otherwise see the wrong nodeStack head.)
378
- // Rust: `impl Trait for Type { ... }` — creates implements edge from Type to Trait
379
- else if (nodeType === 'impl_item') {
380
- this.extractRustImplItem(node);
381
- }
382
- // Visit children (unless the extract method already visited them)
383
- if (!skipChildren) {
384
- for (let i = 0; i < node.namedChildCount; i++) {
385
- const child = node.namedChild(i);
386
- if (child) {
387
- this.visitNode(child);
388
- }
389
- }
390
- }
391
- }
392
- /**
393
- * Create a Node object
394
- */
395
- createNode(kind, name, node, extra) {
396
- // Skip nodes with empty/missing names — they are not meaningful symbols
397
- // and would cause FK violations when edges reference them (see issue #42)
398
- if (!name) {
399
- return null;
400
- }
401
- const id = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, kind, name, node.startPosition.row + 1);
402
- const newNode = {
403
- id,
404
- kind,
405
- name,
406
- qualifiedName: this.buildQualifiedName(name),
407
- filePath: this.filePath,
408
- language: this.language,
409
- startLine: node.startPosition.row + 1,
410
- endLine: node.endPosition.row + 1,
411
- startColumn: node.startPosition.column,
412
- endColumn: node.endPosition.column,
413
- updatedAt: Date.now(),
414
- ...extra,
415
- };
416
- this.nodes.push(newNode);
417
- // Add containment edge from parent
418
- if (this.nodeStack.length > 0) {
419
- const parentId = this.nodeStack[this.nodeStack.length - 1];
420
- if (parentId) {
421
- this.edges.push({
422
- source: parentId,
423
- target: id,
424
- kind: 'contains',
425
- });
426
- }
427
- }
428
- return newNode;
429
- }
430
- /**
431
- * Find first named child whose type is in the given list.
432
- * Used to locate inner type nodes (e.g. enum_specifier inside a typedef).
433
- */
434
- findChildByTypes(node, types) {
435
- for (let i = 0; i < node.namedChildCount; i++) {
436
- const child = node.namedChild(i);
437
- if (child && types.includes(child.type))
438
- return child;
439
- }
440
- return null;
441
- }
442
- /**
443
- * Build qualified name from node stack
444
- */
445
- buildQualifiedName(name) {
446
- // Build a qualified name from the semantic hierarchy only (no file path).
447
- // The file path is stored separately in filePath and pollutes FTS if included here.
448
- const parts = [];
449
- for (const nodeId of this.nodeStack) {
450
- const node = this.nodes.find((n) => n.id === nodeId);
451
- if (node && node.kind !== 'file') {
452
- parts.push(node.name);
453
- }
454
- }
455
- parts.push(name);
456
- return parts.join('::');
457
- }
458
- /**
459
- * Build an ExtractorContext for passing to language-specific visitNode hooks.
460
- */
461
- makeExtractorContext() {
462
- // eslint-disable-next-line @typescript-eslint/no-this-alias
463
- const self = this;
464
- return {
465
- createNode: (kind, name, node, extra) => self.createNode(kind, name, node, extra),
466
- visitNode: (node) => self.visitNode(node),
467
- visitFunctionBody: (body, functionId) => self.visitFunctionBody(body, functionId),
468
- addUnresolvedReference: (ref) => self.unresolvedReferences.push(ref),
469
- pushScope: (nodeId) => self.nodeStack.push(nodeId),
470
- popScope: () => self.nodeStack.pop(),
471
- get filePath() { return self.filePath; },
472
- get source() { return self.source; },
473
- get nodeStack() { return self.nodeStack; },
474
- get nodes() { return self.nodes; },
475
- };
476
- }
477
- /**
478
- * Check if the current node stack indicates we are inside a class-like node
479
- * (class, struct, interface, trait). File nodes do not count as class-like.
480
- */
481
- isInsideClassLikeNode() {
482
- if (this.nodeStack.length === 0)
483
- return false;
484
- const parentId = this.nodeStack[this.nodeStack.length - 1];
485
- if (!parentId)
486
- return false;
487
- const parentNode = this.nodes.find((n) => n.id === parentId);
488
- if (!parentNode)
489
- return false;
490
- return (parentNode.kind === 'class' ||
491
- parentNode.kind === 'struct' ||
492
- parentNode.kind === 'interface' ||
493
- parentNode.kind === 'trait' ||
494
- parentNode.kind === 'enum' ||
495
- parentNode.kind === 'module');
496
- }
497
- /**
498
- * Extract a function
499
- */
500
- extractFunction(node) {
501
- if (!this.extractor)
502
- return;
503
- // If the language provides getReceiverType and this function has a receiver
504
- // (e.g., Rust function_item inside an impl block), extract as method instead
505
- if (this.extractor.getReceiverType?.(node, this.source)) {
506
- this.extractMethod(node);
507
- return;
508
- }
509
- let name = extractName(node, this.source, this.extractor);
510
- // For arrow functions and function expressions assigned to variables,
511
- // resolve the name from the parent variable_declarator.
512
- // e.g. `export const useAuth = () => { ... }` — the arrow_function node
513
- // has no `name` field; the name lives on the variable_declarator.
514
- if (name === '<anonymous>' &&
515
- (node.type === 'arrow_function' || node.type === 'function_expression')) {
516
- const parent = node.parent;
517
- if (parent?.type === 'variable_declarator') {
518
- const varName = (0, tree_sitter_helpers_1.getChildByField)(parent, 'name');
519
- if (varName) {
520
- name = (0, tree_sitter_helpers_1.getNodeText)(varName, this.source);
521
- }
522
- }
523
- }
524
- if (name === '<anonymous>')
525
- return; // Skip anonymous functions
526
- // Check for misparse artifacts (e.g. C++ macros causing "namespace detail" functions)
527
- // Skip the node but still visit the body for calls and structural nodes
528
- if (this.extractor.isMisparsedFunction?.(name, node)) {
529
- const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
530
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
531
- if (body) {
532
- this.visitFunctionBody(body, '');
533
- }
534
- return;
535
- }
536
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
537
- const signature = this.extractor.getSignature?.(node, this.source);
538
- const visibility = this.extractor.getVisibility?.(node);
539
- const isExported = this.extractor.isExported?.(node, this.source);
540
- const isAsync = this.extractor.isAsync?.(node);
541
- const isStatic = this.extractor.isStatic?.(node);
542
- const funcNode = this.createNode('function', name, node, {
543
- docstring,
544
- signature,
545
- visibility,
546
- isExported,
547
- isAsync,
548
- isStatic,
549
- });
550
- if (!funcNode)
551
- return;
552
- // Extract type annotations (parameter types and return type)
553
- this.extractTypeAnnotations(node, funcNode.id);
554
- // Extract decorators applied to the function (rare in JS/TS but
555
- // present in Python `@decorator def f():` and Java/Kotlin
556
- // annotations on free functions).
557
- this.extractDecoratorsFor(node, funcNode.id);
558
- // Push to stack and visit body
559
- this.nodeStack.push(funcNode.id);
560
- const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
561
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
562
- if (body) {
563
- this.visitFunctionBody(body, funcNode.id);
564
- }
565
- this.nodeStack.pop();
566
- }
567
- /**
568
- * Extract a class
569
- */
570
- extractClass(node, kind = 'class') {
571
- if (!this.extractor)
572
- return;
573
- const name = extractName(node, this.source, this.extractor);
574
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
575
- const visibility = this.extractor.getVisibility?.(node);
576
- const isExported = this.extractor.isExported?.(node, this.source);
577
- const classNode = this.createNode(kind, name, node, {
578
- docstring,
579
- visibility,
580
- isExported,
581
- });
582
- if (!classNode)
583
- return;
584
- // Extract extends/implements
585
- this.extractInheritance(node, classNode.id);
586
- // Extract decorators applied to the class (`@Foo class X {}`).
587
- this.extractDecoratorsFor(node, classNode.id);
588
- // Push to stack and visit body
589
- this.nodeStack.push(classNode.id);
590
- let body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
591
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
592
- if (!body)
593
- body = node;
594
- // Visit all children for methods and properties
595
- for (let i = 0; i < body.namedChildCount; i++) {
596
- const child = body.namedChild(i);
597
- if (child) {
598
- this.visitNode(child);
599
- }
600
- }
601
- this.nodeStack.pop();
602
- }
603
- /**
604
- * Extract a method
605
- */
606
- extractMethod(node) {
607
- if (!this.extractor)
608
- return;
609
- // For languages with receiver types (Go, Rust), include receiver in qualified name
610
- // so FTS can match "scrapeLoop.run" → qualified_name "...::scrapeLoop::run"
611
- const receiverType = this.extractor.getReceiverType?.(node, this.source);
612
- // For most languages, only extract as method if inside a class-like node
613
- // Languages with methodsAreTopLevel (e.g. Go) always treat them as methods
614
- // Languages with getReceiverType (e.g. Rust) extract as method when receiver is found
615
- if (!this.isInsideClassLikeNode() && !this.extractor.methodsAreTopLevel && !receiverType) {
616
- // Skip method_definition nodes inside object literals (getters/setters/methods
617
- // in inline objects). These are ephemeral and create noise (e.g., Svelte context
618
- // objects: `ctx.set({ get view() { ... } })`).
619
- if (node.parent?.type === 'object' || node.parent?.type === 'object_expression') {
620
- return;
621
- }
622
- // Not inside a class-like node and no receiver type, treat as function
623
- this.extractFunction(node);
624
- return;
625
- }
626
- const name = extractName(node, this.source, this.extractor);
627
- // Check for misparse artifacts (e.g. C++ "switch" inside macro-confused class body)
628
- if (this.extractor.isMisparsedFunction?.(name, node)) {
629
- const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
630
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
631
- if (body) {
632
- this.visitFunctionBody(body, '');
633
- }
634
- return;
635
- }
636
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
637
- const signature = this.extractor.getSignature?.(node, this.source);
638
- const visibility = this.extractor.getVisibility?.(node);
639
- const isAsync = this.extractor.isAsync?.(node);
640
- const isStatic = this.extractor.isStatic?.(node);
641
- const extraProps = {
642
- docstring,
643
- signature,
644
- visibility,
645
- isAsync,
646
- isStatic,
647
- };
648
- if (receiverType) {
649
- extraProps.qualifiedName = `${receiverType}::${name}`;
650
- }
651
- const methodNode = this.createNode('method', name, node, extraProps);
652
- if (!methodNode)
653
- return;
654
- // For methods with a receiver type but no class-like parent on the stack
655
- // (e.g., Rust impl blocks), add a contains edge from the owning struct/trait
656
- if (receiverType && !this.isInsideClassLikeNode()) {
657
- const ownerNode = this.nodes.find((n) => n.name === receiverType &&
658
- n.filePath === this.filePath &&
659
- (n.kind === 'struct' || n.kind === 'class' || n.kind === 'enum' || n.kind === 'trait'));
660
- if (ownerNode) {
661
- this.edges.push({
662
- source: ownerNode.id,
663
- target: methodNode.id,
664
- kind: 'contains',
665
- });
666
- }
667
- }
668
- // Extract type annotations (parameter types and return type)
669
- this.extractTypeAnnotations(node, methodNode.id);
670
- // Extract decorators (`@Get('/list') list() {}`).
671
- this.extractDecoratorsFor(node, methodNode.id);
672
- // Push to stack and visit body
673
- this.nodeStack.push(methodNode.id);
674
- const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
675
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
676
- if (body) {
677
- this.visitFunctionBody(body, methodNode.id);
678
- }
679
- this.nodeStack.pop();
680
- }
681
- /**
682
- * Extract an interface/protocol/trait
683
- */
684
- extractInterface(node) {
685
- if (!this.extractor)
686
- return;
687
- const name = extractName(node, this.source, this.extractor);
688
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
689
- const isExported = this.extractor.isExported?.(node, this.source);
690
- const kind = this.extractor.interfaceKind ?? 'interface';
691
- const interfaceNode = this.createNode(kind, name, node, {
692
- docstring,
693
- isExported,
694
- });
695
- if (!interfaceNode)
696
- return;
697
- // Extract extends (interface inheritance)
698
- this.extractInheritance(node, interfaceNode.id);
699
- // Visit body children for interface methods and nested types
700
- this.nodeStack.push(interfaceNode.id);
701
- let body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
702
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
703
- if (!body)
704
- body = node;
705
- for (let i = 0; i < body.namedChildCount; i++) {
706
- const child = body.namedChild(i);
707
- if (child) {
708
- this.visitNode(child);
709
- }
710
- }
711
- this.nodeStack.pop();
712
- }
713
- /**
714
- * Extract a struct
715
- */
716
- extractStruct(node) {
717
- if (!this.extractor)
718
- return;
719
- // Skip forward declarations and type references (no body = not a definition)
720
- const body = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
721
- if (!body)
722
- return;
723
- const name = extractName(node, this.source, this.extractor);
724
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
725
- const visibility = this.extractor.getVisibility?.(node);
726
- const isExported = this.extractor.isExported?.(node, this.source);
727
- const structNode = this.createNode('struct', name, node, {
728
- docstring,
729
- visibility,
730
- isExported,
731
- });
732
- if (!structNode)
733
- return;
734
- // Extract inheritance (e.g. Swift: struct HTTPMethod: RawRepresentable)
735
- this.extractInheritance(node, structNode.id);
736
- // Push to stack for field extraction
737
- this.nodeStack.push(structNode.id);
738
- for (let i = 0; i < body.namedChildCount; i++) {
739
- const child = body.namedChild(i);
740
- if (child) {
741
- this.visitNode(child);
742
- }
743
- }
744
- this.nodeStack.pop();
745
- }
746
- /**
747
- * Extract an enum
748
- */
749
- extractEnum(node) {
750
- if (!this.extractor)
751
- return;
752
- // Skip forward declarations and type references (no body = not a definition)
753
- const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
754
- ?? (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
755
- if (!body)
756
- return;
757
- const name = extractName(node, this.source, this.extractor);
758
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
759
- const visibility = this.extractor.getVisibility?.(node);
760
- const isExported = this.extractor.isExported?.(node, this.source);
761
- const enumNode = this.createNode('enum', name, node, {
762
- docstring,
763
- visibility,
764
- isExported,
765
- });
766
- if (!enumNode)
767
- return;
768
- // Extract inheritance (e.g. Swift: enum AFError: Error)
769
- this.extractInheritance(node, enumNode.id);
770
- // Push to stack and visit body children (enum members, nested types, methods)
771
- this.nodeStack.push(enumNode.id);
772
- const memberTypes = this.extractor.enumMemberTypes;
773
- for (let i = 0; i < body.namedChildCount; i++) {
774
- const child = body.namedChild(i);
775
- if (!child)
776
- continue;
777
- if (memberTypes?.includes(child.type)) {
778
- this.extractEnumMembers(child);
779
- }
780
- else {
781
- this.visitNode(child);
782
- }
783
- }
784
- this.nodeStack.pop();
785
- }
786
- /**
787
- * Extract enum member names from an enum member node.
788
- * Handles multi-case declarations (Swift: `case put, delete`) and single-case patterns.
789
- */
790
- extractEnumMembers(node) {
791
- // Try field-based name first (e.g. Rust enum_variant has a 'name' field)
792
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
793
- if (nameNode) {
794
- this.createNode('enum_member', (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source), node);
795
- return;
796
- }
797
- // Check for identifier-like children (Swift: simple_identifier, TS: property_identifier)
798
- let found = false;
799
- for (let i = 0; i < node.namedChildCount; i++) {
800
- const child = node.namedChild(i);
801
- if (child && (child.type === 'simple_identifier' || child.type === 'identifier' || child.type === 'property_identifier')) {
802
- this.createNode('enum_member', (0, tree_sitter_helpers_1.getNodeText)(child, this.source), child);
803
- found = true;
804
- }
805
- }
806
- // If the node itself IS the identifier (e.g. TS property_identifier directly in enum body)
807
- if (!found && node.namedChildCount === 0) {
808
- this.createNode('enum_member', (0, tree_sitter_helpers_1.getNodeText)(node, this.source), node);
809
- }
810
- }
811
- /**
812
- * Extract a class property declaration (e.g. C# `public string Name { get; set; }`).
813
- * Extracts as 'property' kind node inside the owning class.
814
- */
815
- extractProperty(node) {
816
- if (!this.extractor)
817
- return;
818
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
819
- const visibility = this.extractor.getVisibility?.(node);
820
- const isStatic = this.extractor.isStatic?.(node) ?? false;
821
- // Property name is a direct identifier child
822
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name')
823
- || node.namedChildren.find(c => c.type === 'identifier');
824
- if (!nameNode)
825
- return;
826
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
827
- // Get property type from the type child (first named child that isn't modifier or identifier)
828
- const typeNode = node.namedChildren.find(c => c.type !== 'modifier' && c.type !== 'modifiers'
829
- && c.type !== 'identifier' && c.type !== 'accessor_list'
830
- && c.type !== 'accessors' && c.type !== 'equals_value_clause');
831
- const typeText = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : undefined;
832
- const signature = typeText ? `${typeText} ${name}` : name;
833
- const propNode = this.createNode('property', name, node, {
834
- docstring,
835
- signature,
836
- visibility,
837
- isStatic,
838
- });
839
- // `@Inject() private svc: Foo` and similar — capture the
840
- // decorator->target relationship for class properties too.
841
- if (propNode) {
842
- this.extractDecoratorsFor(node, propNode.id);
843
- }
844
- }
845
- /**
846
- * Extract a class field declaration (e.g. Java field_declaration, C# field_declaration).
847
- * Extracts each declarator as a 'field' kind node inside the owning class.
848
- */
849
- extractField(node) {
850
- if (!this.extractor)
851
- return;
852
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
853
- const visibility = this.extractor.getVisibility?.(node);
854
- const isStatic = this.extractor.isStatic?.(node) ?? false;
855
- // Java field_declaration: "private final String name = value;" → variable_declarator(s) are direct children
856
- // C# field_declaration: wraps in variable_declaration → variable_declarator(s)
857
- let declarators = node.namedChildren.filter(c => c.type === 'variable_declarator');
858
- // C#: look inside variable_declaration wrapper
859
- if (declarators.length === 0) {
860
- const varDecl = node.namedChildren.find(c => c.type === 'variable_declaration');
861
- if (varDecl) {
862
- declarators = varDecl.namedChildren.filter(c => c.type === 'variable_declarator');
863
- }
864
- }
865
- // PHP property_declaration: property_element → variable_name → name
866
- if (declarators.length === 0) {
867
- const propElements = node.namedChildren.filter(c => c.type === 'property_element');
868
- if (propElements.length > 0) {
869
- // Get type annotation if present (e.g. "string", "int", "?Foo")
870
- const typeNode = node.namedChildren.find(c => c.type !== 'visibility_modifier' && c.type !== 'static_modifier'
871
- && c.type !== 'readonly_modifier' && c.type !== 'property_element'
872
- && c.type !== 'var_modifier');
873
- const typeText = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : undefined;
874
- for (const elem of propElements) {
875
- const varName = elem.namedChildren.find(c => c.type === 'variable_name');
876
- const nameNode = varName?.namedChildren.find(c => c.type === 'name');
877
- if (!nameNode)
878
- continue;
879
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
880
- const signature = typeText ? `${typeText} $${name}` : `$${name}`;
881
- this.createNode('field', name, elem, {
882
- docstring,
883
- signature,
884
- visibility,
885
- isStatic,
886
- });
887
- }
888
- return;
889
- }
890
- }
891
- if (declarators.length > 0) {
892
- // Get field type from the type child
893
- // Java: type is a direct child of field_declaration
894
- // C#: type is inside variable_declaration wrapper
895
- const varDecl = node.namedChildren.find(c => c.type === 'variable_declaration');
896
- const typeSearchNode = varDecl ?? node;
897
- const typeNode = typeSearchNode.namedChildren.find(c => c.type !== 'modifiers' && c.type !== 'modifier' && c.type !== 'variable_declarator'
898
- && c.type !== 'variable_declaration' && c.type !== 'marker_annotation' && c.type !== 'annotation');
899
- const typeText = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : undefined;
900
- for (const decl of declarators) {
901
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(decl, 'name')
902
- || decl.namedChildren.find(c => c.type === 'identifier');
903
- if (!nameNode)
904
- continue;
905
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
906
- const signature = typeText ? `${typeText} ${name}` : name;
907
- const fieldNode = this.createNode('field', name, decl, {
908
- docstring,
909
- signature,
910
- visibility,
911
- isStatic,
912
- });
913
- // Java/Kotlin annotations / TS field decorators sit on the
914
- // outer field_declaration, not on the individual declarator.
915
- if (fieldNode)
916
- this.extractDecoratorsFor(node, fieldNode.id);
917
- }
918
- }
919
- else {
920
- // Fallback: try to find an identifier child directly
921
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name')
922
- || node.namedChildren.find(c => c.type === 'identifier');
923
- if (nameNode) {
924
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
925
- this.createNode('field', name, node, {
926
- docstring,
927
- visibility,
928
- isStatic,
929
- });
930
- }
931
- }
932
- }
933
- /**
934
- * Extract a variable declaration (const, let, var, etc.)
935
- *
936
- * Extracts top-level and module-level variable declarations.
937
- * Captures the variable name and first 100 chars of initializer in signature for searchability.
938
- */
939
- extractVariable(node) {
940
- if (!this.extractor)
941
- return;
942
- // Different languages have different variable declaration structures
943
- // TypeScript/JavaScript: lexical_declaration contains variable_declarator children
944
- // Python: assignment has left (identifier) and right (value)
945
- // Go: var_declaration, short_var_declaration, const_declaration
946
- const isConst = this.extractor.isConst?.(node) ?? false;
947
- const kind = isConst ? 'constant' : 'variable';
948
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
949
- const isExported = this.extractor.isExported?.(node, this.source) ?? false;
950
- // Extract variable declarators based on language
951
- if (this.language === 'typescript' || this.language === 'javascript' ||
952
- this.language === 'tsx' || this.language === 'jsx') {
953
- // Handle lexical_declaration and variable_declaration
954
- // These contain one or more variable_declarator children
955
- for (let i = 0; i < node.namedChildCount; i++) {
956
- const child = node.namedChild(i);
957
- if (child?.type === 'variable_declarator') {
958
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(child, 'name');
959
- const valueNode = (0, tree_sitter_helpers_1.getChildByField)(child, 'value');
960
- if (nameNode) {
961
- // Skip destructured patterns (e.g., `let { x, y } = $props()` in Svelte)
962
- // These produce ugly multi-line names like "{ class: className }"
963
- if (nameNode.type === 'object_pattern' || nameNode.type === 'array_pattern') {
964
- continue;
965
- }
966
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
967
- // Arrow functions / function expressions: extract as function instead of variable
968
- if (valueNode && (valueNode.type === 'arrow_function' || valueNode.type === 'function_expression')) {
969
- this.extractFunction(valueNode);
970
- continue;
971
- }
972
- // Capture first 100 chars of initializer for context (stored in signature for searchability)
973
- const initValue = valueNode ? (0, tree_sitter_helpers_1.getNodeText)(valueNode, this.source).slice(0, 100) : undefined;
974
- const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
975
- const varNode = this.createNode(kind, name, child, {
976
- docstring,
977
- signature: initSignature,
978
- isExported,
979
- });
980
- // Extract type annotation references (e.g., const x: ITextModel = ...)
981
- if (varNode) {
982
- this.extractVariableTypeAnnotation(child, varNode.id);
983
- }
984
- }
985
- }
986
- }
987
- }
988
- else if (this.language === 'python' || this.language === 'ruby') {
989
- // Python/Ruby assignment: left = right
990
- const left = (0, tree_sitter_helpers_1.getChildByField)(node, 'left') || node.namedChild(0);
991
- const right = (0, tree_sitter_helpers_1.getChildByField)(node, 'right') || node.namedChild(1);
992
- if (left && left.type === 'identifier') {
993
- const name = (0, tree_sitter_helpers_1.getNodeText)(left, this.source);
994
- // Skip if name starts with lowercase and looks like a function call result
995
- // Python constants are usually UPPER_CASE
996
- const initValue = right ? (0, tree_sitter_helpers_1.getNodeText)(right, this.source).slice(0, 100) : undefined;
997
- const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
998
- this.createNode(kind, name, node, {
999
- docstring,
1000
- signature: initSignature,
1001
- });
1002
- }
1003
- }
1004
- else if (this.language === 'go') {
1005
- // Go: var_declaration, short_var_declaration, const_declaration
1006
- // These can have multiple identifiers on the left
1007
- const specs = node.namedChildren.filter(c => c.type === 'var_spec' || c.type === 'const_spec');
1008
- for (const spec of specs) {
1009
- const nameNode = spec.namedChild(0);
1010
- if (nameNode && nameNode.type === 'identifier') {
1011
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
1012
- const valueNode = spec.namedChildCount > 1 ? spec.namedChild(spec.namedChildCount - 1) : null;
1013
- const initValue = valueNode ? (0, tree_sitter_helpers_1.getNodeText)(valueNode, this.source).slice(0, 100) : undefined;
1014
- const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
1015
- this.createNode(node.type === 'const_declaration' ? 'constant' : 'variable', name, spec, {
1016
- docstring,
1017
- signature: initSignature,
1018
- });
1019
- }
1020
- }
1021
- // Handle short_var_declaration (:=)
1022
- if (node.type === 'short_var_declaration') {
1023
- const left = (0, tree_sitter_helpers_1.getChildByField)(node, 'left');
1024
- const right = (0, tree_sitter_helpers_1.getChildByField)(node, 'right');
1025
- if (left) {
1026
- // Can be expression_list with multiple identifiers
1027
- const identifiers = left.type === 'expression_list'
1028
- ? left.namedChildren.filter(c => c.type === 'identifier')
1029
- : [left];
1030
- for (const id of identifiers) {
1031
- const name = (0, tree_sitter_helpers_1.getNodeText)(id, this.source);
1032
- const initValue = right ? (0, tree_sitter_helpers_1.getNodeText)(right, this.source).slice(0, 100) : undefined;
1033
- const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
1034
- this.createNode('variable', name, node, {
1035
- docstring,
1036
- signature: initSignature,
1037
- });
1038
- }
1039
- }
1040
- }
1041
- }
1042
- else {
1043
- // Generic fallback for other languages
1044
- // Try to find identifier children
1045
- for (let i = 0; i < node.namedChildCount; i++) {
1046
- const child = node.namedChild(i);
1047
- if (child?.type === 'identifier' || child?.type === 'variable_declarator') {
1048
- const name = child.type === 'identifier'
1049
- ? (0, tree_sitter_helpers_1.getNodeText)(child, this.source)
1050
- : extractName(child, this.source, this.extractor);
1051
- if (name && name !== '<anonymous>') {
1052
- this.createNode(kind, name, child, {
1053
- docstring,
1054
- isExported,
1055
- });
1056
- }
1057
- }
1058
- }
1059
- }
1060
- }
1061
- /**
1062
- * Extract a type alias (e.g. `export type X = ...` in TypeScript).
1063
- * For languages like Go, resolveTypeAliasKind detects when the type_spec
1064
- * wraps a struct or interface definition and creates the correct node kind.
1065
- * Returns true if children should be skipped (struct/interface handled body visiting).
1066
- */
1067
- extractTypeAlias(node) {
1068
- if (!this.extractor)
1069
- return false;
1070
- const name = extractName(node, this.source, this.extractor);
1071
- if (name === '<anonymous>')
1072
- return false;
1073
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
1074
- const isExported = this.extractor.isExported?.(node, this.source);
1075
- // Check if this type alias is actually a struct or interface definition
1076
- // (e.g. Go: `type Foo struct { ... }` is a type_spec wrapping struct_type)
1077
- const resolvedKind = this.extractor.resolveTypeAliasKind?.(node, this.source);
1078
- if (resolvedKind === 'struct') {
1079
- const structNode = this.createNode('struct', name, node, { docstring, isExported });
1080
- if (!structNode)
1081
- return true;
1082
- // Visit body children for field extraction
1083
- this.nodeStack.push(structNode.id);
1084
- // Try Go-style 'type' field first, then find inner struct child (C typedef struct)
1085
- const typeChild = (0, tree_sitter_helpers_1.getChildByField)(node, 'type')
1086
- || this.findChildByTypes(node, this.extractor.structTypes);
1087
- if (typeChild) {
1088
- // Extract struct embedding (e.g. Go: `type DB struct { *Head; Queryable }`)
1089
- this.extractInheritance(typeChild, structNode.id);
1090
- const body = (0, tree_sitter_helpers_1.getChildByField)(typeChild, this.extractor.bodyField) || typeChild;
1091
- for (let i = 0; i < body.namedChildCount; i++) {
1092
- const child = body.namedChild(i);
1093
- if (child)
1094
- this.visitNode(child);
1095
- }
1096
- }
1097
- this.nodeStack.pop();
1098
- return true;
1099
- }
1100
- if (resolvedKind === 'enum') {
1101
- const enumNode = this.createNode('enum', name, node, { docstring, isExported });
1102
- if (!enumNode)
1103
- return true;
1104
- this.nodeStack.push(enumNode.id);
1105
- // Find the inner enum type child (e.g. C: typedef enum { ... } name)
1106
- const innerEnum = this.findChildByTypes(node, this.extractor.enumTypes);
1107
- if (innerEnum) {
1108
- this.extractInheritance(innerEnum, enumNode.id);
1109
- const body = this.extractor.resolveBody?.(innerEnum, this.extractor.bodyField)
1110
- ?? (0, tree_sitter_helpers_1.getChildByField)(innerEnum, this.extractor.bodyField);
1111
- if (body) {
1112
- const memberTypes = this.extractor.enumMemberTypes;
1113
- for (let i = 0; i < body.namedChildCount; i++) {
1114
- const child = body.namedChild(i);
1115
- if (!child)
1116
- continue;
1117
- if (memberTypes?.includes(child.type)) {
1118
- this.extractEnumMembers(child);
1119
- }
1120
- else {
1121
- this.visitNode(child);
1122
- }
1123
- }
1124
- }
1125
- }
1126
- this.nodeStack.pop();
1127
- return true;
1128
- }
1129
- if (resolvedKind === 'interface') {
1130
- const kind = this.extractor.interfaceKind ?? 'interface';
1131
- const interfaceNode = this.createNode(kind, name, node, { docstring, isExported });
1132
- if (!interfaceNode)
1133
- return true;
1134
- // Extract interface inheritance from the inner type node
1135
- const typeChild = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
1136
- if (typeChild)
1137
- this.extractInheritance(typeChild, interfaceNode.id);
1138
- return true;
1139
- }
1140
- const typeAliasNode = this.createNode('type_alias', name, node, {
1141
- docstring,
1142
- isExported,
1143
- });
1144
- // Extract type references from the alias value (e.g., `type X = ITextModel | null`)
1145
- if (typeAliasNode && this.TYPE_ANNOTATION_LANGUAGES.has(this.language)) {
1146
- // The value is everything after the `=`, which is typically the last named child
1147
- // In tree-sitter TS: type_alias_declaration has name + value children
1148
- const value = (0, tree_sitter_helpers_1.getChildByField)(node, 'value');
1149
- if (value) {
1150
- this.extractTypeRefsFromSubtree(value, typeAliasNode.id);
1151
- }
1152
- }
1153
- return false;
1154
- }
1155
- // extractExportedVariables removed — the walker now descends into
1156
- // export_statement children and the inner declaration's dedicated
1157
- // extractor (extractVariable, extractFunction, extractClass, etc.)
1158
- // handles the symbol with isExported=true via parent-walk in the
1159
- // language extractor's isExported predicate.
1160
- /**
1161
- * Extract an import
1162
- *
1163
- * Creates an import node with the full import statement stored in signature for searchability.
1164
- * Also creates unresolved references for resolution purposes.
1165
- */
1166
- extractImport(node) {
1167
- if (!this.extractor)
1168
- return;
1169
- const importText = (0, tree_sitter_helpers_1.getNodeText)(node, this.source).trim();
1170
- // Try language-specific hook first
1171
- if (this.extractor.extractImport) {
1172
- const info = this.extractor.extractImport(node, this.source);
1173
- if (info) {
1174
- this.createNode('import', info.moduleName, node, {
1175
- signature: info.signature,
1176
- });
1177
- // Create unresolved reference unless the hook handled it
1178
- if (!info.handledRefs && info.moduleName && this.nodeStack.length > 0) {
1179
- const parentId = this.nodeStack[this.nodeStack.length - 1];
1180
- if (parentId) {
1181
- this.unresolvedReferences.push({
1182
- fromNodeId: parentId,
1183
- referenceName: info.moduleName,
1184
- referenceKind: 'imports',
1185
- line: node.startPosition.row + 1,
1186
- column: node.startPosition.column,
1187
- });
1188
- }
1189
- }
1190
- return;
1191
- }
1192
- // Hook returned null — fall through to multi-import inline handlers only
1193
- // (hook returning null means "I didn't handle this" for multi-import cases,
1194
- // NOT "use generic fallback" — the hook already declined)
1195
- }
1196
- // Multi-import cases that create multiple nodes (can't be expressed with single-return hook)
1197
- // Python import_statement: import os, sys (creates one import per module)
1198
- if (this.language === 'python' && node.type === 'import_statement') {
1199
- for (let i = 0; i < node.namedChildCount; i++) {
1200
- const child = node.namedChild(i);
1201
- if (child?.type === 'dotted_name') {
1202
- this.createNode('import', (0, tree_sitter_helpers_1.getNodeText)(child, this.source), node, {
1203
- signature: importText,
1204
- });
1205
- }
1206
- else if (child?.type === 'aliased_import') {
1207
- const dottedName = child.namedChildren.find(c => c.type === 'dotted_name');
1208
- if (dottedName) {
1209
- this.createNode('import', (0, tree_sitter_helpers_1.getNodeText)(dottedName, this.source), node, {
1210
- signature: importText,
1211
- });
1212
- }
1213
- }
1214
- }
1215
- return;
1216
- }
1217
- // Go imports: single or grouped (creates one import per spec)
1218
- if (this.language === 'go') {
1219
- const parentId = this.nodeStack.length > 0 ? this.nodeStack[this.nodeStack.length - 1] : null;
1220
- const extractFromSpec = (spec) => {
1221
- const stringLiteral = spec.namedChildren.find(c => c.type === 'interpreted_string_literal');
1222
- if (stringLiteral) {
1223
- const importPath = (0, tree_sitter_helpers_1.getNodeText)(stringLiteral, this.source).replace(/['"]/g, '');
1224
- if (importPath) {
1225
- this.createNode('import', importPath, spec, {
1226
- signature: (0, tree_sitter_helpers_1.getNodeText)(spec, this.source).trim(),
1227
- });
1228
- // Create unresolved reference so the resolver can create imports edges
1229
- if (parentId) {
1230
- this.unresolvedReferences.push({
1231
- fromNodeId: parentId,
1232
- referenceName: importPath,
1233
- referenceKind: 'imports',
1234
- line: spec.startPosition.row + 1,
1235
- column: spec.startPosition.column,
1236
- });
1237
- }
1238
- }
1239
- }
1240
- };
1241
- const importSpecList = node.namedChildren.find(c => c.type === 'import_spec_list');
1242
- if (importSpecList) {
1243
- for (const spec of importSpecList.namedChildren.filter(c => c.type === 'import_spec')) {
1244
- extractFromSpec(spec);
1245
- }
1246
- }
1247
- else {
1248
- const importSpec = node.namedChildren.find(c => c.type === 'import_spec');
1249
- if (importSpec) {
1250
- extractFromSpec(importSpec);
1251
- }
1252
- }
1253
- return;
1254
- }
1255
- // PHP grouped imports: use X\{A, B} (creates one import per item)
1256
- if (this.language === 'php') {
1257
- const namespacePrefix = node.namedChildren.find(c => c.type === 'namespace_name');
1258
- const useGroup = node.namedChildren.find(c => c.type === 'namespace_use_group');
1259
- if (namespacePrefix && useGroup) {
1260
- const prefix = (0, tree_sitter_helpers_1.getNodeText)(namespacePrefix, this.source);
1261
- const useClauses = useGroup.namedChildren.filter((c) => c.type === 'namespace_use_group_clause' || c.type === 'namespace_use_clause');
1262
- for (const clause of useClauses) {
1263
- const nsName = clause.namedChildren.find((c) => c.type === 'namespace_name');
1264
- const name = nsName
1265
- ? nsName.namedChildren.find((c) => c.type === 'name')
1266
- : clause.namedChildren.find((c) => c.type === 'name');
1267
- if (name) {
1268
- const fullPath = `${prefix}\\${(0, tree_sitter_helpers_1.getNodeText)(name, this.source)}`;
1269
- this.createNode('import', fullPath, node, {
1270
- signature: importText,
1271
- });
1272
- }
1273
- }
1274
- return;
1275
- }
1276
- }
1277
- // If a hook exists but returned null, it intentionally declined this node — don't create fallback
1278
- if (this.extractor.extractImport)
1279
- return;
1280
- // Generic fallback for languages without hooks
1281
- this.createNode('import', importText, node, {
1282
- signature: importText,
1283
- });
1284
- }
1285
- /**
1286
- * Extract a function call
1287
- */
1288
- extractCall(node) {
1289
- if (this.nodeStack.length === 0)
1290
- return;
1291
- const callerId = this.nodeStack[this.nodeStack.length - 1];
1292
- if (!callerId)
1293
- return;
1294
- // Get the function/method being called
1295
- let calleeName = '';
1296
- // Java/Kotlin method_invocation has 'object' + 'name' fields instead of 'function'
1297
- // PHP member_call_expression has 'object' + 'name', scoped_call_expression has 'scope' + 'name'
1298
- const nameField = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
1299
- const objectField = (0, tree_sitter_helpers_1.getChildByField)(node, 'object') || (0, tree_sitter_helpers_1.getChildByField)(node, 'scope');
1300
- if (nameField && objectField && (node.type === 'method_invocation' || node.type === 'member_call_expression' || node.type === 'scoped_call_expression')) {
1301
- // Method call with explicit receiver: receiver.method() / $receiver->method() / ClassName::method()
1302
- const methodName = (0, tree_sitter_helpers_1.getNodeText)(nameField, this.source);
1303
- let receiverName = (0, tree_sitter_helpers_1.getNodeText)(objectField, this.source);
1304
- // Strip PHP $ prefix from variable names
1305
- receiverName = receiverName.replace(/^\$/, '');
1306
- if (methodName) {
1307
- // Skip self/this/parent/static receivers — they don't aid resolution
1308
- const SKIP_RECEIVERS = new Set(['self', 'this', 'cls', 'super', 'parent', 'static']);
1309
- if (SKIP_RECEIVERS.has(receiverName)) {
1310
- calleeName = methodName;
1311
- }
1312
- else {
1313
- calleeName = `${receiverName}.${methodName}`;
1314
- }
1315
- }
1316
- }
1317
- else {
1318
- const func = (0, tree_sitter_helpers_1.getChildByField)(node, 'function') || node.namedChild(0);
1319
- if (func) {
1320
- if (func.type === 'member_expression' || func.type === 'attribute' || func.type === 'selector_expression' || func.type === 'navigation_expression') {
1321
- // Method call: obj.method() or obj.field.method()
1322
- // Go uses selector_expression with 'field', JS/TS uses member_expression with 'property'
1323
- // Kotlin uses navigation_expression with navigation_suffix > simple_identifier
1324
- let property = (0, tree_sitter_helpers_1.getChildByField)(func, 'property') || (0, tree_sitter_helpers_1.getChildByField)(func, 'field');
1325
- if (!property) {
1326
- const child1 = func.namedChild(1);
1327
- // Kotlin: navigation_suffix wraps the method name — extract simple_identifier from it
1328
- if (child1?.type === 'navigation_suffix') {
1329
- property = child1.namedChildren.find((c) => c.type === 'simple_identifier') ?? child1;
1330
- }
1331
- else {
1332
- property = child1;
1333
- }
1334
- }
1335
- if (property) {
1336
- const methodName = (0, tree_sitter_helpers_1.getNodeText)(property, this.source);
1337
- // Include receiver name for qualified resolution (e.g., console.print → "console.print")
1338
- // This helps the resolver distinguish method calls from bare function calls
1339
- // (e.g., Python's console.print() vs builtin print())
1340
- // Skip self/this/cls as they don't aid resolution
1341
- const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') || (0, tree_sitter_helpers_1.getChildByField)(func, 'operand') || func.namedChild(0);
1342
- const SKIP_RECEIVERS = new Set(['self', 'this', 'cls', 'super']);
1343
- if (receiver && (receiver.type === 'identifier' || receiver.type === 'simple_identifier')) {
1344
- const receiverName = (0, tree_sitter_helpers_1.getNodeText)(receiver, this.source);
1345
- if (!SKIP_RECEIVERS.has(receiverName)) {
1346
- calleeName = `${receiverName}.${methodName}`;
1347
- }
1348
- else {
1349
- calleeName = methodName;
1350
- }
1351
- }
1352
- else {
1353
- calleeName = methodName;
1354
- }
1355
- }
1356
- }
1357
- else if (func.type === 'scoped_identifier' || func.type === 'scoped_call_expression') {
1358
- // Scoped call: Module::function()
1359
- calleeName = (0, tree_sitter_helpers_1.getNodeText)(func, this.source);
1360
- }
1361
- else {
1362
- calleeName = (0, tree_sitter_helpers_1.getNodeText)(func, this.source);
1363
- }
1364
- }
1365
- }
1366
- if (calleeName) {
1367
- this.unresolvedReferences.push({
1368
- fromNodeId: callerId,
1369
- referenceName: calleeName,
1370
- referenceKind: 'calls',
1371
- line: node.startPosition.row + 1,
1372
- column: node.startPosition.column,
1373
- });
1374
- }
1375
- }
1376
- /**
1377
- * `new Foo(...)` / `Foo::new(...)` / object_creation_expression —
1378
- * emit an `instantiates` reference to the class name. The resolver
1379
- * then links it to the class node, producing the `instantiates`
1380
- * edge that powers "what creates instances of X" queries.
1381
- *
1382
- * Children are still walked so nested calls inside the constructor
1383
- * arguments (`new Foo(bar())`) get their own `calls` references.
1384
- */
1385
- extractInstantiation(node) {
1386
- if (this.nodeStack.length === 0)
1387
- return;
1388
- const fromId = this.nodeStack[this.nodeStack.length - 1];
1389
- if (!fromId)
1390
- return;
1391
- // The class name is in the `constructor`/`type`/first-named-child
1392
- // depending on grammar.
1393
- const ctor = (0, tree_sitter_helpers_1.getChildByField)(node, 'constructor') ||
1394
- (0, tree_sitter_helpers_1.getChildByField)(node, 'type') ||
1395
- (0, tree_sitter_helpers_1.getChildByField)(node, 'name') ||
1396
- node.namedChild(0);
1397
- if (!ctor)
1398
- return;
1399
- let className = (0, tree_sitter_helpers_1.getNodeText)(ctor, this.source);
1400
- // Strip type-argument suffix first: `new Map<K, V>()` would
1401
- // otherwise produce className 'Map<K, V>' (the constructor
1402
- // field is a `generic_type` node) and resolution would fail
1403
- // because no class is named with the angle-bracket suffix.
1404
- const ltIdx = className.indexOf('<');
1405
- if (ltIdx > 0)
1406
- className = className.slice(0, ltIdx);
1407
- // For namespaced/qualified constructors (`new ns.Foo()`,
1408
- // `new ns::Foo()`) keep the trailing identifier — that's what
1409
- // matches a class node in the index.
1410
- const lastDot = Math.max(className.lastIndexOf('.'), className.lastIndexOf('::'));
1411
- if (lastDot >= 0)
1412
- className = className.slice(lastDot + 1).replace(/^[:.]/, '');
1413
- className = className.trim();
1414
- if (className) {
1415
- this.unresolvedReferences.push({
1416
- fromNodeId: fromId,
1417
- referenceName: className,
1418
- referenceKind: 'instantiates',
1419
- line: node.startPosition.row + 1,
1420
- column: node.startPosition.column,
1421
- });
1422
- }
1423
- }
1424
- /**
1425
- * Scan `declNode` and its preceding siblings (within the parent's
1426
- * named children) for decorator nodes, emitting a `decorates`
1427
- * reference from `decoratedId` to each decorator's function name.
1428
- *
1429
- * Why preceding siblings: in TypeScript, `@Foo class Bar {}` parses
1430
- * as an `export_statement` (or top-level wrapper) with the
1431
- * `decorator` as a child *before* the `class_declaration` — so the
1432
- * decorator isn't a child of the class itself. For methods/
1433
- * properties, the decorator IS a direct child of the declaration,
1434
- * so we also scan declNode.namedChildren.
1435
- *
1436
- * Idempotent across grammars: if neither location yields decorators
1437
- * (most non-decorator-using languages), the function is a no-op.
1438
- */
1439
- extractDecoratorsFor(declNode, decoratedId) {
1440
- const consider = (n) => {
1441
- if (!n)
1442
- return;
1443
- // `marker_annotation` is Java's grammar for arg-less annotations
1444
- // (`@Override`, `@Deprecated`); without including it, every
1445
- // such Java annotation would be silently skipped.
1446
- if (n.type !== 'decorator' &&
1447
- n.type !== 'annotation' &&
1448
- n.type !== 'marker_annotation') {
1449
- return;
1450
- }
1451
- // Find the leading identifier: skip the `@` punct, unwrap
1452
- // a call_expression if the decorator is invoked with args.
1453
- let target = null;
1454
- for (let i = 0; i < n.namedChildCount; i++) {
1455
- const child = n.namedChild(i);
1456
- if (!child)
1457
- continue;
1458
- if (child.type === 'call_expression') {
1459
- const fn = (0, tree_sitter_helpers_1.getChildByField)(child, 'function') ?? child.namedChild(0);
1460
- if (fn)
1461
- target = fn;
1462
- if (target)
1463
- break;
1464
- }
1465
- if (child.type === 'identifier' ||
1466
- child.type === 'member_expression' ||
1467
- child.type === 'scoped_identifier' ||
1468
- child.type === 'navigation_expression') {
1469
- target = child;
1470
- break;
1471
- }
1472
- }
1473
- if (!target)
1474
- return;
1475
- let name = (0, tree_sitter_helpers_1.getNodeText)(target, this.source);
1476
- const lastDot = Math.max(name.lastIndexOf('.'), name.lastIndexOf('::'));
1477
- if (lastDot >= 0)
1478
- name = name.slice(lastDot + 1).replace(/^[:.]/, '');
1479
- if (!name)
1480
- return;
1481
- this.unresolvedReferences.push({
1482
- fromNodeId: decoratedId,
1483
- referenceName: name,
1484
- referenceKind: 'decorates',
1485
- line: n.startPosition.row + 1,
1486
- column: n.startPosition.column,
1487
- });
1488
- };
1489
- // 1. Decorators that are direct children of the declaration
1490
- // (method/property style, also some grammars for class).
1491
- for (let i = 0; i < declNode.namedChildCount; i++) {
1492
- consider(declNode.namedChild(i));
1493
- }
1494
- // 2. Decorators that are PRECEDING siblings of the declaration
1495
- // inside the parent's children (TypeScript class style).
1496
- // Walk BACKWARDS from the declaration and stop at the first
1497
- // non-decorator sibling — without that stop, decorators
1498
- // belonging to an EARLIER unrelated declaration leak in
1499
- // (e.g. `@A class Foo {} @B class Bar {}` would otherwise
1500
- // attribute @A to Bar).
1501
- //
1502
- // Note on identity: tree-sitter web bindings return fresh JS
1503
- // wrapper objects from `parent`/`namedChild` navigation, so
1504
- // `sibling === declNode` is unreliable — `startIndex` does
1505
- // the matching instead.
1506
- const parent = declNode.parent;
1507
- if (parent) {
1508
- const declStart = declNode.startIndex;
1509
- let declIdx = -1;
1510
- for (let i = 0; i < parent.namedChildCount; i++) {
1511
- const sibling = parent.namedChild(i);
1512
- if (sibling && sibling.startIndex === declStart) {
1513
- declIdx = i;
1514
- break;
1515
- }
1516
- }
1517
- if (declIdx > 0) {
1518
- for (let j = declIdx - 1; j >= 0; j--) {
1519
- const sibling = parent.namedChild(j);
1520
- if (!sibling)
1521
- continue;
1522
- if (sibling.type !== 'decorator' && sibling.type !== 'annotation' && sibling.type !== 'marker_annotation') {
1523
- break; // non-decorator separator → stop consuming
1524
- }
1525
- consider(sibling);
1526
- }
1527
- }
1528
- }
1529
- }
1530
- /**
1531
- * Visit function body and extract calls (and structural nodes).
1532
- *
1533
- * In addition to call expressions, this also detects class/struct/enum
1534
- * definitions inside function bodies. This handles two cases:
1535
- * 1. Local class/struct/enum definitions (valid in C++, Java, etc.)
1536
- * 2. C++ macro misparsing — macros like NLOHMANN_JSON_NAMESPACE_BEGIN cause
1537
- * tree-sitter to interpret the namespace block as a function_definition,
1538
- * hiding real class/struct/enum nodes inside the "function body".
1539
- */
1540
- visitFunctionBody(body, _functionId) {
1541
- if (!this.extractor)
1542
- return;
1543
- const visitForCallsAndStructure = (node) => {
1544
- const nodeType = node.type;
1545
- if (this.extractor.callTypes.includes(nodeType)) {
1546
- this.extractCall(node);
1547
- }
1548
- else if (INSTANTIATION_KINDS.has(nodeType)) {
1549
- // `new Foo()` inside a function body — emit an `instantiates`
1550
- // reference. Without this branch the body walker only knew
1551
- // about `call_expression`, so constructor invocations
1552
- // produced no graph edges at all.
1553
- this.extractInstantiation(node);
1554
- }
1555
- else if (this.extractor.extractBareCall) {
1556
- const calleeName = this.extractor.extractBareCall(node, this.source);
1557
- if (calleeName && this.nodeStack.length > 0) {
1558
- const callerId = this.nodeStack[this.nodeStack.length - 1];
1559
- if (callerId) {
1560
- this.unresolvedReferences.push({
1561
- fromNodeId: callerId,
1562
- referenceName: calleeName,
1563
- referenceKind: 'calls',
1564
- line: node.startPosition.row + 1,
1565
- column: node.startPosition.column,
1566
- });
1567
- }
1568
- }
1569
- }
1570
- // Extract structural nodes found inside function bodies.
1571
- // Each extract method visits its own children, so we return after extracting.
1572
- if (this.extractor.classTypes.includes(nodeType)) {
1573
- const classification = this.extractor.classifyClassNode?.(node) ?? 'class';
1574
- if (classification === 'struct')
1575
- this.extractStruct(node);
1576
- else if (classification === 'enum')
1577
- this.extractEnum(node);
1578
- else if (classification === 'interface')
1579
- this.extractInterface(node);
1580
- else if (classification === 'trait')
1581
- this.extractClass(node, 'trait');
1582
- else
1583
- this.extractClass(node);
1584
- return;
1585
- }
1586
- if (this.extractor.structTypes.includes(nodeType)) {
1587
- this.extractStruct(node);
1588
- return;
1589
- }
1590
- if (this.extractor.enumTypes.includes(nodeType)) {
1591
- this.extractEnum(node);
1592
- return;
1593
- }
1594
- if (this.extractor.interfaceTypes.includes(nodeType)) {
1595
- this.extractInterface(node);
1596
- return;
1597
- }
1598
- for (let i = 0; i < node.namedChildCount; i++) {
1599
- const child = node.namedChild(i);
1600
- if (child) {
1601
- visitForCallsAndStructure(child);
1602
- }
1603
- }
1604
- };
1605
- visitForCallsAndStructure(body);
1606
- }
1607
- /**
1608
- * Extract inheritance relationships
1609
- */
1610
- extractInheritance(node, classId) {
1611
- // Look for extends/implements clauses
1612
- for (let i = 0; i < node.namedChildCount; i++) {
1613
- const child = node.namedChild(i);
1614
- if (!child)
1615
- continue;
1616
- if (child.type === 'extends_clause' ||
1617
- child.type === 'superclass' ||
1618
- child.type === 'base_clause' || // PHP class extends
1619
- child.type === 'extends_interfaces' // Java interface extends
1620
- ) {
1621
- // Extract parent class/interface names
1622
- // Java uses type_list wrapper: superclass -> type_identifier, extends_interfaces -> type_list -> type_identifier
1623
- const typeList = child.namedChildren.find((c) => c.type === 'type_list');
1624
- const targets = typeList ? typeList.namedChildren : [child.namedChild(0)];
1625
- for (const target of targets) {
1626
- if (target) {
1627
- const name = (0, tree_sitter_helpers_1.getNodeText)(target, this.source);
1628
- this.unresolvedReferences.push({
1629
- fromNodeId: classId,
1630
- referenceName: name,
1631
- referenceKind: 'extends',
1632
- line: target.startPosition.row + 1,
1633
- column: target.startPosition.column,
1634
- });
1635
- }
1636
- }
1637
- }
1638
- if (child.type === 'implements_clause' ||
1639
- child.type === 'class_interface_clause' ||
1640
- child.type === 'super_interfaces' || // Java class implements
1641
- child.type === 'interfaces' // Dart
1642
- ) {
1643
- // Extract implemented interfaces
1644
- // Java uses type_list wrapper: super_interfaces -> type_list -> type_identifier
1645
- const typeList = child.namedChildren.find((c) => c.type === 'type_list');
1646
- const targets = typeList ? typeList.namedChildren : child.namedChildren;
1647
- for (const iface of targets) {
1648
- if (iface) {
1649
- const name = (0, tree_sitter_helpers_1.getNodeText)(iface, this.source);
1650
- this.unresolvedReferences.push({
1651
- fromNodeId: classId,
1652
- referenceName: name,
1653
- referenceKind: 'implements',
1654
- line: iface.startPosition.row + 1,
1655
- column: iface.startPosition.column,
1656
- });
1657
- }
1658
- }
1659
- }
1660
- // Python superclass list: `class Flask(Scaffold, Mixin):`
1661
- // argument_list contains identifier children for each parent class
1662
- if (child.type === 'argument_list' && node.type === 'class_definition') {
1663
- for (const arg of child.namedChildren) {
1664
- if (arg.type === 'identifier' || arg.type === 'attribute') {
1665
- const name = (0, tree_sitter_helpers_1.getNodeText)(arg, this.source);
1666
- this.unresolvedReferences.push({
1667
- fromNodeId: classId,
1668
- referenceName: name,
1669
- referenceKind: 'extends',
1670
- line: arg.startPosition.row + 1,
1671
- column: arg.startPosition.column,
1672
- });
1673
- }
1674
- }
1675
- }
1676
- // Go interface embedding: `type Querier interface { LabelQuerier; ... }`
1677
- // constraint_elem wraps the embedded interface type identifier
1678
- if (child.type === 'constraint_elem') {
1679
- const typeId = child.namedChildren.find((c) => c.type === 'type_identifier');
1680
- if (typeId) {
1681
- const name = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
1682
- this.unresolvedReferences.push({
1683
- fromNodeId: classId,
1684
- referenceName: name,
1685
- referenceKind: 'extends',
1686
- line: typeId.startPosition.row + 1,
1687
- column: typeId.startPosition.column,
1688
- });
1689
- }
1690
- }
1691
- // Go struct embedding: field_declaration without field_identifier
1692
- // e.g. `type DB struct { *Head; Queryable }` — no field name means embedded type
1693
- if (child.type === 'field_declaration') {
1694
- const hasFieldIdentifier = child.namedChildren.some((c) => c.type === 'field_identifier');
1695
- if (!hasFieldIdentifier) {
1696
- const typeId = child.namedChildren.find((c) => c.type === 'type_identifier');
1697
- if (typeId) {
1698
- const name = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
1699
- this.unresolvedReferences.push({
1700
- fromNodeId: classId,
1701
- referenceName: name,
1702
- referenceKind: 'extends',
1703
- line: typeId.startPosition.row + 1,
1704
- column: typeId.startPosition.column,
1705
- });
1706
- }
1707
- }
1708
- }
1709
- // Rust trait supertraits: `trait SubTrait: SuperTrait + Display { ... }`
1710
- // trait_bounds contains type_identifier, generic_type, or higher_ranked_trait_bound children
1711
- if (child.type === 'trait_bounds') {
1712
- for (const bound of child.namedChildren) {
1713
- let typeName;
1714
- let posNode;
1715
- if (bound.type === 'type_identifier') {
1716
- typeName = (0, tree_sitter_helpers_1.getNodeText)(bound, this.source);
1717
- posNode = bound;
1718
- }
1719
- else if (bound.type === 'generic_type') {
1720
- // e.g. `Deserialize<'de>`
1721
- const inner = bound.namedChildren.find((c) => c.type === 'type_identifier');
1722
- if (inner) {
1723
- typeName = (0, tree_sitter_helpers_1.getNodeText)(inner, this.source);
1724
- posNode = inner;
1725
- }
1726
- }
1727
- else if (bound.type === 'higher_ranked_trait_bound') {
1728
- // e.g. `for<'de> Deserialize<'de>`
1729
- const generic = bound.namedChildren.find((c) => c.type === 'generic_type');
1730
- const typeId = generic?.namedChildren.find((c) => c.type === 'type_identifier')
1731
- ?? bound.namedChildren.find((c) => c.type === 'type_identifier');
1732
- if (typeId) {
1733
- typeName = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
1734
- posNode = typeId;
1735
- }
1736
- }
1737
- if (typeName && posNode) {
1738
- this.unresolvedReferences.push({
1739
- fromNodeId: classId,
1740
- referenceName: typeName,
1741
- referenceKind: 'extends',
1742
- line: posNode.startPosition.row + 1,
1743
- column: posNode.startPosition.column,
1744
- });
1745
- }
1746
- }
1747
- }
1748
- // C#: `class Movie : BaseItem, IPlugin` → base_list with identifier children
1749
- // base_list combines both base class and interfaces in a single colon-separated list.
1750
- // We emit all as 'extends' since the syntax doesn't distinguish them.
1751
- if (child.type === 'base_list') {
1752
- for (const baseType of child.namedChildren) {
1753
- if (baseType) {
1754
- // For generic base types like `ClientBase<T>`, extract just the type name
1755
- const name = baseType.type === 'generic_name'
1756
- ? (0, tree_sitter_helpers_1.getNodeText)(baseType.namedChildren.find((c) => c.type === 'identifier') ?? baseType, this.source)
1757
- : (0, tree_sitter_helpers_1.getNodeText)(baseType, this.source);
1758
- this.unresolvedReferences.push({
1759
- fromNodeId: classId,
1760
- referenceName: name,
1761
- referenceKind: 'extends',
1762
- line: baseType.startPosition.row + 1,
1763
- column: baseType.startPosition.column,
1764
- });
1765
- }
1766
- }
1767
- }
1768
- // Kotlin: `class Foo : Bar, Baz` → delegation_specifier > user_type > type_identifier
1769
- // Also handles `class Foo : Bar()` → delegation_specifier > constructor_invocation > user_type
1770
- if (child.type === 'delegation_specifier') {
1771
- const userType = child.namedChildren.find((c) => c.type === 'user_type');
1772
- const constructorInvocation = child.namedChildren.find((c) => c.type === 'constructor_invocation');
1773
- const target = userType ?? constructorInvocation;
1774
- if (target) {
1775
- const typeId = target.type === 'user_type'
1776
- ? target.namedChildren.find((c) => c.type === 'type_identifier') ?? target
1777
- : target.namedChildren.find((c) => c.type === 'user_type')?.namedChildren.find((c) => c.type === 'type_identifier')
1778
- ?? target.namedChildren.find((c) => c.type === 'user_type') ?? target;
1779
- const name = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
1780
- this.unresolvedReferences.push({
1781
- fromNodeId: classId,
1782
- referenceName: name,
1783
- referenceKind: 'extends',
1784
- line: typeId.startPosition.row + 1,
1785
- column: typeId.startPosition.column,
1786
- });
1787
- }
1788
- }
1789
- // Swift: inheritance_specifier > user_type > type_identifier
1790
- // Used for class inheritance, protocol conformance, and protocol inheritance
1791
- if (child.type === 'inheritance_specifier') {
1792
- const userType = child.namedChildren.find((c) => c.type === 'user_type');
1793
- const typeId = userType?.namedChildren.find((c) => c.type === 'type_identifier');
1794
- if (typeId) {
1795
- const name = (0, tree_sitter_helpers_1.getNodeText)(typeId, this.source);
1796
- this.unresolvedReferences.push({
1797
- fromNodeId: classId,
1798
- referenceName: name,
1799
- referenceKind: 'extends',
1800
- line: typeId.startPosition.row + 1,
1801
- column: typeId.startPosition.column,
1802
- });
1803
- }
1804
- }
1805
- // JavaScript class_heritage has bare identifier without extends_clause wrapper
1806
- // e.g. `class Foo extends Bar {}` → class_heritage → identifier("Bar")
1807
- if ((child.type === 'identifier' || child.type === 'type_identifier') &&
1808
- node.type === 'class_heritage') {
1809
- const name = (0, tree_sitter_helpers_1.getNodeText)(child, this.source);
1810
- this.unresolvedReferences.push({
1811
- fromNodeId: classId,
1812
- referenceName: name,
1813
- referenceKind: 'extends',
1814
- line: child.startPosition.row + 1,
1815
- column: child.startPosition.column,
1816
- });
1817
- }
1818
- // Recurse into container nodes (e.g. field_declaration_list in Go structs,
1819
- // class_heritage in TypeScript which wraps extends_clause/implements_clause)
1820
- if (child.type === 'field_declaration_list' || child.type === 'class_heritage') {
1821
- this.extractInheritance(child, classId);
1822
- }
1823
- }
1824
- }
1825
- /**
1826
- * Rust `impl Trait for Type` — creates an implements edge from Type to Trait.
1827
- * For plain `impl Type { ... }` (no trait), no inheritance edge is needed.
1828
- */
1829
- extractRustImplItem(node) {
1830
- // Check if this is `impl Trait for Type` by looking for a `for` keyword
1831
- const hasFor = node.children.some((c) => c.type === 'for' && !c.isNamed);
1832
- if (!hasFor)
1833
- return;
1834
- // In `impl Trait for Type`, the type_identifiers are:
1835
- // first = Trait name, last = implementing Type name
1836
- // Also handle generic types like `impl<T> Trait for MyStruct<T>`
1837
- const typeIdents = node.namedChildren.filter((c) => c.type === 'type_identifier' || c.type === 'generic_type' || c.type === 'scoped_type_identifier');
1838
- if (typeIdents.length < 2)
1839
- return;
1840
- const traitNode = typeIdents[0];
1841
- const typeNode = typeIdents[typeIdents.length - 1];
1842
- // Get the trait name (handle scoped paths like std::fmt::Display)
1843
- const traitName = traitNode.type === 'scoped_type_identifier'
1844
- ? this.source.substring(traitNode.startIndex, traitNode.endIndex)
1845
- : (0, tree_sitter_helpers_1.getNodeText)(traitNode, this.source);
1846
- // Get the implementing type name (extract inner type_identifier for generics)
1847
- let typeName;
1848
- if (typeNode.type === 'generic_type') {
1849
- const inner = typeNode.namedChildren.find((c) => c.type === 'type_identifier');
1850
- typeName = inner ? (0, tree_sitter_helpers_1.getNodeText)(inner, this.source) : (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source);
1851
- }
1852
- else {
1853
- typeName = (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source);
1854
- }
1855
- // Find the struct/type node for the implementing type
1856
- const typeNodeId = this.findNodeByName(typeName);
1857
- if (typeNodeId) {
1858
- this.unresolvedReferences.push({
1859
- fromNodeId: typeNodeId,
1860
- referenceName: traitName,
1861
- referenceKind: 'implements',
1862
- line: traitNode.startPosition.row + 1,
1863
- column: traitNode.startPosition.column,
1864
- });
1865
- }
1866
- }
1867
- /**
1868
- * Find a previously-extracted node by name (used for back-references like impl blocks)
1869
- */
1870
- findNodeByName(name) {
1871
- for (const node of this.nodes) {
1872
- if (node.name === name && (node.kind === 'struct' || node.kind === 'enum' || node.kind === 'class')) {
1873
- return node.id;
1874
- }
1875
- }
1876
- return undefined;
1877
- }
1878
- /**
1879
- * Languages that support type annotations (TypeScript, etc.)
1880
- */
1881
- TYPE_ANNOTATION_LANGUAGES = new Set([
1882
- 'typescript', 'tsx', 'dart', 'kotlin', 'swift', 'rust', 'go', 'java', 'csharp',
1883
- ]);
1884
- /**
1885
- * Built-in/primitive type names that shouldn't create references
1886
- */
1887
- BUILTIN_TYPES = new Set([
1888
- 'string', 'number', 'boolean', 'void', 'null', 'undefined', 'never', 'any', 'unknown',
1889
- 'object', 'symbol', 'bigint', 'true', 'false',
1890
- // Rust
1891
- 'str', 'bool', 'i8', 'i16', 'i32', 'i64', 'i128', 'isize',
1892
- 'u8', 'u16', 'u32', 'u64', 'u128', 'usize', 'f32', 'f64', 'char',
1893
- // Java/C#
1894
- 'int', 'long', 'short', 'byte', 'float', 'double', 'char',
1895
- // Go
1896
- 'int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64',
1897
- 'float32', 'float64', 'complex64', 'complex128', 'rune', 'error',
1898
- ]);
1899
- /**
1900
- * Extract type references from type annotations on a function/method/field node.
1901
- * Creates 'references' edges for parameter types, return types, and field types.
1902
- */
1903
- extractTypeAnnotations(node, nodeId) {
1904
- if (!this.extractor)
1905
- return;
1906
- if (!this.TYPE_ANNOTATION_LANGUAGES.has(this.language))
1907
- return;
1908
- // Extract parameter type annotations
1909
- const params = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.paramsField || 'parameters');
1910
- if (params) {
1911
- this.extractTypeRefsFromSubtree(params, nodeId);
1912
- }
1913
- // Extract return type annotation
1914
- const returnType = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.returnField || 'return_type');
1915
- if (returnType) {
1916
- this.extractTypeRefsFromSubtree(returnType, nodeId);
1917
- }
1918
- // Extract direct type annotation (for class fields like `model: ITextModel`)
1919
- const typeAnnotation = node.namedChildren.find((c) => c.type === 'type_annotation');
1920
- if (typeAnnotation) {
1921
- this.extractTypeRefsFromSubtree(typeAnnotation, nodeId);
1922
- }
1923
- }
1924
- /**
1925
- * Extract type references from a variable's type annotation.
1926
- */
1927
- extractVariableTypeAnnotation(node, nodeId) {
1928
- if (!this.TYPE_ANNOTATION_LANGUAGES.has(this.language))
1929
- return;
1930
- // Find type_annotation child (covers TS `: Type`, Rust `: Type`, etc.)
1931
- const typeAnnotation = node.namedChildren.find((c) => c.type === 'type_annotation');
1932
- if (typeAnnotation) {
1933
- this.extractTypeRefsFromSubtree(typeAnnotation, nodeId);
1934
- }
1935
- }
1936
- /**
1937
- * Recursively walk a subtree and extract all type_identifier references.
1938
- * Handles unions, intersections, generics, arrays, etc.
1939
- */
1940
- extractTypeRefsFromSubtree(node, fromNodeId) {
1941
- if (node.type === 'type_identifier') {
1942
- const typeName = (0, tree_sitter_helpers_1.getNodeText)(node, this.source);
1943
- if (typeName && !this.BUILTIN_TYPES.has(typeName)) {
1944
- this.unresolvedReferences.push({
1945
- fromNodeId,
1946
- referenceName: typeName,
1947
- referenceKind: 'references',
1948
- line: node.startPosition.row + 1,
1949
- column: node.startPosition.column,
1950
- });
1951
- }
1952
- return; // type_identifier is a leaf
1953
- }
1954
- // Recurse into children (handles union_type, intersection_type, generic_type, etc.)
1955
- for (let i = 0; i < node.namedChildCount; i++) {
1956
- const child = node.namedChild(i);
1957
- if (child) {
1958
- this.extractTypeRefsFromSubtree(child, fromNodeId);
1959
- }
1960
- }
1961
- }
1962
- /**
1963
- * Handle Pascal-specific AST structures.
1964
- * Returns true if the node was fully handled and children should be skipped.
1965
- */
1966
- visitPascalNode(node) {
1967
- const nodeType = node.type;
1968
- // Unit/Program/Library → module node
1969
- if (nodeType === 'unit' || nodeType === 'program' || nodeType === 'library') {
1970
- const moduleNameNode = node.namedChildren.find((c) => c.type === 'moduleName');
1971
- const name = moduleNameNode ? (0, tree_sitter_helpers_1.getNodeText)(moduleNameNode, this.source) : '';
1972
- // Fallback to filename without extension if module name is empty
1973
- const moduleName = name || path.basename(this.filePath).replace(/\.[^.]+$/, '');
1974
- this.createNode('module', moduleName, node);
1975
- // Continue visiting children (interface/implementation sections)
1976
- for (let i = 0; i < node.namedChildCount; i++) {
1977
- const child = node.namedChild(i);
1978
- if (child)
1979
- this.visitNode(child);
1980
- }
1981
- return true;
1982
- }
1983
- // declType wraps declClass/declIntf/declEnum/type-alias
1984
- // The name lives on declType, the inner node determines the kind
1985
- if (nodeType === 'declType') {
1986
- this.extractPascalDeclType(node);
1987
- return true;
1988
- }
1989
- // declUses → import nodes for each unit name
1990
- if (nodeType === 'declUses') {
1991
- this.extractPascalUses(node);
1992
- return true;
1993
- }
1994
- // declConsts → container; visit children for individual declConst
1995
- if (nodeType === 'declConsts') {
1996
- for (let i = 0; i < node.namedChildCount; i++) {
1997
- const child = node.namedChild(i);
1998
- if (child?.type === 'declConst') {
1999
- this.extractPascalConst(child);
2000
- }
2001
- }
2002
- return true;
2003
- }
2004
- // declConst at top level (outside declConsts)
2005
- if (nodeType === 'declConst') {
2006
- this.extractPascalConst(node);
2007
- return true;
2008
- }
2009
- // declTypes → container for type declarations
2010
- if (nodeType === 'declTypes') {
2011
- for (let i = 0; i < node.namedChildCount; i++) {
2012
- const child = node.namedChild(i);
2013
- if (child)
2014
- this.visitNode(child);
2015
- }
2016
- return true;
2017
- }
2018
- // declVars → container for variable declarations
2019
- if (nodeType === 'declVars') {
2020
- for (let i = 0; i < node.namedChildCount; i++) {
2021
- const child = node.namedChild(i);
2022
- if (child?.type === 'declVar') {
2023
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(child, 'name');
2024
- if (nameNode) {
2025
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2026
- this.createNode('variable', name, child);
2027
- }
2028
- }
2029
- }
2030
- return true;
2031
- }
2032
- // defProc in implementation section → extract calls but don't create duplicate nodes
2033
- if (nodeType === 'defProc') {
2034
- this.extractPascalDefProc(node);
2035
- return true;
2036
- }
2037
- // declProp → property node
2038
- if (nodeType === 'declProp') {
2039
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
2040
- if (nameNode) {
2041
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2042
- const visibility = this.extractor.getVisibility?.(node);
2043
- this.createNode('property', name, node, { visibility });
2044
- }
2045
- return true;
2046
- }
2047
- // declField → field node
2048
- if (nodeType === 'declField') {
2049
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
2050
- if (nameNode) {
2051
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2052
- const visibility = this.extractor.getVisibility?.(node);
2053
- this.createNode('field', name, node, { visibility });
2054
- }
2055
- return true;
2056
- }
2057
- // declSection → visit children (propagates visibility via getVisibility)
2058
- if (nodeType === 'declSection') {
2059
- for (let i = 0; i < node.namedChildCount; i++) {
2060
- const child = node.namedChild(i);
2061
- if (child)
2062
- this.visitNode(child);
2063
- }
2064
- return true;
2065
- }
2066
- // exprCall → extract function call reference
2067
- if (nodeType === 'exprCall') {
2068
- this.extractPascalCall(node);
2069
- return true;
2070
- }
2071
- // interface/implementation sections → visit children
2072
- if (nodeType === 'interface' || nodeType === 'implementation') {
2073
- for (let i = 0; i < node.namedChildCount; i++) {
2074
- const child = node.namedChild(i);
2075
- if (child)
2076
- this.visitNode(child);
2077
- }
2078
- return true;
2079
- }
2080
- // block (begin..end) → visit for calls
2081
- if (nodeType === 'block') {
2082
- this.visitPascalBlock(node);
2083
- return true;
2084
- }
2085
- return false;
2086
- }
2087
- /**
2088
- * Extract a Pascal declType node (class, interface, enum, or type alias)
2089
- */
2090
- extractPascalDeclType(node) {
2091
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
2092
- if (!nameNode)
2093
- return;
2094
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2095
- // Find the inner type declaration
2096
- const declClass = node.namedChildren.find((c) => c.type === 'declClass');
2097
- const declIntf = node.namedChildren.find((c) => c.type === 'declIntf');
2098
- const typeChild = node.namedChildren.find((c) => c.type === 'type');
2099
- if (declClass) {
2100
- const classNode = this.createNode('class', name, node);
2101
- if (classNode) {
2102
- // Extract inheritance from typeref children of declClass
2103
- this.extractPascalInheritance(declClass, classNode.id);
2104
- // Visit class body
2105
- this.nodeStack.push(classNode.id);
2106
- for (let i = 0; i < declClass.namedChildCount; i++) {
2107
- const child = declClass.namedChild(i);
2108
- if (child)
2109
- this.visitNode(child);
2110
- }
2111
- this.nodeStack.pop();
2112
- }
2113
- }
2114
- else if (declIntf) {
2115
- const ifaceNode = this.createNode('interface', name, node);
2116
- if (ifaceNode) {
2117
- // Visit interface members
2118
- this.nodeStack.push(ifaceNode.id);
2119
- for (let i = 0; i < declIntf.namedChildCount; i++) {
2120
- const child = declIntf.namedChild(i);
2121
- if (child)
2122
- this.visitNode(child);
2123
- }
2124
- this.nodeStack.pop();
2125
- }
2126
- }
2127
- else if (typeChild) {
2128
- // Check if it contains a declEnum
2129
- const declEnum = typeChild.namedChildren.find((c) => c.type === 'declEnum');
2130
- if (declEnum) {
2131
- const enumNode = this.createNode('enum', name, node);
2132
- if (enumNode) {
2133
- // Extract enum members
2134
- this.nodeStack.push(enumNode.id);
2135
- for (let i = 0; i < declEnum.namedChildCount; i++) {
2136
- const child = declEnum.namedChild(i);
2137
- if (child?.type === 'declEnumValue') {
2138
- const memberName = (0, tree_sitter_helpers_1.getChildByField)(child, 'name');
2139
- if (memberName) {
2140
- this.createNode('enum_member', (0, tree_sitter_helpers_1.getNodeText)(memberName, this.source), child);
2141
- }
2142
- }
2143
- }
2144
- this.nodeStack.pop();
2145
- }
2146
- }
2147
- else {
2148
- // Simple type alias: type TFoo = string / type TFoo = Integer
2149
- this.createNode('type_alias', name, node);
2150
- }
2151
- }
2152
- else {
2153
- // Fallback: could be a forward declaration or simple alias
2154
- this.createNode('type_alias', name, node);
2155
- }
2156
- }
2157
- /**
2158
- * Extract Pascal uses clause into individual import nodes
2159
- */
2160
- extractPascalUses(node) {
2161
- const importText = (0, tree_sitter_helpers_1.getNodeText)(node, this.source).trim();
2162
- for (let i = 0; i < node.namedChildCount; i++) {
2163
- const child = node.namedChild(i);
2164
- if (child?.type === 'moduleName') {
2165
- const unitName = (0, tree_sitter_helpers_1.getNodeText)(child, this.source);
2166
- this.createNode('import', unitName, child, {
2167
- signature: importText,
2168
- });
2169
- // Create unresolved reference for resolution
2170
- if (this.nodeStack.length > 0) {
2171
- const parentId = this.nodeStack[this.nodeStack.length - 1];
2172
- if (parentId) {
2173
- this.unresolvedReferences.push({
2174
- fromNodeId: parentId,
2175
- referenceName: unitName,
2176
- referenceKind: 'imports',
2177
- line: child.startPosition.row + 1,
2178
- column: child.startPosition.column,
2179
- });
2180
- }
2181
- }
2182
- }
2183
- }
2184
- }
2185
- /**
2186
- * Extract a Pascal constant declaration
2187
- */
2188
- extractPascalConst(node) {
2189
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
2190
- if (!nameNode)
2191
- return;
2192
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2193
- const defaultValue = node.namedChildren.find((c) => c.type === 'defaultValue');
2194
- const sig = defaultValue ? (0, tree_sitter_helpers_1.getNodeText)(defaultValue, this.source) : undefined;
2195
- this.createNode('constant', name, node, { signature: sig });
2196
- }
2197
- /**
2198
- * Extract Pascal inheritance (extends/implements) from declClass typeref children
2199
- */
2200
- extractPascalInheritance(declClass, classId) {
2201
- const typerefs = declClass.namedChildren.filter((c) => c.type === 'typeref');
2202
- for (let i = 0; i < typerefs.length; i++) {
2203
- const ref = typerefs[i];
2204
- const name = (0, tree_sitter_helpers_1.getNodeText)(ref, this.source);
2205
- this.unresolvedReferences.push({
2206
- fromNodeId: classId,
2207
- referenceName: name,
2208
- referenceKind: i === 0 ? 'extends' : 'implements',
2209
- line: ref.startPosition.row + 1,
2210
- column: ref.startPosition.column,
2211
- });
2212
- }
2213
- }
2214
- /**
2215
- * Extract calls and resolve method context from a Pascal defProc (implementation body).
2216
- * Does not create a new node — the declaration was already captured from the interface section.
2217
- */
2218
- extractPascalDefProc(node) {
2219
- // Find the matching declaration node by name to use as call parent
2220
- const declProc = node.namedChildren.find((c) => c.type === 'declProc');
2221
- if (!declProc)
2222
- return;
2223
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(declProc, 'name');
2224
- if (!nameNode)
2225
- return;
2226
- const fullName = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source).trim();
2227
- // fullName is like "TAuthService.Create"
2228
- const shortName = fullName.includes('.') ? fullName.split('.').pop() : fullName;
2229
- const fullNameKey = fullName.toLowerCase();
2230
- const shortNameKey = shortName.toLowerCase();
2231
- // Build method index on first use (O(n) once, then O(1) per lookup)
2232
- if (!this.methodIndex) {
2233
- this.methodIndex = new Map();
2234
- for (const n of this.nodes) {
2235
- if (n.kind === 'method' || n.kind === 'function') {
2236
- const nameKey = n.name.toLowerCase();
2237
- // Keep first seen short-name mapping to avoid silently overwriting earlier entries.
2238
- if (!this.methodIndex.has(nameKey)) {
2239
- this.methodIndex.set(nameKey, n.id);
2240
- }
2241
- // For Pascal methods, also index qualified forms (e.g. TAuthService.Create).
2242
- if (n.kind === 'method') {
2243
- const qualifiedParts = n.qualifiedName.split('::');
2244
- if (qualifiedParts.length >= 2) {
2245
- // Create suffix keys so both "Module.Class.Method" and "Class.Method" can resolve.
2246
- for (let i = 0; i < qualifiedParts.length - 1; i++) {
2247
- const scopedName = qualifiedParts.slice(i).join('.').toLowerCase();
2248
- this.methodIndex.set(scopedName, n.id);
2249
- }
2250
- }
2251
- }
2252
- }
2253
- }
2254
- }
2255
- const parentId = this.methodIndex.get(fullNameKey) ||
2256
- this.methodIndex.get(shortNameKey) ||
2257
- this.nodeStack[this.nodeStack.length - 1];
2258
- if (!parentId)
2259
- return;
2260
- // Visit the block for calls
2261
- const block = node.namedChildren.find((c) => c.type === 'block');
2262
- if (block) {
2263
- this.nodeStack.push(parentId);
2264
- this.visitPascalBlock(block);
2265
- this.nodeStack.pop();
2266
- }
2267
- }
2268
- /**
2269
- * Extract function calls from a Pascal expression
2270
- */
2271
- extractPascalCall(node) {
2272
- if (this.nodeStack.length === 0)
2273
- return;
2274
- const callerId = this.nodeStack[this.nodeStack.length - 1];
2275
- if (!callerId)
2276
- return;
2277
- // Get the callee name — first child is typically the identifier or exprDot
2278
- const firstChild = node.namedChild(0);
2279
- if (!firstChild)
2280
- return;
2281
- let calleeName = '';
2282
- if (firstChild.type === 'exprDot') {
2283
- // Qualified call: Obj.Method(...)
2284
- const identifiers = firstChild.namedChildren.filter((c) => c.type === 'identifier');
2285
- if (identifiers.length > 0) {
2286
- calleeName = identifiers.map((id) => (0, tree_sitter_helpers_1.getNodeText)(id, this.source)).join('.');
2287
- }
2288
- }
2289
- else if (firstChild.type === 'identifier') {
2290
- calleeName = (0, tree_sitter_helpers_1.getNodeText)(firstChild, this.source);
2291
- }
2292
- if (calleeName) {
2293
- this.unresolvedReferences.push({
2294
- fromNodeId: callerId,
2295
- referenceName: calleeName,
2296
- referenceKind: 'calls',
2297
- line: node.startPosition.row + 1,
2298
- column: node.startPosition.column,
2299
- });
2300
- }
2301
- // Also visit arguments for nested calls
2302
- const args = node.namedChildren.find((c) => c.type === 'exprArgs');
2303
- if (args) {
2304
- this.visitPascalBlock(args);
2305
- }
2306
- }
2307
- /**
2308
- * Recursively visit a Pascal block/statement tree for call expressions
2309
- */
2310
- visitPascalBlock(node) {
2311
- for (let i = 0; i < node.namedChildCount; i++) {
2312
- const child = node.namedChild(i);
2313
- if (!child)
2314
- continue;
2315
- if (child.type === 'exprCall') {
2316
- this.extractPascalCall(child);
2317
- }
2318
- else if (child.type === 'exprDot') {
2319
- // Check if exprDot contains an exprCall
2320
- for (let j = 0; j < child.namedChildCount; j++) {
2321
- const grandchild = child.namedChild(j);
2322
- if (grandchild?.type === 'exprCall') {
2323
- this.extractPascalCall(grandchild);
2324
- }
2325
- }
2326
- }
2327
- else {
2328
- this.visitPascalBlock(child);
2329
- }
2330
- }
2331
- }
2332
- }
2333
- exports.TreeSitterExtractor = TreeSitterExtractor;
2334
- /**
2335
- * Extract nodes and edges from source code.
2336
- *
2337
- * If `frameworkNames` is provided, framework-specific extractors matching
2338
- * those names and the file's language are run after the tree-sitter pass.
2339
- * Their nodes/references/errors are merged into the returned result.
2340
- */
2341
- function extractFromSource(filePath, source, language, frameworkNames) {
2342
- const detectedLanguage = language || (0, grammars_1.detectLanguage)(filePath, source);
2343
- const fileExtension = path.extname(filePath).toLowerCase();
2344
- let result;
2345
- // Use custom extractor for Svelte
2346
- if (detectedLanguage === 'svelte') {
2347
- const extractor = new svelte_extractor_1.SvelteExtractor(filePath, source);
2348
- result = extractor.extract();
2349
- }
2350
- else if (detectedLanguage === 'vue') {
2351
- // Use custom extractor for Vue
2352
- const extractor = new vue_extractor_1.VueExtractor(filePath, source);
2353
- result = extractor.extract();
2354
- }
2355
- else if (detectedLanguage === 'liquid') {
2356
- // Use custom extractor for Liquid
2357
- const extractor = new liquid_extractor_1.LiquidExtractor(filePath, source);
2358
- result = extractor.extract();
2359
- }
2360
- else if (detectedLanguage === 'pascal' &&
2361
- (fileExtension === '.dfm' || fileExtension === '.fmx')) {
2362
- // Use custom extractor for DFM/FMX form files
2363
- const extractor = new dfm_extractor_1.DfmExtractor(filePath, source);
2364
- result = extractor.extract();
2365
- }
2366
- else {
2367
- const extractor = new TreeSitterExtractor(filePath, source, detectedLanguage);
2368
- result = extractor.extract();
2369
- }
2370
- // Framework-specific extraction (routes, middleware, etc.)
2371
- if (frameworkNames && frameworkNames.length > 0) {
2372
- const allResolvers = (0, frameworks_1.getAllFrameworkResolvers)();
2373
- const applicable = (0, frameworks_1.getApplicableFrameworks)(allResolvers.filter((r) => frameworkNames.includes(r.name)), detectedLanguage);
2374
- for (const fw of applicable) {
2375
- if (!fw.extract)
2376
- continue;
2377
- try {
2378
- const fwResult = fw.extract(filePath, source);
2379
- result.nodes.push(...fwResult.nodes);
2380
- result.unresolvedReferences.push(...fwResult.references);
2381
- }
2382
- catch (err) {
2383
- result.errors.push({
2384
- message: `Framework extractor '${fw.name}' failed: ${err instanceof Error ? err.message : String(err)}`,
2385
- filePath,
2386
- severity: 'warning',
2387
- });
2388
- }
2389
- }
2390
- }
2391
- return result;
2392
- }
2393
- //# sourceMappingURL=tree-sitter.js.map