goscript 0.0.41 → 0.0.43

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.
Files changed (109) hide show
  1. package/compiler/analysis.go +260 -2
  2. package/compiler/assignment.go +6 -0
  3. package/compiler/compiler.go +98 -8
  4. package/compiler/decl.go +42 -20
  5. package/compiler/expr-call.go +37 -1
  6. package/compiler/expr-type.go +20 -0
  7. package/compiler/lit.go +4 -20
  8. package/compiler/spec-struct.go +3 -4
  9. package/compiler/spec-value.go +18 -1
  10. package/compiler/spec.go +15 -8
  11. package/compiler/stmt-range.go +1 -1
  12. package/compiler/stmt.go +118 -17
  13. package/compiler/type-assert.go +4 -4
  14. package/compiler/type.go +63 -5
  15. package/dist/gs/builtin/map.d.ts +4 -4
  16. package/dist/gs/builtin/map.js +6 -3
  17. package/dist/gs/builtin/map.js.map +1 -1
  18. package/dist/gs/builtin/slice.d.ts +7 -0
  19. package/dist/gs/builtin/slice.js +12 -0
  20. package/dist/gs/builtin/slice.js.map +1 -1
  21. package/dist/gs/builtin/type.js +8 -70
  22. package/dist/gs/builtin/type.js.map +1 -1
  23. package/dist/gs/fmt/fmt.d.ts +22 -21
  24. package/dist/gs/fmt/fmt.js +12 -12
  25. package/dist/gs/fmt/fmt.js.map +1 -1
  26. package/dist/gs/internal/testlog/index.d.ts +1 -0
  27. package/dist/gs/internal/testlog/index.js +5 -0
  28. package/dist/gs/internal/testlog/index.js.map +1 -0
  29. package/dist/gs/io/io.d.ts +13 -13
  30. package/dist/gs/io/io.js +31 -21
  31. package/dist/gs/io/io.js.map +1 -1
  32. package/dist/gs/maps/iter.gs.d.ts +7 -0
  33. package/dist/gs/maps/iter.gs.js +65 -0
  34. package/dist/gs/maps/iter.gs.js.map +1 -0
  35. package/dist/gs/maps/maps.gs.d.ts +7 -0
  36. package/dist/gs/maps/maps.gs.js +79 -0
  37. package/dist/gs/maps/maps.gs.js.map +1 -0
  38. package/dist/gs/path/filepath/match.d.ts +4 -3
  39. package/dist/gs/path/filepath/match.js +2 -2
  40. package/dist/gs/path/filepath/match.js.map +1 -1
  41. package/dist/gs/path/filepath/path.d.ts +2 -2
  42. package/dist/gs/path/filepath/path.js +3 -3
  43. package/dist/gs/path/filepath/path.js.map +1 -1
  44. package/dist/gs/reflect/abi.d.ts +59 -0
  45. package/dist/gs/reflect/abi.gs.d.ts +59 -0
  46. package/dist/gs/reflect/abi.gs.js +79 -0
  47. package/dist/gs/reflect/abi.gs.js.map +1 -0
  48. package/dist/gs/reflect/abi.js +79 -0
  49. package/dist/gs/reflect/abi.js.map +1 -0
  50. package/dist/gs/reflect/badlinkname.d.ts +52 -0
  51. package/dist/gs/reflect/badlinkname.gs.d.ts +52 -0
  52. package/dist/gs/reflect/badlinkname.gs.js +72 -0
  53. package/dist/gs/reflect/badlinkname.gs.js.map +1 -0
  54. package/dist/gs/reflect/badlinkname.js +72 -0
  55. package/dist/gs/reflect/badlinkname.js.map +1 -0
  56. package/dist/gs/reflect/deepequal.gs.d.ts +25 -0
  57. package/dist/gs/reflect/deepequal.gs.js +308 -0
  58. package/dist/gs/reflect/deepequal.gs.js.map +1 -0
  59. package/dist/gs/reflect/float32reg_generic.gs.d.ts +2 -0
  60. package/dist/gs/reflect/float32reg_generic.gs.js +10 -0
  61. package/dist/gs/reflect/float32reg_generic.gs.js.map +1 -0
  62. package/dist/gs/reflect/index.gs.d.ts +1 -0
  63. package/dist/gs/reflect/index.gs.js +3 -0
  64. package/dist/gs/reflect/index.gs.js.map +1 -0
  65. package/dist/gs/reflect/iter.gs.d.ts +3 -0
  66. package/dist/gs/reflect/iter.gs.js +24 -0
  67. package/dist/gs/reflect/iter.gs.js.map +1 -0
  68. package/dist/gs/reflect/makefunc.gs.d.ts +34 -0
  69. package/dist/gs/reflect/makefunc.gs.js +288 -0
  70. package/dist/gs/reflect/makefunc.gs.js.map +1 -0
  71. package/dist/gs/reflect/map_swiss.gs.d.ts +14 -0
  72. package/dist/gs/reflect/map_swiss.gs.js +70 -0
  73. package/dist/gs/reflect/map_swiss.gs.js.map +1 -0
  74. package/dist/gs/reflect/reflect.gs.d.ts +132 -0
  75. package/dist/gs/reflect/reflect.gs.js +437 -0
  76. package/dist/gs/reflect/reflect.gs.js.map +1 -0
  77. package/dist/gs/reflect/swapper.gs.d.ts +1 -0
  78. package/dist/gs/reflect/swapper.gs.js +32 -0
  79. package/dist/gs/reflect/swapper.gs.js.map +1 -0
  80. package/dist/gs/reflect/type.gs.d.ts +4 -0
  81. package/dist/gs/reflect/type.gs.js +21 -0
  82. package/dist/gs/reflect/type.gs.js.map +1 -0
  83. package/dist/gs/reflect/value.gs.d.ts +4 -0
  84. package/dist/gs/reflect/value.gs.js +12 -0
  85. package/dist/gs/reflect/value.gs.js.map +1 -0
  86. package/dist/gs/reflect/visiblefields.gs.d.ts +3 -0
  87. package/dist/gs/reflect/visiblefields.gs.js +123 -0
  88. package/dist/gs/reflect/visiblefields.gs.js.map +1 -0
  89. package/dist/gs/strings/reader.d.ts +1 -1
  90. package/dist/gs/strings/reader.js.map +1 -1
  91. package/dist/gs/stringslite/index.d.ts +1 -0
  92. package/dist/gs/stringslite/index.js +2 -0
  93. package/dist/gs/stringslite/index.js.map +1 -0
  94. package/dist/gs/stringslite/strings.d.ts +11 -0
  95. package/dist/gs/stringslite/strings.js +67 -0
  96. package/dist/gs/stringslite/strings.js.map +1 -0
  97. package/dist/gs/time/time.d.ts +69 -0
  98. package/dist/gs/time/time.js +350 -0
  99. package/dist/gs/time/time.js.map +1 -1
  100. package/gs/builtin/map.ts +12 -8
  101. package/gs/builtin/slice.ts +13 -0
  102. package/gs/builtin/type.ts +8 -100
  103. package/gs/fmt/fmt.ts +33 -33
  104. package/gs/io/io.ts +47 -39
  105. package/gs/path/filepath/match.ts +4 -4
  106. package/gs/path/filepath/path.ts +3 -3
  107. package/gs/strings/reader.ts +1 -1
  108. package/gs/time/time.ts +403 -0
  109. package/package.json +1 -1
