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 2 commits 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.
extractClass only matched method_definition, so abstract-class members declared as abstract_method_signature (`abstract run(): void;`) or method_signature (`run(): void;`) were silently dropped, leaving the recovered abstract-class hierarchy with empty or partial methods. Accept both signature node types alongside method_definition. Adds tests covering a mixed abstract/concrete class, a purely abstract contract, and decorated (and decorated+exported) abstract classes to lock the already-correct decorator dispatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
1. Abstract method signatures dropped from 2. Decorated abstract classes — Verified against tree-sitter-typescript 0.23.2: for 3. Dart parity (#435) — Left as a separate follow-up. #435 is already merged, so the two can't land together; Dart is a distinct extractor with a different grammar and would need its own node-shape verification rather than assuming parity. Out of scope for this TS-only PR. Nit ( Full core suite green (701/701), tsc clean. |
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