Completes the cross-file dependency graph behind impact / affected / explore across all 22 supported languages and 14 web frameworks, validated on real-world repos (measured fair-coverage table added to the README). Per-language resolution + framework resolvers/synthesizers (Lua/Luau require, Shopify OS 2.0 Liquid sections, Delphi forms, Rust cross-module + Rocket macros, Swift Fluent, SvelteKit/Nuxt loader/component conventions, RN/Expo bridges). 0 cross-family false edges, full suite green (1187 passed). See #708. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
import type { Node as SyntaxNode } from 'web-tree-sitter';
|
|
import { getNodeText, getChildByField } from '../tree-sitter-helpers';
|
|
import type { LanguageExtractor } from '../tree-sitter-types';
|
|
|
|
export const javaExtractor: LanguageExtractor = {
|
|
functionTypes: [],
|
|
classTypes: ['class_declaration'],
|
|
methodTypes: ['method_declaration', 'constructor_declaration'],
|
|
// `annotation_type_declaration` is `@interface Foo { … }` — an annotation
|
|
// definition. Without it, annotation types (`@SerializedName`, `@GetMapping`,
|
|
// JPA/Spring annotations) aren't nodes, so the `@Foo` usages that DO get
|
|
// extracted can't resolve and the annotation file shows zero dependents.
|
|
interfaceTypes: ['interface_declaration', 'annotation_type_declaration'],
|
|
structTypes: [],
|
|
enumTypes: ['enum_declaration'],
|
|
enumMemberTypes: ['enum_constant'],
|
|
typeAliasTypes: [],
|
|
importTypes: ['import_declaration'],
|
|
callTypes: ['method_invocation'],
|
|
variableTypes: ['local_variable_declaration'],
|
|
fieldTypes: ['field_declaration'],
|
|
nameField: 'name',
|
|
bodyField: 'body',
|
|
paramsField: 'parameters',
|
|
returnField: 'type',
|
|
getSignature: (node, source) => {
|
|
const params = getChildByField(node, 'parameters');
|
|
const returnType = getChildByField(node, 'type');
|
|
if (!params) return undefined;
|
|
const paramsText = getNodeText(params, source);
|
|
return returnType ? getNodeText(returnType, source) + ' ' + paramsText : paramsText;
|
|
},
|
|
getVisibility: (node) => {
|
|
for (let i = 0; i < node.childCount; i++) {
|
|
const child = node.child(i);
|
|
if (child?.type === 'modifiers') {
|
|
const text = child.text;
|
|
if (text.includes('public')) return 'public';
|
|
if (text.includes('private')) return 'private';
|
|
if (text.includes('protected')) return 'protected';
|
|
}
|
|
}
|
|
return undefined;
|
|
},
|
|
isStatic: (node) => {
|
|
for (let i = 0; i < node.childCount; i++) {
|
|
const child = node.child(i);
|
|
if (child?.type === 'modifiers' && child.text.includes('static')) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
extractImport: (node, source) => {
|
|
const importText = source.substring(node.startIndex, node.endIndex).trim();
|
|
const scopedId = node.namedChildren.find((c: SyntaxNode) => c.type === 'scoped_identifier');
|
|
if (scopedId) {
|
|
const moduleName = source.substring(scopedId.startIndex, scopedId.endIndex);
|
|
return { moduleName, signature: importText };
|
|
}
|
|
return null;
|
|
},
|
|
packageTypes: ['package_declaration'],
|
|
extractPackage: (node, source) => {
|
|
// package_declaration → scoped_identifier or identifier (single-segment)
|
|
const id = node.namedChildren.find(
|
|
(c: SyntaxNode) => c.type === 'scoped_identifier' || c.type === 'identifier'
|
|
);
|
|
return id ? source.substring(id.startIndex, id.endIndex).trim() : null;
|
|
},
|
|
};
|