goscript 0.0.10 → 0.0.13

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 (103) hide show
  1. package/README.md +3 -3
  2. package/builtin/builtin.go +11 -0
  3. package/builtin/builtin.ts +170 -65
  4. package/cmd/goscript/cmd_compile.go +28 -8
  5. package/compiler/compile.go +42 -34
  6. package/compiler/compile_decls.go +12 -0
  7. package/compiler/compile_expr.go +135 -8
  8. package/compiler/compile_field.go +22 -1
  9. package/compiler/compile_spec.go +141 -4
  10. package/compiler/compile_stmt.go +124 -43
  11. package/compiler/compiler.go +1 -0
  12. package/compiler/config.go +2 -0
  13. package/compiler/file_compiler.go +1 -1
  14. package/compiler/index.test.ts +1 -1
  15. package/compiler/output_path.go +1 -1
  16. package/compiler/types/tokens.go +1 -0
  17. package/compiler/writer.go +1 -1
  18. package/dist/builtin/builtin.d.ts +63 -1
  19. package/dist/builtin/builtin.js +81 -0
  20. package/dist/builtin/builtin.js.map +1 -1
  21. package/dist/compliance/tests/async_basic/async_basic.gs.js +1 -1
  22. package/dist/compliance/tests/async_basic/async_basic.gs.js.map +1 -1
  23. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +1 -0
  24. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +82 -0
  25. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -0
  26. package/dist/compliance/tests/channel_basic/channel_basic.gs.js +1 -1
  27. package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +1 -1
  28. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +1 -1
  29. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +1 -1
  30. package/dist/compliance/tests/copy_independence/copy_independence.gs.js +1 -1
  31. package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
  32. package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +1 -0
  33. package/dist/compliance/tests/defer_statement/defer_statement.gs.js +75 -0
  34. package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -0
  35. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +3 -3
  36. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -1
  37. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +1 -0
  38. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +29 -0
  39. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +1 -0
  40. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +1 -0
  41. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +14 -0
  42. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +1 -0
  43. package/dist/compliance/tests/for_range/for_range.gs.js +1 -1
  44. package/dist/compliance/tests/for_range/for_range.gs.js.map +1 -1
  45. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +1 -1
  46. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +1 -1
  47. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
  48. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +1 -0
  49. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +12 -0
  50. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +1 -0
  51. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +2 -2
  52. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -1
  53. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +3 -3
  54. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
  55. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +13 -3
  56. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
  57. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +1 -0
  58. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +51 -0
  59. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -0
  60. package/dist/compliance/tests/map_support/map_support.gs.js +1 -1
  61. package/dist/compliance/tests/map_support/map_support.gs.js.map +1 -1
  62. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +1 -1
  63. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +1 -1
  64. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +1 -1
  65. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +1 -1
  66. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +1 -1
  67. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +1 -1
  68. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +1 -1
  69. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +1 -1
  70. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +1 -1
  71. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +1 -1
  72. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +1 -1
  73. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +1 -1
  74. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +1 -1
  75. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +1 -1
  76. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +1 -1
  77. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +1 -1
  78. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +1 -1
  79. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +1 -1
  80. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +2 -1
  81. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +1 -1
  82. package/dist/compliance/tests/select_statement/select_statement.gs.js +2 -4
  83. package/dist/compliance/tests/select_statement/select_statement.gs.js.map +1 -1
  84. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +1 -1
  85. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
  86. package/dist/compliance/tests/slices/slices.gs.js +281 -8
  87. package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
  88. package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +1 -0
  89. package/dist/compliance/tests/string_conversion/string_conversion.gs.js +41 -0
  90. package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +1 -0
  91. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +1 -0
  92. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +48 -0
  93. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -0
  94. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +1 -1
  95. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
  96. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +1 -0
  97. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +26 -0
  98. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +1 -0
  99. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +1 -1
  100. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +1 -1
  101. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +1 -1
  102. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
  103. package/package.json +4 -3
@@ -108,6 +108,43 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
108
108
  }
109
109
  c.tsw.WriteLiterally("]")
110
110
  return nil
