goscript 0.0.60 → 0.0.62

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 (87) hide show
  1. package/README.md +9 -0
  2. package/compiler/analysis.go +974 -369
  3. package/compiler/assignment.go +72 -0
  4. package/compiler/compiler.go +74 -15
  5. package/compiler/composite-lit.go +29 -8
  6. package/compiler/decl.go +67 -98
  7. package/compiler/expr-call-async.go +26 -1
  8. package/compiler/expr-call-builtins.go +60 -4
  9. package/compiler/expr-call-helpers.go +182 -0
  10. package/compiler/expr-call-type-conversion.go +37 -5
  11. package/compiler/expr-call.go +25 -33
  12. package/compiler/expr-selector.go +71 -1
  13. package/compiler/expr-type.go +49 -3
  14. package/compiler/expr.go +37 -28
  15. package/compiler/index.test.ts +3 -1
  16. package/compiler/lit.go +13 -4
  17. package/compiler/spec-struct.go +42 -9
  18. package/compiler/spec-value.go +2 -2
  19. package/compiler/spec.go +42 -5
  20. package/compiler/stmt-assign.go +71 -0
  21. package/compiler/stmt-range.go +2 -2
  22. package/compiler/stmt.go +130 -10
  23. package/compiler/type-utils.go +40 -16
  24. package/compiler/type.go +50 -12
  25. package/dist/gs/builtin/builtin.d.ts +8 -1
  26. package/dist/gs/builtin/builtin.js +26 -1
  27. package/dist/gs/builtin/builtin.js.map +1 -1
  28. package/dist/gs/builtin/errors.d.ts +1 -0
  29. package/dist/gs/builtin/errors.js +8 -0
  30. package/dist/gs/builtin/errors.js.map +1 -1
  31. package/dist/gs/builtin/slice.d.ts +5 -4
  32. package/dist/gs/builtin/slice.js +51 -21
  33. package/dist/gs/builtin/slice.js.map +1 -1
  34. package/dist/gs/builtin/type.d.ts +28 -2
  35. package/dist/gs/builtin/type.js +132 -0
  36. package/dist/gs/builtin/type.js.map +1 -1
  37. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  38. package/dist/gs/bytes/reader.gs.js +1 -1
  39. package/dist/gs/bytes/reader.gs.js.map +1 -1
  40. package/dist/gs/internal/byteorder/index.d.ts +6 -0
  41. package/dist/gs/internal/byteorder/index.js +34 -0
  42. package/dist/gs/internal/byteorder/index.js.map +1 -1
  43. package/dist/gs/reflect/index.d.ts +3 -3
  44. package/dist/gs/reflect/index.js +2 -2
  45. package/dist/gs/reflect/index.js.map +1 -1
  46. package/dist/gs/reflect/map.d.ts +3 -2
  47. package/dist/gs/reflect/map.js +37 -3
  48. package/dist/gs/reflect/map.js.map +1 -1
  49. package/dist/gs/reflect/type.d.ts +55 -8
  50. package/dist/gs/reflect/type.js +889 -23
  51. package/dist/gs/reflect/type.js.map +1 -1
  52. package/dist/gs/reflect/types.d.ts +11 -12
  53. package/dist/gs/reflect/types.js +26 -15
  54. package/dist/gs/reflect/types.js.map +1 -1
  55. package/dist/gs/reflect/value.d.ts +4 -4
  56. package/dist/gs/reflect/value.js +8 -2
  57. package/dist/gs/reflect/value.js.map +1 -1
  58. package/dist/gs/slices/slices.d.ts +32 -0
  59. package/dist/gs/slices/slices.js +81 -0
  60. package/dist/gs/slices/slices.js.map +1 -1
  61. package/dist/gs/sync/atomic/type.gs.d.ts +2 -2
  62. package/dist/gs/sync/atomic/type.gs.js +12 -2
  63. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  64. package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
  65. package/dist/gs/unicode/utf8/utf8.js +10 -6
  66. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  67. package/go.mod +4 -4
  68. package/go.sum +8 -16
  69. package/gs/builtin/builtin.ts +27 -2
  70. package/gs/builtin/errors.ts +12 -0
  71. package/gs/builtin/slice.ts +77 -14
  72. package/gs/builtin/type.ts +167 -2
  73. package/gs/bytes/reader.gs.ts +2 -2
  74. package/gs/internal/byteorder/index.ts +40 -0
  75. package/gs/math/hypot.gs.test.ts +3 -1
  76. package/gs/math/pow10.gs.test.ts +5 -4
  77. package/gs/reflect/index.ts +6 -3
  78. package/gs/reflect/map.test.ts +7 -6
  79. package/gs/reflect/map.ts +49 -7
  80. package/gs/reflect/type.ts +1139 -43
  81. package/gs/reflect/types.ts +34 -21
  82. package/gs/reflect/value.ts +12 -6
  83. package/gs/slices/slices.ts +92 -0
  84. package/gs/sync/atomic/type.gs.ts +14 -5
  85. package/gs/sync/meta.json +1 -1
  86. package/gs/unicode/utf8/utf8.ts +12 -8
  87. package/package.json +13 -13
