fix(typescript-extractor): capture parenless arrow params and abstract classes#442
fix(typescript-extractor): capture parenless arrow params and abstract classes#442tirth8205 wants to merge 1 commit into
Conversation
…t classes
A single-parameter arrow function written without parentheses
(`x => f(x)`) exposes its lone parameter under the `parameter`
(singular) field, not inside a `formal_parameters` node, so the
extractor silently dropped it. Now read that field first.
`abstract class Foo {}` parses as `abstract_class_declaration`, a node
type neither processTopLevelNode nor processExportStatement matched, so
abstract classes (and their methods/exports) vanished from the
structural graph. Both switches now handle `abstract_class_declaration`
alongside `class_declaration`; extractClass works unchanged.
Adds typescript-extractor.test.ts covering both cases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thejesh23
left a comment
There was a problem hiding this comment.
1. Abstract method signatures still dropped from methods
extractClass only matches method_definition, but abstract members parse as abstract_method_signature (e.g. abstract run(): void;). For the common case of an abstract base class declaring its contract, the resulting classes[].methods is empty or partial — exactly the hierarchy this PR is trying to recover. The new abstract-class test uses a concrete run() {} body, so this gap is not exercised.
2. Decorated abstract classes
TS decorators (@Injectable() abstract class X {}) wrap the class in a decorator + class_declaration/abstract_class_declaration pair, and when combined with export the node layout shifts again. Neither processTopLevelNode nor processExportStatement walks past a leading decorator sibling, so decorated abstract services (very common in Nest/Angular code) still disappear. Worth at least one test covering @injectable() abstract class X {}.
3. Sibling Dart PR has the same shape
PR #435 adds the Dart extractor and Dart also has abstract class plus parenless single-param closures via =>; the same two omissions (abstract-method-signature extraction, decorator/annotation wrapping) are likely to recur there. Worth aligning the fixes/tests across the two extractors before both land.
Nit: singleParam.text will inline any incidental whitespace/comments between the bare identifier and =>; harmless today but singleParam.childForFieldName("name")?.text ?? singleParam.text would be more defensive.
Problem
Two structural-analysis gaps in the TypeScript/JavaScript extractor:
Parenless single-param arrow functions —
const g = x => doThing(x)puts the lone parameter under thearrow_function'sparameter(singular) field, not inside aformal_parametersnode.extractVariableDeclarationsonly looked forparameters/formal_parameters, so the parameter was silently dropped (params: []). This form is ubiquitous in map/filter callbacks.Abstract classes —
abstract class Foo {}parses asabstract_class_declaration, notclass_declaration. NeitherprocessTopLevelNodenorprocessExportStatementmatched that node type, so abstract classes disappeared entirely: no class entry, no methods/properties, andexport abstract class X {}produced no export entry either. Whole base-class/service hierarchies vanished from the graph.Fix
extractVariableDeclarations, readvalueNode.childForFieldName("parameter")first and use it as the single param when present; otherwise fall back to the existingparameters/formal_parameterslookup.case "abstract_class_declaration"alongsidecase "class_declaration"in bothprocessTopLevelNodeandprocessExportStatement.extractClassalready locates the name viatype_identifier/identifierand readsclass_body, so it works unchanged for the abstract variant.Testing
packages/core/src/plugins/extractors/__tests__/typescript-extractor.test.ts(modeled on the sibling extractor tests, loading thetree-sitter-typescriptWASM grammar) with cases for the parenless arrow param and for exported/non-exported abstract classes.[]; classes/exports empty) and pass after.vitest runfor the core package is green (35 files, 697 tests, no regressions);tsc --noEmitexits 0; ESLint on the changed files is clean.🤖 Generated with Claude Code