111
+ case *ast.SliceExpr:
112
+ // Translate Go slice expression to goscript.slice(x, low, high, max)
113
+ c.tsw.WriteLiterally("goscript.slice(")
114
+ if err := c.WriteValueExpr(exp.X); err != nil {
115
+ return err
116
+ }
117
+ // low argument
118
+ c.tsw.WriteLiterally(", ")
119
+ if exp.Low != nil {
120
+ if err := c.WriteValueExpr(exp.Low); err != nil {
121
+ return err
122
+ }
123
+ } else {
124
+ c.tsw.WriteLiterally("undefined")
125
+ }
126
+ // high argument
127
+ c.tsw.WriteLiterally(", ")
128
+ if exp.High != nil {
129
+ if err := c.WriteValueExpr(exp.High); err != nil {
130
+ return err
131
+ }
132
+ } else {
133
+ c.tsw.WriteLiterally("undefined")
134
+ }
135
+ // max argument (only for full slice expressions)
136
+ if exp.Slice3 {
137
+ c.tsw.WriteLiterally(", ")
138
+ if exp.Max != nil {
139
+ if err := c.WriteValueExpr(exp.Max); err != nil {
140
+ return err
141
+ }
142
+ } else {
143
+ c.tsw.WriteLiterally("undefined")
144
+ }
145
+ }
146
+ c.tsw.WriteLiterally(")")
147
+ return nil
111
148
  case *ast.ParenExpr:
112
149
  // Translate (X) to (X)
113
150
  c.tsw.WriteLiterally("(")
@@ -278,13 +315,14 @@ func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
278
315
 
279
316
  // If there are embedded interfaces, write the extends clause
280
317
  if len(embeddedInterfaces) > 0 {
281
- c.tsw.WriteLiterally(" extends ")
318
+ c.tsw.WriteLiterally("extends ")
282
319
  c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
320
+ c.tsw.WriteLiterally(" ")
283
321
  }
284
322
 
285
323
  // Write the interface body on the same line
286
- c.tsw.WriteLiterally("{") // Removed leading space
287
- c.tsw.WriteLine("") // Newline after opening brace
324
+ c.tsw.WriteLiterally("{")
325
+ c.tsw.WriteLine("") // Newline after opening brace
288
326
  c.tsw.Indent(1)
289
327
 
290
328
  // Write named methods
@@ -340,6 +378,28 @@ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
340
378
  func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
341
379
  expFun := exp.Fun
342
380
 
381
+ // Handle array type conversions like []rune(string)
382
+ if arrayType, isArrayType := expFun.(*ast.ArrayType); isArrayType {
383
+ // Check if it's a []rune type
384
+ if ident, isIdent := arrayType.Elt.(*ast.Ident); isIdent && ident.Name == "rune" {
385
+ // Check if the argument is a string
386
+ if len(exp.Args) == 1 {
387
+ arg := exp.Args[0]
388
+ if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
389
+ if basic, isBasic := tv.Type.Underlying().(*gtypes.Basic); isBasic && basic.Kind() == gtypes.String {
390
+ // Translate []rune(stringValue) to goscript.stringToRunes(stringValue)
391
+ c.tsw.WriteLiterally("goscript.stringToRunes(")
392
+ if err := c.WriteValueExpr(arg); err != nil {
393
+ return fmt.Errorf("failed to write argument for []rune(string) conversion: %w", err)
394
+ }
395
+ c.tsw.WriteLiterally(")")
396
+ return nil // Handled []rune(string)
397
+ }
398
+ }
399
+ }
400
+ }
401
+ }
402
+
343
403
  if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