@@ -219,6 +219,15 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
219
219
  if shouldApplyClone(c.pkg, r) {
220
220
  // When cloning for value assignment, mark the result as struct value
221
221
  c.tsw.WriteLiterally("$.markAsStructValue(")
222
+
223
+ // Check if RHS is an async call - if so, wrap in parentheses so .clone() binds correctly
224
+ // Example: (await asyncFunc()).clone() instead of await asyncFunc().clone()
225
+ needsParensForAsync := false
226
+ if callExpr, isCall := r.(*ast.CallExpr); isCall && c.isCallExprAsync(callExpr) {
227
+ needsParensForAsync = true
228
+ c.tsw.WriteLiterally("(")
229
+ }
230
+
222
231
  // For other expressions, we need to handle variable referenced access differently
223
232
  if _, isIdent := r.(*ast.Ident); isIdent {
224
233
  // For identifiers, WriteValueExpr already adds .value if needed
@@ -236,6 +245,9 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
236
245
  }
237
246
  }
238
247
 
248
+ if needsParensForAsync {
249
+ c.tsw.WriteLiterally(")")
250
+ }
239
251
  c.tsw.WriteLiterally(".clone())") // Always add clone for struct values
240
252
  } else {
241
253
  // Check if this is a pointer variable assignment to an interface type
@@ -270,6 +282,11 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
270
282
  }
271
283
 
272
284
  // Non-struct case: write RHS normally
285
+ // Check if this is a primitive error type being assigned to an error interface
286
+ if c.writePrimitiveErrorWrapperForAssign(lhs, r, i) {
287
+ continue
288
+ }
289
+
273
290
  if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
274
291
  return err
275
292
  }
@@ -288,6 +305,61 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
288
305
  return nil
289
306
  }
290
307
 
308
+ // writePrimitiveErrorWrapperForAssign checks if an RHS value is a primitive type
309
+ // that implements the error interface being assigned to an error-typed LHS,
310
+ // and if so, wraps it with $.wrapPrimitiveError.
311
+ // Returns true if the wrapper was written, false otherwise.
312
+ func (c *GoToTSCompiler) writePrimitiveErrorWrapperForAssign(lhs []ast.Expr, rhs ast.Expr, rhsIndex int) bool {
313
+ // Only handle single assignments for now
314
+ if len(lhs) != 1 || rhsIndex != 0 {
315
+ return false
316
+ }
317
+
318
+ // Get the LHS type
319
+ lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
320
+ if lhsType == nil {
321
+ return false
322
+ }
323
+
324
+ // Check if the LHS type is the error interface
325
+ if iface, ok := lhsType.Underlying().(*types.Interface); !ok || iface.String() != "interface{Error() string}" {
326
+ return false
327
+ }
328
+
329
+ // Get the actual type of the RHS expression
330
+ rhsType := c.pkg.TypesInfo.TypeOf(rhs)
331
+ if rhsType == nil {
332
+ return false
333
+ }
334
+
335
+ // Check if the RHS type is a wrapper type (named type with basic underlying type)
336
+ if !c.isWrapperType(rhsType) {
337
+ return false
338
+ }
339
+
340
+ // Check if the RHS type has an Error() method
341
+ if !c.typeHasMethods(rhsType, "Error") {
342
+ return false
343
+ }
344
+
345
+ // Get the qualified type name for the Error function
346
+ typeName := c.getQualifiedTypeName(rhsType)
347
+ if typeName == "" {
348
+ return false
349
+ }
350
+
351
+ // Write: $.wrapPrimitiveError(value, TypeName_Error)
352
+ c.tsw.WriteLiterally("$.wrapPrimitiveError(")
353
+ if err := c.WriteValueExpr(rhs); err != nil {
354
+ return false
355
+ }
356
+ c.tsw.WriteLiterally(", ")
357
+ c.tsw.WriteLiterally(typeName)
358
+ c.tsw.WriteLiterally("_Error)")
359
+
360
+ return true
361
+ }
362
+
291
363
  // writeBlankIdentifierAssign handles assignment to blank identifier (_)
