goscript 0.0.28 → 0.0.29

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 (73) hide show
  1. package/compiler/analysis.go +41 -2
  2. package/compiler/expr-call.go +87 -15
  3. package/compiler/expr-selector.go +100 -0
  4. package/compiler/spec-value.go +79 -8
  5. package/compiler/spec.go +236 -0
  6. package/dist/gs/builtin/builtin.d.ts +1 -0
  7. package/dist/gs/builtin/builtin.js +11 -0
  8. package/dist/gs/builtin/builtin.js.map +1 -1
  9. package/dist/gs/internal/oserror/errors.d.ts +6 -0
  10. package/dist/gs/internal/oserror/errors.js +7 -0
  11. package/dist/gs/internal/oserror/errors.js.map +1 -0
  12. package/dist/gs/internal/oserror/index.d.ts +1 -0
  13. package/dist/gs/internal/oserror/index.js +2 -0
  14. package/dist/gs/internal/oserror/index.js.map +1 -0
  15. package/dist/gs/io/fs/format.d.ts +3 -0
  16. package/dist/gs/io/fs/format.js +56 -0
  17. package/dist/gs/io/fs/format.js.map +1 -0
  18. package/dist/gs/io/fs/fs.d.ts +79 -0
  19. package/dist/gs/io/fs/fs.js +200 -0
  20. package/dist/gs/io/fs/fs.js.map +1 -0
  21. package/dist/gs/io/fs/glob.d.ts +10 -0
  22. package/dist/gs/io/fs/glob.js +141 -0
  23. package/dist/gs/io/fs/glob.js.map +1 -0
  24. package/dist/gs/io/fs/index.d.ts +8 -0
  25. package/dist/gs/io/fs/index.js +9 -0
  26. package/dist/gs/io/fs/index.js.map +1 -0
  27. package/dist/gs/io/fs/readdir.d.ts +7 -0
  28. package/dist/gs/io/fs/readdir.js +152 -0
  29. package/dist/gs/io/fs/readdir.js.map +1 -0
  30. package/dist/gs/io/fs/readfile.d.ts +6 -0
  31. package/dist/gs/io/fs/readfile.js +118 -0
  32. package/dist/gs/io/fs/readfile.js.map +1 -0
  33. package/dist/gs/io/fs/stat.d.ts +6 -0
  34. package/dist/gs/io/fs/stat.js +87 -0
  35. package/dist/gs/io/fs/stat.js.map +1 -0
  36. package/dist/gs/io/fs/sub.d.ts +6 -0
  37. package/dist/gs/io/fs/sub.js +172 -0
  38. package/dist/gs/io/fs/sub.js.map +1 -0
  39. package/dist/gs/io/fs/walk.d.ts +7 -0
  40. package/dist/gs/io/fs/walk.js +76 -0
  41. package/dist/gs/io/fs/walk.js.map +1 -0
  42. package/dist/gs/path/index.d.ts +2 -0
  43. package/dist/gs/path/index.js +3 -0
  44. package/dist/gs/path/index.js.map +1 -0
  45. package/dist/gs/path/match.d.ts +6 -0
  46. package/dist/gs/path/match.js +281 -0
  47. package/dist/gs/path/match.js.map +1 -0
  48. package/dist/gs/path/path.d.ts +7 -0
  49. package/dist/gs/path/path.js +256 -0
  50. package/dist/gs/path/path.js.map +1 -0
  51. package/dist/gs/time/time.d.ts +11 -2
  52. package/dist/gs/time/time.js +337 -12
  53. package/dist/gs/time/time.js.map +1 -1
  54. package/gs/builtin/builtin.ts +13 -0
  55. package/gs/internal/oserror/errors.ts +14 -0
  56. package/gs/internal/oserror/index.ts +1 -0
  57. package/gs/io/fs/format.ts +65 -0
  58. package/gs/io/fs/fs.ts +359 -0
  59. package/gs/io/fs/glob.ts +167 -0
  60. package/gs/io/fs/godoc.txt +35 -0
  61. package/gs/io/fs/index.ts +8 -0
  62. package/gs/io/fs/readdir.ts +126 -0
  63. package/gs/io/fs/readfile.ts +77 -0
  64. package/gs/io/fs/stat.ts +38 -0
  65. package/gs/io/fs/sub.ts +208 -0
  66. package/gs/io/fs/walk.ts +89 -0
  67. package/gs/path/index.ts +2 -0
  68. package/gs/path/match.ts +307 -0
  69. package/gs/path/path.ts +301 -0
  70. package/gs/strings/reader.test.ts +0 -1
  71. package/gs/time/time.ts +325 -12
  72. package/package.json +1 -1
  73. package/gs/time/godoc.md +0 -116