344
404
  switch funIdent.String() {
345
405
  case "println":
@@ -433,7 +493,20 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
433
493
 
434
494
  // Handle slice creation
435
495
  if _, ok := exp.Args[0].(*ast.ArrayType); ok {
436
- c.tsw.WriteLiterally("goscript.makeSlice(")
496
+ // Get the slice type information
497
+ sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
498
+ if sliceType == nil {
499
+ return errors.New("could not get type information for slice in make call")
500
+ }
501
+ goElemType, ok := sliceType.Underlying().(*gtypes.Slice)
502
+ if !ok {
503
+ return errors.New("expected slice type for make call")
504
+ }
505
+
506
+ c.tsw.WriteLiterally("goscript.makeSlice<")
507
+ c.WriteGoType(goElemType.Elem()) // Write the element type
508
+ c.tsw.WriteLiterally(">(")
509
+
437
510
  if len(exp.Args) >= 2 {
438
511
  if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
439
512
  return err
@@ -457,10 +530,18 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
457
530
  // Fallthrough for unhandled make calls (e.g., channels)
458
531
  return errors.New("unhandled make call")
459
532
  case "string":
460
- // Handle string() conversion, specifically for rune to string
533
+ // Handle string() conversion
461
534
  if len(exp.Args) == 1 {
462
- // Check if the argument is a rune (int32) or a call to rune()
463
535
  arg := exp.Args[0]
536
+
537
+ // Case 1: Argument is a string literal string("...")
538
+ if basicLit, isBasicLit := arg.(*ast.BasicLit); isBasicLit && basicLit.Kind == token.STRING {
539
+ // Translate string("...") to "..." (no-op)
540
+ c.WriteBasicLitValue(basicLit)
541
+ return nil // Handled string literal conversion
542
+ }
543
+
544
+ // Case 2: Argument is a rune (int32) or a call to rune()
464
545
  innerCall, isCallExpr := arg.(*ast.CallExpr)
465
546
 
466
547
  if isCallExpr {
@@ -490,6 +571,19 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
490
571
  c.tsw.WriteLiterally(")")
491
572
  return nil // Handled string(int32)
492
573
  }
574
+
575
+ // Case 3: Argument is a slice of runes string([]rune{...})
576
+ if sliceType, isSlice := tv.Type.Underlying().(*gtypes.Slice); isSlice {
577
+ if basic, isBasic := sliceType.Elem().Underlying().(*gtypes.Basic); isBasic && basic.Kind() == gtypes.Int32 {
578
+ // Translate string([]rune) to goscript.runesToString(...)
579
+ c.tsw.WriteLiterally("goscript.runesToString(")
580
+ if err := c.WriteValueExpr(arg); err != nil {
581
+ return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
582
+ }
583
+ c.tsw.WriteLiterally(")")
584
+ return nil // Handled string([]rune)
585
+ }
586
+ }
493
587
  }
494
588
  }
495
589
  // Return error for other unhandled string conversions
@@ -617,6 +711,17 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
617
711
  return nil
618
712
  }
619
713
 
714
+ // Check if the operator is a bitwise operator
715
+ isBitwise := false
716
+ switch exp.Op {
717
+ case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT:
718
+ isBitwise = true
719
+ }
720
+
721
+ if isBitwise {
722
+ c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
723
+ }
724
+
620
725
  if err := c.WriteValueExpr(exp.X); err != nil {
621
726
  return fmt.Errorf("failed to write binary expression left operand: %w", err)
622
727
  }
@@ -632,6 +737,11 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
632
737
  if err := c.WriteValueExpr(exp.Y); err != nil {
633
738
  return fmt.Errorf("failed to write binary expression right operand: %w", err)
634
739
  }
740
+
741
+ if isBitwise {
742
+ c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
743
+ }
744
+
635
745
  return nil
636
746
  }
637
747
 
@@ -692,6 +802,16 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
692
802
 
693
803
  // Handle array literals
694
804
  if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
805
+ // Special case: empty slice literal
806
+ if len(exp.Elts) == 0 {
807
+ // Generate: ([] as ElementType[])
808
+ c.tsw.WriteLiterally("([] as ")
809
+ // Write the element type using the existing function
810
+ c.WriteTypeExpr(arrType.Elt)
811
+ c.tsw.WriteLiterally("[])") // Close the type assertion
812
+ return nil // Handled empty slice literal
813
+ }
814
+
695
815
  c.tsw.WriteLiterally("[")
696
816
  // Use type info to get array length and element type
697
817
  var arrayLen int
@@ -779,7 +899,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
779
899
  // Typed literal, likely a struct: new Type({...})
780
900
  c.tsw.WriteLiterally("new ")
781
901
  c.WriteTypeExpr(exp.Type)
782
- c.tsw.WriteLiterally("({ ")
902
+ c.tsw.WriteLiterally("({")
783
903
  for i, elm := range exp.Elts {
784
904
  if i != 0 {
785
905
  c.tsw.WriteLiterally(", ")
@@ -788,7 +908,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
788
908
  return fmt.Errorf("failed to write struct literal field: %w", err)
789
909
  }
790
910
  }
791
- c.tsw.WriteLiterally(" })")
911
+ c.tsw.WriteLiterally("})")
792
912
  return nil
793
913
  }
794
914
  }
@@ -886,10 +1006,17 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
886
1006
 
887
1007
  c.tsw.WriteLiterally(" => ")
888
1008
 
1009
+ // Save previous async state and set current state based on isAsync
1010
+ previousAsyncState := c.inAsyncFunction
1011
+ c.inAsyncFunction = isAsync
1012
+
889
1013
  // Write function body