292
364
  func (c *GoToTSCompiler) writeBlankIdentifierAssign(rhs ast.Expr) error {
293
365
  c.tsw.WriteLiterally("/* _ = */ ")
@@ -13,7 +13,6 @@ import (
13
13
  "os"
14
14
  "path/filepath"
15
15
  "slices"
16
- "sort"
17
16
  "strings"
18
17
 
19
18
  gs "github.com/aperturerobotics/goscript"
@@ -487,7 +486,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
487
486
 
488
487
  // Write exports if this file has exported symbols
489
488
  if len(valueSymbols) > 0 {
490
- sort.Strings(valueSymbols)
489
+ slices.Sort(valueSymbols)
491
490
  exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
492
491
  strings.Join(valueSymbols, ", "), fileName)
493
492
  if _, err := indexFile.WriteString(exportLine); err != nil {
@@ -497,7 +496,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
497
496
 
498
497
  // Write struct exports (both as types and values)
499
498
  if len(structSymbols) > 0 {
500
- sort.Strings(structSymbols)
499
+ slices.Sort(structSymbols)
501
500
  // Export classes as values (which makes them available as both types and values in TypeScript)
502
501
  exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
503
502
  strings.Join(structSymbols, ", "), fileName)
@@ -507,7 +506,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
507
506
  }
508
507
 
509
508
  if len(typeSymbols) > 0 {
510
- sort.Strings(typeSymbols)
509
+ slices.Sort(typeSymbols)
511
510
  exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
512
511
  strings.Join(typeSymbols, ", "), fileName)
513
512
  if _, err := indexFile.WriteString(exportLine); err != nil {
@@ -612,7 +611,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
612
611
  for sourceFile := range imports {
613
612
  sourceFiles = append(sourceFiles, sourceFile)
614
613
  }
615
- sort.Strings(sourceFiles)
614
+ slices.Sort(sourceFiles)
616
615
 
617
616
  for _, sourceFile := range sourceFiles {
618
617
  functions := imports[sourceFile]
@@ -623,7 +622,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
623
622
  sanitizedFunctions = append(sanitizedFunctions, sanitizeIdentifier(fn))
624
623
  }
625
624
  // Sort functions for consistent output
626
- sort.Strings(sanitizedFunctions)
625
+ slices.Sort(sanitizedFunctions)
627
626
  c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
628
627
  strings.Join(sanitizedFunctions, ", "), sourceFile)
629
628
  }
@@ -637,7 +636,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
637
636
  for sourceFile := range typeImports {
638
637
  sourceFiles = append(sourceFiles, sourceFile)
639
638
  }
640
- sort.Strings(sourceFiles)
639
+ slices.Sort(sourceFiles)
641
640
 
642
641
  for _, sourceFile := range sourceFiles {
643
642
  typeImports := typeImports[sourceFile]
@@ -668,7 +667,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
668
667
  sanitizedTypes = append(sanitizedTypes, sanitizeIdentifier(typeName))
669
668
  }
670
669
  // Sort types for consistent output
671
- sort.Strings(sanitizedTypes)
670
+ slices.Sort(sanitizedTypes)
672
671
  c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
673
672
  strings.Join(sanitizedTypes, ", "), sourceFile)
