goscript 0.0.56 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -589,6 +589,12 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
589
589
 
590
590
  case *ast.TypeAssertExpr:
591
591
  return v.visitTypeAssertExpr(n)
592
+
593
+ case *ast.CompositeLit:
594
+ // Traverse into composite literal elements to detect &variable expressions
595
+ // This is important for cases like: arr := []interface{}{value1, &value2}
596
+ // where value2 needs to be marked as NeedsVarRef due to the &value2 usage
597
+ return v.visitCompositeLit(n)
592
598
  }
593
599
 
594
600
  // For all other nodes, continue traversal
@@ -869,10 +875,89 @@ func (v *analysisVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
869
875
  return v
870
876
  }
871
877
 
872
- // visitTypeAssertExpr handles type assertion expression analysis
873
- func (v *analysisVisitor) visitTypeAssertExpr(n *ast.TypeAssertExpr) ast.Visitor {
874
- // Track interface implementations when we see type assertions
875
- v.trackTypeAssertion(n)
878
+ // visitTypeAssertExpr handles type assertion analysis for interface method implementations
879
+ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) ast.Visitor {
880
+ // Get the type being asserted to
881
+ assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
882
+ if assertedType == nil {
883
+ return v
884
+ }
885
+
886
+ // Check if the asserted type is an interface
887
+ interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
888
+ if !isInterface {
889
+ return v
890
+ }
891
+
892
+ // Get the type of the expression being asserted
893
+ exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
894
+ if exprType == nil {
895
+ return v
896
+ }
897
+
898
+ // Handle pointer types by getting the element type
899
+ if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
900
+ exprType = ptrType.Elem()
901
+ }
902
+
903
+ // Check if the expression type is a named struct type
904
+ namedType, isNamed := exprType.(*types.Named)
905
+ if !isNamed {
906
+ return v
907
+ }
908
+
909
+ // For each method in the interface, check if the struct implements it
910
+ for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
911
+ interfaceMethod := interfaceType.ExplicitMethod(i)
912
+
913
+ // Find the corresponding method in the struct type
914
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
915
+ if structMethod != nil {
916
+ // Determine if this struct method is async using unified system
917
+ isAsync := false
918
+ if obj := structMethod; obj != nil {
919
+ isAsync = v.analysis.IsAsyncFunc(obj)
920
+ }
921
+
922
+ // Track this interface implementation
923
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
924
+ }
925
+ }
926
+ return v
927
+ }
928
+
929
+ // visitCompositeLit analyzes composite literals for address-of expressions
930
+ // This is important for detecting cases like: arr := []interface{}{value1, &value2}
931
+ // where value2 needs to be marked as NeedsVarRef due to the &value2 usage
932
+ func (v *analysisVisitor) visitCompositeLit(compLit *ast.CompositeLit) ast.Visitor {
933
+ // Analyze each element of the composite literal
934
+ for _, elt := range compLit.Elts {
935
+ // Handle both direct elements and key-value pairs
936
+ var expr ast.Expr
937
+ if kv, ok := elt.(*ast.KeyValueExpr); ok {
938
+ // For key-value pairs, analyze the value expression
939
+ expr = kv.Value
940
+ } else {
941
+ // For direct elements, analyze the element expression
942
+ expr = elt
943
+ }
944
+
945
+ // Check if this element is an address-of expression
946
+ if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
947
+ // Found &something in the composite literal
948
+ if ident, ok := unaryExpr.X.(*ast.Ident); ok {
949
+ // Found &variable - mark the variable as needing VarRef
950
+ if obj := v.pkg.TypesInfo.ObjectOf(ident); obj != nil {
951
+ // Record that this variable has its address taken
952
+ usageInfo := v.getOrCreateUsageInfo(obj)
953
+ usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
954
+ Object: nil, // No specific destination object for composite literals
955
+ Type: AddressOfAssignment,
956
+ })
957
+ }
958
+ }
959
+ }
960
+ }
876
961
  return v