@@ -57,6 +57,7 @@ type NodeInfo struct {
57
57
  EnclosingFuncDecl *ast.FuncDecl
58
58
  EnclosingFuncLit *ast.FuncLit
59
59
  IsInsideFunction bool // true if this declaration is inside a function body
60
+ IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
60
61
  }
61
62
 
62
63
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
@@ -236,6 +237,18 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
236
237
  return false
237
238
  }
238
239
 
240
+ // IsMethodValue returns whether the given SelectorExpr node is a method value that needs binding.
241
+ func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
242
+ if node == nil {
243
+ return false
244
+ }
245
+ nodeInfo := a.NodeData[node]
246
+ if nodeInfo == nil {
247
+ return false
248
+ }
249
+ return nodeInfo.IsMethodValue
250
+ }
251
+
239
252
  // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
240
253
  type analysisVisitor struct {
241
254
  // analysis stores information gathered during the traversal
@@ -566,8 +579,20 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
566
579
  return v
567
580
 
568
581
  case *ast.SelectorExpr:
569
- // No need to track private field access since all fields are public
570
- return v
582
+ // Initialize NodeData for this selector expression
583
+ if v.analysis.NodeData[n] == nil {
584
+ v.analysis.NodeData[n] = &NodeInfo{}
585
+ }
586
+ v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
587
+
588
+ // Check if this is a method value (method being used as a value, not called immediately)
589
+ if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
590
+ if selection.Kind() == types.MethodVal {
591
+ // This is a method value - mark it for binding during code generation
592
+ v.analysis.NodeData[n].IsMethodValue = true
593
+ }
594
+ }
595
+ return v // Continue traversal
571
596
 
572
597
  case *ast.AssignStmt:
573
598
  for i, currentLHSExpr := range n.Lhs {
@@ -859,6 +884,20 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
859
884
 
860
885
  // Walk the AST with our visitor
861
886
  ast.Walk(visitor, file)
887
+
888
+ // Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
889
+ // This distinguishes between method calls (obj.Method()) and method values (obj.Method)
890
+ ast.Inspect(file, func(n ast.Node) bool {
891
+ if callExpr, ok := n.(*ast.CallExpr); ok {
892
+ if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
893
+ // This SelectorExpr is the function being called, so it's NOT a method value
894
+ if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
895
+ nodeInfo.IsMethodValue = false
896
+ }
897
+ }
898
+ }
899
+ return true
900
+ })
862
901
  }
863
902
 
864
903
  // getNamedReturns retrieves the named returns for a function
@@ -513,16 +513,51 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
513
513
  }
514
514
  return errors.New("unhandled append call with incorrect number of arguments")
515
515
  case "byte":