@@ -43,6 +43,14 @@ type VariableUsageInfo struct {
43
43
  Destinations []AssignmentInfo
44
44
  }
45
45
 
46
+ // ShadowingInfo tracks variable shadowing in if statement initializations
47
+ type ShadowingInfo struct {
48
+ // ShadowedVariables maps shadowed variable names to their outer scope objects
49
+ ShadowedVariables map[string]types.Object
50
+ // TempVariables maps shadowed variable names to temporary variable names
51
+ TempVariables map[string]string
52
+ }
53
+
46
54
  // FunctionTypeInfo represents Go function type information for reflection
47
55
  type FunctionTypeInfo struct {
48
56
  Params []types.Type // Parameter types
@@ -69,8 +77,9 @@ type NodeInfo struct {
69
77
  IsBareReturn bool
70
78
  EnclosingFuncDecl *ast.FuncDecl
71
79
  EnclosingFuncLit *ast.FuncLit
72
- IsInsideFunction bool // true if this declaration is inside a function body
73
- IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
80
+ IsInsideFunction bool // true if this declaration is inside a function body
81
+ IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
82
+ ShadowingInfo *ShadowingInfo // variable shadowing information for if statements
74
83
  }
75
84
 
76
85
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
@@ -116,6 +125,14 @@ type PackageAnalysis struct {
116
125
  // FunctionCalls maps file names to the functions they call from other files
117
126
  // Key: filename (without .go extension), Value: map[sourceFile][]functionNames
118
127
  FunctionCalls map[string]map[string][]string
128
+
129
+ // TypeDefs maps file names to the types defined in that file
130
+ // Key: filename (without .go extension), Value: list of type names
131
+ TypeDefs map[string][]string
132
+
133
+ // TypeCalls maps file names to the types they reference from other files
134
+ // Key: filename (without .go extension), Value: map[sourceFile][]typeNames
135
+ TypeCalls map[string]map[string][]string
119
136
  }
120
137
 
121
138
  // NewAnalysis creates a new Analysis instance.
@@ -137,6 +154,8 @@ func NewPackageAnalysis() *PackageAnalysis {
137
154
  return &PackageAnalysis{
138
155
  FunctionDefs: make(map[string][]string),
139
156
  FunctionCalls: make(map[string]map[string][]string),
157
+ TypeDefs: make(map[string][]string),
158
+ TypeCalls: make(map[string]map[string][]string),
140
159
  }
141
160
  }
142
161
 
@@ -270,6 +289,30 @@ func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
270
289
  return nodeInfo.IsMethodValue
271
290
  }
