goscript 0.0.44 → 0.0.47

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 (49) hide show
  1. package/README.md +198 -504
  2. package/compiler/analysis.go +98 -0
  3. package/compiler/compiler.go +20 -70
  4. package/compiler/decl.go +63 -40
  5. package/compiler/expr-call-async.go +101 -0
  6. package/compiler/expr-call-builtins.go +133 -0
  7. package/compiler/expr-call-helpers.go +138 -0
  8. package/compiler/expr-call-make.go +568 -0
  9. package/compiler/expr-call-type-conversion.go +424 -0
  10. package/compiler/expr-call.go +59 -1305
  11. package/compiler/expr.go +126 -4
  12. package/compiler/spec-struct.go +50 -30
  13. package/compiler/spec.go +69 -112
  14. package/compiler/type.go +51 -0
  15. package/dist/gs/builtin/builtin.d.ts +3 -1
  16. package/dist/gs/builtin/builtin.js +6 -0
  17. package/dist/gs/builtin/builtin.js.map +1 -1
  18. package/dist/gs/builtin/errors.js +1 -1
  19. package/dist/gs/builtin/slice.js +2 -1
  20. package/dist/gs/builtin/slice.js.map +1 -1
  21. package/dist/gs/fmt/fmt.js.map +1 -1
  22. package/dist/gs/io/fs/fs.d.ts +12 -1
  23. package/dist/gs/io/fs/fs.js +106 -30
  24. package/dist/gs/io/fs/fs.js.map +1 -1
  25. package/dist/gs/os/types_js.gs.d.ts +1 -3
  26. package/dist/gs/os/types_js.gs.js +2 -1
  27. package/dist/gs/os/types_js.gs.js.map +1 -1
  28. package/dist/gs/os/types_unix.gs.js +2 -2
  29. package/dist/gs/os/types_unix.gs.js.map +1 -1
  30. package/dist/gs/path/filepath/path.d.ts +1 -1
  31. package/dist/gs/path/filepath/path.js +1 -1
  32. package/dist/gs/path/filepath/path.js.map +1 -1
  33. package/dist/gs/reflect/value.js.map +1 -1
  34. package/dist/gs/time/time.js +11 -3
  35. package/dist/gs/time/time.js.map +1 -1
  36. package/gs/builtin/builtin.ts +8 -3
  37. package/gs/builtin/errors.ts +2 -2
  38. package/gs/builtin/slice.ts +24 -8
  39. package/gs/fmt/fmt.ts +12 -3
  40. package/gs/fmt/index.ts +1 -1
  41. package/gs/io/fs/fs.ts +100 -31
  42. package/gs/io/fs/godoc.txt +370 -17
  43. package/gs/os/types_js.gs.ts +2 -2
  44. package/gs/os/types_unix.gs.ts +2 -2
  45. package/gs/path/filepath/path.test.ts +2 -2
  46. package/gs/path/filepath/path.ts +3 -10
  47. package/gs/reflect/value.ts +10 -2
  48. package/gs/time/time.ts +52 -17
  49. package/package.json +1 -1
@@ -61,6 +61,7 @@ type FunctionTypeInfo struct {
61
61
  // FunctionInfo consolidates function-related tracking data.
62
62
  type FunctionInfo struct {
63
63
  IsAsync bool
64
+ ReceiverUsed bool
64
65
  NamedReturns []string
65
66
  }
66
67
 
@@ -195,6 +196,17 @@ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
195
196
  return funcInfo.IsAsync
196
197
  }
197
198
 
199
+ func (a *Analysis) IsReceiverUsed(obj types.Object) bool {
200
+ if obj == nil {
201
+ return false
202
+ }
203
+ funcInfo := a.FunctionData[obj]
204
+ if funcInfo == nil {
205
+ return false
206
+ }
207
+ return funcInfo.ReceiverUsed
208
+ }
209
+
198
210
  // IsFuncLitAsync checks if a function literal is async based on our analysis.
199
211
  func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
