@@ -94,6 +94,11 @@ describe('Language Detection', () => {
9494 expect ( detectLanguage ( 'main.dart' ) ) . toBe ( 'dart' ) ;
9595 } ) ;
9696
97+ it ( 'should detect Zig files' , ( ) => {
98+ expect ( detectLanguage ( 'main.zig' ) ) . toBe ( 'zig' ) ;
99+ expect ( detectLanguage ( 'build.zon' ) ) . toBe ( 'zig' ) ;
100+ } ) ;
101+
97102 it ( 'should return unknown for unsupported extensions' , ( ) => {
98103 expect ( detectLanguage ( 'styles.css' ) ) . toBe ( 'unknown' ) ;
99104 expect ( detectLanguage ( 'data.json' ) ) . toBe ( 'unknown' ) ;
@@ -122,6 +127,7 @@ describe('Language Support', () => {
122127 expect ( languages ) . toContain ( 'swift' ) ;
123128 expect ( languages ) . toContain ( 'kotlin' ) ;
124129 expect ( languages ) . toContain ( 'dart' ) ;
130+ expect ( languages ) . toContain ( 'zig' ) ;
125131 } ) ;
126132} ) ;
127133
@@ -3649,3 +3655,202 @@ class Svc {
36493655 expect ( decoratedNode ?. name ) . toBe ( 'method' ) ;
36503656 } ) ;
36513657} ) ;
3658+
3659+ describe ( 'Zig Extraction' , ( ) => {
3660+ it ( 'should extract top-level function declarations' , ( ) => {
3661+ const code = `
3662+ pub fn add(a: i32, b: i32) i32 {
3663+ return a + b;
3664+ }
3665+
3666+ fn internal(x: u8) void {
3667+ _ = x;
3668+ }
3669+ ` ;
3670+ const result = extractFromSource ( 'math.zig' , code ) ;
3671+
3672+ const pubFn = result . nodes . find ( ( n ) => n . kind === 'function' && n . name === 'add' ) ;
3673+ expect ( pubFn ) . toBeDefined ( ) ;
3674+ expect ( pubFn ?. visibility ) . toBe ( 'public' ) ;
3675+
3676+ const privFn = result . nodes . find ( ( n ) => n . kind === 'function' && n . name === 'internal' ) ;
3677+ expect ( privFn ) . toBeDefined ( ) ;
3678+ expect ( privFn ?. visibility ) . toBe ( 'private' ) ;
3679+ } ) ;
3680+
3681+ it ( 'should extract function signatures' , ( ) => {
3682+ const code = `
3683+ pub fn add(a: i32, b: i32) i32 {
3684+ return a + b;
3685+ }
3686+ ` ;
3687+ const result = extractFromSource ( 'math.zig' , code ) ;
3688+ const fn = result . nodes . find ( ( n ) => n . name === 'add' ) ;
3689+ expect ( fn ?. signature ) . toContain ( '(a: i32, b: i32)' ) ;
3690+ } ) ;
3691+
3692+ it ( 'should extract struct declarations with fields' , ( ) => {
3693+ const code = `
3694+ pub const Vec2 = struct {
3695+ x: f32,
3696+ y: f32,
3697+ };
3698+ ` ;
3699+ const result = extractFromSource ( 'vec.zig' , code ) ;
3700+
3701+ const struct = result . nodes . find ( ( n ) => n . kind === 'struct' ) ;
3702+ expect ( struct ) . toBeDefined ( ) ;
3703+ expect ( struct ?. name ) . toBe ( 'Vec2' ) ;
3704+ expect ( struct ?. visibility ) . toBe ( 'public' ) ;
3705+
3706+ const fields = result . nodes . filter ( ( n ) => n . kind === 'field' ) ;
3707+ const fieldNames = fields . map ( ( f ) => f . name ) ;
3708+ expect ( fieldNames ) . toContain ( 'x' ) ;
3709+ expect ( fieldNames ) . toContain ( 'y' ) ;
3710+ } ) ;
3711+
3712+ it ( 'should extract struct methods' , ( ) => {
3713+ const code = `
3714+ pub const Vec2 = struct {
3715+ x: f32,
3716+ y: f32,
3717+
3718+ pub fn length(self: Vec2) f32 {
3719+ return @sqrt(self.x * self.x + self.y * self.y);
3720+ }
3721+
3722+ fn dot(self: Vec2, other: Vec2) f32 {
3723+ return self.x * other.x + self.y * other.y;
3724+ }
3725+ };
3726+ ` ;
3727+ const result = extractFromSource ( 'vec.zig' , code ) ;
3728+
3729+ const pubMethod = result . nodes . find ( ( n ) => n . kind === 'method' && n . name === 'length' ) ;
3730+ expect ( pubMethod ) . toBeDefined ( ) ;
3731+ expect ( pubMethod ?. visibility ) . toBe ( 'public' ) ;
3732+
3733+ const privMethod = result . nodes . find ( ( n ) => n . kind === 'method' && n . name === 'dot' ) ;
3734+ expect ( privMethod ) . toBeDefined ( ) ;
3735+ expect ( privMethod ?. visibility ) . toBe ( 'private' ) ;
3736+
3737+ // Methods should be contained by the struct
3738+ const containsEdges = result . edges . filter ( ( e ) => e . kind === 'contains' ) ;
3739+ const struct = result . nodes . find ( ( n ) => n . kind === 'struct' ) ;
3740+ expect ( containsEdges . some ( ( e ) => e . source === struct ?. id && e . target === pubMethod ?. id ) ) . toBe ( true ) ;
3741+ } ) ;
3742+
3743+ it ( 'should extract enum declarations with members' , ( ) => {
3744+ const code = `
3745+ pub const Color = enum {
3746+ red,
3747+ green,
3748+ blue,
3749+ };
3750+ ` ;
3751+ const result = extractFromSource ( 'color.zig' , code ) ;
3752+
3753+ const enumNode = result . nodes . find ( ( n ) => n . kind === 'enum' ) ;
3754+ expect ( enumNode ) . toBeDefined ( ) ;
3755+ expect ( enumNode ?. name ) . toBe ( 'Color' ) ;
3756+
3757+ const members = result . nodes . filter ( ( n ) => n . kind === 'enum_member' ) ;
3758+ const memberNames = members . map ( ( m ) => m . name ) ;
3759+ expect ( memberNames ) . toContain ( 'red' ) ;
3760+ expect ( memberNames ) . toContain ( 'green' ) ;
3761+ expect ( memberNames ) . toContain ( 'blue' ) ;
3762+ } ) ;
3763+
3764+ it ( 'should extract error sets as enums' , ( ) => {
3765+ const code = `
3766+ pub const AppError = error {
3767+ OutOfMemory,
3768+ InvalidInput,
3769+ };
3770+ ` ;
3771+ const result = extractFromSource ( 'errors.zig' , code ) ;
3772+
3773+ const enumNode = result . nodes . find ( ( n ) => n . kind === 'enum' && n . name === 'AppError' ) ;
3774+ expect ( enumNode ) . toBeDefined ( ) ;
3775+
3776+ const members = result . nodes . filter ( ( n ) => n . kind === 'enum_member' ) ;
3777+ const memberNames = members . map ( ( m ) => m . name ) ;
3778+ expect ( memberNames ) . toContain ( 'OutOfMemory' ) ;
3779+ expect ( memberNames ) . toContain ( 'InvalidInput' ) ;
3780+ } ) ;
3781+
3782+ it ( 'should extract @import as import node' , ( ) => {
3783+ const code = `
3784+ const std = @import("std");
3785+ const math = @import("./math.zig");
3786+ ` ;
3787+ const result = extractFromSource ( 'main.zig' , code ) ;
3788+
3789+ const importStd = result . nodes . find ( ( n ) => n . kind === 'import' && n . name === 'std' ) ;
3790+ expect ( importStd ) . toBeDefined ( ) ;
3791+
3792+ const importMath = result . nodes . find ( ( n ) => n . kind === 'import' && n . name === 'math' ) ;
3793+ expect ( importMath ) . toBeDefined ( ) ;
3794+
3795+ // Should produce unresolved references for the module paths
3796+ const stdRef = result . unresolvedReferences . find ( ( r ) => r . referenceName === 'std' ) ;
3797+ expect ( stdRef ) . toBeDefined ( ) ;
3798+ expect ( stdRef ?. referenceKind ) . toBe ( 'imports' ) ;
3799+ } ) ;
3800+
3801+ it ( 'should extract chained @import as import node' , ( ) => {
3802+ const code = `
3803+ const io = @import("std").io;
3804+ ` ;
3805+ const result = extractFromSource ( 'main.zig' , code ) ;
3806+
3807+ const importIo = result . nodes . find ( ( n ) => n . kind === 'import' && n . name === 'io' ) ;
3808+ expect ( importIo ) . toBeDefined ( ) ;
3809+ } ) ;
3810+
3811+ it ( 'should extract plain constants and variables' , ( ) => {
3812+ const code = `
3813+ pub const PI: f64 = 3.14159;
3814+ var global_count: i32 = 0;
3815+ ` ;
3816+ const result = extractFromSource ( 'consts.zig' , code ) ;
3817+
3818+ const pi = result . nodes . find ( ( n ) => n . name === 'PI' ) ;
3819+ expect ( pi ) . toBeDefined ( ) ;
3820+ expect ( pi ?. kind ) . toBe ( 'constant' ) ;
3821+ expect ( pi ?. visibility ) . toBe ( 'public' ) ;
3822+
3823+ const count = result . nodes . find ( ( n ) => n . name === 'global_count' ) ;
3824+ expect ( count ) . toBeDefined ( ) ;
3825+ expect ( count ?. kind ) . toBe ( 'variable' ) ;
3826+ expect ( count ?. visibility ) . toBe ( 'private' ) ;
3827+ } ) ;
3828+
3829+ it ( 'should extract test declarations as functions' , ( ) => {
3830+ const code = `
3831+ test "addition works" {
3832+ const x = 1 + 2;
3833+ _ = x;
3834+ }
3835+ ` ;
3836+ const result = extractFromSource ( 'math_test.zig' , code ) ;
3837+
3838+ const testFn = result . nodes . find ( ( n ) => n . kind === 'function' && n . name === 'addition works' ) ;
3839+ expect ( testFn ) . toBeDefined ( ) ;
3840+ } ) ;
3841+
3842+ it ( 'should extract function calls' , ( ) => {
3843+ const code = `
3844+ fn helper() void {}
3845+
3846+ pub fn main() void {
3847+ helper();
3848+ }
3849+ ` ;
3850+ const result = extractFromSource ( 'main.zig' , code ) ;
3851+
3852+ const callRef = result . unresolvedReferences . find ( ( r ) => r . referenceKind === 'calls' ) ;
3853+ expect ( callRef ) . toBeDefined ( ) ;
3854+ expect ( callRef ?. referenceName ) . toBe ( 'helper' ) ;
3855+ } ) ;
3856+ } ) ;
0 commit comments