272
291
 
292
+ // HasVariableShadowing returns whether the given node has variable shadowing issues
293
+ func (a *Analysis) HasVariableShadowing(node ast.Node) bool {
294
+ if node == nil {
295
+ return false
296
+ }
297
+ nodeInfo := a.NodeData[node]
298
+ if nodeInfo == nil {
299
+ return false
300
+ }
301
+ return nodeInfo.ShadowingInfo != nil
302
+ }
303
+
304
+ // GetShadowingInfo returns the variable shadowing information for the given node
305
+ func (a *Analysis) GetShadowingInfo(node ast.Node) *ShadowingInfo {
306
+ if node == nil {
307
+ return nil
308
+ }
309
+ nodeInfo := a.NodeData[node]
310
+ if nodeInfo == nil {
311
+ return nil
312
+ }
313
+ return nodeInfo.ShadowingInfo
314
+ }
315
+
273
316
  // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
274
317
  type analysisVisitor struct {
275
318
  // analysis stores information gathered during the traversal
@@ -631,6 +674,19 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
631
674
  return v // Continue traversal
632
675
 
633
676
  case *ast.AssignStmt:
677
+ // Detect variable shadowing in any := assignment
678
+ if n.Tok == token.DEFINE {
679
+ shadowingInfo := v.detectVariableShadowing(n)
680
+ if shadowingInfo != nil {
681
+ // Store shadowing info on the assignment statement itself
682
+ if v.analysis.NodeData[n] == nil {
683
+ v.analysis.NodeData[n] = &NodeInfo{}
684
+ }
685
+ v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
686
+ }
687
+ }
688
+
689
+ // Continue with the existing assignment analysis logic
634
690
  for i, currentLHSExpr := range n.Lhs {
635
691
  if i >= len(n.Rhs) {
636
692
  break // Should not happen in valid Go
@@ -779,6 +835,22 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
779
835
  }
780
836
  }
781
837
  return v // Continue traversal
838
+
839
+ case *ast.IfStmt:
840
+ // Detect variable shadowing in if statement initializations
841
+ if n.Init != nil {
842
+ if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
843
+ shadowingInfo := v.detectVariableShadowing(assignStmt)
844
+ if shadowingInfo != nil {
845
+ // Initialize NodeData for this if statement
846
+ if v.analysis.NodeData[n] == nil {
847
+ v.analysis.NodeData[n] = &NodeInfo{}
848
+ }
849
+ v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
850
+ }
851
+ }
852
+ }
853
+ return v // Continue traversal
782
854
  }
783
855
 
784
856
  // For all other nodes, continue traversal
@@ -974,6 +1046,7 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
974
1046
  baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
975
1047
 
976
1048
  var functions []string
1049
+ var types []string
977
1050
  for _, decl := range syntax.Decls {
978
1051
  if funcDecl, ok := decl.(*ast.FuncDecl); ok {
979
1052
  // Only collect top-level functions (not methods)
@@ -981,11 +1054,22 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
981
1054
  functions = append(functions, funcDecl.Name.Name)
982
1055
  }
983
1056
  }