200
212
  if funcLit == nil {
@@ -488,6 +500,24 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
488
500
  // Add the receiver variable to the VariableUsage map
489
501
  // to ensure it is properly analyzed for varRefing
490
502
  v.getOrCreateUsageInfo(v.currentReceiver)
503
+
504
+ // Check if receiver is used in method body
505
+ receiverUsed := false
506
+ if n.Body != nil {
507
+ if v.isInterfaceMethod(n) {
508
+ receiverUsed = true
509
+ } else {
510
+ receiverUsed = v.containsReceiverUsage(n.Body, vr)
511
+ }
512
+ }
513
+
514
+ // Update function data with receiver usage info
515
+ if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
516
+ if v.analysis.FunctionData[obj] == nil {
517
+ v.analysis.FunctionData[obj] = &FunctionInfo{}
518
+ }
519
+ v.analysis.FunctionData[obj].ReceiverUsed = receiverUsed
520
+ }
491
521
  }
492
522
  }
493
523
  }
@@ -945,6 +975,74 @@ func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
945
975
  return hasDefer
946
976
  }
947
977
 
978
+ // containsReceiverUsage checks if a method body contains any references to the receiver variable.
979
+ func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.Var) bool {
980
+ if receiver == nil {
981
+ return false
982
+ }
983
+
984
+ var hasReceiverUsage bool
985
+
986
+ ast.Inspect(node, func(n ast.Node) bool {
987
+ if n == nil {
988
+ return true
989
+ }
990
+
991
+ switch expr := n.(type) {
992
+ case *ast.Ident:
993
+ // Check if this identifier refers to the receiver variable
994
+ if obj := v.pkg.TypesInfo.Uses[expr]; obj != nil && obj == receiver {
995
+ hasReceiverUsage = true
996
+ return false
997
+ }
998
+ case *ast.SelectorExpr:
999
+ // Check if selector expression uses the receiver (e.g., m.Field, m.Method())
1000
+ if ident, ok := expr.X.(*ast.Ident); ok {
1001
+ if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil && obj == receiver {
1002
+ hasReceiverUsage = true
1003
+ return false
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ return true
1009
+ })
1010
+
1011
+ return hasReceiverUsage
1012
+ }
1013
+
1014
+ func (v *analysisVisitor) isInterfaceMethod(decl *ast.FuncDecl) bool {
1015
+ if decl.Recv == nil {
1016
+ return false
1017
+ }
1018
+
1019
+ // Get the method name
1020
+ methodName := decl.Name.Name
1021
+
1022
+ // Get the receiver variable
1023
+ var receiver *types.Var
1024
+ if len(decl.Recv.List) > 0 && len(decl.Recv.List[0].Names) > 0 {
1025
+ if ident := decl.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
1026
+ if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
1027
+ if vr, ok := def.(*types.Var); ok {
1028
+ receiver = vr
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+
1034
+ return v.couldImplementInterfaceMethod(methodName, receiver)
1035
+ }
1036
+
1037
+ func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, receiver *types.Var) bool {
1038
+ // Check if method is exported (interface methods must be exported)
1039
+ if !ast.IsExported(methodName) {
1040
+ return false
1041
+ }
1042
+
1043
+ return false
1044
+ }
1045
+
948
1046
  // AnalyzeFile analyzes a Go source file AST and populates the Analysis struct with information
949
1047
  // that will be used during code generation to properly handle pointers, variables that need varRefing, etc.
950
1048
  func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap ast.CommentMap) {
@@ -374,68 +374,6 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
374
374
  }
375
375
  defer indexFile.Close() //nolint:errcheck
376
376
 
377
- // Collect exported symbols from all files in the package
378
- var exportedSymbols []string
379
-
380
- // Iterate through all syntax files to find exported symbols
381
- for i, syntax := range c.pkg.Syntax {
382
- fileName := c.pkg.CompiledGoFiles[i]
383
- baseFileName := filepath.Base(fileName)
384
- gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
385
-
386
- // Only include this file if it was compiled (in our compiledFiles list)
387
- fileWasCompiled := false
388
- for _, compiledFile := range compiledFiles {
389
- if compiledFile == gsFileName {
390
- fileWasCompiled = true
391
- break
392
- }
393
- }
394
- if !fileWasCompiled {
395
- continue
396
- }
397
-
398
- // Analyze declarations in this file to find exported symbols
399
- for _, decl := range syntax.Decls {
400
- switch d := decl.(type) {
401
- case *ast.FuncDecl:
402
- // Only include top-level functions (not methods)
403
- if d.Recv == nil && d.Name.IsExported() {
404
- exportedSymbols = append(exportedSymbols, d.Name.Name)
405
- }
406
- case *ast.GenDecl:
407
- for _, spec := range d.Specs {
408
- switch s := spec.(type) {
409
- case *ast.TypeSpec:
410
- if s.Name.IsExported() {
411
- exportedSymbols = append(exportedSymbols, s.Name.Name)
412
- }
413
- case *ast.ValueSpec:
414
- for _, name := range s.Names {
415
- if name.IsExported() {
416
- exportedSymbols = append(exportedSymbols, name.Name)
417
- }
418
- }
419
- }
420
- }
421
- }
422
- }
423
- }
424
-
425
- // Remove duplicates and sort
426
- symbolMap := make(map[string]bool)
427
- for _, symbol := range exportedSymbols {
428
- symbolMap[symbol] = true
429
- }
430
-
431
- var uniqueSymbols []string
432
- for symbol := range symbolMap {
433
- uniqueSymbols = append(uniqueSymbols, symbol)
434
- }
435
-
436
- // Sort for consistent output
437
- sort.Strings(uniqueSymbols)
438
-
439
377
  // Write selective re-exports for each compiled file
440
378
  for _, fileName := range compiledFiles {
441
379
  // Check if this is a protobuf file
@@ -452,7 +390,8 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
452
390
  }
453
391
 
454
392
  // Find which symbols this file exports
455
- var fileSymbols []string
393
+ var valueSymbols []string
394
+ var typeSymbols []string
456
395
 
457
396
  // Find the corresponding syntax file
458
397
  for i, syntax := range c.pkg.Syntax {
@@ -469,19 +408,21 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
469
408
  switch d := decl.(type) {
470
409
  case *ast.FuncDecl:
471
410
  if d.Recv == nil && d.Name.IsExported() {
472
- fileSymbols = append(fileSymbols, d.Name.Name)
411
+ valueSymbols = append(valueSymbols, d.Name.Name)
473
412
  }
474
413
  case *ast.GenDecl:
475
414
  for _, spec := range d.Specs {
476
415
  switch s := spec.(type) {
477
416
  case *ast.TypeSpec:
478
417
  if s.Name.IsExported() {
479
- fileSymbols = append(fileSymbols, s.Name.Name)
418
+ // All type declarations (interfaces, structs, type definitions, type aliases)
419
+ // become TypeScript types and must be exported with "export type"
420
+ typeSymbols = append(typeSymbols, s.Name.Name)
480
421
  }
481
422
  case *ast.ValueSpec:
482
423
  for _, name := range s.Names {
483
424
  if name.IsExported() {
484
- fileSymbols = append(fileSymbols, name.Name)
425
+ valueSymbols = append(valueSymbols, name.Name)
485
426
  }
486
427
  }
487
428
  }
@@ -491,11 +432,20 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
491
432
  break
492
433
  }
493
434
 
494
- // Write selective export if this file has exported symbols
495
- if len(fileSymbols) > 0 {
496
- sort.Strings(fileSymbols)
435
+ // Write exports if this file has exported symbols
436
+ if len(valueSymbols) > 0 {
437
+ sort.Strings(valueSymbols)
497
438
  exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
498
- strings.Join(fileSymbols, ", "), fileName)
439
+ strings.Join(valueSymbols, ", "), fileName)
440
+ if _, err := indexFile.WriteString(exportLine); err != nil {
441
+ return err
442
+ }
443
+ }
444
+
445
+ if len(typeSymbols) > 0 {
446
+ sort.Strings(typeSymbols)
447
+ exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
448
+ strings.Join(typeSymbols, ", "), fileName)
499
449
  if _, err := indexFile.WriteString(exportLine); err != nil {
500
450
  return err
501
451
  }
package/compiler/decl.go CHANGED
@@ -142,6 +142,49 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
142
142
  //
143
143
  // This function assumes it is called only for `FuncDecl` nodes that are methods.
144
144
  func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
145
+ _, err := c.writeMethodSignature(decl)
146
+ if err != nil {
147
+ return err
148
+ }
149
+
150
+ return c.writeMethodBodyWithReceiverBinding(decl, "this")
151
+ }
152
+
153
+ // writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
154
+ // It declares each named return variable with its appropriate type and zero value.
155
+ func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
156
+ if results == nil {
157
+ return nil
158
+ }
159
+
160
+ for _, field := range results.List {
161
+ for _, name := range field.Names {
162
+ c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
163
+ c.WriteTypeExpr(field.Type)
164
+ c.tsw.WriteLiterally(" = ")
165
+ c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
166
+ c.tsw.WriteLine("")
167
+ }
168
+ }
169
+ return nil
170
+ }
171
+
172
+ // hasNamedReturns checks if a function type has any named return parameters.
173
+ func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
174
+ if results == nil {
175
+ return false
176
+ }
177
+
178
+ for _, field := range results.List {
179
+ if len(field.Names) > 0 {
180
+ return true
181
+ }
182
+ }
183
+ return false
184
+ }
185
+
186
+ // writeMethodSignature writes the TypeScript method signature including async, public modifiers, name, parameters, and return type
187
+ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error) {
145
188
  if decl.Doc != nil {
146
189
  c.WriteDoc(decl.Doc)
147
190
  }
@@ -162,11 +205,10 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
162
205
 
163
206
  // Keep original Go casing for method names
164
207
  if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
165
- return err
208
+ return isAsync, err
166
209
  }