516
- // Translate byte(val) to $.byte(val)
516
+ // Translate byte(arg) to $.byte(arg)
517
+ if len(exp.Args) != 1 {
518
+ return errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
519
+ }
520
+ c.tsw.WriteLiterally("$.byte(")
521
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
522
+ return fmt.Errorf("failed to write argument for byte() conversion: %w", err)
523
+ }
524
+ c.tsw.WriteLiterally(")")
525
+ return nil // Handled byte() conversion
526
+ case "int":
527
+ // Handle int() conversion
517
528
  if len(exp.Args) == 1 {
518
- c.tsw.WriteLiterally("$.byte(")
529
+ arg := exp.Args[0]
530
+
531
+ // Check if we're converting FROM a type with receiver methods TO int
532
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
533
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
534
+ argTypeName := namedArgType.Obj().Name()
535
+ // Check if the argument type has receiver methods
536
+ if c.hasReceiverMethods(argTypeName) {
537
+ // Check if we're converting to int (the underlying type)
538
+ if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
539
+ // This is a conversion from a type with methods to int
540
+ // Use valueOf() instead of $.int()
541
+ if err := c.WriteValueExpr(arg); err != nil {
542
+ return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
543
+ }
544
+ c.tsw.WriteLiterally(".valueOf()")
545
+ return nil // Handled conversion from type with methods to int
546
+ }
547
+ }
548
+ }
549
+ }
550
+
551
+ // Default case: Translate int(value) to $.int(value)
552
+ c.tsw.WriteLiterally("$.int(")
519
553
  if err := c.WriteValueExpr(exp.Args[0]); err != nil {
520
- return err
554
+ return fmt.Errorf("failed to write argument for int() conversion: %w", err)
521
555
  }
522
556
  c.tsw.WriteLiterally(")")
523
- return nil // Handled byte
557
+ return nil // Handled int() conversion
524
558
  }
525
- return errors.New("unhandled byte call with incorrect number of arguments")
559
+ // Return error for incorrect number of arguments
560
+ return fmt.Errorf("unhandled int conversion with incorrect number of arguments: %d != 1", len(exp.Args))
526
561
  default:
527
562
  // Check if this is a type conversion to a function type
528
563
  if funIdent != nil {
@@ -531,6 +566,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
531
566
  if typeName, isType := obj.(*types.TypeName); isType {
532
567
  // Make sure we have exactly one argument
533
568
  if len(exp.Args) == 1 {
569
+ arg := exp.Args[0]
570
+
571
+ // Check if we're converting FROM a type with receiver methods TO its underlying type
572
+ if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
573
+ if namedArgType, isNamed := argType.(*types.Named); isNamed {
574
+ argTypeName := namedArgType.Obj().Name()
575
+ // Check if the argument type has receiver methods
576
+ if c.hasReceiverMethods(argTypeName) {
577
+ // Check if we're converting to the underlying type
578
+ targetType := typeName.Type()
579
+ underlyingType := namedArgType.Underlying()
580
+ if types.Identical(targetType, underlyingType) {
581
+ // This is a conversion from a type with methods to its underlying type
582
+ // Use valueOf() instead of TypeScript cast
583
+ if err := c.WriteValueExpr(arg); err != nil {
584
+ return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
585
+ }
586
+ c.tsw.WriteLiterally(".valueOf()")
587
+ return nil // Handled conversion from type with methods to underlying type
588
+ }
589
+ }
590
+ }
591
+ }
592
+
534
593
  // Check if this is a function type
535
594
  if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
536
595
  // For function types, we need to add a __goTypeName property
@@ -545,17 +604,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
545
604
  c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
546
605
  return nil // Handled function type cast
547
606
  } else {
548
- // For non-function types, use the TypeScript "as" operator
549
- c.tsw.WriteLiterally("(")
550
- if err := c.WriteValueExpr(exp.Args[0]); err != nil {
551
- return fmt.Errorf("failed to write argument for type cast: %w", err)
552
- }
607
+ // Check if this type has receiver methods
608
+ if c.hasReceiverMethods(funIdent.String()) {
609
+ // For types with methods, use class constructor
610
+ c.tsw.WriteLiterally("new ")
611
+ c.tsw.WriteLiterally(funIdent.String())
612
+ c.tsw.WriteLiterally("(")
613
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
614
+ return fmt.Errorf("failed to write argument for type constructor: %w", err)
615
+ }
616
+ c.tsw.WriteLiterally(")")
617
+ return nil // Handled type with methods conversion
618
+ } else {
619
+ // For non-function types without methods, use the TypeScript "as" operator
620
+ c.tsw.WriteLiterally("(")
621
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
622
+ return fmt.Errorf("failed to write argument for type cast: %w", err)
623
+ }
553
624
 
554
- // Then use the TypeScript "as" operator with the mapped type name
555
- c.tsw.WriteLiterally(" as ")
556
- c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
557
- c.tsw.WriteLiterally(")")
558
- return nil // Handled non-function type cast
625
+ // Then use the TypeScript "as" operator with the mapped type name
626
+ c.tsw.WriteLiterally(" as ")
627
+ c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
628
+ c.tsw.WriteLiterally(")")
629
+ return nil // Handled non-function type cast
630
+ }
559
631
  }