877
962
  }
878
963
 
@@ -56,10 +56,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
56
56
 
57
57
  // Handle the RHS expression (potentially adding .clone() for structs)
58
58
  if shouldApplyClone(c.pkg, rhs[0]) {
59
+ // When cloning for value assignment, mark the result as struct value
60
+ c.tsw.WriteLiterally("$.markAsStructValue(")
59
61
  if err := c.WriteValueExpr(rhs[0]); err != nil {
60
62
  return err
61
63
  }
62
- c.tsw.WriteLiterally(".clone()")
64
+ c.tsw.WriteLiterally(".clone())")
63
65
  } else {
64
66
  if err := c.WriteValueExpr(rhs[0]); err != nil {
65
67
  return err
@@ -338,8 +340,37 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
338
340
  }
339
341
  }
340
342
 
343
+ // Check for pointer-to-pointer assignment
344
+ if rhsIsIdent && rhsObj != nil && len(lhs) == 1 {
345
+ lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
346
+ rhsType := rhsObj.Type()
347
+
348
+ if lhsType != nil && rhsType != nil {
349
+ // Check if both LHS and RHS are pointer types
350
+ if _, lhsIsPtr := lhsType.(*types.Pointer); lhsIsPtr {
351
+ if _, rhsIsPtr := rhsType.(*types.Pointer); rhsIsPtr {
352
+ // This is pointer-to-pointer assignment
353
+ // The key question: is the RHS variable itself varref'd?
354
+ // - If RHS is varref'd (like pp1), use .value to get the actual pointer
355
+ // - If RHS is not varref'd (like p1), use the variable directly
356
+
357
+ if c.analysis.NeedsVarRef(rhsObj) {
358
+ // RHS variable is varref'd, so we need its .value to get the actual pointer
359
+ c.WriteIdent(rhsIdent, true) // Add .value access
360
+ } else {
361
+ // RHS variable is not varref'd, so it directly holds the pointer
362
+ c.WriteIdent(rhsIdent, false) // No .value access
363
+ }
364
+ continue
365
+ }
366
+ }
367
+ }
368
+ }
369
+
341
370
  // Handle different cases for struct cloning
342
371
  if shouldApplyClone(c.pkg, r) {
372
+ // When cloning for value assignment, mark the result as struct value
373
+ c.tsw.WriteLiterally("$.markAsStructValue(")
343
374
  // For other expressions, we need to handle variable referenced access differently
344
375
  if _, isIdent := r.(*ast.Ident); isIdent {
345
376
  // For identifiers, WriteValueExpr already adds .value if needed
@@ -357,8 +388,39 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
357
388
  }
358
389
  }
359
390
 