674
673
  }
@@ -676,6 +675,43 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
676
675
  }
677
676
  }
678
677
 
678
+ // Generate auto-imports for variables from other files in the same package
679
+ if varImports := c.PackageAnalysis.VariableCalls[currentFileName]; varImports != nil {
680
+ // Sort source files for consistent import order
681
+ var sourceFiles []string
682
+ for sourceFile := range varImports {
683
+ sourceFiles = append(sourceFiles, sourceFile)
684
+ }
685
+ slices.Sort(sourceFiles)
686
+
687
+ for _, sourceFile := range sourceFiles {
688
+ variables := varImports[sourceFile]
689
+ if len(variables) > 0 {
690
+ // Apply sanitization to variable names
691
+ var sanitizedVariables []string
692
+ for _, varName := range variables {
693
+ sanitizedVariables = append(sanitizedVariables, sanitizeIdentifier(varName))
694
+ }
695
+ // Sort variables for consistent output
696
+ slices.Sort(sanitizedVariables)
697
+ c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
698
+ strings.Join(sanitizedVariables, ", "), sourceFile)
699
+ }
700
+ }
701
+ }
702
+
703
+ // Write synthetic imports (for promoted methods from embedded structs)
704
+ // Sort by package name for consistent output
705
+ var syntheticPkgNames []string
706
+ for pkgName := range c.Analysis.SyntheticImports {
707
+ syntheticPkgNames = append(syntheticPkgNames, pkgName)
708
+ }
709
+ slices.Sort(syntheticPkgNames)
710
+ for _, pkgName := range syntheticPkgNames {
711
+ imp := c.Analysis.SyntheticImports[pkgName]
712
+ c.codeWriter.WriteImport(pkgName, imp.importPath+"/index.js")
713
+ }
714
+
679
715
  c.codeWriter.WriteLine("") // Add a newline after imports
680
716
 
681
717
  if err := goWriter.WriteDecls(f.Decls); err != nil {
@@ -697,6 +733,10 @@ type GoToTSCompiler struct {
697
733
 
698
734
  // Context flags
699
735
  insideAddressOf bool // true when processing operand of & operator
736
+
737
+ // renamedVars tracks variables that have been renamed to avoid type shadowing
738
+ // Key: types.Object of the original variable, Value: new name to use
739
+ renamedVars map[types.Object]string
700
740
  }
701
741
 
702
742
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -704,9 +744,10 @@ type GoToTSCompiler struct {
704
744
  // analysis results (`Analysis`) to guide the translation process.
705
745
  func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
706
746
  return &GoToTSCompiler{
707
- tsw: tsw,
708
- pkg: pkg,
709
- analysis: analysis,
747
+ tsw: tsw,
748
+ pkg: pkg,
749
+ analysis: analysis,
750
+ renamedVars: make(map[types.Object]string),
710
751
  }
711
752
  }
712
753
 
@@ -785,6 +826,18 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
785
826
  // Use TypesInfo to find the object associated with the identifier
786
827
  obj := c.objectOfIdent(exp)
787
828
 
829
+ // Check if this variable has been renamed to avoid type shadowing
830
+ if obj != nil {
831
+ if renamedName, ok := c.renamedVars[obj]; ok {
832
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(renamedName))
833
+ // Determine if we need to access .value based on analysis data
834
+ if accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
835
+ c.tsw.WriteLiterally("!.value")
836
+ }
837
+ return
838
+ }
839
+ }
840
+
788
841
  // Check if this identifier refers to a constant