890
1014
  if err := c.WriteStmt(exp.Body); err != nil {
1015
+ c.inAsyncFunction = previousAsyncState // Restore state before returning error
891
1016
  return fmt.Errorf("failed to write function literal body: %w", err)
892
1017
  }
893
1018
 
1019
+ // Restore previous async state
1020
+ c.inAsyncFunction = previousAsyncState
894
1021
  return nil
895
1022
  }
@@ -3,6 +3,7 @@ package compiler
3
3
  import (
4
4
  "fmt"
5
5
  "go/ast"
6
+ gtypes "go/types"
6
7
  )
7
8
 
8
9
  // WriteFieldList writes a field list.
@@ -50,6 +51,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
50
51
  c.WriteDoc(field.Comment)
51
52
  }
52
53
  }
54
+
53
55
  for _, name := range field.Names {
54
56
  isExported := name.IsExported()
55
57
 
@@ -68,14 +70,33 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
68
70
  c.tsw.WriteLiterally(name.Name)
69
71
  }
70
72
 
73
+ // Check if field type is an interface
74
+ isInterface := false
75
+ if c.pkg != nil && c.pkg.TypesInfo != nil {
76
+ if tv, ok := c.pkg.TypesInfo.Types[field.Type]; ok && tv.Type != nil {
77
+ _, isInterface = tv.Type.Underlying().(*gtypes.Interface)
78
+ }
79
+ }
80
+
71
81
  // write type for struct fields (not arguments)
72
82
  c.tsw.WriteLiterally(": ")
73
83
  c.WriteTypeExpr(field.Type) // Use WriteTypeExpr for field type
74
84
 
