feat: Add resolveBody hook for JS/TS class field function extraction

Addresses arrow function class fields like `field = () => { ... }` where the function body is nested inside field_definition nodes. Adds resolveBody method to traverse field_definition → arrow_function/function_expression → body and handles HOF wrapper patterns like `field = throttle(() => { ... })` by searching call_expression arguments. Enables proper function body extraction for class field functions in both JavaScript and TypeScript.
This commit is contained in:
Colby McHenry
2026-04-07 09:40:50 -05:00
parent 9382a087f4
commit 49e670c223
2 changed files with 57 additions and 0 deletions
+28
View File
@@ -14,6 +14,34 @@ export const javascriptExtractor: LanguageExtractor = {
variableTypes: ['lexical_declaration', 'variable_declaration'],
nameField: 'name',
bodyField: 'body',
resolveBody: (node, bodyField) => {
// field_definition (arrow function class fields) nest the body inside
// an arrow_function or function_expression child:
// field_definition → arrow_function → body (statement_block)
// Also handles wrapper patterns like: field = throttle((e) => { ... })
// field_definition → call_expression → arguments → arrow_function → body
if (node.type === 'field_definition') {
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'arrow_function' || child.type === 'function_expression') {
return getChildByField(child, bodyField);
}
if (child.type === 'call_expression') {
const args = getChildByField(child, 'arguments');
if (args) {
for (let j = 0; j < args.namedChildCount; j++) {
const arg = args.namedChild(j);
if (arg && (arg.type === 'arrow_function' || arg.type === 'function_expression')) {
return getChildByField(arg, bodyField);
}
}
}
}
}
}
return null;
},
paramsField: 'parameters',
getSignature: (node, source) => {
const params = getChildByField(node, 'parameters');
+29
View File
@@ -15,6 +15,35 @@ export const typescriptExtractor: LanguageExtractor = {
variableTypes: ['lexical_declaration', 'variable_declaration'],
nameField: 'name',
bodyField: 'body',
resolveBody: (node, bodyField) => {
// public_field_definition (arrow function class fields) nest the body inside
// an arrow_function or function_expression child:
// public_field_definition → arrow_function → body (statement_block)
// Also handles wrapper patterns like: field = withBatchedUpdates((e) => { ... })
// public_field_definition → call_expression → arguments → arrow_function → body
if (node.type === 'public_field_definition') {
for (let i = 0; i < node.namedChildCount; i++) {
const child = node.namedChild(i);
if (!child) continue;
if (child.type === 'arrow_function' || child.type === 'function_expression') {
return getChildByField(child, bodyField);
}
// Check inside call_expression arguments (HOF wrappers like throttle, debounce)
if (child.type === 'call_expression') {
const args = getChildByField(child, 'arguments');
if (args) {
for (let j = 0; j < args.namedChildCount; j++) {
const arg = args.namedChild(j);
if (arg && (arg.type === 'arrow_function' || arg.type === 'function_expression')) {
return getChildByField(arg, bodyField);
}
}
}
}
}
}
return null;
},
paramsField: 'parameters',
returnField: 'return_type',
getSignature: (node, source) => {