1057
+ if genDecl, ok := decl.(*ast.GenDecl); ok {
1058
+ // Collect type declarations
1059
+ for _, spec := range genDecl.Specs {
1060
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
1061
+ types = append(types, typeSpec.Name.Name)
1062
+ }
1063
+ }
1064
+ }
984
1065
  }
985
1066
 
986
1067
  if len(functions) > 0 {
987
1068
  analysis.FunctionDefs[baseFileName] = functions
988
1069
  }
1070
+ if len(types) > 0 {
1071
+ analysis.TypeDefs[baseFileName] = types
1072
+ }
989
1073
  }
990
1074
 
991
1075
  // Second pass: analyze function calls and determine which need imports
@@ -1049,6 +1133,71 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1049
1133
  }
1050
1134
  }
1051
1135
 
1136
+ // Third pass: analyze type references and determine which need imports
1137
+ for i, syntax := range pkg.Syntax {
1138
+ fileName := pkg.CompiledGoFiles[i]
1139
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1140
+
1141
+ // Find all type references in this file
1142
+ typeRefsFromOtherFiles := make(map[string][]string)
1143
+
1144
+ ast.Inspect(syntax, func(n ast.Node) bool {
1145
+ // Look for type references in struct fields, function parameters, etc.
1146
+ if ident, ok := n.(*ast.Ident); ok {
1147
+ // Check if this identifier refers to a type
1148
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1149
+ if _, ok := obj.(*types.TypeName); ok {
1150
+ typeName := ident.Name
1151
+
1152
+ // Check if this type is defined in the current file
1153
+ currentFileTypes := analysis.TypeDefs[baseFileName]
1154
+ isDefinedInCurrentFile := false
1155
+ for _, t := range currentFileTypes {
1156
+ if t == typeName {
1157
+ isDefinedInCurrentFile = true
1158
+ break
1159
+ }
1160
+ }
1161
+
1162
+ // If not defined in current file, find which file defines it
1163
+ if !isDefinedInCurrentFile {
1164
+ for sourceFile, types := range analysis.TypeDefs {
1165
+ if sourceFile == baseFileName {
1166
+ continue // Skip current file
1167
+ }
1168
+ for _, t := range types {
1169
+ if t == typeName {
1170
+ // Found the type in another file
1171
+ if typeRefsFromOtherFiles[sourceFile] == nil {
1172
+ typeRefsFromOtherFiles[sourceFile] = []string{}
1173
+ }
1174
+ // Check if already added to avoid duplicates
1175
+ found := false
1176
+ for _, existing := range typeRefsFromOtherFiles[sourceFile] {
1177
+ if existing == typeName {
1178
+ found = true
1179
+ break
1180
+ }
1181
+ }
1182
+ if !found {
1183
+ typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
1184
+ }
1185
+ break
1186
+ }
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ }
1192
+ }
1193
+ return true
1194
+ })
1195
+
1196
+ if len(typeRefsFromOtherFiles) > 0 {
1197
+ analysis.TypeCalls[baseFileName] = typeRefsFromOtherFiles
1198
+ }
1199
+ }
1200
+
1052
1201
  return analysis
1053
1202
  }
1054
1203
 
@@ -1225,3 +1374,112 @@ func (v *analysisVisitor) markFunctionVariable(ident *ast.Ident, funcType *types
1225
1374
  v.analysis.MarkFunctionForReflection(funcNode, funcType)
1226
1375
  }
1227
1376
  }
1377
+
1378
+ // detectVariableShadowing detects variable shadowing in any := assignment
1379
+ func (v *analysisVisitor) detectVariableShadowing(assignStmt *ast.AssignStmt) *ShadowingInfo {
1380
+ shadowingInfo := &ShadowingInfo{
1381
+ ShadowedVariables: make(map[string]types.Object),
1382
+ TempVariables: make(map[string]string),
1383
+ }
1384
+
1385
+ hasShadowing := false
1386
+
1387
+ // First, collect all LHS variable names that are being declared
1388
+ lhsVarNames := make(map[string]*ast.Ident)
1389
+ for _, lhsExpr := range assignStmt.Lhs {
1390
+ if lhsIdent, ok := lhsExpr.(*ast.Ident); ok && lhsIdent.Name != "_" {
1391
+ lhsVarNames[lhsIdent.Name] = lhsIdent
1392
+ }
1393
+ }
1394
+
1395
+ // Next, check all RHS expressions for usage of variables that are also being declared on LHS
1396
+ for _, rhsExpr := range assignStmt.Rhs {
1397
+ v.findVariableUsageInExpr(rhsExpr, lhsVarNames, shadowingInfo, &hasShadowing)
1398
+ }
1399
+
1400
+ if hasShadowing {
1401
+ return shadowingInfo
1402
+ }
1403
+ return nil
1404
+ }
1405
+
1406
+ // findVariableUsageInExpr recursively searches for variable usage in an expression
1407
+ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map[string]*ast.Ident, shadowingInfo *ShadowingInfo, hasShadowing *bool) {
1408
+ if expr == nil {
1409
+ return
1410
+ }
1411
+
1412
+ switch e := expr.(type) {
1413
+ case *ast.Ident:
1414
+ // Check if this identifier is being shadowed
1415
+ if lhsIdent, exists := lhsVarNames[e.Name]; exists {
1416
+ // This variable is being used on RHS but also declared on LHS - this is shadowing!
1417
+
1418
+ // Get the outer scope object for this variable
1419
+ if outerObj := v.pkg.TypesInfo.Uses[e]; outerObj != nil {
1420
+ // Make sure this isn't the same object as the LHS (which would mean no shadowing)
1421
+ if lhsObj := v.pkg.TypesInfo.Defs[lhsIdent]; lhsObj != outerObj {
1422
+ shadowingInfo.ShadowedVariables[e.Name] = outerObj
1423
+ shadowingInfo.TempVariables[e.Name] = "_temp_" + e.Name
1424
+ *hasShadowing = true
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ case *ast.CallExpr:
1430
+ // Check function arguments
1431
+ for _, arg := range e.Args {
1432
+ v.findVariableUsageInExpr(arg, lhsVarNames, shadowingInfo, hasShadowing)
1433
+ }
1434
+ // Check function expression itself
1435
+ v.findVariableUsageInExpr(e.Fun, lhsVarNames, shadowingInfo, hasShadowing)
1436
+
1437
+ case *ast.SelectorExpr:
1438
+ // Check the base expression (e.g., x in x.Method())
1439
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1440
+
1441
+ case *ast.IndexExpr:
1442
+ // Check both the expression and index (e.g., arr[i])
1443
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1444
+ v.findVariableUsageInExpr(e.Index, lhsVarNames, shadowingInfo, hasShadowing)
1445
+
1446
+ case *ast.SliceExpr:
1447
+ // Check the expression and slice bounds
1448
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1449
+ if e.Low != nil {
1450
+ v.findVariableUsageInExpr(e.Low, lhsVarNames, shadowingInfo, hasShadowing)
1451
+ }
1452
+ if e.High != nil {
1453
+ v.findVariableUsageInExpr(e.High, lhsVarNames, shadowingInfo, hasShadowing)
1454
+ }
1455
+ if e.Max != nil {
1456
+ v.findVariableUsageInExpr(e.Max, lhsVarNames, shadowingInfo, hasShadowing)
1457
+ }
1458
+
1459
+ case *ast.UnaryExpr:
1460
+ // Check the operand (e.g., &x, -x, !x)
1461
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1462
+
1463
+ case *ast.BinaryExpr:
1464
+ // Check both operands (e.g., x + y)
1465
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1466
+ v.findVariableUsageInExpr(e.Y, lhsVarNames, shadowingInfo, hasShadowing)
1467
+
1468
+ case *ast.ParenExpr:
1469
+ // Check the parenthesized expression
1470
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1471
+
1472
+ case *ast.TypeAssertExpr:
1473
+ // Check the expression being type-asserted
1474
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1475
+
1476
+ case *ast.StarExpr:
1477
+ // Check the expression being dereferenced
1478
+ v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
1479
+
1480
+ // Add more expression types as needed
1481
+ default:
1482
+ // For other expression types, we might need to add specific handling
1483
+ // For now, we'll ignore them as they're less common in shadowing scenarios
1484
+ }
1485
+ }
@@ -124,6 +124,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
124
124
  }
125
125
  }
126
126
 
127
+ // Add semicolon before destructuring assignment to prevent TypeScript
128
+ // from interpreting it as array access on the previous line
129
+ if tok != token.DEFINE {
130
+ c.tsw.WriteLiterally(";")
131
+ }
132
+
127
133
  // Use array destructuring for multi-variable assignments
128
134
  c.tsw.WriteLiterally("[")
129
135
  for i, l := range lhs {
@@ -621,6 +621,61 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
621
621
  }
622
622
  }
