goscript 0.0.11 → 0.0.14

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 (99) hide show
  1. package/LICENSE +2 -1
  2. package/README.md +13 -9
  3. package/builtin/builtin.go +11 -0
  4. package/builtin/builtin.ts +11 -1
  5. package/cmd/goscript/cmd_compile.go +22 -4
  6. package/compiler/compile.go +16 -25
  7. package/compiler/compile_expr.go +80 -10
  8. package/compiler/compile_field.go +22 -1
  9. package/compiler/compile_spec.go +38 -8
  10. package/compiler/compile_stmt.go +36 -29
  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/index.ts +1 -1
  16. package/compiler/output_path.go +1 -1
  17. package/compiler/types/tokens.go +1 -0
  18. package/compiler/writer.go +1 -1
  19. package/dist/builtin/builtin.d.ts +6 -0
  20. package/dist/builtin/builtin.js +10 -1
  21. package/dist/builtin/builtin.js.map +1 -1
  22. package/dist/compiler/index.d.ts +1 -1
  23. package/dist/compliance/tests/async_basic/async_basic.gs.js +1 -1
  24. package/dist/compliance/tests/async_basic/async_basic.gs.js.map +1 -1
  25. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +1 -1
  26. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -1
  27. package/dist/compliance/tests/channel_basic/channel_basic.gs.js +1 -1
  28. package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +1 -1
  29. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +1 -1
  30. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +1 -1
  31. package/dist/compliance/tests/copy_independence/copy_independence.gs.js +1 -1
  32. package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
  33. package/dist/compliance/tests/defer_statement/defer_statement.gs.js +1 -1
  34. package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -1
  35. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +2 -2
  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_range/for_range.gs.js +1 -1
  41. package/dist/compliance/tests/for_range/for_range.gs.js.map +1 -1
  42. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +1 -1
  43. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +1 -1
  44. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
  45. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +1 -0
  46. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +12 -0
  47. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +1 -0
  48. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +2 -2
  49. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -1
  50. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +2 -2
  51. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
  52. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +2 -2
  53. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
  54. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +1 -1
  55. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -1
  56. package/dist/compliance/tests/map_support/map_support.gs.js +1 -1
  57. package/dist/compliance/tests/map_support/map_support.gs.js.map +1 -1
  58. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +1 -1
  59. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +1 -1
  60. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +1 -1
  61. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +1 -1
  62. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +1 -1
  63. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +1 -1
  64. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +1 -1
  65. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +1 -1
  66. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +1 -1
  67. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +1 -1
  68. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +1 -1
  69. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +1 -1
  70. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +1 -1
  71. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +1 -1
  72. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +1 -1
  73. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +1 -1
  74. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +1 -1
  75. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +1 -1
  76. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +2 -1
  77. 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
  78. package/dist/compliance/tests/select_statement/select_statement.gs.js +2 -4
  79. package/dist/compliance/tests/select_statement/select_statement.gs.js.map +1 -1
  80. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +1 -1
  81. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
  82. package/dist/compliance/tests/slices/slices.gs.js +1 -1
  83. package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
  84. package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +1 -0
  85. package/dist/compliance/tests/string_conversion/string_conversion.gs.js +41 -0
  86. package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +1 -0
  87. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +1 -1
  88. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -1
  89. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +1 -1
  90. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
  91. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +1 -0
  92. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +26 -0
  93. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +1 -0
  94. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +1 -1
  95. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +1 -1
  96. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +1 -1
  97. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
  98. package/go.mod +1 -1
  99. package/package.json +18 -3