560
632
  }
561
633
  }
@@ -38,6 +38,14 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
38
38
  }
39
39
  }
40
40
 
41
+ // Check if this is a method value (method being used as a value, not called immediately)
42
+ if c.analysis.IsMethodValue(exp) {
43
+ // This is a method value - we need to bind it properly
44
+ if selection := c.pkg.TypesInfo.Selections[exp]; selection != nil {
45
+ return c.writeMethodValue(exp, selection)
46
+ }
47
+ }
48
+
41
49
  // --- Special case for dereferenced pointer to struct with field access: (*p).field or (**p).field etc ---
42
50
  var baseExpr ast.Expr = exp.X
43
51
  // Look inside parentheses if present
@@ -148,3 +156,95 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
148
156
  c.WriteIdent(exp.Sel, false)
149
157
  return nil
150
158
  }
159
+
160
+ // writeMethodValue handles method values (methods used as values, not called immediately)
161
+ // and generates proper binding code to maintain the 'this' context.
162
+ func (c *GoToTSCompiler) writeMethodValue(exp *ast.SelectorExpr, selection *types.Selection) error {
163
+ // Get the method signature to understand the receiver type
164
+ methodObj := selection.Obj().(*types.Func)
165
+ sig := methodObj.Type().(*types.Signature)
166
+ recv := sig.Recv()
167
+
168
+ if recv == nil {
169
+ // This shouldn't happen for method values, but handle gracefully
170
+ return fmt.Errorf("method value has no receiver: %s", methodObj.Name())
171
+ }
172
+
173
+ // Determine if this is a pointer receiver or value receiver
174
+ recvType := recv.Type()
175
+ isPointerReceiver := false
176
+ if _, ok := recvType.(*types.Pointer); ok {
177
+ isPointerReceiver = true
178
+ }
179
+
180
+ // Get the base expression type to understand what we're working with
181
+ baseType := c.pkg.TypesInfo.TypeOf(exp.X)
182
+ baseIsPointer := false
183
+ if _, ok := baseType.(*types.Pointer); ok {
184
+ baseIsPointer = true
185
+ }
186
+
187
+ // Write the receiver expression
188
+ if err := c.WriteValueExpr(exp.X); err != nil {
189
+ return fmt.Errorf("failed to write method value receiver: %w", err)
190
+ }
191
+
192
+ // Add null assertion if needed
193
+ if baseIsPointer {
194
+ c.tsw.WriteLiterally("!")
195
+ }
196
+
197
+ // Handle different receiver type combinations according to Go semantics
198
+ if isPointerReceiver && !baseIsPointer {
199
+ // Pointer receiver method on value type: t.Mp equivalent to (&t).Mp
200
+ // The receiver should be the address of the value
201
+ c.tsw.WriteLiterally(".")
202
+ c.WriteIdent(exp.Sel, false)
203
+ c.tsw.WriteLiterally(".bind(")
204
+ if err := c.WriteValueExpr(exp.X); err != nil {
205
+ return fmt.Errorf("failed to write method value receiver for binding: %w", err)
206
+ }
207
+ if baseIsPointer {
208
+ c.tsw.WriteLiterally("!")
209
+ }
210
+ c.tsw.WriteLiterally(")")
211
+ } else if !isPointerReceiver && baseIsPointer {
212
+ // Value receiver method on pointer type: pt.Mv equivalent to (*pt).Mv
213
+ // The receiver should be a copy of the dereferenced value
214
+ c.tsw.WriteLiterally(".value.")
215
+ c.WriteIdent(exp.Sel, false)
216
+ c.tsw.WriteLiterally(".bind(")
217
+ if err := c.WriteValueExpr(exp.X); err != nil {
218
+ return fmt.Errorf("failed to write method value receiver for binding: %w", err)
219
+ }
220
+ c.tsw.WriteLiterally("!.value.clone())")
221
+ } else if !isPointerReceiver && !baseIsPointer {
222
+ // Value receiver method on value type: t.Mv
223
+ // The receiver should be a copy of the value
224
+ c.tsw.WriteLiterally(".")
225
+ c.WriteIdent(exp.Sel, false)
226
+ c.tsw.WriteLiterally(".bind(")
227
+ if err := c.WriteValueExpr(exp.X); err != nil {
228
+ return fmt.Errorf("failed to write method value receiver for binding: %w", err)
229
+ }
230
+ if baseIsPointer {
231
+ c.tsw.WriteLiterally("!")
232
+ }
233
+ c.tsw.WriteLiterally(".clone())")
234
+ } else {
235
+ // Pointer receiver method on pointer type: pt.Mp
236
+ // The receiver should be the pointer itself
237
+ c.tsw.WriteLiterally(".")
238
+ c.WriteIdent(exp.Sel, false)
239
+ c.tsw.WriteLiterally(".bind(")
240
+ if err := c.WriteValueExpr(exp.X); err != nil {
241
+ return fmt.Errorf("failed to write method value receiver for binding: %w", err)
242
+ }
243
+ if baseIsPointer {
244
+ c.tsw.WriteLiterally("!")
245
+ }
246
+ c.tsw.WriteLiterally(")")
247
+ }
248
+
249
+ return nil
250
+ }
@@ -216,21 +216,92 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
216
216
  c.tsw.WriteLiterally(")")