623
623
 
624
+ // Generate auto-imports for types from other files in the same package
625
+ if typeImports := c.PackageAnalysis.TypeCalls[currentFileName]; typeImports != nil {
626
+ // Sort source files for consistent import order
627
+ var sourceFiles []string
628
+ for sourceFile := range typeImports {
629
+ sourceFiles = append(sourceFiles, sourceFile)
630
+ }
631
+ sort.Strings(sourceFiles)
632
+
633
+ for _, sourceFile := range sourceFiles {
634
+ typeImports := typeImports[sourceFile]
635
+ if len(typeImports) > 0 {
636
+ // Filter out protobuf types - they should be imported from .pb.js files, not .gs.js files
637
+ var nonProtobufTypes []string
638
+ for _, typeName := range typeImports {
639
+ // Check if this type is a protobuf type by looking for it in the package
640
+ isProtobuf := false
641
+ if typeObj := c.pkg.Types.Scope().Lookup(typeName); typeObj != nil {
642
+ objType := typeObj.Type()
643
+ if namedType, ok := objType.(*types.Named); ok {
644
+ obj := namedType.Obj()
645
+ if obj != nil && obj.Pkg() != nil {
646
+ // Check if the type is defined in the current package and has a corresponding .pb.go file
647
+ if obj.Pkg() == c.pkg.Types {
648
+ // Check if there's a .pb.go file in the package that exports this type
649
+ // For now, we'll use a simple heuristic: if the type name ends with "Msg"
650
+ // and there's a .pb.go file in the package, assume it's a protobuf type
651
+ if strings.HasSuffix(typeName, "Msg") {
652
+ // Check if there are any .pb.go files in this package
653
+ for _, fileName := range c.pkg.CompiledGoFiles {
654
+ if strings.HasSuffix(fileName, ".pb.go") {
655
+ isProtobuf = true
656
+ break
657
+ }
658
+ }
659
+ }
660
+ }
661
+ }
662
+ }
663
+ }
664
+ if !isProtobuf {
665
+ nonProtobufTypes = append(nonProtobufTypes, typeName)
666
+ }
667
+ }
668
+
669
+ if len(nonProtobufTypes) > 0 {
670
+ // Sort types for consistent output
671
+ sort.Strings(nonProtobufTypes)
672
+ c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
673
+ strings.Join(nonProtobufTypes, ", "), sourceFile)
674
+ }
675
+ }
676
+ }
677
+ }
678
+
624
679
  c.codeWriter.WriteLine("") // Add a newline after imports
