@colbymchenry/codegraph 0.7.3 → 0.7.4

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 (172) hide show
  1. package/README.md +57 -0
  2. package/dist/bin/codegraph.js +39 -4
  3. package/dist/bin/codegraph.js.map +1 -1
  4. package/dist/bin/node-version-check.d.ts +17 -0
  5. package/dist/bin/node-version-check.d.ts.map +1 -0
  6. package/dist/bin/node-version-check.js +39 -0
  7. package/dist/bin/node-version-check.js.map +1 -0
  8. package/dist/db/index.d.ts +9 -2
  9. package/dist/db/index.d.ts.map +1 -1
  10. package/dist/db/index.js +17 -7
  11. package/dist/db/index.js.map +1 -1
  12. package/dist/db/migrations.d.ts +1 -1
  13. package/dist/db/migrations.d.ts.map +1 -1
  14. package/dist/db/migrations.js +11 -1
  15. package/dist/db/migrations.js.map +1 -1
  16. package/dist/db/queries.d.ts +16 -0
  17. package/dist/db/queries.d.ts.map +1 -1
  18. package/dist/db/queries.js +144 -7
  19. package/dist/db/queries.js.map +1 -1
  20. package/dist/db/schema.sql +6 -3
  21. package/dist/db/sqlite-adapter.d.ts +23 -4
  22. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  23. package/dist/db/sqlite-adapter.js +51 -11
  24. package/dist/db/sqlite-adapter.js.map +1 -1
  25. package/dist/extraction/grammars.d.ts +1 -1
  26. package/dist/extraction/grammars.d.ts.map +1 -1
  27. package/dist/extraction/grammars.js +12 -4
  28. package/dist/extraction/grammars.js.map +1 -1
  29. package/dist/extraction/index.d.ts +20 -0
  30. package/dist/extraction/index.d.ts.map +1 -1
  31. package/dist/extraction/index.js +111 -7
  32. package/dist/extraction/index.js.map +1 -1
  33. package/dist/extraction/languages/index.d.ts.map +1 -1
  34. package/dist/extraction/languages/index.js +2 -0
  35. package/dist/extraction/languages/index.js.map +1 -1
  36. package/dist/extraction/languages/scala.d.ts +3 -0
  37. package/dist/extraction/languages/scala.d.ts.map +1 -0
  38. package/dist/extraction/languages/scala.js +139 -0
  39. package/dist/extraction/languages/scala.js.map +1 -0
  40. package/dist/extraction/parse-worker.js +39 -2
  41. package/dist/extraction/parse-worker.js.map +1 -1
  42. package/dist/extraction/tree-sitter.d.ts +32 -16
  43. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  44. package/dist/extraction/tree-sitter.js +266 -67
  45. package/dist/extraction/tree-sitter.js.map +1 -1
  46. package/dist/extraction/vue-extractor.d.ts +36 -0
  47. package/dist/extraction/vue-extractor.d.ts.map +1 -0
  48. package/dist/extraction/vue-extractor.js +163 -0
  49. package/dist/extraction/vue-extractor.js.map +1 -0
  50. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  51. package/dist/index.d.ts +7 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +9 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/mcp/index.d.ts.map +1 -1
  56. package/dist/mcp/index.js +6 -1
  57. package/dist/mcp/index.js.map +1 -1
  58. package/dist/mcp/server-instructions.d.ts +19 -0
  59. package/dist/mcp/server-instructions.d.ts.map +1 -0
  60. package/dist/mcp/server-instructions.js +59 -0
  61. package/dist/mcp/server-instructions.js.map +1 -0
  62. package/dist/mcp/tools.d.ts.map +1 -1
  63. package/dist/mcp/tools.js +13 -2
  64. package/dist/mcp/tools.js.map +1 -1
  65. package/dist/resolution/frameworks/csharp.d.ts.map +1 -1
  66. package/dist/resolution/frameworks/csharp.js +72 -78
  67. package/dist/resolution/frameworks/csharp.js.map +1 -1
  68. package/dist/resolution/frameworks/express.d.ts.map +1 -1
  69. package/dist/resolution/frameworks/express.js +47 -26
  70. package/dist/resolution/frameworks/express.js.map +1 -1
  71. package/dist/resolution/frameworks/go.d.ts.map +1 -1
  72. package/dist/resolution/frameworks/go.js +41 -72
  73. package/dist/resolution/frameworks/go.js.map +1 -1
  74. package/dist/resolution/frameworks/index.d.ts +7 -0
  75. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  76. package/dist/resolution/frameworks/index.js +13 -1
  77. package/dist/resolution/frameworks/index.js.map +1 -1
  78. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  79. package/dist/resolution/frameworks/java.js +36 -40
  80. package/dist/resolution/frameworks/java.js.map +1 -1
  81. package/dist/resolution/frameworks/laravel.d.ts.map +1 -1
  82. package/dist/resolution/frameworks/laravel.js +94 -44
  83. package/dist/resolution/frameworks/laravel.js.map +1 -1
  84. package/dist/resolution/frameworks/python.d.ts.map +1 -1
  85. package/dist/resolution/frameworks/python.js +163 -151
  86. package/dist/resolution/frameworks/python.js.map +1 -1
  87. package/dist/resolution/frameworks/react.d.ts.map +1 -1
  88. package/dist/resolution/frameworks/react.js +3 -2
  89. package/dist/resolution/frameworks/react.js.map +1 -1
  90. package/dist/resolution/frameworks/ruby.d.ts.map +1 -1
  91. package/dist/resolution/frameworks/ruby.js +39 -91
  92. package/dist/resolution/frameworks/ruby.js.map +1 -1
  93. package/dist/resolution/frameworks/rust.d.ts.map +1 -1
  94. package/dist/resolution/frameworks/rust.js +50 -43
  95. package/dist/resolution/frameworks/rust.js.map +1 -1
  96. package/dist/resolution/frameworks/svelte.d.ts.map +1 -1
  97. package/dist/resolution/frameworks/svelte.js +3 -2
  98. package/dist/resolution/frameworks/svelte.js.map +1 -1
  99. package/dist/resolution/frameworks/swift.d.ts.map +1 -1
  100. package/dist/resolution/frameworks/swift.js +54 -46
  101. package/dist/resolution/frameworks/swift.js.map +1 -1
  102. package/dist/resolution/frameworks/vue.d.ts +9 -0
  103. package/dist/resolution/frameworks/vue.d.ts.map +1 -0
  104. package/dist/resolution/frameworks/vue.js +306 -0
  105. package/dist/resolution/frameworks/vue.js.map +1 -0
  106. package/dist/resolution/import-resolver.d.ts +17 -1
  107. package/dist/resolution/import-resolver.d.ts.map +1 -1
  108. package/dist/resolution/import-resolver.js +256 -46
  109. package/dist/resolution/import-resolver.js.map +1 -1
  110. package/dist/resolution/index.d.ts +8 -0
  111. package/dist/resolution/index.d.ts.map +1 -1
  112. package/dist/resolution/index.js +60 -1
  113. package/dist/resolution/index.js.map +1 -1
  114. package/dist/resolution/name-matcher.d.ts.map +1 -1
  115. package/dist/resolution/name-matcher.js +21 -0
  116. package/dist/resolution/name-matcher.js.map +1 -1
  117. package/dist/resolution/path-aliases.d.ts +68 -0
  118. package/dist/resolution/path-aliases.d.ts.map +1 -0
  119. package/dist/resolution/path-aliases.js +238 -0
  120. package/dist/resolution/path-aliases.js.map +1 -0
  121. package/dist/resolution/strip-comments.d.ts +27 -0
  122. package/dist/resolution/strip-comments.d.ts.map +1 -0
  123. package/dist/resolution/strip-comments.js +441 -0
  124. package/dist/resolution/strip-comments.js.map +1 -0
  125. package/dist/resolution/types.d.ts +54 -3
  126. package/dist/resolution/types.d.ts.map +1 -1
  127. package/dist/search/query-parser.d.ts +57 -0
  128. package/dist/search/query-parser.d.ts.map +1 -0
  129. package/dist/search/query-parser.js +177 -0
  130. package/dist/search/query-parser.js.map +1 -0
  131. package/dist/types.d.ts +11 -4
  132. package/dist/types.d.ts.map +1 -1
  133. package/dist/types.js +68 -1
  134. package/dist/types.js.map +1 -1
  135. package/package.json +5 -2
  136. package/dist/installer/banner.d.ts +0 -40
  137. package/dist/installer/banner.d.ts.map +0 -1
  138. package/dist/installer/banner.js +0 -165
  139. package/dist/installer/banner.js.map +0 -1
  140. package/dist/installer/prompts.d.ts +0 -18
  141. package/dist/installer/prompts.d.ts.map +0 -1
  142. package/dist/installer/prompts.js +0 -113
  143. package/dist/installer/prompts.js.map +0 -1
  144. package/dist/sentry.d.ts +0 -24
  145. package/dist/sentry.d.ts.map +0 -1
  146. package/dist/sentry.js +0 -166
  147. package/dist/sentry.js.map +0 -1
  148. package/dist/sync/git-hooks.d.ts +0 -66
  149. package/dist/sync/git-hooks.d.ts.map +0 -1
  150. package/dist/sync/git-hooks.js +0 -281
  151. package/dist/sync/git-hooks.js.map +0 -1
  152. package/dist/vectors/embedder.d.ts +0 -140
  153. package/dist/vectors/embedder.d.ts.map +0 -1
  154. package/dist/vectors/embedder.js +0 -338
  155. package/dist/vectors/embedder.js.map +0 -1
  156. package/dist/vectors/index.d.ts +0 -9
  157. package/dist/vectors/index.d.ts.map +0 -1
  158. package/dist/vectors/index.js +0 -20
  159. package/dist/vectors/index.js.map +0 -1
  160. package/dist/vectors/manager.d.ts +0 -119
  161. package/dist/vectors/manager.d.ts.map +0 -1
  162. package/dist/vectors/manager.js +0 -274
  163. package/dist/vectors/manager.js.map +0 -1
  164. package/dist/vectors/search.d.ts +0 -134
  165. package/dist/vectors/search.d.ts.map +0 -1
  166. package/dist/vectors/search.js +0 -411
  167. package/dist/vectors/search.js.map +0 -1
  168. package/dist/visualizer/public/index.html +0 -1994
  169. package/dist/visualizer/server.d.ts +0 -46
  170. package/dist/visualizer/server.d.ts.map +0 -1
  171. package/dist/visualizer/server.js +0 -491
  172. package/dist/visualizer/server.js.map +0 -1