360
- c.tsw.WriteLiterally(".clone()") // Always add clone for struct values
391
+ c.tsw.WriteLiterally(".clone())") // Always add clone for struct values
361
392
  } else {
393
+ // Check if this is a pointer variable assignment to an interface type
394
+ if rhsIsIdent && rhsObj != nil {
395
+ // Check if LHS is interface type and RHS is a pointer variable
396
+ if len(lhs) == 1 {
397
+ lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
398
+ rhsType := rhsObj.Type()
399
+
400
+ if lhsType != nil && rhsType != nil {
401
+ // Check if LHS is interface and RHS is pointer
402
+ if _, isInterface := lhsType.Underlying().(*types.Interface); isInterface {
403
+ if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
404
+ // This is pointer-to-interface assignment
405
+ // For pointer variables that point to varrefed values, write without .value
406
+ // We want to pass the VarRef object itself to the interface, not its .value
407
+ if c.analysis.NeedsVarRefAccess(rhsObj) {
408
+ // Write the pointer variable without .value access
409
+ c.WriteIdent(rhsIdent, false)
410
+ continue
411
+ }
412
+
413
+ // Check if this is a struct pointer for the element type
414
+ if _, isStruct := ptrType.Elem().Underlying().(*types.Struct); isStruct {
415
+ // Struct pointer to interface - might need special handling
416
+ // Continue to normal WriteValueExpr handling
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+ }
423
+
362
424
  // Non-struct case: write RHS normally
363
425
  if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
364
426
  return err
@@ -704,6 +704,9 @@ type GoToTSCompiler struct {
704
704
  pkg *packages.Package
705
705
 
706
706
  analysis *Analysis
707
+
708
+ // Context flags
709
+ insideAddressOf bool // true when processing operand of & operator
707
710
  }
708
711
 
709
712
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -212,6 +212,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
212
212
  var structType *types.Struct
213
213
  isStructLiteral := false
214
214
  isAnonymousStruct := false
215
+ needsValueMarkerClose := false // Track if we need to close $.markAsStructValue()
215
216
 
216
217
  if namedType, ok := litType.(*types.Named); ok {
217
218
  if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
@@ -224,8 +225,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
224
225
  return err
225
226
  }
226
227
  } else {
227
- // Named struct, use constructor
228
- c.tsw.WriteLiterally("new ")
228
+ // Named struct value, use constructor
229
+ if !c.insideAddressOf {
230
+ // Only mark as struct value if not inside address-of operator
231
+ c.tsw.WriteLiterally("$.markAsStructValue(new ")
232
+ needsValueMarkerClose = true
233
+ } else {
234
+ c.tsw.WriteLiterally("new ")
235
+ }
229
236
  c.WriteTypeExpr(exp.Type)
230
237
  }
231
238
  }
@@ -241,8 +248,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
241
248
  return err
242
249
  }
243
250
  } else {
244
- // Type alias for struct, use constructor
245
- c.tsw.WriteLiterally("new ")
251
+ // Type alias for struct value, use constructor
252
+ if !c.insideAddressOf {
253
+ // Only mark as struct value if not inside address-of operator
254
+ c.tsw.WriteLiterally("$.markAsStructValue(new ")
255
+ needsValueMarkerClose = true
256
+ } else {
257
+ c.tsw.WriteLiterally("new ")
258
+ }
246
259
  c.WriteTypeExpr(exp.Type)
247
260
  }
248
261
  }
@@ -483,6 +496,10 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
483
496
  c.tsw.WriteLiterally("}")
484
497
  } else {
485
498
  c.tsw.WriteLiterally("})")
499
+ // Close markAsStructValue wrapper if we opened one
500
+ if needsValueMarkerClose {
501
+ c.tsw.WriteLiterally(")")
502
+ }
486
503
  }
487
504
 