789
842
  if obj != nil {
790
843
  if constObj, isConst := obj.(*types.Const); isConst {
@@ -844,7 +897,7 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
844
897
  if exp.List == nil {
845
898
  // Default case
846
899
  c.tsw.WriteLiterally("default:")
847
- c.tsw.WriteLine("")
900
+ c.tsw.WriteLine(" {")
848
901
  } else {
849
902
  // Case with expressions
850
903
  // For Go's `case expr1, expr2:`, we translate to:
@@ -852,18 +905,23 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
852
905
  // case expr2:
853
906
  // ... body ...
854
907
  // break
855
- for _, caseExpr := range exp.List {
908
+ for i, caseExpr := range exp.List {
856
909
  c.tsw.WriteLiterally("case ")
857
910
  if err := c.WriteValueExpr(caseExpr); err != nil {
858
911
  return fmt.Errorf("failed to write case clause expression: %w", err)
859
912
  }
860
913
  c.tsw.WriteLiterally(":")
861
- c.tsw.WriteLine("")
914
+ // Only add opening brace after the last case label
915
+ if i == len(exp.List)-1 {
916
+ c.tsw.WriteLine(" {")
917
+ } else {
918
+ c.tsw.WriteLine("")
919
+ }
862
920
  }
863
921
  }
864
922
 
865
923
  // The body is written once, after all case labels for this clause.
866
- // Indentation for the body starts here.
924
+ // Wrap in block to provide Go-like case scope semantics.
867
925
  c.tsw.Indent(1)
868
926
  for _, stmt := range exp.Body {
869
927
  if err := c.WriteStmt(stmt); err != nil {
@@ -873,6 +931,7 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
873
931
  // Add break statement (Go's switch has implicit breaks, TS needs explicit break)
874
932
  c.tsw.WriteLine("break")
875
933
  c.tsw.Indent(-1)
934
+ c.tsw.WriteLine("}")
876
935
  return nil
877
936
  }
878
937
 
@@ -341,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
341
341
  case *types.Map, *types.Struct:
342
342
  // Handle struct directly with the struct literal logic
343
343
  if structType, ok := underlying.(*types.Struct); ok {
344
- return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
344
+ return c.writeUntypedStructLiteral(exp, tv.Type, structType)
345
345
  }
346
346
  // Map case would be handled here
347
347
  return fmt.Errorf("untyped map composite literals not yet supported")
@@ -356,7 +356,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
356
356
  // This is an anonymous struct literal with inferred pointer type
357
357
  // Just create the struct object directly - no var-refing needed
358
358
  // Anonymous literals are not variables, so they don't get var-refed
359
- return c.writeUntypedStructLiteral(exp, elemType) // true = anonymous
359
+ return c.writeUntypedStructLiteral(exp, ptrType.Elem(), elemType)
360
360
  default:
361
361
  return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
362
362
  }
@@ -384,7 +384,7 @@ func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
384
384
  }
385
385
 
386
386
  // writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
387
- func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, structType *types.Struct) error {
387
+ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, actualType types.Type, structType *types.Struct) error {
388
388
  // Create field mapping like the typed struct case
389
389
  directFields := make(map[string]ast.Expr)
390
390
 
@@ -408,8 +408,23 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
408
408
  }
409
409
  }
410
410
 
411
- // Write the object literal (always anonymous for untyped)
412
- c.tsw.WriteLiterally("{")
411
+ // Check if this is a named type
412
+ isNamed := false
413
+ if _, ok := actualType.(*types.Named); ok {
414
+ isNamed = true
415
+ }
416
+
417
+ // Write the object literal
418
+ if isNamed {
419
+ // For named structs, use constructor
420
+ c.tsw.WriteLiterally("$.markAsStructValue(new ")
421
+ // Write the type name
422
+ c.WriteGoType(actualType, GoTypeContextGeneral)
423
+ c.tsw.WriteLiterally("({")
424
+ } else {
425
+ // For truly anonymous structs, just write a simple object literal
426
+ c.tsw.WriteLiterally("{")
427
+ }
413
428
 
414
429
  firstFieldWritten := false
415
430
  // Write fields in order
@@ -434,7 +449,12 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
434
449
  firstFieldWritten = true
435
450
  }
436
451
 
437
- c.tsw.WriteLiterally("}")
452
+ // Close the object literal
453
+ if isNamed {
454
+ c.tsw.WriteLiterally("}))")
455
+ } else {
456
+ c.tsw.WriteLiterally("}")
457
+ }
438
458
  return nil
439
459
  }
440
460
 