85
+ // Append "| null" for interface fields
86
+ if !isArguments && isInterface {
87
+ c.tsw.WriteLiterally(" | null")
88
+ }
89
+
75
90
  if !isArguments {
76
91
  // write initializer with zero value for struct fields
77
92
  c.tsw.WriteLiterally(" = ")
78
- c.WriteZeroValueForType(field.Type)
93
+
94
+ // Special initialization for interface fields
95
+ if isInterface {
96
+ c.tsw.WriteLiterally("null")
97
+ } else {
98
+ c.WriteZeroValueForType(field.Type)
99
+ }
79
100
 
80
101
  // write tag comment if any for struct fields
81
102
  if field.Tag != nil {
@@ -4,6 +4,7 @@ import (
4
4
  "fmt"
5
5
  "go/ast"
6
6
  "go/types"
7
+ gtypes "go/types"
7
8
  "sort"
8
9
  "strings"
9
10
  )
@@ -57,6 +58,20 @@ func (c *GoToTSCompiler) collectMethodNames(structName string) string {
57
58
  return strings.Join(methodNames, ", ")
58
59
  }
59
60
 
61
+ // getTypeExprName returns a string representation of a type expression.
62
+ func (c *GoToTSCompiler) getTypeExprName(expr ast.Expr) string {
63
+ switch t := expr.(type) {
64
+ case *ast.Ident:
65
+ return t.Name
66
+ case *ast.SelectorExpr:
67
+ return fmt.Sprintf("%s.%s", c.getTypeExprName(t.X), t.Sel.Name)
68
+ case *ast.StarExpr:
69
+ return c.getTypeExprName(t.X) // Unwrap pointer type
70
+ default:
71
+ return "embedded" // Fallback for complex type expressions
72
+ }
73
+ }
74
+
60
75
  // collectInterfaceMethods returns a comma-separated string of method names for an interface
61
76
  func (c *GoToTSCompiler) collectInterfaceMethods(interfaceType *ast.InterfaceType) string {
62
77
  // Use a map to ensure uniqueness of method names
@@ -120,12 +135,32 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
120
135
 
121
136
  switch t := a.Type.(type) {
122
137
  case *ast.StructType:
123
- // write the class definition
138
+ // Detect embedded struct (anonymous field) to support Go struct embedding.
139
+ var embeddedExpr ast.Expr
140
+ for _, f := range t.Fields.List {
141
+ if len(f.Names) == 0 { // anonymous field ⇒ embedded type
142
+ embeddedExpr = f.Type
143
+ break // only the first embedded struct is supported for now
144
+ }
145
+ }
146
+ // Write the class header, adding "extends Embedded" when an embedded struct is present.
124
147
  c.tsw.WriteLiterally("class ")
125
148
  if err := c.WriteValueExpr(a.Name); err != nil { // Class name is a value identifier
126
149
  return err
127
150
  }
128
- c.tsw.WriteLine(" {")
151
+ c.tsw.WriteLiterally(" ")
152
+
153
+ if embeddedExpr != nil {
154
+ // For pointer embedding (*T) unwrap the star so we extend T, not (T|null)
155
+ if star, ok := embeddedExpr.(*ast.StarExpr); ok {
156
+ embeddedExpr = star.X
157
+ }
158
+ c.tsw.WriteLiterally("extends ")
159
+ c.WriteTypeExpr(embeddedExpr) // Embedded type name
160
+ c.tsw.WriteLiterally(" ")
161
+ }
162
+
163
+ c.tsw.WriteLine("{")
129
164
  c.tsw.Indent(1)
130
165
 
131
166
  // className is the name of the class type
@@ -164,7 +199,38 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
164
199
 
165
200
  // constructor and clone using Object.assign for compactness
166
201
  c.tsw.WriteLine("")
167
- c.tsw.WriteLinef("constructor(init?: Partial<%s>) { if (init) Object.assign(this, init as any); }", className)
202
+ if embeddedExpr != nil {
203
+ // Determine the embedded type's identifier (strip any package prefix)
204
+ embeddedTypeNameFull := c.getTypeExprName(embeddedExpr)
205
+ propName := embeddedTypeNameFull
206
+ if idx := strings.LastIndex(propName, "."); idx != -1 {
207
+ propName = propName[idx+1:] // keep only the last segment for the property key
208
+ }
209
+
210
+ // The init parameter allows either flattened fields or a nested {Embedded: {...}} object
211
+ c.tsw.WriteLinef(
212
+ "constructor(init?: Partial<%s> & { %s?: Partial<%s> }) {",
213
+ className, propName, propName,
214
+ )
215
+ c.tsw.Indent(1)
216
+
217
+ // Pass either the nested embedded object or the full init directly to super()
218
+ c.tsw.WriteLinef("super(init?.%s || init);", propName)
219
+
220
+ // Copy fields that belong only to this derived class
221
+ c.tsw.WriteLine("if (init) {")
222
+ c.tsw.Indent(1)
223
+ c.tsw.WriteLinef("const { %s, ...rest } = init as any;", propName)
224
+ c.tsw.WriteLine("Object.assign(this, rest);")
225
+ c.tsw.Indent(-1)
226
+ c.tsw.WriteLine("}")
227
+
228
+ c.tsw.Indent(-1)
229
+ c.tsw.WriteLine("}")
230
+ } else {
231
+ // For a base class without embedding, use simple constructor
232
+ c.tsw.WriteLinef("constructor(init?: Partial<%s>) { if (init) Object.assign(this, init as any); }", className)
233
+ }
168
234
  c.tsw.WriteLinef("public clone(): %s { return Object.assign(Object.create(%s.prototype) as %s, this); }", className, className, className)
169
235
 
170
236
  // Add code to register the type with the runtime system
@@ -219,8 +285,17 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
219
285
  c.WriteDoc(decl.Doc)
220
286
  }
221
287
 
288
+ // Determine if method is async by checking for async operations in the body
289
+ isAsync := c.containsAsyncOperations(decl.Body)
290
+
222
291
  // Methods are typically public in the TS output
223
292
  c.tsw.WriteLiterally("public ")
293
+
294
+ // Add async modifier if needed
295
+ if isAsync {
296
+ c.tsw.WriteLiterally("async ")
297
+ }
298
+
224
299
  // Keep original Go casing for method names
225
300
  if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
226
301
  return err
@@ -238,6 +313,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
238
313
  // Handle return type
239
314
  if funcType.Results != nil && len(funcType.Results.List) > 0 {
240
315
  c.tsw.WriteLiterally(": ")
316
+ if isAsync {
317
+ c.tsw.WriteLiterally("Promise<")
318
+ }
241
319
  if len(funcType.Results.List) == 1 {
242
320
  // Single return value
243
321
  resultType := funcType.Results.List[0].Type
@@ -253,12 +331,27 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
253
331
  }
254
332
  c.tsw.WriteLiterally("]")
255
333
  }
334
+ if isAsync {
335
+ c.tsw.WriteLiterally(">")
336
+ }
256
337
  } else {
257
338
  // No return value -> void
258
- c.tsw.WriteLiterally(": void")
339
+ if isAsync {
340
+ c.tsw.WriteLiterally(": Promise<void>")
341
+ } else {
342
+ c.tsw.WriteLiterally(": void")
343
+ }
259
344
  }
260
345
 
261
346
  c.tsw.WriteLiterally(" ")
347
+
348
+ // Check if function body has defer statements
349
+ c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
350
+
351
+ // Save previous async state and set current state based on isAsync
352
+ previousAsyncState := c.inAsyncFunction
353
+ c.inAsyncFunction = isAsync
354
+
262
355
  // Bind receiver name to this
263
356
  if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
264
357
  recvName := recvField.Names[0].Name
@@ -266,21 +359,40 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
266
359
  c.tsw.WriteLine("{")
267
360
  c.tsw.Indent(1)
268
361
  c.tsw.WriteLinef("const %s = this", recvName)
362
+
363
+ // Add using statement if needed
364
+ if c.nextBlockNeedsDefer {
365
+ if c.inAsyncFunction {
366
+ c.tsw.WriteLine("await using __defer = new goscript.AsyncDisposableStack();")
367
+ } else {
368
+ c.tsw.WriteLine("using cleanup = new goscript.DisposableStack();")
369
+ }
370
+ c.nextBlockNeedsDefer = false
371
+ }
372
+
269
373
  // write method body without outer braces
270
374
  for _, stmt := range decl.Body.List {
271
375
  if err := c.WriteStmt(stmt); err != nil {
376
+ c.inAsyncFunction = previousAsyncState // Restore state before returning error
272
377
  return fmt.Errorf("failed to write statement in function body: %w", err)
273
378
  }
274
379
  }
275
380
  c.tsw.Indent(-1)
276
381
  c.tsw.WriteLine("}")
382
+
383
+ // Restore previous async state
384
+ c.inAsyncFunction = previousAsyncState
277
385
  return nil
278
386
  }
279
387
  }