217
217
  }
218
218
  } else {
219
- // Regular initializer, clone if needed
220
- if shouldApplyClone(c.pkg, initializerExpr) {
221
- if err := c.WriteValueExpr(initializerExpr); err != nil {
222
- return err
219
+ // Check if this is a named type with methods and the initializer is a basic value
220
+ if namedType, isNamed := goType.(*types.Named); isNamed {
221
+ typeName := namedType.Obj().Name()
222
+ if c.hasReceiverMethods(typeName) {
223
+ // Check if the initializer is a basic literal or simple value that needs wrapping
224
+ needsConstructor := false
225
+ switch initializerExpr.(type) {
226
+ case *ast.BasicLit:
227
+ needsConstructor = true
228
+ case *ast.Ident:
229
+ // Check if it's a simple identifier (not a function call or complex expression)
230
+ if ident := initializerExpr.(*ast.Ident); ident.Name != "nil" {
231
+ // Check if this identifier refers to a value of the underlying type
232
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
233
+ if objType := obj.Type(); objType != nil {
234
+ // If the identifier's type matches the underlying type, wrap it
235
+ if types.Identical(objType, namedType.Underlying()) {
236
+ needsConstructor = true
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ if needsConstructor {
244
+ c.tsw.WriteLiterallyf("new %s(", typeName)
245
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
246
+ return err
247
+ }
248
+ c.tsw.WriteLiterally(")")
249
+ } else {
250
+ // Regular initializer for named type (e.g., function call that returns the type)
251
+ if shouldApplyClone(c.pkg, initializerExpr) {
252
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
253
+ return err
254
+ }
255
+ c.tsw.WriteLiterally(".clone()")
256
+ } else {
257
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
258
+ return err
259
+ }
260
+ }
261
+ }
262
+ } else {
263
+ // Named type without methods, handle normally
264
+ if shouldApplyClone(c.pkg, initializerExpr) {
265
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
266
+ return err
267
+ }
268
+ c.tsw.WriteLiterally(".clone()")
269
+ } else {
270
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
271
+ return err
272
+ }
273
+ }
223
274
  }
224
- c.tsw.WriteLiterally(".clone()")
225
275
  } else {
226
- if err := c.WriteValueExpr(initializerExpr); err != nil {
227
- return err
276
+ // Regular initializer, clone if needed
277
+ if shouldApplyClone(c.pkg, initializerExpr) {
278
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
279
+ return err
280
+ }
281
+ c.tsw.WriteLiterally(".clone()")
282
+ } else {
283
+ if err := c.WriteValueExpr(initializerExpr); err != nil {
284
+ return err
285
+ }
228
286
  }
229
287
  }
230
288
  }
231
289
  } else {
232
290
  // No initializer, use the zero value directly
233
- c.WriteZeroValueForType(goType)
291
+ // Check if this is a named type with methods
292
+ if namedType, isNamed := goType.(*types.Named); isNamed {
293
+ typeName := namedType.Obj().Name()
294
+ if c.hasReceiverMethods(typeName) {
295
+ // For named types with methods, create a new instance with zero value
296
+ c.tsw.WriteLiterallyf("new %s(", typeName)
297
+ c.WriteZeroValueForType(namedType.Underlying())
298
+ c.tsw.WriteLiterally(")")
299
+ } else {
300
+ c.WriteZeroValueForType(goType)
301
+ }
302
+ } else {
303
+ c.WriteZeroValueForType(goType)
304
+ }
234
305
  }