625
680
 
626
681
  if err := goWriter.WriteDecls(f.Decls); err != nil {
@@ -639,6 +694,9 @@ type GoToTSCompiler struct {
639
694
  pkg *packages.Package
640
695
 
641
696
  analysis *Analysis
697
+
698
+ // shadowingContext tracks temporary variable mappings when we're in a shadowing context
699
+ shadowingContext map[string]string
642
700
  }
643
701
 
644
702
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -646,9 +704,10 @@ type GoToTSCompiler struct {
646
704
  // analysis results (`Analysis`) to guide the translation process.
647
705
  func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
648
706
  return &GoToTSCompiler{
649
- tsw: tsw,
650
- pkg: pkg,
651
- analysis: analysis,
707
+ tsw: tsw,
708
+ pkg: pkg,
709
+ analysis: analysis,
710
+ shadowingContext: make(map[string]string),
652
711
  }
653
712
  }
654
713
 
@@ -670,6 +729,12 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
670
729
  return
671
730
  }
672
731
 
732
+ // Check if we're in a shadowing context and should use a temporary variable
733
+ if tempVarName, exists := c.shadowingContext[exp.Name]; exists {
734
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(tempVarName))
735
+ return
736
+ }
737
+
673
738
  // Use TypesInfo to find the object associated with the identifier
674
739
  var obj types.Object
675
740
  obj = c.pkg.TypesInfo.Uses[exp]
@@ -680,11 +745,30 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
680
745
  // Check if this identifier refers to a constant