@@ -47,6 +47,8 @@ const languages_1 = require("./languages");
47
47
  const liquid_extractor_1 = require("./liquid-extractor");
48
48
  const svelte_extractor_1 = require("./svelte-extractor");
49
49
  const dfm_extractor_1 = require("./dfm-extractor");
50
+ const vue_extractor_1 = require("./vue-extractor");
51
+ const frameworks_1 = require("../resolution/frameworks");
50
52
  // Re-export for backward compatibility
51
53
  var tree_sitter_helpers_2 = require("./tree-sitter-helpers");
52
54
  Object.defineProperty(exports, "generateNodeId", { enumerable: true, get: function () { return tree_sitter_helpers_2.generateNodeId; } });
@@ -111,6 +113,16 @@ function extractName(node, source, extractor) {
111
113
  }
112
114
  return '<anonymous>';
113
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
+ ]);
114
126
  /**
115
127
  * TreeSitterExtractor - Main extraction class
116
128
  */
@@ -331,12 +343,19 @@ class TreeSitterExtractor {
331
343
  this.extractVariable(node);
332
344
  skipChildren = true; // extractVariable handles children
333
345
  }
334
- // Check for export statements containing non-function variable declarations
335
- // e.g. `export const X = create(...)`, `export const X = { ... }`
336
- else if (nodeType === 'export_statement') {
337
- this.extractExportedVariables(node);
338
- // Don't skip children still need to visit inner nodes (functions, calls, etc.)
339
- }
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.
340
359
  // Check for imports