235
306
  }
236
307
  c.tsw.WriteLine("") // Finish the declaration line
package/compiler/spec.go CHANGED
@@ -150,6 +150,237 @@ func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType
150
150
  c.tsw.WriteLiterally(")")
151
151
  }
152
152
 
153
+ // hasReceiverMethods checks if a type declaration has any receiver methods defined
154
+ func (c *GoToTSCompiler) hasReceiverMethods(typeName string) bool {
155
+ for _, fileSyntax := range c.pkg.Syntax {
156
+ for _, decl := range fileSyntax.Decls {
157
+ funcDecl, isFunc := decl.(*ast.FuncDecl)
158
+ if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
159
+ continue
160
+ }
161
+ recvField := funcDecl.Recv.List[0]
162
+ recvType := recvField.Type
163
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
164
+ recvType = starExpr.X
165
+ }
166
+
167
+ // Check for both simple identifiers (FileMode) and generic types (FileMode[T])
168
+ var recvTypeName string
169
+ if ident, ok := recvType.(*ast.Ident); ok {
170
+ recvTypeName = ident.Name
171
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
172
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
173
+ recvTypeName = ident.Name
174
+ }
175
+ }
176
+
177
+ if recvTypeName == typeName {
178
+ return true
179
+ }
180
+ }
181
+ }
182
+ return false
183
+ }
184
+
185
+ // WriteNamedTypeWithMethods generates a TypeScript class for a Go named type that has receiver methods
186
+ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
187
+ className := a.Name.Name
188
+ underlyingType := c.pkg.TypesInfo.TypeOf(a.Type)
189
+
190
+ // Add export for Go-exported types (but not if inside a function)
191
+ isInsideFunction := false
192
+ if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
193
+ isInsideFunction = nodeInfo.IsInsideFunction
194
+ }
195
+
196
+ if a.Name.IsExported() && !isInsideFunction {
197
+ c.tsw.WriteLiterally("export ")
198
+ }
199
+
200
+ c.tsw.WriteLiterallyf("class %s {", className)
201
+ c.tsw.WriteLine("")
202
+ c.tsw.Indent(1)
203
+
204
+ // Constructor that takes the underlying type value
205
+ c.tsw.WriteLiterally("constructor(private _value: ")
206
+ c.WriteGoType(underlyingType, GoTypeContextGeneral)
207
+ c.tsw.WriteLine(") {}")
208
+ c.tsw.WriteLine("")
209
+
210
+ // valueOf method to get the underlying value (for type conversions and operations)
211
+ c.tsw.WriteLiterally("valueOf(): ")
212
+ c.WriteGoType(underlyingType, GoTypeContextGeneral)
213
+ c.tsw.WriteLine(" {")
214
+ c.tsw.Indent(1)
215
+ c.tsw.WriteLine("return this._value")
216
+ c.tsw.Indent(-1)
217
+ c.tsw.WriteLine("}")
218
+ c.tsw.WriteLine("")
219
+
220
+ // toString method for string conversion
221
+ c.tsw.WriteLine("toString(): string {")
222
+ c.tsw.Indent(1)
223
+ c.tsw.WriteLine("return String(this._value)")
224
+ c.tsw.Indent(-1)
225
+ c.tsw.WriteLine("}")
226
+ c.tsw.WriteLine("")
227
+
228
+ // Static from method for type conversion
229
+ c.tsw.WriteLiterallyf("static from(value: ")
230
+ c.WriteGoType(underlyingType, GoTypeContextGeneral)
231
+ c.tsw.WriteLiterallyf("): %s {", className)
232
+ c.tsw.WriteLine("")
233
+ c.tsw.Indent(1)
234
+ c.tsw.WriteLiterallyf("return new %s(value)", className)
235
+ c.tsw.WriteLine("")
236
+ c.tsw.Indent(-1)
237
+ c.tsw.WriteLine("}")
238
+
239
+ // Add receiver methods for this type
240
+ for _, fileSyntax := range c.pkg.Syntax {
241
+ for _, decl := range fileSyntax.Decls {
242
+ funcDecl, isFunc := decl.(*ast.FuncDecl)
243
+ if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
244
+ continue
245
+ }
246
+ recvField := funcDecl.Recv.List[0]
247
+ recvType := recvField.Type
248
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
249
+ recvType = starExpr.X
250
+ }
251
+
252
+ // Check for both simple identifiers (FileMode) and generic types (FileMode[T])
253
+ var recvTypeName string
254
+ if ident, ok := recvType.(*ast.Ident); ok {
255
+ recvTypeName = ident.Name
256
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
257
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
258
+ recvTypeName = ident.Name
259
+ }
260
+ }
261
+
262
+ if recvTypeName == className {
263
+ c.tsw.WriteLine("")
264
+ if err := c.writeNamedTypeMethod(funcDecl, className); err != nil {
265
+ return err
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ c.tsw.Indent(-1)
272
+ c.tsw.WriteLine("}")
273
+
274
+ return nil
275
+ }
276
+
277
+ // writeNamedTypeMethod writes a method for a named type, handling receiver binding properly
278
+ func (c *GoToTSCompiler) writeNamedTypeMethod(decl *ast.FuncDecl, className string) error {
279
+ if decl.Doc != nil {
280
+ c.WriteDoc(decl.Doc)
281
+ }
282
+
283
+ // Determine if method is async
284
+ var isAsync bool
285
+ if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
286
+ isAsync = c.analysis.IsAsyncFunc(obj)
287
+ }
288
+
289
+ // Methods are typically public in the TS output
290
+ c.tsw.WriteLiterally("public ")
291
+
292
+ // Add async modifier if needed
293
+ if isAsync {
294
+ c.tsw.WriteLiterally("async ")
295
+ }
296
+
297
+ // Keep original Go casing for method names
298
+ if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
299
+ return err
300
+ }
301
+
302
+ // Write signature (parameters and return type)
303
+ funcType := decl.Type
304
+ c.tsw.WriteLiterally("(")
305
+ if funcType.Params != nil {
306
+ c.WriteFieldList(funcType.Params, true) // true = arguments
307
+ }
308
+ c.tsw.WriteLiterally(")")
309
+
310
+ // Handle return type
311
+ if funcType.Results != nil && len(funcType.Results.List) > 0 {
312
+ c.tsw.WriteLiterally(": ")
313
+ if isAsync {
314
+ c.tsw.WriteLiterally("Promise<")
315
+ }
316
+ if len(funcType.Results.List) == 1 {
317
+ // Single return value
318
+ resultType := funcType.Results.List[0].Type
319
+ c.WriteTypeExpr(resultType)
320
+ } else {
321
+ // Multiple return values -> tuple type
322
+ c.tsw.WriteLiterally("[")
323
+ for i, field := range funcType.Results.List {
324
+ if i > 0 {
325
+ c.tsw.WriteLiterally(", ")
326
+ }
327
+ c.WriteTypeExpr(field.Type)
328
+ }
329
+ c.tsw.WriteLiterally("]")
330
+ }
331
+ if isAsync {
332
+ c.tsw.WriteLiterally(">")
333
+ }
334
+ } else {
335
+ // No return value -> void
336
+ if isAsync {
337
+ c.tsw.WriteLiterally(": Promise<void>")
338
+ } else {
339
+ c.tsw.WriteLiterally(": void")
340
+ }
341
+ }
342
+
343
+ c.tsw.WriteLiterally(" ")
344
+
345
+ // For named types with methods, bind receiver name to this._value
346
+ if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
347
+ recvName := recvField.Names[0].Name
348
+ if recvName != "_" {
349
+ c.tsw.WriteLine("{")
350
+ c.tsw.Indent(1)
351
+ // Bind the receiver name to this._value for value types
352
+ sanitizedRecvName := c.sanitizeIdentifier(recvName)
353
+ c.tsw.WriteLinef("const %s = this._value", sanitizedRecvName)
354
+
355
+ // Add using statement if needed
356
+ if c.analysis.NeedsDefer(decl.Body) {
357
+ if c.analysis.IsInAsyncFunction(decl) {
358
+ c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
359
+ } else {
360
+ c.tsw.WriteLine("using cleanup = new $.DisposableStack();")
361
+ }
362
+ }
363
+
364
+ // write method body without outer braces
365
+ for _, stmt := range decl.Body.List {
366
+ if err := c.WriteStmt(stmt); err != nil {
367
+ return fmt.Errorf("failed to write statement in function body: %w", err)
368
+ }
369
+ }
370
+ c.tsw.Indent(-1)
371
+ c.tsw.WriteLine("}")
372
+
373
+ return nil
374
+ }
375
+ }
376
+ // no named receiver, write whole body
377
+ if err := c.WriteStmt(decl.Body); err != nil {
378
+ return fmt.Errorf("failed to write function body: %w", err)
379
+ }
380
+
381
+ return nil
382
+ }
383
+
153
384
  // WriteTypeSpec writes the type specification to the output.
