import { getNodeText, getChildByField } from '../tree-sitter-helpers'; import type { LanguageExtractor } from '../tree-sitter-types'; export const typescriptExtractor: LanguageExtractor = { functionTypes: ['function_declaration', 'arrow_function', 'function_expression'], classTypes: ['class_declaration'], methodTypes: ['method_definition', 'public_field_definition'], interfaceTypes: ['interface_declaration'], structTypes: [], enumTypes: ['enum_declaration'], typeAliasTypes: ['type_alias_declaration'], importTypes: ['import_statement'], callTypes: ['call_expression'], variableTypes: ['lexical_declaration', 'variable_declaration'], nameField: 'name', bodyField: 'body', paramsField: 'parameters', returnField: 'return_type', getSignature: (node, source) => { const params = getChildByField(node, 'parameters'); const returnType = getChildByField(node, 'return_type'); if (!params) return undefined; let sig = getNodeText(params, source); if (returnType) { sig += ': ' + getNodeText(returnType, source).replace(/^:\s*/, ''); } return sig; }, getVisibility: (node) => { for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child?.type === 'accessibility_modifier') { const text = child.text; if (text === 'public') return 'public'; if (text === 'private') return 'private'; if (text === 'protected') return 'protected'; } } return undefined; }, isExported: (node, _source) => { // Walk the parent chain to find an export_statement ancestor. // This correctly handles deeply nested nodes like arrow functions // inside variable declarations: `export const X = () => { ... }` // where the arrow_function is 3 levels deep under export_statement. let current = node.parent; while (current) { if (current.type === 'export_statement') return true; current = current.parent; } return false; }, isAsync: (node) => { for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child?.type === 'async') return true; } return false; }, isStatic: (node) => { for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child?.type === 'static') return true; } return false; }, isConst: (node) => { // For lexical_declaration, check if it's 'const' or 'let' // For variable_declaration, it's always 'var' if (node.type === 'lexical_declaration') { for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child?.type === 'const') return true; } } return false; }, extractImport: (node, source) => { const sourceField = node.childForFieldName('source'); if (sourceField) { const moduleName = source.substring(sourceField.startIndex, sourceField.endIndex).replace(/['"]/g, ''); if (moduleName) { return { moduleName, signature: source.substring(node.startIndex, node.endIndex).trim() }; } } return null; }, };