@@ -606,9 +626,10 @@ func (c *GoToTSCompiler) categorizeStructFields(
606
626
  }
607
627
  }
608
628
 
609
- // Handle the case where an anonymous struct has values without keys
629
+ // Handle the case where a struct has values without keys (positional initialization)
610
630
  // This block processes non-key-value elements and associates them with struct fields.
611
- if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
631
+ // This applies to both named and anonymous structs.
632
+ if len(exp.Elts) > 0 && len(directFields) == 0 {
612
633
  // Check if any elements in the composite literal are not key-value pairs.
613
634
  hasNonKeyValueElts := false
614
635
  for _, elt := range exp.Elts {
package/compiler/decl.go CHANGED
@@ -5,7 +5,7 @@ import (
5
5
  "go/ast"
6
6
  "go/token"
7
7
  "go/types"
8
- "sort"
8
+ "slices"
9
9
  )
10
10
 
11
11
  // WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
@@ -210,7 +210,7 @@ func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap
210
210
  }
211
211
 
212
212
  // Sort dependencies for deterministic output
213
- sort.Strings(deps)
213
+ slices.Sort(deps)
214
214
  return deps
215
215
  }
216
216
 
@@ -252,122 +252,82 @@ func (c *GoToTSCompiler) extractStructFieldDependencies(fieldType ast.Expr, type
252
252
  return deps
253
253
  }
254
254
 
255
- // sortVarSpecsByTypeDependencies sorts variable declarations based on their type dependencies
255
+ // sortVarSpecsByTypeDependencies sorts variable declarations based on their value dependencies
256
+ // to ensure that variables are initialized in the correct order (respecting JavaScript's TDZ).
257
+ // For example: var StdEncoding = NewEncoding(...) must come before var RawStdEncoding = StdEncoding.WithPadding(...)
256
258
  func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
257
259
  if len(varSpecs) <= 1 {
258
260
  return varSpecs, nil
259
261
  }
260
262
 
261
- // Build type name map
262
- typeSpecMap := make(map[string]*ast.TypeSpec)
263
- for _, typeSpec := range typeSpecs {
264
- typeSpecMap[typeSpec.Name.Name] = typeSpec
265
- }
266
-
267
- // Group variables by dependency status with names for sorting
268
- type namedVarSpec struct {
269
- spec *ast.ValueSpec
270
- name string
271
- }
272
-
273
- var independentVars []namedVarSpec
274
- var dependentVars []namedVarSpec
275
-
263
+ // Build a map of variable names to their specs
264
+ varSpecMap := make(map[string]*ast.ValueSpec)
265
+ varNames := []string{}
276
266
  for _, varSpec := range varSpecs {
277
- // Get variable name for sorting
278
- varName := ""
279
267
  if len(varSpec.Names) > 0 {
280
- varName = varSpec.Names[0].Name
268
+ name := varSpec.Names[0].Name
269
+ varSpecMap[name] = varSpec
270
+ varNames = append(varNames, name)
281
271
  }
272
+ }
282
273
 
283
- hasDependency := false
284
-
285
- // Check type annotation
286
- if varSpec.Type != nil {
287
- deps := c.extractTypeDependencies(varSpec.Type, typeSpecMap)
288
- if len(deps) > 0 {
289
- hasDependency = true
290
- }
291
- }
274
+ // Build dependency graph: varName -> list of variables it depends on
275
+ dependencies := make(map[string][]string)
276
+ for _, name := range varNames {
277
+ dependencies[name] = []string{}
278
+ }
292
279
 