167
210
 
168
211
  // Write signature (parameters and return type)
169
- // We adapt the logic from WriteFuncType here, but without the 'function' keyword
170
212
  funcType := decl.Type
171
213
  c.tsw.WriteLiterally("(")
172
214
  if funcType.Params != nil {
@@ -208,23 +250,37 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
208
250
  }
209
251
 
210
252
  c.tsw.WriteLiterally(" ")
253
+ return isAsync, nil
254
+ }
211
255
 
212
- // Bind receiver name to this
256
+ // writeMethodBodyWithReceiverBinding writes the method body with optional receiver binding
257
+ // receiverTarget should be "this" for struct methods or "this._value" for named type methods
258
+ func (c *GoToTSCompiler) writeMethodBodyWithReceiverBinding(decl *ast.FuncDecl, receiverTarget string) error {
259
+ // Bind receiver name conditionally
213
260
  if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
214
261
  recvName := recvField.Names[0].Name
215
262
  if recvName != "_" {
263
+ // Check if receiver is actually used
264
+ var needsReceiverBinding bool
265
+ if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
266
+ needsReceiverBinding = c.analysis.IsReceiverUsed(obj)
267
+ }
268
+
216
269
  c.tsw.WriteLine("{")
217
270
  c.tsw.Indent(1)
218
- // Sanitize the receiver name to avoid conflicts with TypeScript reserved words
219
- sanitizedRecvName := c.sanitizeIdentifier(recvName)
220
- c.tsw.WriteLinef("const %s = this", sanitizedRecvName)
271
+
272
+ if needsReceiverBinding {
273
+ // Sanitize the receiver name to avoid conflicts with TypeScript reserved words
274
+ sanitizedRecvName := c.sanitizeIdentifier(recvName)
275
+ c.tsw.WriteLinef("const %s = %s", sanitizedRecvName, receiverTarget)
276
+ }
221
277
 
222
278
  // Add using statement if needed
223
279
  if c.analysis.NeedsDefer(decl.Body) {
224
280
  if c.analysis.IsInAsyncFunction(decl) {
225
281
  c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
226
282
  } else {
227
- c.tsw.WriteLine("using cleanup = new $.DisposableStack();")
283
+ c.tsw.WriteLine("using __defer = new $.DisposableStack();")
228
284
  }
229
285
  }
230
286
 
@@ -252,36 +308,3 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
252
308
 
253
309
  return nil
254
310
  }