280
388
  // no named receiver, write whole body
281
389
  if err := c.WriteStmt(decl.Body); err != nil {
390
+ c.inAsyncFunction = previousAsyncState // Restore state before returning error
282
391
  return fmt.Errorf("failed to write function body: %w", err)
283
392
  }
393
+
394
+ // Restore previous async state
395
+ c.inAsyncFunction = previousAsyncState
284
396
  return nil
285
397
  }
286
398
 
@@ -296,13 +408,30 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
296
408
  if len(a.Names) == 1 {
297
409
  name := a.Names[0]
298
410
  c.tsw.WriteLiterally(name.Name)
411
+
412
+ // Check if it's an interface type
413
+ isInterface := false
414
+ if c.pkg != nil && c.pkg.TypesInfo != nil && a.Type != nil {
415
+ if tv, ok := c.pkg.TypesInfo.Types[a.Type]; ok && tv.Type != nil {
416
+ _, isInterface = tv.Type.Underlying().(*gtypes.Interface)
417
+ }
418
+ }
419
+
299
420
  if a.Type != nil {
300
421
  c.tsw.WriteLiterally(": ")
301
422
  c.WriteTypeExpr(a.Type) // Variable type annotation
302
423
 
424
+ // Append "| null" for interface types
425
+ if isInterface {
426
+ c.tsw.WriteLiterally(" | null")
427
+ }
428
+
303
429
  // Check if it's an array type declaration without an initial value
304
430
  if _, isArrayType := a.Type.(*ast.ArrayType); isArrayType && len(a.Values) == 0 {
305
431
  c.tsw.WriteLiterally(" = []")
432
+ } else if isInterface && len(a.Values) == 0 {
433
+ // Interface with no initialization value, initialize to null
434
+ c.tsw.WriteLiterally(" = null")
306
435
  }
307
436
  }
308
437
  if len(a.Values) > 0 {
@@ -366,6 +495,14 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
366
495
 
367
496
  // WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
368
497
  func (c *GoToTSCompiler) WriteInterfaceMethodSignature(field *ast.Field) error {
498
+ // Include comments
499
+ if field.Doc != nil {
500
+ c.WriteDoc(field.Doc)
501
+ }
502
+ if field.Comment != nil {
503
+ c.WriteDoc(field.Comment)
504
+ }
505
+
369
506
  if len(field.Names) == 0 {
370
507
  // Should not happen for named methods in an interface, but handle defensively
371
508
  return fmt.Errorf("interface method field has no name")