package/LICENSE CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019-2024 Christian Stewart
3
+ Copyright (c) 2024-2025 Aperture Robotics, LLC.
4
+ Copyright (c) 2019-2025 Christian Stewart
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  [![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]
4
4
 
5
- [GoDoc]: https://godoc.org/github.com/paralin/goscript
6
- [GoDoc Widget]: https://godoc.org/github.com/paralin/goscript?status.svg
7
- [Go Report Card Widget]: https://goreportcard.com/badge/github.com/paralin/goscript
8
- [Go Report Card]: https://goreportcard.com/report/github.com/paralin/goscript
5
+ [GoDoc]: https://godoc.org/github.com/aperturerobotics/goscript
6
+ [GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/goscript?status.svg
7
+ [Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/goscript
8
+ [Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/goscript
9
9
 
10
10
  ## Introduction
11
11
 
@@ -15,6 +15,10 @@ It works on translating Go to TypeScript on the AST level.
15
15
 
16
16
  For detailed information on the compiler's design, refer to the [design document](./design/DESIGN.md).
17
17
 
18
+ > Right now goscript looks pretty cool if you problem is "I want this self-sufficient algorithm be available in Go and JS runtimes". gopherjs's ambition, however, has always been "any valid Go program can run in a browser". There is a lot that goes on in gopherjs that is necessary for supporting the standard library, which goes beyond cross-language translation.
19
+ >
20
+ > — [nevkontakte](https://gophers.slack.com/archives/C039C0R2T/p1745870396945719), developer of [GopherJS](https://github.com/gopherjs/gopherjs)
21
+
18
22
  GoScript intends to compile a subset of the Go language to TypeScript in a
19
23
  non-spec-compliant way. It is intended only for bringing over high-level logic
20
24
  from Go to TypeScript, so not all programs will work correctly:
@@ -63,7 +67,7 @@ import (
63
67
  "context"
64
68
  "log"
65
69
 
66
- "github.com/paralin/goscript/compiler"
70
+ "github.com/aperturerobotics/goscript/compiler"
67
71
  "github.com/sirupsen/logrus"
68
72
  )
69
73
 
@@ -306,7 +310,7 @@ func main() {
306
310
  Generated with `goscript compile .`:
307
311
 
308
312
  ```typescript
309
- import * as goscript from "@go/builtin"
313
+ import * as goscript from "@goscript/builtin"
310
314
 
311
315
  class MyStruct {
312
316
  // MyInt is a public integer field, initialized to zero.
@@ -460,10 +464,10 @@ export async function main(): Promise<void> {
460
464
 
461
465
  Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
462
466
 
463
- All Go import paths are prefixed with `@go/` and can be imported in TypeScript:
467
+ All Go import paths are prefixed with `@goscript/` and can be imported in TypeScript:
464
468
 
465
469
  ```typescript
466
- import { MyAsyncFunction, MyStruct } from '@go/github.com/myorg/mypackage';
470
+ import { MyAsyncFunction, MyStruct } from '@goscript/github.com/myorg/mypackage';
467
471
 
468
472
  // Example of importing and using a compiled Go async function
469
473
  async function runGoCode() {
@@ -480,4 +484,4 @@ async function runGoCode() {
480
484
 
481
485
  ## License
482
486
 
483
- MIT
487
+ MIT
@@ -0,0 +1,11 @@
1
+ package builtin
2
+
3
+ import (
4
+ _ "embed"
5
+ )
6
+
7
+ // BuiltinTs contains the contents of the builtin.ts file which provides
8
+ // runtime support for GoScript compiled code.
9
+ //
10
+ //go:embed builtin.ts
11
+ var BuiltinTs string
@@ -90,6 +90,15 @@ export const stringToRunes = (str: string): number[] => {
90
90
  return Array.from(str).map((c) => c.codePointAt(0) || 0)
91
91
  }
92
92
 
93
+ /**
94
+ * Converts an array of Unicode code points (runes) to a string.
95
+ * @param runes The input array of numbers representing Unicode code points.
96
+ * @returns The resulting string.
97
+ */
98
+ export const runesToString = (runes: number[]): string => {
99
+ return String.fromCharCode(...runes);
100
+ };
101
+
93
102
  /**
94
103
  * Gets a value from a map, with a default value if the key doesn't exist.
95
104
  * @param map The map to get from.
@@ -262,7 +271,7 @@ export function typeAssert<T>(
262
271
  }
263
272
  break
264
273
 
265
- case TypeKind.Basic:
274
+ case TypeKind.Basic: {
266
275
  // For basic types, check if the value matches the expected JavaScript type
267
276
  // This is a simple check for common basic types
268
277
  const basicType = typeof value
@@ -274,6 +283,7 @@ export function typeAssert<T>(
274
283
  return { value: value as T, ok: true }
275
284
  }
276
285
  break
286
+ }
277
287
 
278
288
  case TypeKind.Pointer:
279
289
  // For pointers, check if value is not null or undefined
@@ -2,17 +2,22 @@ package main
2
2
 
3
3
  import (
4
4
  "context"
5
+ "slices"
5
6
 
6
7
  "github.com/aperturerobotics/cli"
7
- "github.com/paralin/goscript/compiler"
8
+ "github.com/aperturerobotics/goscript/compiler"
8
9
  "github.com/pkg/errors"
9
10
  "github.com/sirupsen/logrus"
11
+
12
+ // _ ensure we include the builtin package
13
+ _ "github.com/aperturerobotics/goscript/builtin"
10
14
  )
11
15
 
12
16
  var (
13
- cliCompiler *compiler.Compiler
14
- cliCompilerConfig compiler.Config
15
- cliCompilerPkg cli.StringSlice
17
+ cliCompiler *compiler.Compiler
18
+ cliCompilerConfig compiler.Config
19
+ cliCompilerPkg cli.StringSlice
20
+ cliCompilerBuildFlags cli.StringSlice
16
21
  )
17
22
 
18
23
  // CompileCommands are commands related to compiling code.
@@ -33,6 +38,7 @@ var CompileCommands = []*cli.Command{{
33
38
  Name: "package",
34
39
  Usage: "the package(s) to compile",
35
40
  Aliases: []string{"p", "packages"},
41
+ EnvVars: []string{"GOSCRIPT_PACKAGES"},
36
42
  Destination: &cliCompilerPkg,
37
43
  },
38
44
  &cli.StringFlag{
@@ -40,12 +46,21 @@ var CompileCommands = []*cli.Command{{
40
46
  Usage: "the output typescript path to use",
41
47
  Destination: &cliCompilerConfig.OutputPathRoot,
42
48
  Value: "./output",
49
+ EnvVars: []string{"GOSCRIPT_OUTPUT"},
43
50
  },
44
51
  &cli.StringFlag{
45
52
  Name: "dir",
46
53
  Usage: "the working directory to use for the compiler (default: current directory)",
47
54
  Destination: &cliCompilerConfig.Dir,
48
55
  Value: "",
56
+ EnvVars: []string{"GOSCRIPT_DIR"},
57
+ },
58
+ &cli.StringSliceFlag{
59
+ Name: "build-flags",
60
+ Aliases: []string{"b", "buildflags", "build-flag", "buildflag"},
61
+ Usage: "Go build flags (tags) to use during analysis",
62
+ Destination: &cliCompilerBuildFlags,
63
+ EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
49
64
  },
50
65
  },
51
66
  }}
@@ -57,5 +72,8 @@ func compilePackage(c *cli.Context) error {
57
72
  return errors.New("package(s) must be specified")
58
73
  }
59
74
 
75
+ // build flags
76
+ cliCompilerConfig.BuildFlags = slices.Clone(cliCompilerBuildFlags.Value())
77
+
60
78
  return cliCompiler.CompilePackages(context.Background(), pkgs...)
61
79
  }
@@ -4,9 +4,9 @@ import (
4
4
  "fmt"
5
5
  "go/ast"
6
6
  "go/token"
7
- "go/types"
7
+ gtypes "go/types"
8
8
 
9
- gstypes "github.com/paralin/goscript/compiler/types"
9
+ gstypes "github.com/aperturerobotics/goscript/compiler/types"
10
10
  "golang.org/x/tools/go/packages"
11
11
  )
12
12
 
@@ -19,14 +19,12 @@ type GoToTSCompiler struct {
19
19
  asyncFuncs map[string]bool // Track which functions are async
20
20
  nextBlockNeedsDefer bool // Track if the next block should have a "using" statement
21
21
  inAsyncFunction bool // Track if we're inside an async function
22
-
23
- tempVarCounter int // Counter for generating unique temporary variable names
24
22
  }
25
23
 
26
24
  // WriteGoType writes a Go type as a TypeScript type.
27
- func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
25
+ func (c *GoToTSCompiler) WriteGoType(typ gtypes.Type) {
28
26
  switch t := typ.(type) {
29
- case *types.Basic:
27
+ case *gtypes.Basic:
30
28
  // Handle basic types (int, string, etc.)
31
29
  name := t.Name()
32
30
  if tsType, ok := gstypes.GoBuiltinToTypescript(name); ok {
@@ -34,31 +32,31 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
34
32
  } else {
35
33
  c.tsw.WriteLiterally(name)
36
34
  }
37
- case *types.Named:
35
+ case *gtypes.Named:
38
36
  // Handle named types (custom types)
39
37
  c.tsw.WriteLiterally(t.Obj().Name())
40
- case *types.Pointer:
38
+ case *gtypes.Pointer:
41
39
  // Handle pointer types (*T becomes T | null)
42
40
  c.WriteGoType(t.Elem())
43
41
  c.tsw.WriteLiterally(" | null")
44
- case *types.Slice, *types.Array:
42
+ case *gtypes.Slice, *gtypes.Array:
45
43
  // Handle array/slice types ([]T or [N]T becomes T[])
46
- var elemType types.Type
47
- if slice, ok := t.(*types.Slice); ok {
44
+ var elemType gtypes.Type
45
+ if slice, ok := t.(*gtypes.Slice); ok {
48
46
  elemType = slice.Elem()
49
- } else if array, ok := t.(*types.Array); ok {
47
+ } else if array, ok := t.(*gtypes.Array); ok {
50
48
  elemType = array.Elem()
51
49
  }
52
50
  c.WriteGoType(elemType)
53
51
  c.tsw.WriteLiterally("[]")
54
- case *types.Map:
52
+ case *gtypes.Map:
55
53
  // Handle map types (map[K]V becomes Map<K, V>)
56
54
  c.tsw.WriteLiterally("Map<")
57
55
  c.WriteGoType(t.Key())
58
56
  c.tsw.WriteLiterally(", ")
59
57
  c.WriteGoType(t.Elem())
60
58
  c.tsw.WriteLiterally(">")
61
- case *types.Chan:
59
+ case *gtypes.Chan:
62
60
  // Handle channel types (chan T becomes goscript.Channel<T>)
63
61
  c.tsw.WriteLiterally("goscript.Channel<")
64
62
  c.WriteGoType(t.Elem())
@@ -93,16 +91,9 @@ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, cmap ast.Commen
93
91
  asyncFuncs: make(map[string]bool),
94
92
  nextBlockNeedsDefer: false,
95
93
  inAsyncFunction: false,
96
- tempVarCounter: 0, // Initialize counter
97
94
  }
98
95
  }
99
96
 
100
- // newTempVar generates a unique temporary variable name.
101
- func (c *GoToTSCompiler) newTempVar() string {
102
- c.tempVarCounter++
103
- return fmt.Sprintf("_tempVar%d", c.tempVarCounter)
104
- }
105
-
106
97
  // isAsyncFunc determines if a function is asynchronous
107
98
  // A function is async if it contains channel operations or calls other async functions
108
99
  func (c *GoToTSCompiler) isAsyncFunc(name string) bool {
@@ -145,7 +136,7 @@ func (c *GoToTSCompiler) containsAsyncOperations(node ast.Node) bool {
145
136
  // Handles array types recursively.
146
137
  func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
147
138
  switch t := typ.(type) {
148
- case *types.Array:
139
+ case *gtypes.Array:
149
140
  c.tsw.WriteLiterally("[")
150
141
  for i := 0; i < int(t.Len()); i++ {
151
142
  if i > 0 {
@@ -170,11 +161,11 @@ func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
170
161
  c.WriteZeroValueForType(t.Elt)
171
162
  }
172
163
  c.tsw.WriteLiterally("]")
173
- case *types.Basic:
164
+ case *gtypes.Basic:
174
165
  switch t.Kind() {
175
- case types.Bool:
166
+ case gtypes.Bool:
176
167
  c.tsw.WriteLiterally("false")
177
- case types.String:
168
+ case gtypes.String:
178
169
  c.tsw.WriteLiterally(`""`)
179
170
  default:
180
171
  c.tsw.WriteLiterally("0")
@@ -9,7 +9,7 @@ import (
9
9
  "strconv"
10
10
  "strings"
11
11
 
12
- gstypes "github.com/paralin/goscript/compiler/types"
12
+ gstypes "github.com/aperturerobotics/goscript/compiler/types"
13
13
  )
14
14
 
15
15
  // WriteTypeExpr writes an expression that represents a type.
@@ -315,13 +315,14 @@ func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
315
315
 
316
316
  // If there are embedded interfaces, write the extends clause
317
317
  if len(embeddedInterfaces) > 0 {
318
- c.tsw.WriteLiterally(" extends ")
318
+ c.tsw.WriteLiterally("extends ")
319
319
  c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
320
+ c.tsw.WriteLiterally(" ")
320
321
  }
321
322
 
322
323
  // Write the interface body on the same line
323
- c.tsw.WriteLiterally("{") // Removed leading space
324
- c.tsw.WriteLine("") // Newline after opening brace
324
+ c.tsw.WriteLiterally("{")
325
+ c.tsw.WriteLine("") // Newline after opening brace
325
326
  c.tsw.Indent(1)
326
327
 
327
328
  // Write named methods
@@ -377,6 +378,28 @@ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
377
378
  func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
378
379
  expFun := exp.Fun
379
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
+
380
403
  if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
381
404
  switch funIdent.String() {
382
405
  case "println":
@@ -507,10 +530,18 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
507
530
  // Fallthrough for unhandled make calls (e.g., channels)
508
531
  return errors.New("unhandled make call")
509
532
  case "string":
510
- // Handle string() conversion, specifically for rune to string
533
+ // Handle string() conversion
511
534
  if len(exp.Args) == 1 {
512
- // Check if the argument is a rune (int32) or a call to rune()
513
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()
514
545
  innerCall, isCallExpr := arg.(*ast.CallExpr)
515
546
 
516
547
  if isCallExpr {
@@ -540,6 +571,19 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
540
571
  c.tsw.WriteLiterally(")")
541
572
  return nil // Handled string(int32)
542
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
+ }
543
587
  }
544
588
  }
545
589
  // Return error for other unhandled string conversions
@@ -667,6 +711,17 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
667
711
  return nil
668
712
  }
669
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
+
670
725
  if err := c.WriteValueExpr(exp.X); err != nil {
671
726
  return fmt.Errorf("failed to write binary expression left operand: %w", err)
672
727
  }
@@ -682,6 +737,11 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
682
737
  if err := c.WriteValueExpr(exp.Y); err != nil {
683
738
  return fmt.Errorf("failed to write binary expression right operand: %w", err)
684
739
  }
740
+
741
+ if isBitwise {
742
+ c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
743
+ }
744
+
685
745
  return nil
686
746
  }
687
747
 
@@ -742,6 +802,16 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
742
802
 
743
803
  // Handle array literals
744
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
+
745
815
  c.tsw.WriteLiterally("[")
746
816
  // Use type info to get array length and element type
747
817
  var arrayLen int
@@ -829,7 +899,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
829
899
  // Typed literal, likely a struct: new Type({...})
830
900
  c.tsw.WriteLiterally("new ")
831
901
  c.WriteTypeExpr(exp.Type)
832
- c.tsw.WriteLiterally("({ ")
902
+ c.tsw.WriteLiterally("({")
833
903
  for i, elm := range exp.Elts {
834
904
  if i != 0 {
835
905
  c.tsw.WriteLiterally(", ")
@@ -838,7 +908,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
838
908
  return fmt.Errorf("failed to write struct literal field: %w", err)
839
909
  }
840
910
  }
841
- c.tsw.WriteLiterally(" })")
911
+ c.tsw.WriteLiterally("})")
842
912
  return nil
843
913
  }
844
914
  }
@@ -935,7 +1005,7 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
935
1005
  }
936
1006
 
937
1007
  c.tsw.WriteLiterally(" => ")
938
-
1008
+
939
1009
  // Save previous async state and set current state based on isAsync
940
1010
  previousAsyncState := c.inAsyncFunction
941
1011
  c.inAsyncFunction = isAsync
@@ -945,7 +1015,7 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
945
1015
  c.inAsyncFunction = previousAsyncState // Restore state before returning error
946
1016
  return fmt.Errorf("failed to write function literal body: %w", err)
947
1017
  }
948
-
1018
+
949
1019
  // Restore previous async state
950
1020
  c.inAsyncFunction = previousAsyncState
951
1021
  return nil
@@ -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
  )
@@ -147,15 +148,19 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
147
148
  if err := c.WriteValueExpr(a.Name); err != nil { // Class name is a value identifier
148
149
  return err
149
150
  }
151
+ c.tsw.WriteLiterally(" ")
152
+
150
153
  if embeddedExpr != nil {
151
154
  // For pointer embedding (*T) unwrap the star so we extend T, not (T|null)
152
155
  if star, ok := embeddedExpr.(*ast.StarExpr); ok {
153
156
  embeddedExpr = star.X
154
157
  }
155
- c.tsw.WriteLiterally(" extends ")
158
+ c.tsw.WriteLiterally("extends ")
156
159
  c.WriteTypeExpr(embeddedExpr) // Embedded type name
160
+ c.tsw.WriteLiterally(" ")
157
161
  }
158
- c.tsw.WriteLine(" {")
162
+
163
+ c.tsw.WriteLine("{")
159
164
  c.tsw.Indent(1)
160
165
 
161
166
  // className is the name of the class type
@@ -282,15 +287,15 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
282
287
 
283
288
  // Determine if method is async by checking for async operations in the body
284
289
  isAsync := c.containsAsyncOperations(decl.Body)
285
-
290
+
286
291
  // Methods are typically public in the TS output
287
292
  c.tsw.WriteLiterally("public ")
288
-
293
+
289
294
  // Add async modifier if needed
290
295
  if isAsync {
291
296
  c.tsw.WriteLiterally("async ")
292
297
  }
293
-
298
+
294
299
  // Keep original Go casing for method names
295
300
  if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
296
301
  return err
@@ -342,7 +347,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
342
347
 
343
348
  // Check if function body has defer statements
344
349
  c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
345
-
350
+
346
351
  // Save previous async state and set current state based on isAsync
347
352
  previousAsyncState := c.inAsyncFunction
348
353
  c.inAsyncFunction = isAsync
@@ -374,7 +379,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
374
379
  }
375
380
  c.tsw.Indent(-1)
376
381
  c.tsw.WriteLine("}")
377
-
382
+
378
383
  // Restore previous async state
379
384
  c.inAsyncFunction = previousAsyncState
380
385
  return nil
@@ -385,7 +390,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
385
390
  c.inAsyncFunction = previousAsyncState // Restore state before returning error
386
391
  return fmt.Errorf("failed to write function body: %w", err)
387
392
  }
388
-
393
+
389
394
  // Restore previous async state
390
395
  c.inAsyncFunction = previousAsyncState
391
396
  return nil
@@ -403,13 +408,30 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
403
408
  if len(a.Names) == 1 {
404
409
  name := a.Names[0]
405
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
+
406
420
  if a.Type != nil {
407
421
  c.tsw.WriteLiterally(": ")
408
422
  c.WriteTypeExpr(a.Type) // Variable type annotation
409
423
 
424
+ // Append "| null" for interface types
425
+ if isInterface {
426
+ c.tsw.WriteLiterally(" | null")
427
+ }
428
+
410
429
  // Check if it's an array type declaration without an initial value
411
430
  if _, isArrayType := a.Type.(*ast.ArrayType); isArrayType && len(a.Values) == 0 {
412
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")
413
435
  }
414
436
  }
415
437
  if len(a.Values) > 0 {
@@ -473,6 +495,14 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
473
495
 
474
496
  // WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
475
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
+
476
506
  if len(field.Names) == 0 {
477
507
  // Should not happen for named methods in an interface, but handle defensively
478
508
  return fmt.Errorf("interface method field has no name")