@optave/codegraph 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +137 -86
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +54 -58
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +81 -39
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/branch-compare.d.ts.map +1 -1
- package/dist/cli/commands/branch-compare.js +4 -0
- package/dist/cli/commands/branch-compare.js.map +1 -1
- package/dist/cli/commands/diff-impact.d.ts.map +1 -1
- package/dist/cli/commands/diff-impact.js +2 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +3 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +16 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +31 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +7 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +100 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +4 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +51 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +64 -59
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +352 -107
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +49 -18
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +2 -2
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +32 -21
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +3 -4
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +141 -89
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +23 -8
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +65 -54
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.js +58 -67
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +83 -81
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +12 -5
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +9 -9
- package/src/ast-analysis/engine.ts +176 -104
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
- package/src/ast-analysis/visitors/complexity-visitor.ts +89 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/branch-compare.ts +4 -0
- package/src/cli/commands/diff-impact.ts +2 -1
- package/src/cli/commands/info.ts +3 -2
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +34 -0
- package/src/db/repository/native-repository.ts +104 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +55 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +77 -81
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +409 -99
- package/src/domain/graph/builder/stages/build-edges.ts +45 -19
- package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
- package/src/domain/graph/builder/stages/finalize.ts +2 -2
- package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +143 -89
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +26 -7
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/go.ts +43 -33
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +66 -54
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/python.ts +31 -25
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +57 -61
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +130 -110
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +26 -5
package/src/extractors/go.ts
CHANGED
|
@@ -80,21 +80,9 @@ function handleGoFuncDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
80
80
|
|
|
81
81
|
function handleGoMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
82
82
|
const nameNode = node.childForFieldName('name');
|
|
83
|
-
const receiver = node.childForFieldName('receiver');
|
|
84
83
|
if (!nameNode) return;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < receiver.childCount; i++) {
|
|
88
|
-
const param = receiver.child(i);
|
|
89
|
-
if (!param) continue;
|
|
90
|
-
const typeNode = param.childForFieldName('type');
|
|
91
|
-
if (typeNode) {
|
|
92
|
-
receiverType =
|
|
93
|
-
typeNode.type === 'pointer_type' ? typeNode.text.replace(/^\*/, '') : typeNode.text;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
84
|
+
const receiver = node.childForFieldName('receiver');
|
|
85
|
+
const receiverType = receiver ? extractGoReceiverType(receiver) : null;
|
|
98
86
|
const fullName = receiverType ? `${receiverType}.${nameNode.text}` : nameNode.text;
|
|
99
87
|
const params = extractGoParameters(node.childForFieldName('parameters'));
|
|
100
88
|
ctx.definitions.push({
|
|
@@ -107,6 +95,19 @@ function handleGoMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
107
95
|
});
|
|
108
96
|
}
|
|
109
97
|
|
|
98
|
+
/** Extract the receiver type name from a method receiver parameter list. */
|
|
99
|
+
function extractGoReceiverType(receiver: TreeSitterNode): string | null {
|
|
100
|
+
for (let i = 0; i < receiver.childCount; i++) {
|
|
101
|
+
const param = receiver.child(i);
|
|
102
|
+
if (!param) continue;
|
|
103
|
+
const typeNode = param.childForFieldName('type');
|
|
104
|
+
if (typeNode) {
|
|
105
|
+
return typeNode.type === 'pointer_type' ? typeNode.text.replace(/^\*/, '') : typeNode.text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
110
111
|
function handleGoTypeDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
111
112
|
for (let i = 0; i < node.childCount; i++) {
|
|
112
113
|
const spec = node.child(i);
|
|
@@ -403,13 +404,38 @@ function extractGoParameters(paramListNode: TreeSitterNode | null): SubDeclarati
|
|
|
403
404
|
* This performs file-local matching (cross-file matching requires build-edges).
|
|
404
405
|
*/
|
|
405
406
|
function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
407
|
+
const { interfaceMethods, structMethods, structLines } = collectGoMethodSets(ctx);
|
|
408
|
+
|
|
409
|
+
// Match: struct satisfies interface if it has all interface methods (name-only;
|
|
410
|
+
// signatures are not verified — treat as candidate match, not definitive).
|
|
411
|
+
// NOTE: embedded interfaces (type_elem nodes) are not resolved — composite
|
|
412
|
+
// interfaces like `type ReadWriter interface { Reader; Writer }` will have an
|
|
413
|
+
// empty method set and be silently excluded from matching.
|
|
414
|
+
for (const [structName, methods] of structMethods) {
|
|
415
|
+
for (const [ifaceName, ifaceMethods] of interfaceMethods) {
|
|
416
|
+
if (ifaceMethods.size > 0 && [...ifaceMethods].every((m) => methods.has(m))) {
|
|
417
|
+
ctx.classes.push({
|
|
418
|
+
name: structName,
|
|
419
|
+
implements: ifaceName,
|
|
420
|
+
line: structLines.get(structName) || 1,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Collect interface and struct method sets from definitions for structural matching. */
|
|
428
|
+
function collectGoMethodSets(ctx: ExtractorOutput): {
|
|
429
|
+
interfaceMethods: Map<string, Set<string>>;
|
|
430
|
+
structMethods: Map<string, Set<string>>;
|
|
431
|
+
structLines: Map<string, number>;
|
|
432
|
+
} {
|
|
406
433
|
const interfaceMethods = new Map<string, Set<string>>();
|
|
407
434
|
const structMethods = new Map<string, Set<string>>();
|
|
408
435
|
const structLines = new Map<string, number>();
|
|
409
|
-
|
|
410
|
-
// Collect interface and struct definitions
|
|
411
436
|
const interfaceNames = new Set<string>();
|
|
412
437
|
const structNames = new Set<string>();
|
|
438
|
+
|
|
413
439
|
for (const def of ctx.definitions) {
|
|
414
440
|
if (def.kind === 'interface') interfaceNames.add(def.name);
|
|
415
441
|
if (def.kind === 'struct') {
|
|
@@ -418,7 +444,6 @@ function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
|
418
444
|
}
|
|
419
445
|
}
|
|
420
446
|
|
|
421
|
-
// Collect methods grouped by receiver type
|
|
422
447
|
for (const def of ctx.definitions) {
|
|
423
448
|
if (def.kind !== 'method' || !def.name.includes('.')) continue;
|
|
424
449
|
const dotIdx = def.name.indexOf('.');
|
|
@@ -435,22 +460,7 @@ function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
462
|
|
|
438
|
-
|
|
439
|
-
// signatures are not verified — treat as candidate match, not definitive).
|
|
440
|
-
// NOTE: embedded interfaces (type_elem nodes) are not resolved — composite
|
|
441
|
-
// interfaces like `type ReadWriter interface { Reader; Writer }` will have an
|
|
442
|
-
// empty method set and be silently excluded from matching.
|
|
443
|
-
for (const [structName, methods] of structMethods) {
|
|
444
|
-
for (const [ifaceName, ifaceMethods] of interfaceMethods) {
|
|
445
|
-
if (ifaceMethods.size > 0 && [...ifaceMethods].every((m) => methods.has(m))) {
|
|
446
|
-
ctx.classes.push({
|
|
447
|
-
name: structName,
|
|
448
|
-
implements: ifaceName,
|
|
449
|
-
line: structLines.get(structName) || 1,
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
463
|
+
return { interfaceMethods, structMethods, structLines };
|
|
454
464
|
}
|
|
455
465
|
|
|
456
466
|
function extractStructFields(structTypeNode: TreeSitterNode): SubDeclaration[] {
|
|
@@ -110,6 +110,20 @@ export function findParentNode(
|
|
|
110
110
|
return null;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Resolve a container's body node by trying each field name in order.
|
|
115
|
+
*/
|
|
116
|
+
function resolveBodyNode(
|
|
117
|
+
containerNode: TreeSitterNode,
|
|
118
|
+
bodyFields: readonly string[],
|
|
119
|
+
): TreeSitterNode | null {
|
|
120
|
+
for (const field of bodyFields) {
|
|
121
|
+
const body = containerNode.childForFieldName(field) || findChild(containerNode, field);
|
|
122
|
+
if (body) return body;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
/**
|
|
114
128
|
* Extract child declarations from a container node's body.
|
|
115
129
|
* Finds the body via `bodyFields` (tries childForFieldName then findChild for each),
|
|
@@ -126,22 +140,17 @@ export function extractBodyMembers(
|
|
|
126
140
|
nameField: string = 'name',
|
|
127
141
|
visibility?: (member: TreeSitterNode) => SubDeclaration['visibility'],
|
|
128
142
|
): SubDeclaration[] {
|
|
143
|
+
const body = resolveBodyNode(containerNode, bodyFields);
|
|
144
|
+
if (!body) return [];
|
|
129
145
|
const members: SubDeclaration[] = [];
|
|
130
|
-
let body: TreeSitterNode | null = null;
|
|
131
|
-
for (const field of bodyFields) {
|
|
132
|
-
body = containerNode.childForFieldName(field) || findChild(containerNode, field);
|
|
133
|
-
if (body) break;
|
|
134
|
-
}
|
|
135
|
-
if (!body) return members;
|
|
136
146
|
for (let i = 0; i < body.childCount; i++) {
|
|
137
147
|
const member = body.child(i);
|
|
138
148
|
if (!member || member.type !== memberType) continue;
|
|
139
149
|
const nn = member.childForFieldName(nameField);
|
|
140
|
-
if (nn)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
150
|
+
if (!nn) continue;
|
|
151
|
+
const entry: SubDeclaration = { name: nn.text, kind, line: member.startPosition.row + 1 };
|
|
152
|
+
if (visibility) entry.visibility = visibility(member);
|
|
153
|
+
members.push(entry);
|
|
145
154
|
}
|
|
146
155
|
return members;
|
|
147
156
|
}
|
|
@@ -162,24 +171,29 @@ export function lastPathSegment(path: string, separator: string = '/'): string {
|
|
|
162
171
|
return path.split(separator).pop() ?? path;
|
|
163
172
|
}
|
|
164
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Parse visibility from a modifier node's text content.
|
|
176
|
+
*/
|
|
177
|
+
function parseModifierText(text: string): 'public' | 'private' | 'protected' | undefined {
|
|
178
|
+
if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
|
|
179
|
+
// C# 'private protected' — accessible to derived types in same assembly → protected
|
|
180
|
+
if (text === 'private protected') return 'protected';
|
|
181
|
+
// Compound modifiers node (Java: "public static") — scan its text for a keyword
|
|
182
|
+
for (const kw of VISIBILITY_KEYWORDS) {
|
|
183
|
+
if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
165
188
|
export function extractModifierVisibility(
|
|
166
189
|
node: TreeSitterNode,
|
|
167
190
|
modifierTypes: Set<string> = DEFAULT_MODIFIER_TYPES,
|
|
168
191
|
): 'public' | 'private' | 'protected' | undefined {
|
|
169
192
|
for (let i = 0; i < node.childCount; i++) {
|
|
170
193
|
const child = node.child(i);
|
|
171
|
-
if (!child) continue;
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
const text = child.text;
|
|
175
|
-
if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
|
|
176
|
-
// C# 'private protected' — accessible to derived types in same assembly → protected
|
|
177
|
-
if (text === 'private protected') return 'protected';
|
|
178
|
-
// Compound modifiers node (Java: "public static") — scan its text for a keyword
|
|
179
|
-
for (const kw of VISIBILITY_KEYWORDS) {
|
|
180
|
-
if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
|
|
181
|
-
}
|
|
182
|
-
}
|
|
194
|
+
if (!child || !modifierTypes.has(child.type)) continue;
|
|
195
|
+
const result = parseModifierText(child.text);
|
|
196
|
+
if (result) return result;
|
|
183
197
|
}
|
|
184
198
|
return undefined;
|
|
185
199
|
}
|
package/src/extractors/java.ts
CHANGED
|
@@ -83,27 +83,7 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
83
83
|
children: classChildren.length > 0 ? classChildren : undefined,
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
if (superclass) {
|
|
88
|
-
for (let i = 0; i < superclass.childCount; i++) {
|
|
89
|
-
const child = superclass.child(i);
|
|
90
|
-
if (
|
|
91
|
-
child &&
|
|
92
|
-
(child.type === 'type_identifier' ||
|
|
93
|
-
child.type === 'identifier' ||
|
|
94
|
-
child.type === 'generic_type')
|
|
95
|
-
) {
|
|
96
|
-
const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
|
|
97
|
-
if (superName)
|
|
98
|
-
ctx.classes.push({
|
|
99
|
-
name: nameNode.text,
|
|
100
|
-
extends: superName,
|
|
101
|
-
line: node.startPosition.row + 1,
|
|
102
|
-
});
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
86
|
+
extractJavaSuperclass(node, nameNode.text, ctx);
|
|
107
87
|
|
|
108
88
|
const interfaces = node.childForFieldName('interfaces');
|
|
109
89
|
if (interfaces) {
|
|
@@ -111,6 +91,32 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
111
91
|
}
|
|
112
92
|
}
|
|
113
93
|
|
|
94
|
+
/** Extract the superclass (extends) relationship from a Java class declaration. */
|
|
95
|
+
function extractJavaSuperclass(
|
|
96
|
+
node: TreeSitterNode,
|
|
97
|
+
className: string,
|
|
98
|
+
ctx: ExtractorOutput,
|
|
99
|
+
): void {
|
|
100
|
+
const superclass = node.childForFieldName('superclass');
|
|
101
|
+
if (!superclass) return;
|
|
102
|
+
const superName = findJavaSuperTypeName(superclass);
|
|
103
|
+
if (superName) {
|
|
104
|
+
ctx.classes.push({ name: className, extends: superName, line: node.startPosition.row + 1 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Find the type name from a superclass node (handles generic_type unwrapping). */
|
|
109
|
+
function findJavaSuperTypeName(superclass: TreeSitterNode): string | undefined {
|
|
110
|
+
for (let i = 0; i < superclass.childCount; i++) {
|
|
111
|
+
const child = superclass.child(i);
|
|
112
|
+
if (!child) continue;
|
|
113
|
+
if (JAVA_TYPE_NODE_TYPES.has(child.type)) {
|
|
114
|
+
return resolveJavaIfaceName(child);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
const JAVA_TYPE_NODE_TYPES = new Set(['type_identifier', 'identifier', 'generic_type']);
|
|
115
121
|
|
|
116
122
|
/** Resolve interface name from a type node (handles generic_type unwrapping). */
|
|
@@ -161,19 +167,26 @@ function handleJavaInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): vo
|
|
|
161
167
|
endLine: nodeEndLine(node),
|
|
162
168
|
});
|
|
163
169
|
const body = node.childForFieldName('body');
|
|
164
|
-
if (body)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
if (body) extractJavaInterfaceMethods(body, nameNode.text, ctx);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Extract method declarations from a Java interface body. */
|
|
174
|
+
function extractJavaInterfaceMethods(
|
|
175
|
+
body: TreeSitterNode,
|
|
176
|
+
ifaceName: string,
|
|
177
|
+
ctx: ExtractorOutput,
|
|
178
|
+
): void {
|
|
179
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
180
|
+
const child = body.child(i);
|
|
181
|
+
if (child && child.type === 'method_declaration') {
|
|
182
|
+
const methName = child.childForFieldName('name');
|
|
183
|
+
if (methName) {
|
|
184
|
+
ctx.definitions.push({
|
|
185
|
+
name: `${ifaceName}.${methName.text}`,
|
|
186
|
+
kind: 'method',
|
|
187
|
+
line: child.startPosition.row + 1,
|
|
188
|
+
endLine: child.endPosition.row + 1,
|
|
189
|
+
});
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
192
|
}
|
|
@@ -321,23 +334,29 @@ function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
|
|
|
321
334
|
for (let i = 0; i < body.childCount; i++) {
|
|
322
335
|
const member = body.child(i);
|
|
323
336
|
if (!member || member.type !== 'field_declaration') continue;
|
|
324
|
-
|
|
325
|
-
const child = member.child(j);
|
|
326
|
-
if (!child || child.type !== 'variable_declarator') continue;
|
|
327
|
-
const nameNode = child.childForFieldName('name');
|
|
328
|
-
if (nameNode) {
|
|
329
|
-
fields.push({
|
|
330
|
-
name: nameNode.text,
|
|
331
|
-
kind: 'property',
|
|
332
|
-
line: member.startPosition.row + 1,
|
|
333
|
-
visibility: extractModifierVisibility(member),
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
+
extractFieldDeclarators(member, fields);
|
|
337
338
|
}
|
|
338
339
|
return fields;
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
/** Extract variable_declarator names from a field_declaration node. */
|
|
343
|
+
function extractFieldDeclarators(member: TreeSitterNode, fields: SubDeclaration[]): void {
|
|
344
|
+
const vis = extractModifierVisibility(member);
|
|
345
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
346
|
+
const child = member.child(j);
|
|
347
|
+
if (!child || child.type !== 'variable_declarator') continue;
|
|
348
|
+
const nameNode = child.childForFieldName('name');
|
|
349
|
+
if (nameNode) {
|
|
350
|
+
fields.push({
|
|
351
|
+
name: nameNode.text,
|
|
352
|
+
kind: 'property',
|
|
353
|
+
line: member.startPosition.row + 1,
|
|
354
|
+
visibility: vis,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
341
360
|
function extractEnumConstants(enumNode: TreeSitterNode): SubDeclaration[] {
|
|
342
361
|
return extractBodyMembers(enumNode, ['body', 'enum_body'], 'enum_constant', 'constant');
|
|
343
362
|
}
|
|
@@ -328,34 +328,7 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
328
328
|
if (inner) declNode = inner;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
if (t === 'lexical_declaration' || t === 'variable_declaration') {
|
|
333
|
-
if (declNode.text.startsWith('const ')) {
|
|
334
|
-
for (let j = 0; j < declNode.childCount; j++) {
|
|
335
|
-
const declarator = declNode.child(j);
|
|
336
|
-
if (!declarator || declarator.type !== 'variable_declarator') continue;
|
|
337
|
-
const nameN = declarator.childForFieldName('name');
|
|
338
|
-
const valueN = declarator.childForFieldName('value');
|
|
339
|
-
if (!nameN || nameN.type !== 'identifier' || !valueN) continue;
|
|
340
|
-
// Skip functions — already captured by query patterns
|
|
341
|
-
const valType = valueN.type;
|
|
342
|
-
if (
|
|
343
|
-
valType === 'arrow_function' ||
|
|
344
|
-
valType === 'function_expression' ||
|
|
345
|
-
valType === 'function'
|
|
346
|
-
)
|
|
347
|
-
continue;
|
|
348
|
-
if (isConstantValue(valueN)) {
|
|
349
|
-
definitions.push({
|
|
350
|
-
name: nameN.text,
|
|
351
|
-
kind: 'constant',
|
|
352
|
-
line: declNode.startPosition.row + 1,
|
|
353
|
-
endLine: nodeEndLine(declNode),
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
331
|
+
extractConstDeclarators(declNode, definitions);
|
|
359
332
|
|
|
360
333
|
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
361
334
|
if (child.type !== 'export_statement') {
|
|
@@ -364,6 +337,33 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
364
337
|
}
|
|
365
338
|
}
|
|
366
339
|
|
|
340
|
+
/** Extract constant definitions from a `const` declaration node. */
|
|
341
|
+
function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definition[]): void {
|
|
342
|
+
const t = declNode.type;
|
|
343
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
|
|
344
|
+
if (!declNode.text.startsWith('const ')) return;
|
|
345
|
+
|
|
346
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
347
|
+
const declarator = declNode.child(j);
|
|
348
|
+
if (!declarator || declarator.type !== 'variable_declarator') continue;
|
|
349
|
+
const nameN = declarator.childForFieldName('name');
|
|
350
|
+
const valueN = declarator.childForFieldName('value');
|
|
351
|
+
if (!nameN || nameN.type !== 'identifier' || !valueN) continue;
|
|
352
|
+
// Skip functions — already captured by query patterns
|
|
353
|
+
const valType = valueN.type;
|
|
354
|
+
if (valType === 'arrow_function' || valType === 'function_expression' || valType === 'function')
|
|
355
|
+
continue;
|
|
356
|
+
if (isConstantValue(valueN)) {
|
|
357
|
+
definitions.push({
|
|
358
|
+
name: nameN.text,
|
|
359
|
+
kind: 'constant',
|
|
360
|
+
line: declNode.startPosition.row + 1,
|
|
361
|
+
endLine: nodeEndLine(declNode),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
367
|
/**
|
|
368
368
|
* Recursive walk to find dynamic import() calls.
|
|
369
369
|
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
@@ -678,24 +678,7 @@ function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
678
678
|
const fn = node.childForFieldName('function');
|
|
679
679
|
if (!fn) return;
|
|
680
680
|
if (fn.type === 'import') {
|
|
681
|
-
|
|
682
|
-
if (args) {
|
|
683
|
-
const strArg = findChild(args, 'string');
|
|
684
|
-
if (strArg) {
|
|
685
|
-
const modPath = strArg.text.replace(/['"]/g, '');
|
|
686
|
-
const names = extractDynamicImportNames(node);
|
|
687
|
-
ctx.imports.push({
|
|
688
|
-
source: modPath,
|
|
689
|
-
names,
|
|
690
|
-
line: node.startPosition.row + 1,
|
|
691
|
-
dynamicImport: true,
|
|
692
|
-
});
|
|
693
|
-
} else {
|
|
694
|
-
debug(
|
|
695
|
-
`Skipping non-static dynamic import() at line ${node.startPosition.row + 1} (template literal or variable)`,
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
681
|
+
handleDynamicImportCall(node, ctx.imports);
|
|
699
682
|
} else {
|
|
700
683
|
const callInfo = extractCallInfo(fn, node);
|
|
701
684
|
if (callInfo) ctx.calls.push(callInfo);
|
|
@@ -706,6 +689,22 @@ function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
706
689
|
}
|
|
707
690
|
}
|
|
708
691
|
|
|
692
|
+
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
693
|
+
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
694
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
695
|
+
if (!args) return;
|
|
696
|
+
const strArg = findChild(args, 'string');
|
|
697
|
+
if (strArg) {
|
|
698
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
699
|
+
const names = extractDynamicImportNames(node);
|
|
700
|
+
imports.push({ source: modPath, names, line: node.startPosition.row + 1, dynamicImport: true });
|
|
701
|
+
} else {
|
|
702
|
+
debug(
|
|
703
|
+
`Skipping non-static dynamic import() at line ${node.startPosition.row + 1} (template literal or variable)`,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
709
708
|
function handleImportStmt(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
710
709
|
const isTypeOnly = node.text.startsWith('import type');
|
|
711
710
|
const source = node.childForFieldName('source') || findChild(node, 'string');
|
|
@@ -998,21 +997,34 @@ function handleVarDeclaratorTypeMap(
|
|
|
998
997
|
const nameN = node.childForFieldName('name');
|
|
999
998
|
if (!nameN || nameN.type !== 'identifier') return;
|
|
1000
999
|
|
|
1001
|
-
// Type annotation: const x: Foo = …
|
|
1002
1000
|
const typeAnno = findChild(node, 'type_annotation');
|
|
1001
|
+
const valueN = node.childForFieldName('value');
|
|
1002
|
+
|
|
1003
|
+
// Constructor on the same declaration wins over annotation: the runtime type is
|
|
1004
|
+
// what matters for call resolution (e.g. `const x: Base = new Derived()` should
|
|
1005
|
+
// resolve `x.render()` to `Derived.render`, not `Base.render`).
|
|
1006
|
+
// When no constructor is present, annotation still takes precedence over factory.
|
|
1007
|
+
if (valueN?.type === 'new_expression') {
|
|
1008
|
+
const ctorType = extractNewExprTypeName(valueN);
|
|
1009
|
+
if (ctorType) {
|
|
1010
|
+
setTypeMapEntry(typeMap, nameN.text, ctorType, 1.0);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Type annotation: const x: Foo = … → confidence 0.9
|
|
1003
1016
|
if (typeAnno) {
|
|
1004
1017
|
const typeName = extractSimpleTypeName(typeAnno);
|
|
1005
|
-
if (typeName)
|
|
1018
|
+
if (typeName) {
|
|
1019
|
+
setTypeMapEntry(typeMap, nameN.text, typeName, 0.9);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1006
1022
|
}
|
|
1007
1023
|
|
|
1008
|
-
const valueN = node.childForFieldName('value');
|
|
1009
1024
|
if (!valueN) return;
|
|
1010
1025
|
|
|
1011
|
-
// Constructor
|
|
1012
|
-
if (valueN.type === 'new_expression')
|
|
1013
|
-
const ctorType = extractNewExprTypeName(valueN);
|
|
1014
|
-
if (ctorType) setTypeMapEntry(typeMap, nameN.text, ctorType, 1.0);
|
|
1015
|
-
}
|
|
1026
|
+
// Constructor already handled above — only factory path remains.
|
|
1027
|
+
if (valueN.type === 'new_expression') return;
|
|
1016
1028
|
// Factory method: const x = Foo.create() → confidence 0.7
|
|
1017
1029
|
else if (valueN.type === 'call_expression') {
|
|
1018
1030
|
const fn = valueN.childForFieldName('function');
|