293
- // Check initializer expressions for type usage
294
- if !hasDependency {
295
- for _, value := range varSpec.Values {
296
- if c.hasTypeReferences(value, typeSpecMap) {
297
- hasDependency = true
298
- break
299
- }
300
- }
280
+ // Extract value dependencies from initializer expressions
281
+ for _, varSpec := range varSpecs {
282
+ if len(varSpec.Names) == 0 {
283
+ continue
301
284
  }
285
+ varName := varSpec.Names[0].Name
302
286
 
303
- namedVar := namedVarSpec{spec: varSpec, name: varName}
304
- if hasDependency {
305
- dependentVars = append(dependentVars, namedVar)
306
- } else {
307
- independentVars = append(independentVars, namedVar)
287
+ // Check initializer expressions for variable references
288
+ for _, value := range varSpec.Values {
289
+ deps := c.extractVarDependencies(value, varSpecMap)
290
+ dependencies[varName] = append(dependencies[varName], deps...)
308
291
  }
309
292
  }
310
293
 
311
- // Sort both groups by name for deterministic output
312
- sort.Slice(independentVars, func(i, j int) bool {
313
- return independentVars[i].name < independentVars[j].name
314
- })
315
- sort.Slice(dependentVars, func(i, j int) bool {
316
- return dependentVars[i].name < dependentVars[j].name
317
- })
294
+ // Perform topological sort
295
+ sorted, err := c.topologicalSort(dependencies)
296
+ if err != nil {
297
+ return nil, err
298
+ }
318
299
 
319
- // Return independent variables first, then dependent ones
300
+ // Build result in sorted order
320
301
  result := make([]*ast.ValueSpec, 0, len(varSpecs))
321
- for _, namedVar := range independentVars {
322
- result = append(result, namedVar.spec)
323
- }
324
- for _, namedVar := range dependentVars {
325
- result = append(result, namedVar.spec)
302
+ for _, varName := range sorted {
303
+ if spec, exists := varSpecMap[varName]; exists {
304
+ result = append(result, spec)
305
+ }
326
306
  }
327
307
 
328
308
  return result, nil
329
309
  }
330
310
 
331
- // hasTypeReferences checks if an expression contains references to local types
332
- func (c *GoToTSCompiler) hasTypeReferences(expr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) bool {
333
- hasRef := false
311
+ // extractVarDependencies extracts variable dependencies from an initializer expression.
312
+ // It returns a list of variable names that the expression depends on.
313
+ func (c *GoToTSCompiler) extractVarDependencies(expr ast.Expr, varSpecMap map[string]*ast.ValueSpec) []string {
314
+ var deps []string
315
+ seen := make(map[string]bool)
334
316
 
335
317
  ast.Inspect(expr, func(n ast.Node) bool {
336
- switch t := n.(type) {
337
- case *ast.CallExpr:
338
- // Check function calls like new Message(), makeChannel<Message>()
339
- if ident, ok := t.Fun.(*ast.Ident); ok {
340
- if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
341
- hasRef = true
342
- return false
318
+ if ident, ok := n.(*ast.Ident); ok {
319
+ // Check if this identifier refers to a package-level variable
320
+ if _, isVar := varSpecMap[ident.Name]; isVar {
321
+ if !seen[ident.Name] {
322
+ deps = append(deps, ident.Name)
323
+ seen[ident.Name] = true
343
324
  }
344
325
  }
345
- // Check type arguments in generic calls
346
- if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
347
- if c.hasTypeReferences(funcType.Index, typeSpecMap) {
348
- hasRef = true
349
- return false
350
- }
351
- }
352
- case *ast.CompositeLit:
353
- // Check composite literals like Message{...}
354
- if ident, ok := t.Type.(*ast.Ident); ok {
355
- if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
356
- hasRef = true
357
- return false
358
- }
359
- }
360
- case *ast.Ident:
361
- // Check direct type references
362
- if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
363
- hasRef = true
364
- return false
365
- }
366
326
  }
367
- return !hasRef // Stop walking if we found a reference
327
+ return true
368
328
  })
369
329
 
370
- return hasRef
330
+ return deps
371
331
  }
372
332
 
373
333
  // topologicalSort performs a topological sort of the dependency graph
@@ -387,7 +347,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
387
347
  // Sort dependencies for consistent output
388
348
  sortedDeps := make([]string, len(deps))
389
349
  copy(sortedDeps, deps)
390
- sort.Strings(sortedDeps)
350
+ slices.Sort(sortedDeps)
391
351
 
392
352
  for _, dep := range sortedDeps {
393
353
  if _, exists := inDegree[dep]; exists {
@@ -399,7 +359,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
399
359
 
400
360
  // Sort neighbors in graph for consistency
401
361
  for node := range graph {
402
- sort.Strings(graph[node])
362
+ slices.Sort(graph[node])
403
363
  }
404
364
 
405
365
  // Find nodes with no incoming edges and sort them
@@ -409,7 +369,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
409
369
  queue = append(queue, node)
410
370
  }
411
371
  }
412
- sort.Strings(queue) // Sort initial queue for deterministic output
372
+ slices.Sort(queue) // Sort initial queue for deterministic output
413
373
 
414
374
  var result []string
415
375
 
@@ -429,7 +389,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
429
389
  }
430
390
 
431
391
  // Sort new zero-degree nodes and add to queue
432
- sort.Strings(newZeroNodes)
392
+ slices.Sort(newZeroNodes)
433
393
  queue = append(queue, newZeroNodes...)
434
394
  }