681
746
  if obj != nil {
682
747
  if constObj, isConst := obj.(*types.Const); isConst {
683
- // Only evaluate constants from the current package being compiled
684
- // Don't evaluate constants from imported packages (they should use their exported names)
685
- // Special case: predeclared constants like iota have a nil package, so we should evaluate them
686
- if constObj.Pkg() == c.pkg.Types || constObj.Pkg() == nil {
687
- // Write the constant's evaluated value instead of the identifier name
748
+ // Evaluate constants to literals in these cases:
749
+ // 1. Predeclared constants (like iota, true, false) - these have nil package
750
+ // 2. Constants from the current package that are computed expressions
751
+ // (like iota-based constants or mathematical expressions)
752
+ //
753
+ // For simple assignments from imported constants (const x = pkg.Y),
754
+ // preserve the variable name for better readability.
755
+
756
+ if constObj.Pkg() == nil {
757
+ // Predeclared constants - always evaluate to literals
758
+ c.writeConstantValue(constObj)
759
+ return
760
+ }
761
+
762
+ if constObj.Pkg() == c.pkg.Types {
763
+ // This is a constant from the current package.
764
+ // Check if it's a simple assignment by looking at the constant value:
765
+ // If the constant has the same name as an identifier in the import,
766
+ // it's likely a simple assignment and we should preserve the name.
767
+ // Otherwise, evaluate to literal (for computed expressions like iota).
768
+
769
+ // For now, let's be conservative and evaluate current package constants
770
+ // to literals to maintain compatibility with existing behavior.
771
+ // This handles iota-based constants correctly.
688
772
  c.writeConstantValue(constObj)
689
773
  return
690
774
  }
@@ -970,6 +1054,12 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
970
1054
  continue
971
1055
  }
972
1056
 
1057
+ // Skip .test.ts files
1058
+ if strings.HasSuffix(fileName, ".test.ts") {
1059
+ // c.le.Debugf("Skipping test file: %s", fileName)
1060
+ continue
1061
+ }
1062
+
973
1063
  // Remove existing file if it exists (but preserve directories)
974
1064
  if stat, err := os.Stat(outputEntryPath); err == nil && !stat.IsDir() {
975
1065
  if err := os.Remove(outputEntryPath); err != nil {
package/compiler/decl.go CHANGED
@@ -101,29 +101,13 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
101
101
  c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
102
102
  c.tsw.WriteLiterally(" ")
103
103
 
104
- hasNamedReturns := false
105
- if decl.Type.Results != nil {
106
- for _, field := range decl.Type.Results.List {
107
- if len(field.Names) > 0 {
108
- hasNamedReturns = true
109
- break
110
- }
111
- }
112
- }
113
-
114
- if hasNamedReturns {
104
+ if c.hasNamedReturns(decl.Type.Results) {
115
105
  c.tsw.WriteLine("{")
116
106
  c.tsw.Indent(1)
117
107
 
118
108
  // Declare named return variables and initialize them to their zero values
119
- for _, field := range decl.Type.Results.List {
120
- for _, name := range field.Names {
121
- c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
122
- c.WriteTypeExpr(field.Type)
123
- c.tsw.WriteLiterally(" = ")
124
- c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
125
- c.tsw.WriteLine("")
126
- }
109
+ if err := c.writeNamedReturnDeclarations(decl.Type.Results); err != nil {
110
+ return fmt.Errorf("failed to write named return declarations: %w", err)
127
111
  }
128
112
  }
129
113
 
@@ -131,7 +115,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
131
115
  return fmt.Errorf("failed to write function body: %w", err)
132
116
  }
133
117
 
134
- if hasNamedReturns {
118
+ if c.hasNamedReturns(decl.Type.Results) {
135
119
  c.tsw.Indent(-1)
136
120
  c.tsw.WriteLine("}")
137
121
  }
@@ -244,6 +228,11 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
244
228
  }
245
229
  }
246
230
 
231
+ // Declare named return variables and initialize them to their zero values
232
+ if err := c.writeNamedReturnDeclarations(decl.Type.Results); err != nil {
233
+ return fmt.Errorf("failed to write named return declarations: %w", err)
234
+ }
235
+
247
236
  // write method body without outer braces
248
237
  for _, stmt := range decl.Body.List {
249
238
  if err := c.WriteStmt(stmt); err != nil {
@@ -263,3 +252,36 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
263
252
 
264
253
  return nil
265
254
  }
255
+
256
+ // writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
257
+ // It declares each named return variable with its appropriate type and zero value.
258
+ func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
259
+ if results == nil {
260
+ return nil
261
+ }
262
+
263
+ for _, field := range results.List {
264
+ for _, name := range field.Names {
265
+ c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
266
+ c.WriteTypeExpr(field.Type)
267
+ c.tsw.WriteLiterally(" = ")
268
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
269
+ c.tsw.WriteLine("")
270
+ }
271
+ }
272
+ return nil
273
+ }
274
+
275
+ // hasNamedReturns checks if a function type has any named return parameters.
276
+ func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
277
+ if results == nil {
278
+ return false
279
+ }
280
+
281
+ for _, field := range results.List {
282
+ if len(field.Names) > 0 {
283
+ return true
284
+ }
285
+ }
286
+ return false
287
+ }
@@ -97,6 +97,37 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
97
97
  }
98
98
  }
99
99
  }