255
-
256
- // writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
257
- // It declares each named return variable with its appropriate type and zero value.
258
- func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
259
- if results == nil {
260
- return nil
261
- }
262
-
263
- for _, field := range results.List {
264
- for _, name := range field.Names {
265
- c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
266
- c.WriteTypeExpr(field.Type)
267
- c.tsw.WriteLiterally(" = ")
268
- c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
269
- c.tsw.WriteLine("")
270
- }
271
- }
272
- return nil
273
- }
274
-
275
- // hasNamedReturns checks if a function type has any named return parameters.
276
- func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
277
- if results == nil {
278
- return false
279
- }
280
-
281
- for _, field := range results.List {
282
- if len(field.Names) > 0 {
283
- return true
284
- }
285
- }
286
- return false
287
- }
@@ -0,0 +1,101 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/types"
6
+ "strings"
7
+ )
8
+
9
+ // writeAsyncCall writes the await prefix for async function calls
10
+ func (c *GoToTSCompiler) writeAsyncCall(exp *ast.CallExpr, funIdent *ast.Ident) bool {
11
+ if funIdent == nil {
12
+ return false
13
+ }
14
+
15
+ // Check if this is an async function call
16
+ if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
17
+ c.tsw.WriteLiterally("await ")
18
+ return true
19
+ }
20
+
21
+ return false
22
+ }
23
+
24
+ // writeAsyncMethodCall writes the await prefix for async method calls
25
+ func (c *GoToTSCompiler) writeAsyncMethodCall(exp *ast.CallExpr) bool {
26
+ selExpr, ok := exp.Fun.(*ast.SelectorExpr)
27
+ if !ok {
28
+ return false
29
+ }
30
+
31
+ // Check if this is a method call on a variable (e.g., mu.Lock())
32
+ ident, ok := selExpr.X.(*ast.Ident)
33
+ if !ok {
34
+ return false
35
+ }
36
+
37
+ // Get the type of the receiver
38
+ obj := c.pkg.TypesInfo.Uses[ident]
39
+ if obj == nil {
40
+ return false
41
+ }
42
+
43
+ varObj, ok := obj.(*types.Var)
44
+ if !ok {
45
+ return false
46
+ }
47
+
48
+ // Get the type name and package
49
+ namedType, ok := varObj.Type().(*types.Named)
50
+ if !ok {
51
+ return false
52
+ }
53
+
54
+ typeName := namedType.Obj().Name()
55
+ methodName := selExpr.Sel.Name
56
+
57
+ // Check if the type is from an imported package
58
+ typePkg := namedType.Obj().Pkg()
59
+ if typePkg == nil || typePkg == c.pkg.Types {
60
+ return false
61
+ }
62
+
63
+ // Use the actual package name from the type information
64
+ pkgName := typePkg.Name()
65
+
66
+ // Check if this method is async based on metadata
67
+ if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
68
+ c.tsw.WriteLiterally("await ")
69
+ return true
70
+ }
71
+
72
+ return false
73
+ }
74
+
75
+ // addNonNullAssertion adds ! for function calls that might return null
76
+ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
77
+ if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
78
+ if _, ok := funType.Underlying().(*types.Signature); ok {
79
+ // Check if this is a function parameter identifier that needs not-null assertion
80
+ if ident, isIdent := expFun.(*ast.Ident); isIdent {
81
+ // Check if this identifier is a function parameter
82
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
83
+ if _, isVar := obj.(*types.Var); isVar {
84
+ // This is a variable (including function parameters)
85
+ // Function parameters that are function types need ! assertion
86
+ c.tsw.WriteLiterally("!")
87
+ }
88
+ }
89
+ } else if _, isNamed := funType.(*types.Named); isNamed {
90
+ c.tsw.WriteLiterally("!")
91
+ }
92
+ } else {
93
+ // Check if the function type is nullable (e.g., func(...) | null)
94
+ // This handles cases where a function call returns a nullable function
95
+ funTypeStr := funType.String()
96
+ if strings.Contains(funTypeStr, "| null") || strings.Contains(funTypeStr, "null |") {
97
+ c.tsw.WriteLiterally("!")
98
+ }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,133 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+
8
+ "github.com/pkg/errors"
9
+ )
10
+
11
+ // writeBuiltinFunction handles built-in Go functions
12
+ func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
13
+ switch funName {
14
+ case "panic":
15
+ c.tsw.WriteLiterally("$.panic")
16
+ return true, nil
17
+ case "println":
18
+ c.tsw.WriteLiterally("console.log")
19
+ return true, nil
20
+ case "len":
21
+ if len(exp.Args) != 1 {
22
+ return true, errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
23
+ }
24
+ c.tsw.WriteLiterally("$.len")
25
+ return true, nil
26
+ case "cap":
27
+ if len(exp.Args) != 1 {
28
+ return true, errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
29
+ }
30
+ c.tsw.WriteLiterally("$.cap")
31
+ return true, nil
32
+ case "new":
33
+ if len(exp.Args) != 1 {
34
+ return true, errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
35
+ }
36
+ c.tsw.WriteLiterally("new ")
37
+ c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
38
+ c.tsw.WriteLiterally("()")
39
+ return true, nil
40
+ case "delete":
41
+ if len(exp.Args) != 2 {
42
+ return true, errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
43
+ }
44
+ c.tsw.WriteLiterally("$.deleteMapEntry")
45
+ return true, nil
46
+ case "copy":
47
+ if len(exp.Args) != 2 {
48
+ return true, errors.Errorf("unhandled copy call with incorrect number of arguments: %d != 2", len(exp.Args))
49
+ }
50
+ c.tsw.WriteLiterally("$.copy")
51
+ return true, nil
52
+ case "recover":
53
+ if len(exp.Args) != 0 {
54
+ return true, errors.Errorf("unhandled recover call with incorrect number of arguments: %d != 0", len(exp.Args))
55
+ }
56
+ c.tsw.WriteLiterally("$.recover")
57
+ return true, nil
58
+ case "make":
59
+ return true, c.WriteCallExprMake(exp)
60
+ case "string":
61
+ return true, c.writeStringConversion(exp)
62
+ case "close":
63
+ if len(exp.Args) != 1 {
64
+ return true, errors.New("unhandled close call with incorrect number of arguments")
65
+ }
66
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
67
+ return true, fmt.Errorf("failed to write channel in close call: %w", err)
68
+ }
69
+ c.tsw.WriteLiterally(".close()")
70
+ return true, nil
71
+ case "append":
72
+ return true, c.writeAppendCall(exp)
73
+ case "byte":
74
+ if len(exp.Args) != 1 {
75
+ return true, errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
76
+ }
77
+ c.tsw.WriteLiterally("$.byte(")
78
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
79
+ return true, fmt.Errorf("failed to write argument for byte() conversion: %w", err)
80
+ }
81
+ c.tsw.WriteLiterally(")")
82
+ return true, nil
83
+ case "int":
84
+ return true, c.writeIntConversion(exp)
85
+ default:
86
+ return false, nil
87
+ }
88
+ }
89
+
90
+ // writeAppendCall handles append() function calls
91
+ func (c *GoToTSCompiler) writeAppendCall(exp *ast.CallExpr) error {
92
+ if len(exp.Args) < 1 {
93
+ return errors.New("unhandled append call with incorrect number of arguments")
94
+ }
95
+
96
+ c.tsw.WriteLiterally("$.append(")
97
+ // The first argument is the slice
98
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
99
+ return fmt.Errorf("failed to write slice in append call: %w", err)
100
+ }
101
+
102
+ // The remaining arguments are the elements to append
103
+ for i, arg := range exp.Args[1:] {
104
+ if i > 0 || len(exp.Args) > 1 {
105
+ c.tsw.WriteLiterally(", ")
106
+ }
107
+
108
+ // Special case: append([]byte, string...) should convert string to bytes
109
+ if exp.Ellipsis != token.NoPos && i == 0 { // This is the first element after slice and has ellipsis
110
+ // Check if the slice is []byte and the argument is a string
111
+ sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
112
+ argType := c.pkg.TypesInfo.TypeOf(arg)
113
+
114
+ if sliceType != nil && argType != nil {
115
+ if c.isByteSliceType(sliceType) && c.isStringType(argType) {
116
+ // Convert string to bytes: append([]byte, string...) -> $.append(slice, ...$.stringToBytes(string))
117
+ c.tsw.WriteLiterally("...$.stringToBytes(")
118
+ if err := c.WriteValueExpr(arg); err != nil {
119
+ return fmt.Errorf("failed to write string argument in append call: %w", err)
120
+ }
121
+ c.tsw.WriteLiterally(")")
122
+ continue
123
+ }
124
+ }
125
+ }
126
+
127
+ if err := c.WriteValueExpr(arg); err != nil {
128
+ return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
129
+ }
130
+ }
131
+ c.tsw.WriteLiterally(")")
132
+ return nil
133
+ }