488
505
  } else {
package/compiler/decl.go CHANGED
@@ -3,7 +3,9 @@ package compiler
3
3
  import (
4
4
  "fmt"
5
5
  "go/ast"
6
+ "go/token"
6
7
  "go/types"
8
+ "sort"
7
9
  )
8
10
 
9
11
  // WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
@@ -17,33 +19,384 @@ import (
17
19
  // variables, or type definitions: It iterates through `d.Specs` and calls
18
20
  // `WriteSpec` for each specification.
19
21
  //
22
+ // Type declarations are sorted by dependencies to ensure referenced types are
23
+ // defined before types that reference them, avoiding initialization order issues.
20
24
  // A newline is added after each processed declaration or spec group for readability.
21
25
  // Unknown declaration types result in a printed diagnostic message.
22
26
  func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
27
+ // Separate type declarations from other declarations for dependency sorting
28
+ var typeSpecs []*ast.TypeSpec
29
+ var varSpecs []*ast.ValueSpec
30
+ var otherDecls []ast.Decl
31
+ var otherSpecs []ast.Spec
32
+
23
33
  for _, decl := range decls {
24
34
  switch d := decl.(type) {
25
35
  case *ast.FuncDecl:
26
36
  // Only handle top-level functions here. Methods are handled within WriteTypeSpec.
27
37
  if d.Recv == nil {
28
- if err := c.WriteFuncDeclAsFunction(d); err != nil {
29
- return err
30
- }
31
- c.tsw.WriteLine("") // Add space after function
38
+ otherDecls = append(otherDecls, d)
32
39
  }
33
40
  case *ast.GenDecl:
34
41
  for _, spec := range d.Specs {
35
- if err := c.WriteSpec(spec); err != nil {
36
- return err
42
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
43
+ typeSpecs = append(typeSpecs, typeSpec)
44
+ } else if varSpec, ok := spec.(*ast.ValueSpec); ok && d.Tok == token.VAR {
45
+ varSpecs = append(varSpecs, varSpec)
46
+ } else {
47
+ otherSpecs = append(otherSpecs, spec)
37
48
  }
38
- c.tsw.WriteLine("") // Add space after spec
39
49
  }
50
+ default:
51
+ otherDecls = append(otherDecls, d)
52
+ }
53
+ }
54
+
55
+ // Sort type declarations by dependencies
56
+ sortedTypeSpecs, err := c.sortTypeSpecsByDependencies(typeSpecs)
57
+ if err != nil {
58
+ return fmt.Errorf("failed to sort type declarations: %w", err)
59
+ }
60
+
61
+ // Sort variable declarations by type dependencies
62
+ sortedVarSpecs, err := c.sortVarSpecsByTypeDependencies(varSpecs, typeSpecs)
63
+ if err != nil {
64
+ return fmt.Errorf("failed to sort variable declarations: %w", err)
65
+ }
66
+
67
+ // Write non-type, non-var declarations first (imports, constants)
68
+ for _, spec := range otherSpecs {
69
+ if err := c.WriteSpec(spec); err != nil {
70
+ return err
71
+ }
72
+ c.tsw.WriteLine("") // Add space after spec
73
+ }
74
+
75
+ // Write sorted type declarations
76
+ for _, typeSpec := range sortedTypeSpecs {
77
+ if err := c.WriteSpec(typeSpec); err != nil {
78
+ return err
79
+ }
80
+ c.tsw.WriteLine("") // Add space after spec
81
+ }
82
+
83
+ // Write sorted variable declarations
84
+ for _, varSpec := range sortedVarSpecs {
85
+ if err := c.WriteSpec(varSpec); err != nil {
86
+ return err
87
+ }
88
+ c.tsw.WriteLine("") // Add space after spec
89
+ }
90
+
91
+ // Write function declarations last
92
+ for _, decl := range otherDecls {
93
+ switch d := decl.(type) {
94
+ case *ast.FuncDecl:
95
+ if err := c.WriteFuncDeclAsFunction(d); err != nil {
96
+ return err
97
+ }
98
+ c.tsw.WriteLine("") // Add space after function
40
99
  default:
41
100
  return fmt.Errorf("unknown decl: %#v", decl)
42
101
  }
43
102
  }
103
+
44
104
  return nil
45
105
  }
46
106
 
107
+ // sortTypeSpecsByDependencies performs a topological sort of type specifications
108
+ // based on their dependencies to ensure referenced types are defined before
109
+ // types that reference them.
110
+ func (c *GoToTSCompiler) sortTypeSpecsByDependencies(typeSpecs []*ast.TypeSpec) ([]*ast.TypeSpec, error) {
111
+ if len(typeSpecs) <= 1 {
112
+ return typeSpecs, nil
113
+ }
114
+
115
+ // Build dependency graph
116
+ dependencies := make(map[string][]string) // typeName -> list of types it depends on
117
+ typeSpecMap := make(map[string]*ast.TypeSpec)
118
+
119
+ // First pass: collect all type names
120
+ for _, typeSpec := range typeSpecs {
121
+ typeName := typeSpec.Name.Name
122
+ typeSpecMap[typeName] = typeSpec
123
+ dependencies[typeName] = []string{}
124
+ }
125
+
126
+ // Second pass: analyze dependencies
127
+ for _, typeSpec := range typeSpecs {
128
+ typeName := typeSpec.Name.Name
129
+ deps := c.extractTypeDependencies(typeSpec.Type, typeSpecMap)
130
+ dependencies[typeName] = deps
131
+ }
132
+
133
+ // Perform topological sort
134
+ sorted, err := c.topologicalSort(dependencies)
135
+ if err != nil {
136
+ return nil, err
137
+ }
138
+
139
+ // Build result in sorted order
140
+ var result []*ast.TypeSpec
141
+ for _, typeName := range sorted {
142
+ if typeSpec, exists := typeSpecMap[typeName]; exists {
143
+ result = append(result, typeSpec)
144
+ }
145
+ }
146
+
147
+ return result, nil
148
+ }
149
+
150
+ // extractTypeDependencies extracts structural type dependencies from a type expression.
151
+ // Only dependencies that affect class initialization order are considered:
152
+ // - Struct field types (cause initialization order issues)
153
+ // - Embedded types (directly affect struct layout)
154
+ // - Type aliases (direct type references)
155
+ // Method parameters and return types are ignored as they're just annotations.
156
+ func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
157
+ var deps []string
158
+
159
+ switch t := typeExpr.(type) {
160
+ case *ast.Ident:
161
+ // Direct type reference (e.g., type MyType OtherType)
162
+ if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
163
+ deps = append(deps, t.Name)
164
+ }
165
+
166
+ case *ast.StructType:
167
+ // Struct type - check field types only
168
+ if t.Fields != nil {
169
+ for _, field := range t.Fields.List {
170
+ fieldDeps := c.extractTypeDependencies(field.Type, typeSpecMap)
171
+ deps = append(deps, fieldDeps...)
172
+ }
173
+ }
174
+
175
+ case *ast.ArrayType:
176
+ // Array type - check element type
177
+ elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
178
+ deps = append(deps, elemDeps...)
179
+
180
+ case *ast.StarExpr:
181
+ // Pointer type - check pointed-to type
182
+ ptrDeps := c.extractTypeDependencies(t.X, typeSpecMap)
183
+ deps = append(deps, ptrDeps...)
184
+
185
+ case *ast.MapType:
186
+ // Map type - check key and value types
187
+ keyDeps := c.extractTypeDependencies(t.Key, typeSpecMap)
188
+ valueDeps := c.extractTypeDependencies(t.Value, typeSpecMap)
189
+ deps = append(deps, keyDeps...)
190
+ deps = append(deps, valueDeps...)
191
+
192
+ case *ast.InterfaceType:
193
+ // Interface type - methods don't create initialization dependencies
194
+ // Only embedded interfaces matter, but those are rare and complex to handle
195
+ // For now, interfaces are considered to have no dependencies
196
+
197
+ case *ast.FuncType:
198
+ // Function types don't create initialization dependencies
199
+
200
+ case *ast.SelectorExpr:
201
+ // External package types don't create local dependencies
202
+
203
+ // Add other type expressions as needed
204
+ }
205
+
206
+ // Sort dependencies for deterministic output
207
+ sort.Strings(deps)
208
+ return deps
209
+ }
210
+
211
+ // sortVarSpecsByTypeDependencies sorts variable declarations based on their type dependencies
212
+ func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
213
+ if len(varSpecs) <= 1 {
214
+ return varSpecs, nil
215
+ }
216
+
217
+ // Build type name map
218
+ typeSpecMap := make(map[string]*ast.TypeSpec)
219
+ for _, typeSpec := range typeSpecs {
220
+ typeSpecMap[typeSpec.Name.Name] = typeSpec
221
+ }
222
+
223
+ // Group variables by dependency status with names for sorting
224
+ type namedVarSpec struct {
225
+ spec *ast.ValueSpec
226
+ name string
227
+ }
228
+
229
+ var independentVars []namedVarSpec
230
+ var dependentVars []namedVarSpec
231
+
232
+ for _, varSpec := range varSpecs {
233
+ // Get variable name for sorting
234
+ varName := ""
235
+ if len(varSpec.Names) > 0 {
236
+ varName = varSpec.Names[0].Name
237
+ }
238
+
239
+ hasDependency := false
240
+
241
+ // Check type annotation
242
+ if varSpec.Type != nil {
243
+ deps := c.extractTypeDependencies(varSpec.Type, typeSpecMap)
244
+ if len(deps) > 0 {
245
+ hasDependency = true
246
+ }
247
+ }
248
+
249
+ // Check initializer expressions for type usage
250
+ if !hasDependency {
251
+ for _, value := range varSpec.Values {
252
+ if c.hasTypeReferences(value, typeSpecMap) {
253
+ hasDependency = true
254
+ break
255
+ }
256
+ }
257
+ }
258
+
259
+ namedVar := namedVarSpec{spec: varSpec, name: varName}
260
+ if hasDependency {
261
+ dependentVars = append(dependentVars, namedVar)
262
+ } else {
263
+ independentVars = append(independentVars, namedVar)
264
+ }
265
+ }
266
+
267
+ // Sort both groups by name for deterministic output
268
+ sort.Slice(independentVars, func(i, j int) bool {
269
+ return independentVars[i].name < independentVars[j].name
270
+ })
271
+ sort.Slice(dependentVars, func(i, j int) bool {
272
+ return dependentVars[i].name < dependentVars[j].name
273
+ })
274
+
275
+ // Return independent variables first, then dependent ones
276
+ result := make([]*ast.ValueSpec, 0, len(varSpecs))
277
+ for _, namedVar := range independentVars {
278
+ result = append(result, namedVar.spec)
279
+ }
280
+ for _, namedVar := range dependentVars {
281
+ result = append(result, namedVar.spec)
282
+ }
283
+
284
+ return result, nil
285
+ }
286
+
287
+ // hasTypeReferences checks if an expression contains references to local types
288
+ func (c *GoToTSCompiler) hasTypeReferences(expr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) bool {
289
+ hasRef := false
290
+
291
+ ast.Inspect(expr, func(n ast.Node) bool {
292
+ switch t := n.(type) {
293
+ case *ast.CallExpr:
294
+ // Check function calls like new Message(), makeChannel<Message>()
295
+ if ident, ok := t.Fun.(*ast.Ident); ok {
296
+ if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
297
+ hasRef = true
298
+ return false
299
+ }
300
+ }
301
+ // Check type arguments in generic calls
302
+ if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
303
+ if c.hasTypeReferences(funcType.Index, typeSpecMap) {
304
+ hasRef = true
305
+ return false
306
+ }
307
+ }
308
+ case *ast.CompositeLit:
309
+ // Check composite literals like Message{...}
310
+ if ident, ok := t.Type.(*ast.Ident); ok {
311
+ if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
312
+ hasRef = true
313
+ return false
314
+ }
315
+ }
316
+ case *ast.Ident:
317
+ // Check direct type references
318
+ if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
319
+ hasRef = true
320
+ return false
321
+ }
322
+ }
323
+ return !hasRef // Stop walking if we found a reference
324
+ })
325
+
326
+ return hasRef
327
+ }
328
+
329
+ // topologicalSort performs a topological sort of the dependency graph
330
+ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]string, error) {
331
+ // Kahn's algorithm for topological sorting with deterministic ordering
332
+ inDegree := make(map[string]int)
333
+ graph := make(map[string][]string)
334
+
335
+ // Initialize in-degree counts and reverse graph
336
+ for node := range dependencies {
337
+ inDegree[node] = 0
338
+ graph[node] = []string{}
339
+ }
340
+
341
+ // Build reverse graph and count in-degrees
342
+ for node, deps := range dependencies {
343
+ // Sort dependencies for consistent output
344
+ sortedDeps := make([]string, len(deps))
345
+ copy(sortedDeps, deps)
346
+ sort.Strings(sortedDeps)
347
+
348
+ for _, dep := range sortedDeps {
349
+ if _, exists := inDegree[dep]; exists {
350
+ graph[dep] = append(graph[dep], node)
351
+ inDegree[node]++
352
+ }
353
+ }
354
+ }
355
+
356
+ // Sort neighbors in graph for consistency
357
+ for node := range graph {
358
+ sort.Strings(graph[node])
359
+ }
360
+
361
+ // Find nodes with no incoming edges and sort them
362
+ var queue []string
363
+ for node, degree := range inDegree {
364
+ if degree == 0 {
365
+ queue = append(queue, node)
366
+ }
367
+ }
368
+ sort.Strings(queue) // Sort initial queue for deterministic output
369
+
370
+ var result []string
371
+
372
+ for len(queue) > 0 {
373
+ // Remove node from queue (already sorted)
374
+ current := queue[0]
375
+ queue = queue[1:]
376
+ result = append(result, current)
377
+
378
+ // Collect new zero-degree nodes
379
+ var newZeroNodes []string
380
+ for _, neighbor := range graph[current] {
381
+ inDegree[neighbor]--
382
+ if inDegree[neighbor] == 0 {
383
+ newZeroNodes = append(newZeroNodes, neighbor)
384
+ }
385
+ }
386
+
387
+ // Sort new zero-degree nodes and add to queue
388
+ sort.Strings(newZeroNodes)
389
+ queue = append(queue, newZeroNodes...)
390
+ }
391
+
392
+ // Check for cycles
393
+ if len(result) != len(dependencies) {
394
+ return nil, fmt.Errorf("circular dependency detected in type declarations")
395
+ }
396
+
397
+ return result, nil
398
+ }
399
+
47
400
  // WriteFuncDeclAsFunction translates a Go function declaration (`ast.FuncDecl`)
