feat(core): DartExtractor — mixin declarations

Add mixin_declaration handling to extractStructure, folding mixins into
classes[] (same convention as class_definition). The `on` constraint
sibling is intentionally ignored for graph purposes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thejesh
2026-06-13 05:15:46 -07:00
Unverified
parent 893208efb3
commit 05ce514db5
2 changed files with 46 additions and 3 deletions
@@ -190,4 +190,33 @@ describe("DartExtractor", () => {
parser.delete();
});
});
describe("extractStructure - mixins", () => {
it("extracts a plain mixin as a class-like entry", () => {
const { tree, parser, root } = parse(`mixin Walker {
void walk() {}
}
`);
const result = extractor.extractStructure(root);
expect(result.classes).toHaveLength(1);
expect(result.classes[0].name).toBe("Walker");
expect(result.classes[0].methods).toContain("walk");
tree.delete();
parser.delete();
});
it("extracts a mixin with an `on` constraint", () => {
const { tree, parser, root } = parse(`mixin Runner on Walker {
void run() {}
}
`);
const result = extractor.extractStructure(root);
expect(result.classes[0].name).toBe("Runner");
expect(result.classes[0].methods).toContain("run");
tree.delete();
parser.delete();
});
});
});
@@ -233,7 +233,10 @@ export class DartExtractor implements LanguageExtractor {
this.extractTopLevelFunction(node, functions, exports);
break;
case "class_definition":
this.extractClassDefinition(node, classes, functions, exports);
this.extractClassLikeDeclaration(node, "class_body", classes, functions, exports);
break;
case "mixin_declaration":
this.extractClassLikeDeclaration(node, "class_body", classes, functions, exports);
break;
}
}
@@ -261,8 +264,19 @@ export class DartExtractor implements LanguageExtractor {
}
}
private extractClassDefinition(
/**
* Extract a class-like declaration that uses a `class_body`-shaped member
* container. Used by `class_definition`, `mixin_declaration`, and (Task 8)
* `extension_declaration`. The only difference between these shapes is the
* body's node type name, which is passed in via `bodyNodeType`.
*
* Anonymous variants (e.g. `extension on Foo` with no name) are handled by
* the caller — this method requires `declNode` to have a leading
* `identifier` child for the name.
*/
private extractClassLikeDeclaration(
declNode: TreeSitterNode,
bodyNodeType: string,
classes: StructuralAnalysis["classes"],
functions: StructuralAnalysis["functions"],
exports: StructuralAnalysis["exports"],
@@ -274,7 +288,7 @@ export class DartExtractor implements LanguageExtractor {
const methods: string[] = [];
const properties: string[] = [];
const body = findChild(declNode, "class_body");
const body = findChild(declNode, bodyNodeType);
if (body) {
collectClassBody(body, methods, properties, functions, exports);
}