341
360
  else if (this.extractor.importTypes.includes(nodeType)) {
342
361
  this.extractImport(node);
@@ -345,6 +364,17 @@ class TreeSitterExtractor {
345
364
  else if (this.extractor.callTypes.includes(nodeType)) {
346
365
  this.extractCall(node);
347
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.)
348
378
  // Rust: `impl Trait for Type { ... }` — creates implements edge from Type to Trait
349
379
  else if (nodeType === 'impl_item') {
350
380
  this.extractRustImplItem(node);
@@ -521,6 +551,10 @@ class TreeSitterExtractor {
521
551
  return;
522
552
  // Extract type annotations (parameter types and return type)
523
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);
524
558
  // Push to stack and visit body
525
559
  this.nodeStack.push(funcNode.id);
526
560
  const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
@@ -549,6 +583,8 @@ class TreeSitterExtractor {
549
583
  return;
550
584
  // Extract extends/implements
551
585
  this.extractInheritance(node, classNode.id);
586
+ // Extract decorators applied to the class (`@Foo class X {}`).
587
+ this.extractDecoratorsFor(node, classNode.id);
552
588
  // Push to stack and visit body
553
589
  this.nodeStack.push(classNode.id);
554
590
  let body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
@@ -631,6 +667,8 @@ class TreeSitterExtractor {
631
667
  }
632
668
  // Extract type annotations (parameter types and return type)
633
669
  this.extractTypeAnnotations(node, methodNode.id);
670
+ // Extract decorators (`@Get('/list') list() {}`).
671
+ this.extractDecoratorsFor(node, methodNode.id);
634
672
  // Push to stack and visit body
635
673
  this.nodeStack.push(methodNode.id);
636
674
  const body = this.extractor.resolveBody?.(node, this.extractor.bodyField)
@@ -792,12 +830,17 @@ class TreeSitterExtractor {
792
830
  && c.type !== 'accessors' && c.type !== 'equals_value_clause');
793
831
  const typeText = typeNode ? (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source) : undefined;
794
832
  const signature = typeText ? `${typeText} ${name}` : name;
795
- this.createNode('property', name, node, {
833
+ const propNode = this.createNode('property', name, node, {
796
834
  docstring,
797
835
  signature,
798
836
  visibility,
799
837
  isStatic,
800
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
+ }
801
844
  }
802
845
  /**
803
846
  * Extract a class field declaration (e.g. Java field_declaration, C# field_declaration).
@@ -861,12 +904,16 @@ class TreeSitterExtractor {
861
904
  continue;
862
905
  const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
863
906
  const signature = typeText ? `${typeText} ${name}` : name;
864
- this.createNode('field', name, decl, {
907
+ const fieldNode = this.createNode('field', name, decl, {
865
908
  docstring,
866
909
  signature,
867
910
  visibility,
868
911
  isStatic,
869
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);
870
917
  }
871
918
  }
872
919
  else {
@@ -1105,54 +1152,11 @@ class TreeSitterExtractor {
1105
1152
  }
1106
1153
  return false;
1107
1154
  }
1108
- /**
1109
- * Extract an exported variable declaration that isn't a function.
1110
- * Handles patterns like:
1111
- * export const X = create(...)
1112
- * export const X = { ... }
1113
- * export const X = [...]
1114
- * export const X = "value"
1115
- *
1116
- * This is called for `export_statement` nodes that contain a
1117
- * `lexical_declaration` with `variable_declarator` children whose
1118
- * values are NOT already handled by functionTypes (arrow_function,
1119
- * function_expression).
1120
- */
1121
- extractExportedVariables(exportNode) {
1122
- if (!this.extractor)
1123
- return;
1124
- // Find the lexical_declaration or variable_declaration child
1125
- for (let i = 0; i < exportNode.namedChildCount; i++) {
1126
- const decl = exportNode.namedChild(i);
1127
- if (!decl || (decl.type !== 'lexical_declaration' && decl.type !== 'variable_declaration')) {
1128
- continue;
1129
- }
1130
- // Iterate over each variable_declarator in the declaration
1131
- for (let j = 0; j < decl.namedChildCount; j++) {
1132
- const declarator = decl.namedChild(j);
1133
- if (!declarator || declarator.type !== 'variable_declarator')
1134
- continue;
1135
- const nameNode = (0, tree_sitter_helpers_1.getChildByField)(declarator, 'name');
1136
- if (!nameNode)
1137
- continue;
1138
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
1139
- // Skip if the value is a function type — those are already handled
1140
- // by extractFunction via the functionTypes dispatch
1141
- const value = (0, tree_sitter_helpers_1.getChildByField)(declarator, 'value');
1142
- if (value) {
1143
- const valueType = value.type;
1144
- if (this.extractor.functionTypes.includes(valueType)) {
1145
- continue; // Already handled by extractFunction
1146
- }
1147
- }
1148
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(exportNode, this.source);
1149
- this.createNode('variable', name, declarator, {
1150
- docstring,
1151
- isExported: true,
1152
- });
1153
- }
1154
- }
1155
- }
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.
1156
1160
  /**
1157
1161
  * Extract an import
1158
1162
  *
@@ -1369,6 +1373,160 @@ class TreeSitterExtractor {
1369
1373
  });
1370
1374
  }
1371
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
+ }
1372
1530
  /**
1373
1531
  * Visit function body and extract calls (and structural nodes).
1374
1532
  *
@@ -1387,6 +1545,13 @@ class TreeSitterExtractor {
1387
1545
  if (this.extractor.callTypes.includes(nodeType)) {
1388
1546
  this.extractCall(node);
1389
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
+ }
1390
1555
  else if (this.extractor.extractBareCall) {
1391
1556
  const calleeName = this.extractor.extractBareCall(node, this.source);
1392
1557
  if (calleeName && this.nodeStack.length > 0) {
@@ -2167,28 +2332,62 @@ class TreeSitterExtractor {
2167
2332
  }
2168
2333
  exports.TreeSitterExtractor = TreeSitterExtractor;
2169
2334
  /**
2170
- * Extract nodes and edges from source code
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.
2171
2340
  */
2172
- function extractFromSource(filePath, source, language) {
2341
+ function extractFromSource(filePath, source, language, frameworkNames) {
2173
2342
  const detectedLanguage = language || (0, grammars_1.detectLanguage)(filePath, source);
2174
2343
  const fileExtension = path.extname(filePath).toLowerCase();
2344
+ let result;
2175
2345
  // Use custom extractor for Svelte
2176
2346
  if (detectedLanguage === 'svelte') {
2177
2347
  const extractor = new svelte_extractor_1.SvelteExtractor(filePath, source);
2178
- return extractor.extract();
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();
2179
2354
  }
2180
- // Use custom extractor for Liquid
2181
- if (detectedLanguage === 'liquid') {
2355
+ else if (detectedLanguage === 'liquid') {
2356
+ // Use custom extractor for Liquid
2182
2357
  const extractor = new liquid_extractor_1.LiquidExtractor(filePath, source);
2183
- return extractor.extract();
2358
+ result = extractor.extract();
2184
2359
  }
2185
- // Use custom extractor for DFM/FMX form files
2186
- if (detectedLanguage === 'pascal' &&
2360
+ else if (detectedLanguage === 'pascal' &&
2187
2361
  (fileExtension === '.dfm' || fileExtension === '.fmx')) {
2362
+ // Use custom extractor for DFM/FMX form files
2188
2363
  const extractor = new dfm_extractor_1.DfmExtractor(filePath, source);
2189
- return extractor.extract();
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
+ }
2190
2390
  }
2191
- const extractor = new TreeSitterExtractor(filePath, source, detectedLanguage);
2192
- return extractor.extract();
2391
+ return result;
2193
2392
  }
2194
2393
  //# sourceMappingURL=tree-sitter.js.map