48
401
  // that does not have a receiver (i.e., it's a regular function, not a method)
49
402
  // into a TypeScript function.
@@ -239,24 +239,24 @@ func (c *GoToTSCompiler) writeMethodValue(exp *ast.SelectorExpr, selection *type
239
239
  // The receiver should be a copy of the dereferenced value
240
240
  c.tsw.WriteLiterally(".value.")
241
241
  c.WriteIdent(exp.Sel, false)
242
- c.tsw.WriteLiterally(".bind(")
242
+ c.tsw.WriteLiterally(".bind($.markAsStructValue(")
243
243
  if err := c.WriteValueExpr(exp.X); err != nil {
244
244
  return fmt.Errorf("failed to write method value receiver for binding: %w", err)
245
245
  }
246
- c.tsw.WriteLiterally("!.value.clone())")
246
+ c.tsw.WriteLiterally("!.value.clone()))")
247
247
  } else if !isPointerReceiver && !baseIsPointer {
248
248
  // Value receiver method on value type: t.Mv
249
249
  // The receiver should be a copy of the value
250
250
  c.tsw.WriteLiterally(".")
251
251
  c.WriteIdent(exp.Sel, false)
252
- c.tsw.WriteLiterally(".bind(")
252
+ c.tsw.WriteLiterally(".bind($.markAsStructValue(")
253
253
  if err := c.WriteValueExpr(exp.X); err != nil {
254
254
  return fmt.Errorf("failed to write method value receiver for binding: %w", err)
255
255
  }
256
256
  if baseIsPointer {
257
257
  c.tsw.WriteLiterally("!")
258
258
  }
259
- c.tsw.WriteLiterally(".clone())")
259
+ c.tsw.WriteLiterally(".clone()))")
260
260
  } else {
261
261
  // Pointer receiver method on pointer type: pt.Mp
262
262
  // The receiver should be the pointer itself