435
395
 
@@ -447,7 +407,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
447
407
  remaining = append(remaining, name)
448
408
  }
449
409
  }
450
- sort.Strings(remaining)
410
+ slices.Sort(remaining)
451
411
 
452
412
  return nil, fmt.Errorf("circular dependency detected in type declarations. Remaining types: %v", remaining)
453
413
  }
@@ -662,11 +622,20 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
662
622
  } else {
663
623
  // Multiple return values -> tuple type
664
624
  c.tsw.WriteLiterally("[")
665
- for i, field := range funcType.Results.List {
666
- if i > 0 {
667
- c.tsw.WriteLiterally(", ")
625
+ first := true
626
+ for _, field := range funcType.Results.List {
627
+ // Each field may represent multiple return values (e.g., "a, b int")
628
+ count := len(field.Names)
629
+ if count == 0 {
630
+ count = 1 // Unnamed return value
631
+ }
632
+ for j := 0; j < count; j++ {
633
+ if !first {
634
+ c.tsw.WriteLiterally(", ")
635
+ }
636
+ first = false
637
+ c.WriteTypeExpr(field.Type)
668
638
  }
669
- c.WriteTypeExpr(field.Type)
670
639
  }
671
640
  c.tsw.WriteLiterally("]")
672
641
  }
@@ -12,7 +12,15 @@ func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
12
12
  case *ast.Ident:
13
13
  // Function call (e.g., func())
14
14
  if obj := c.objectOfIdent(fun); obj != nil {
15
- return c.analysis.IsAsyncFunc(obj)
15
+ // Check if this is a known async function
16
+ if c.analysis.IsAsyncFunc(obj) {
17
+ return true
18
+ }
19
+ // Check if this is a variable that returns async values
20
+ // (e.g., indirect := sync.OnceValue(asyncFunc))
21
+ if c.analysis.IsAsyncReturningVar(obj) {
22
+ return true
23
+ }
16
24
  }
17
25
  return false
18
26
 
@@ -42,6 +50,13 @@ func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
42
50
  objOk = true
43
51
  }
44
52
 
53
+ case *ast.CallExpr:
54
+ // Method call on function result: funcCall().method()
55
+ // Get the type of the function call result
56
+ if callType := c.pkg.TypesInfo.TypeOf(x); callType != nil {
57
+ objOk = true
58
+ }
59
+
45
60
  default:
46
61
  objOk = false
47
62
  }
@@ -144,6 +159,16 @@ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
144
159
  c.tsw.WriteLiterally("!")
145
160
  }
146
161
  }
162
+ } else if selectorExpr, isSelectorExpr := expFun.(*ast.SelectorExpr); isSelectorExpr {
163
+ // Check if this is a field access that returns a function type
164
+ // e.g., s.step where step is a function-typed field
165
+ if selection := c.pkg.TypesInfo.Selections[selectorExpr]; selection != nil {
166
+ // This is a field or method selection
167
+ if selection.Kind() == types.FieldVal {
168
+ // It's a field - function-typed fields may be nil
169
+ c.tsw.WriteLiterally("!")
170
+ }
171
+ }
147
172
  } else if _, isNamed := funType.(*types.Named); isNamed {
148
173
  c.tsw.WriteLiterally("!")
149
174
  }