154
385
  func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
155
386
  if a.Doc != nil {
@@ -165,6 +396,11 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
165
396
  case *ast.InterfaceType:
166
397
  return c.WriteInterfaceTypeSpec(a, t)
167
398
  default:
399
+ // Check if this type has receiver methods
400
+ if c.hasReceiverMethods(a.Name.Name) {
401
+ return c.WriteNamedTypeWithMethods(a)
402
+ }
403
+
168
404
  // type alias - add export for Go-exported types (but not if inside a function)
169
405
  isInsideFunction := false
170
406
  if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
@@ -5,6 +5,7 @@ export * from './io.js';
5
5
  export * from './map.js';
6
6
  export * from './slice.js';
7
7
  export * from './type.js';
8
+ export declare function int(value: number): number;
8
9
  export declare function copy<T>(dst: T[] | Uint8Array, src: T[] | Uint8Array | string): number;
9
10
  export declare function multiplyDuration(duration: any, multiplier: number): any;
10
11
  /**
@@ -5,6 +5,17 @@ export * from './io.js';
5
5
  export * from './map.js';
6
6
  export * from './slice.js';
7
7
  export * from './type.js';
8
+ // int converts a value to a Go int type, handling proper signed integer conversion
9
+ // This ensures that values like 2147483648 (2^31) are properly handled according to Go semantics
10
+ export function int(value) {
11
+ // In Go, int is typically 64-bit on 64-bit systems, but for compatibility with JavaScript
12
+ // we need to handle the conversion properly. The issue is that JavaScript's number type
13
+ // can represent values larger than 32-bit signed integers, but when cast in certain contexts
14
+ // they get interpreted as signed 32-bit integers.
15
+ // For Go's int type on 64-bit systems, we should preserve the full value
16
+ // since JavaScript numbers can safely represent integers up to Number.MAX_SAFE_INTEGER
17
+ return Math.trunc(value);
18
+ }
8
19
  // Copy is the Go builtin function that copies the contents of one slice to another.
9
20
  // It returns the number of elements copied.
10
21
  export function copy(dst, src) {