100
+
101
+ // Handle general slice type conversions like []T(namedType) where namedType has underlying type []T
102
+ if arrayType.Len == nil && len(exp.Args) == 1 {
103
+ arg := exp.Args[0]
104
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
105
+ // Check if the argument is a named type with a slice underlying type
106
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
107
+ // Check if the named type has receiver methods (is a wrapper class)
108
+ typeName := namedArgType.Obj().Name()
109
+ if c.hasReceiverMethods(typeName) {
110
+ // Check if the underlying type matches the target slice type
111
+ if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
112
+ // Get the target slice type
113
+ targetType := c.pkg.TypesInfo.TypeOf(arrayType)
114
+ if targetSliceType, isTargetSlice := targetType.Underlying().(*types.Slice); isTargetSlice {
115
+ // Check if element types are compatible
116
+ if types.Identical(sliceUnderlying.Elem(), targetSliceType.Elem()) {
117
+ // This is a conversion from NamedType to []T where NamedType has underlying []T
118
+ // Use valueOf() to get the underlying slice
119
+ if err := c.WriteValueExpr(arg); err != nil {
120
+ return fmt.Errorf("failed to write argument for slice type conversion: %w", err)
121
+ }
122
+ c.tsw.WriteLiterally(".valueOf()")
123
+ return nil // Handled named type to slice conversion
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
100
131
  }
101
132
 
102
133
  if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
@@ -274,7 +305,12 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
274
305
  }
275
306
 
276
307
  c.tsw.WriteLiterally("$.makeSlice<")
277
- c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
308
+ // Use AST-based type writing to preserve qualified names
309
+ if arrType, ok := exp.Args[0].(*ast.ArrayType); ok {
310
+ c.WriteTypeExpr(arrType.Elt)
311
+ } else {
312
+ c.WriteGoType(goElemType, GoTypeContextGeneral)
313
+ }
278
314
  c.tsw.WriteLiterally(">(")
279
315
 
280
316
  hasCapacity := len(exp.Args) == 3
@@ -45,6 +45,26 @@ func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
45
45
  }
46
46
  }
47
47
 
48
+ // Handle array/slice types to preserve qualified names in element types
49
+ if arrayType, ok := a.(*ast.ArrayType); ok {
50
+ if arrayType.Len == nil {
51
+ // Slice type: []T -> $.Slice<T>
52
+ // Check if it's a []byte slice first
53
+ if ident, ok := arrayType.Elt.(*ast.Ident); ok && ident.Name == "byte" {
54
+ c.tsw.WriteLiterally("$.Bytes")
55
+ return
56
+ }
57
+ c.tsw.WriteLiterally("$.Slice<")
58
+ c.WriteTypeExpr(arrayType.Elt) // Recursively handle element type preserving qualified names
59
+ c.tsw.WriteLiterally(">")
60
+ } else {
61
+ // Array type: [N]T -> T[]
62
+ c.WriteTypeExpr(arrayType.Elt) // Recursively handle element type preserving qualified names
63
+ c.tsw.WriteLiterally("[]")
64
+ }
65
+ return
66
+ }
67
+
48
68
  // Get type information for the expression and use WriteGoType
49
69
  typ := c.pkg.TypesInfo.TypeOf(a)
50
70
  c.WriteGoType(typ, GoTypeContextGeneral)