goscript 0.0.76 → 0.0.78

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 (89) hide show
  1. package/compiler/analysis.go +100 -33
  2. package/compiler/analysis_test.go +2 -7
  3. package/compiler/code-writer.go +2 -2
  4. package/compiler/compiler.go +4 -4
  5. package/compiler/composite-lit.go +4 -6
  6. package/compiler/constraint.go +2 -4
  7. package/compiler/decl.go +34 -0
  8. package/compiler/expr-call-async.go +4 -0
  9. package/compiler/expr-call-helpers.go +98 -8
  10. package/compiler/expr-call-make.go +4 -4
  11. package/compiler/expr-call.go +3 -0
  12. package/compiler/expr-type.go +42 -0
  13. package/compiler/gs_dependencies_test.go +3 -14
  14. package/compiler/index.ts +20 -5
  15. package/compiler/protobuf.go +21 -21
  16. package/compiler/spec-struct.go +195 -30
  17. package/compiler/spec.go +32 -3
  18. package/compiler/stmt-assign.go +2 -2
  19. package/compiler/type-info.go +20 -3
  20. package/compiler/type-utils.go +2 -4
  21. package/compiler/type.go +3 -4
  22. package/dist/compiler/index.js +13 -4
  23. package/dist/compiler/index.js.map +1 -1
  24. package/dist/gs/builtin/slice.js +2 -3
  25. package/dist/gs/builtin/slice.js.map +1 -1
  26. package/dist/gs/builtin/type.d.ts +1 -0
  27. package/dist/gs/builtin/type.js +8 -14
  28. package/dist/gs/builtin/type.js.map +1 -1
  29. package/dist/gs/bytes/buffer.gs.d.ts +1 -0
  30. package/dist/gs/bytes/buffer.gs.js +20 -0
  31. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  32. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +5 -0
  33. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +10 -0
  34. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -0
  35. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.d.ts +50 -0
  36. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js +221 -0
  37. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -0
  38. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.d.ts +1 -0
  39. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js +2 -0
  40. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js.map +1 -0
  41. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.d.ts +1 -0
  42. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js +2 -0
  43. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js.map +1 -0
  44. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.d.ts +56 -0
  45. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js +17 -0
  46. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js.map +1 -0
  47. package/dist/gs/io/fs/format.js +2 -6
  48. package/dist/gs/io/fs/format.js.map +1 -1
  49. package/dist/gs/io/fs/glob.js +18 -23
  50. package/dist/gs/io/fs/glob.js.map +1 -1
  51. package/dist/gs/path/match.js +9 -22
  52. package/dist/gs/path/match.js.map +1 -1
  53. package/dist/gs/reflect/index.d.ts +1 -1
  54. package/dist/gs/reflect/index.js +1 -1
  55. package/dist/gs/reflect/index.js.map +1 -1
  56. package/dist/gs/reflect/type.d.ts +1 -0
  57. package/dist/gs/reflect/type.js +52 -23
  58. package/dist/gs/reflect/type.js.map +1 -1
  59. package/dist/gs/strings/iter.js +1 -1
  60. package/dist/gs/strings/iter.js.map +1 -1
  61. package/dist/gs/strings/reader.js +1 -1
  62. package/dist/gs/strings/reader.js.map +1 -1
  63. package/dist/gs/strings/replace.js +9 -20
  64. package/dist/gs/strings/replace.js.map +1 -1
  65. package/dist/gs/time/time.js +2 -2
  66. package/dist/gs/time/time.js.map +1 -1
  67. package/go.mod +1 -1
  68. package/go.sum +2 -2
  69. package/gs/builtin/slice.ts +2 -2
  70. package/gs/builtin/type.ts +14 -14
  71. package/gs/bytes/buffer.gs.ts +21 -1
  72. package/gs/fmt/fmt.test.ts +1 -1
  73. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +14 -0
  74. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +238 -0
  75. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.ts +1 -0
  76. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +12 -0
  77. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.ts +1 -0
  78. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/meta.json +8 -0
  79. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.ts +94 -0
  80. package/gs/io/fs/format.ts +2 -5
  81. package/gs/io/fs/glob.ts +18 -21
  82. package/gs/path/match.ts +9 -22
  83. package/gs/reflect/index.ts +1 -0
  84. package/gs/reflect/type.ts +62 -25
  85. package/gs/strings/iter.ts +1 -1
  86. package/gs/strings/reader.ts +1 -1
  87. package/gs/strings/replace.ts +13 -18
  88. package/gs/time/time.ts +2 -2
  89. package/package.json +18 -15
@@ -1,6 +1,7 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
+ "slices"
4
5
  "testing"
5
6
 
6
7
  "github.com/sirupsen/logrus"
@@ -36,13 +37,7 @@ func TestReadGsPackageMetadata(t *testing.T) {
36
37
  }
37
38
 
38
39
  // Check for the specific "iter" dependency
39
- foundIter := false
40
- for _, dep := range metadata.Dependencies {
41
- if dep == "iter" {
42
- foundIter = true
43
- break
44
- }
45
- }
40
+ foundIter := slices.Contains(metadata.Dependencies, "iter")
46
41
 
47
42
  if !foundIter {
48
43
  t.Errorf("Expected to find 'iter' dependency, got dependencies: %v", metadata.Dependencies)
@@ -51,13 +46,7 @@ func TestReadGsPackageMetadata(t *testing.T) {
51
46
  // Also check for other expected dependencies from the bytes package
52
47
  expectedDeps := []string{"errors", "io", "iter", "unicode", "unicode/utf8", "unsafe"}
53
48
  for _, expected := range expectedDeps {
54
- found := false
55
- for _, dep := range metadata.Dependencies {
56
- if dep == expected {
57
- found = true
58
- break
59
- }
60
- }
49
+ found := slices.Contains(metadata.Dependencies, expected)
61
50
  if !found {
62
51
  t.Errorf("Expected to find dependency '%s', got dependencies: %v", expected, metadata.Dependencies)
63
52
  }
package/compiler/index.ts CHANGED
@@ -65,15 +65,30 @@ export async function compile(config: CompileConfig): Promise<void> {
65
65
  // Go compiler often prints status messages to stderr, treat as info unless exit code is non-zero
66
66
  console.info(`GoScript stderr:\n${stderr}`);
67
67
  }
68
- } catch (error: any) {
69
- console.error(`GoScript compilation failed: ${error.message}`);
70
- if (error.stderr) {
68
+ } catch (error: unknown) {
69
+ const compileErr =
70
+ error instanceof Error ? error : new Error(String(error));
71
+
72
+ console.error(`GoScript compilation failed: ${compileErr.message}`);
73
+ if (
74
+ typeof error === "object" &&
75
+ error !== null &&
76
+ "stderr" in error &&
77
+ typeof error.stderr === "string"
78
+ ) {
71
79
  console.error(`GoScript stderr:\n${error.stderr}`);
72
80
  }
73
- if (error.stdout) {
81
+ if (
82
+ typeof error === "object" &&
83
+ error !== null &&
84
+ "stdout" in error &&
85
+ typeof error.stdout === "string"
86
+ ) {
74
87
  console.error(`GoScript stdout:\n${error.stdout}`);
75
88
  }
76
- throw new Error(`GoScript compilation failed: ${error.message}`);
89
+ throw new Error(`GoScript compilation failed: ${compileErr.message}`, {
90
+ cause: error,
91
+ });
77
92
  }
78
93
  }
79
94
 
@@ -146,35 +146,35 @@ func (c *PackageCompiler) writeProtobufExports(indexFile *os.File, fileName stri
146
146
  // - export function Name
147
147
  // We avoid type-only exports for now.
148
148
  var exports []string
149
- lines := strings.Split(string(content), "\n")
150
- for _, ln := range lines {
149
+ lines := strings.SplitSeq(string(content), "\n")
150
+ for ln := range lines {
151
151
  l := strings.TrimSpace(ln)
152
- if strings.HasPrefix(l, "export const ") {
153
- rest := strings.TrimPrefix(l, "export const ")
152
+ if after, ok := strings.CutPrefix(l, "export const "); ok {
153
+ rest := after
154
154
  name := takeIdent(rest)
155
155
  if name != "" {
156
156
  exports = append(exports, name)
157
157
  }
158
158
  continue
159
159
  }
160
- if strings.HasPrefix(l, "export interface ") {
161
- rest := strings.TrimPrefix(l, "export interface ")
160
+ if after, ok := strings.CutPrefix(l, "export interface "); ok {
161
+ rest := after
162
162
  name := takeIdent(rest)
163
163
  if name != "" {
164
164
  exports = append(exports, name)
165
165
  }
166
166
  continue
167
167
  }
168
- if strings.HasPrefix(l, "export class ") {
169
- rest := strings.TrimPrefix(l, "export class ")
168
+ if after, ok := strings.CutPrefix(l, "export class "); ok {
169
+ rest := after
170
170
  name := takeIdent(rest)
171
171
  if name != "" {
172
172
  exports = append(exports, name)
173
173
  }
174
174
  continue
175
175
  }
176
- if strings.HasPrefix(l, "export function ") {
177
- rest := strings.TrimPrefix(l, "export function ")
176
+ if after, ok := strings.CutPrefix(l, "export function "); ok {
177
+ rest := after
178
178
  name := takeIdent(rest)
179
179
  if name != "" {
180
180
  exports = append(exports, name)
@@ -236,9 +236,9 @@ func (c *FileCompiler) addProtobufImports() error {
236
236
 
237
237
  for _, fileName := range c.pkg.CompiledGoFiles {
238
238
  baseFileName := filepath.Base(fileName)
239
- if strings.HasSuffix(baseFileName, ".pb.go") {
239
+ if before, ok := strings.CutSuffix(baseFileName, ".pb.go"); ok {
240
240
  // Check if there's a corresponding .pb.ts file
241
- pbTsFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb.ts"
241
+ pbTsFileName := before + ".pb.ts"
242
242
  pbTsPath := filepath.Join(packageDir, pbTsFileName)
243
243
 
244
244
  if _, err := os.Stat(pbTsPath); err == nil {
@@ -252,28 +252,28 @@ func (c *FileCompiler) addProtobufImports() error {
252
252
 
253
253
  // Discover exported identifiers (const/interface/class/function)
254
254
  var exports []string
255
- for _, ln := range strings.Split(string(content), "\n") {
255
+ for ln := range strings.SplitSeq(string(content), "\n") {
256
256
  l := strings.TrimSpace(ln)
257
- if strings.HasPrefix(l, "export const ") {
258
- if name := takeIdent(strings.TrimPrefix(l, "export const ")); name != "" {
257
+ if after, ok := strings.CutPrefix(l, "export const "); ok {
258
+ if name := takeIdent(after); name != "" {
259
259
  exports = append(exports, name)
260
260
  }
261
261
  continue
262
262
  }
263
- if strings.HasPrefix(l, "export interface ") {
264
- if name := takeIdent(strings.TrimPrefix(l, "export interface ")); name != "" {
263
+ if after, ok := strings.CutPrefix(l, "export interface "); ok {
264
+ if name := takeIdent(after); name != "" {
265
265
  exports = append(exports, name)
266
266
  }
267
267
  continue
268
268
  }
269
- if strings.HasPrefix(l, "export class ") {
270
- if name := takeIdent(strings.TrimPrefix(l, "export class ")); name != "" {
269
+ if after, ok := strings.CutPrefix(l, "export class "); ok {
270
+ if name := takeIdent(after); name != "" {
271
271
  exports = append(exports, name)
272
272
  }
273
273
  continue
274
274
  }
275
- if strings.HasPrefix(l, "export function ") {
276
- if name := takeIdent(strings.TrimPrefix(l, "export function ")); name != "" {
275
+ if after, ok := strings.CutPrefix(l, "export function "); ok {
276
+ if name := takeIdent(after); name != "" {
277
277
  exports = append(exports, name)
278
278
  }
279
279
  continue
@@ -4,6 +4,7 @@ import (
4
4
  "fmt"
5
5
  "go/ast"
6
6
  "go/types"
7
+ "maps"
7
8
  "slices"
8
9
  "strings"
9
10
  )
@@ -72,8 +73,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
72
73
  }
73
74
 
74
75
  // Generate getters and setters for EMBEDDED struct fields themselves
75
- for i := range underlyingStruct.NumFields() {
76
- field := underlyingStruct.Field(i)
76
+ for field := range underlyingStruct.Fields() {
77
77
  if field.Anonymous() {
78
78
  fieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
79
79
  c.writeGetterSetter(fieldKeyName, field.Type(), nil, nil, nil)
@@ -95,8 +95,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
95
95
  c.tsw.Indent(1)
96
96
  c.tsw.WriteLine("")
97
97
 
98
- for i := 0; i < underlyingStruct.NumFields(); i++ {
99
- field := underlyingStruct.Field(i)
98
+ for field := range underlyingStruct.Fields() {
100
99
  var fieldKeyName string
101
100
  if field.Anonymous() {
102
101
  fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
@@ -166,25 +165,26 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
166
165
  c.tsw.WriteLine("")
167
166
 
168
167
  // Generate the clone method
169
- cloneReturnType := className
168
+ var cloneReturnType strings.Builder
169
+ cloneReturnType.WriteString(className)
170
170
  if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
171
- cloneReturnType += "<"
171
+ cloneReturnType.WriteString("<")
172
172
  first := true
173
173
  for _, field := range a.TypeParams.List {
174
174
  for _, name := range field.Names {
175
175
  if !first {
176
- cloneReturnType += ", "
176
+ cloneReturnType.WriteString(", ")
177
177
  }
178
178
  first = false
179
- cloneReturnType += name.Name
179
+ cloneReturnType.WriteString(name.Name)
180
180
  }
181
181
  }
182
- cloneReturnType += ">"
182
+ cloneReturnType.WriteString(">")
183
183
  }
184
184
 
185
- c.tsw.WriteLinef("public clone(): %s {", cloneReturnType)
185
+ c.tsw.WriteLinef("public clone(): %s {", cloneReturnType.String())
186
186
  c.tsw.Indent(1)
187
- c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType)
187
+ c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType.String())
188
188
  c.tsw.WriteLine("cloned._fields = {")
189
189
  c.tsw.Indent(1)
190
190
 
@@ -257,8 +257,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
257
257
  seenPromotedFields := make(map[string]bool)
258
258
  directMethods := make(map[string]bool)
259
259
  // Populate directMethods (methods defined directly on this struct type)
260
- for i := range goStructType.NumMethods() {
261
- method := goStructType.Method(i)
260
+ for method := range goStructType.Methods() {
262
261
  sig := method.Type().(*types.Signature)
263
262
  if sig.Recv() != nil {
264
263
  recvType := sig.Recv().Type()
@@ -272,8 +271,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
272
271
  }
273
272
  }
274
273
 
275
- for i := range underlyingStruct.NumFields() {
276
- field := underlyingStruct.Field(i)
274
+ for field := range underlyingStruct.Fields() {
277
275
  if !field.Anonymous() {
278
276
  continue
279
277
  }
@@ -293,8 +291,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
293
291
  // Promoted fields
294
292
  if namedEmbedded, ok := trueEmbeddedType.(*types.Named); ok {
295
293
  if underlyingEmbeddedStruct, ok := namedEmbedded.Underlying().(*types.Struct); ok {
296
- for j := 0; j < underlyingEmbeddedStruct.NumFields(); j++ {
297
- promotedField := underlyingEmbeddedStruct.Field(j)
294
+ for promotedField := range underlyingEmbeddedStruct.Fields() {
298
295
  if !promotedField.Exported() && promotedField.Pkg() != c.pkg.Types {
299
296
  continue
300
297
  }
@@ -304,8 +301,8 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
304
301
  }
305
302
  // Check for conflicts with outer struct's own fields or other promoted fields
306
303
  conflict := false
307
- for k := 0; k < underlyingStruct.NumFields(); k++ {
308
- if !underlyingStruct.Field(k).Anonymous() && underlyingStruct.Field(k).Name() == promotedFieldName {
304
+ for field := range underlyingStruct.Fields() {
305
+ if !field.Anonymous() && field.Name() == promotedFieldName {
309
306
  conflict = true
310
307
  break
311
308
  }
@@ -358,8 +355,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
358
355
  }
359
356
  }
360
357
  embeddedMethodSet := types.NewMethodSet(methodSetType)
361
- for k := range embeddedMethodSet.Len() {
362
- methodSelection := embeddedMethodSet.At(k)
358
+ for methodSelection := range embeddedMethodSet.Methods() {
363
359
  method := methodSelection.Obj().(*types.Func)
364
360
  methodName := method.Name()
365
361
 
@@ -367,8 +363,8 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
367
363
  if len(methodSelection.Index()) == 1 && !directMethods[methodName] && !seenPromotedFields[methodName] {
368
364
  // Check for conflict with outer struct's own fields
369
365
  conflictWithField := false
370
- for k_idx := 0; k_idx < underlyingStruct.NumFields(); k_idx++ {
371
- if !underlyingStruct.Field(k_idx).Anonymous() && underlyingStruct.Field(k_idx).Name() == methodName {
366
+ for field := range underlyingStruct.Fields() {
367
+ if !field.Anonymous() && field.Name() == methodName {
372
368
  conflictWithField = true
373
369
  break
374
370
  }
@@ -457,8 +453,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
457
453
  c.tsw.WriteLiterally(" [")
458
454
  // Collect methods for the struct type
459
455
  var structMethods []*types.Func
460
- for i := range goStructType.NumMethods() {
461
- method := goStructType.Method(i)
456
+ for method := range goStructType.Methods() {
462
457
  // Ensure it's a method directly on this type (not promoted here, promotion handled separately)
463
458
  // Check if receiver is *T or T where T is goStructType
464
459
  sig := method.Type().(*types.Signature)
@@ -517,6 +512,179 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
517
512
  return nil
518
513
  }
519
514
 
515
+ // WriteNamedStructTypeSpec generates a TypeScript class for a named type whose
516
+ // underlying type is a struct defined elsewhere.
517
+ func (c *GoToTSCompiler) WriteNamedStructTypeSpec(a *ast.TypeSpec, named *types.Named) error {
518
+ isInsideFunction := false
519
+ if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
520
+ isInsideFunction = nodeInfo.IsInsideFunction
521
+ }
522
+ if !isInsideFunction {
523
+ c.tsw.WriteLiterally("export ")
524
+ }
525
+ c.tsw.WriteLiterally("class ")
526
+ if err := c.WriteValueExpr(a.Name); err != nil {
527
+ return err
528
+ }
529
+
530
+ if a.TypeParams != nil {
531
+ c.WriteTypeParameters(a.TypeParams)
532
+ }
533
+
534
+ c.tsw.WriteLiterally(" extends ")
535
+ c.WriteTypeExpr(a.Type)
536
+ c.tsw.WriteLiterally(" ")
537
+ c.tsw.WriteLine("{")
538
+ c.tsw.Indent(1)
539
+
540
+ className := sanitizeIdentifier(a.Name.Name)
541
+ underlyingStruct, ok := named.Underlying().(*types.Struct)
542
+ if !ok {
543
+ return fmt.Errorf("underlying type of %s is not a struct", a.Name.Name)
544
+ }
545
+
546
+ c.tsw.WriteLine("constructor(init?: any) {")
547
+ c.tsw.Indent(1)
548
+ c.tsw.WriteLine("super(init)")
549
+ c.tsw.Indent(-1)
550
+ c.tsw.WriteLine("}")
551
+ c.tsw.WriteLine("")
552
+
553
+ c.tsw.WriteLinef("public clone(): %s {", className)
554
+ c.tsw.Indent(1)
555
+ c.tsw.WriteLinef("const cloned = new %s()", className)
556
+ c.tsw.WriteLine("cloned._fields = {")
557
+ c.tsw.Indent(1)
558
+
559
+ firstFieldWritten := false
560
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
561
+ field := underlyingStruct.Field(i)
562
+ fieldType := field.Type()
563
+ var fieldKeyName string
564
+ if field.Anonymous() {
565
+ fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
566
+ } else {
567
+ fieldKeyName = field.Name()
568
+ }
569
+ if fieldKeyName == "_" {
570
+ continue
571
+ }
572
+
573
+ if firstFieldWritten {
574
+ c.tsw.WriteLine(",")
575
+ }
576
+ c.writeClonedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
577
+ firstFieldWritten = true
578
+ }
579
+ if firstFieldWritten {
580
+ c.tsw.WriteLine("")
581
+ }
582
+
583
+ c.tsw.Indent(-1)
584
+ c.tsw.WriteLine("}")
585
+ c.tsw.WriteLine("return cloned")
586
+ c.tsw.Indent(-1)
587
+ c.tsw.WriteLine("}")
588
+
589
+ for _, fileSyntax := range c.pkg.Syntax {
590
+ for _, decl := range fileSyntax.Decls {
591
+ funcDecl, isFunc := decl.(*ast.FuncDecl)
592
+ if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
593
+ continue
594
+ }
595
+ recvType := funcDecl.Recv.List[0].Type
596
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
597
+ recvType = starExpr.X
598
+ }
599
+
600
+ var recvTypeName string
601
+ if ident, ok := recvType.(*ast.Ident); ok {
602
+ recvTypeName = sanitizeIdentifier(ident.Name)
603
+ } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
604
+ if ident, ok := indexExpr.X.(*ast.Ident); ok {
605
+ recvTypeName = sanitizeIdentifier(ident.Name)
606
+ }
607
+ }
608
+
609
+ if recvTypeName == className {
610
+ c.tsw.WriteLine("")
611
+ if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
612
+ return err
613
+ }
614
+ }
615
+ }
616
+ }
617
+
618
+ c.tsw.WriteLine("")
619
+ c.tsw.WriteLine("// Register this type with the runtime type system")
620
+
621
+ structName := className
622
+ pkgPath := c.pkg.Types.Path()
623
+ pkgName := c.pkg.Types.Name()
624
+ if pkgPath != "" && pkgName != "main" {
625
+ structName = pkgPath + "." + className
626
+ } else if pkgName == "main" {
627
+ structName = "main." + className
628
+ }
629
+
630
+ c.tsw.WriteLine("static __typeInfo = $.registerStructType(")
631
+ c.tsw.WriteLinef(" %q,", structName)
632
+ c.tsw.WriteLinef(" new %s(),", className)
633
+ c.tsw.WriteLiterally(" [")
634
+
635
+ var structMethods []*types.Func
636
+ for method := range named.Methods() {
637
+ sig := method.Type().(*types.Signature)
638
+ recv := sig.Recv().Type()
639
+ if ptr, ok := recv.(*types.Pointer); ok {
640
+ recv = ptr.Elem()
641
+ }
642
+ if namedRecv, ok := recv.(*types.Named); ok && namedRecv.Obj() == named.Obj() {
643
+ structMethods = append(structMethods, method)
644
+ }
645
+ }
646
+ c.writeMethodSignatures(structMethods)
647
+ c.tsw.WriteLiterally("],")
648
+ c.tsw.WriteLine("")
649
+
650
+ c.tsw.WriteLinef(" %s,", className)
651
+ c.tsw.WriteLiterally(" {")
652
+ firstField := true
653
+ for i := 0; i < underlyingStruct.NumFields(); i++ {
654
+ field := underlyingStruct.Field(i)
655
+ var fieldKeyName string
656
+ if field.Anonymous() {
657
+ fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
658
+ } else {
659
+ fieldKeyName = field.Name()
660
+ }
661
+ if fieldKeyName == "_" {
662
+ continue
663
+ }
664
+ if !firstField {
665
+ c.tsw.WriteLiterally(", ")
666
+ }
667
+ firstField = false
668
+ c.tsw.WriteLiterallyf("%q: ", fieldKeyName)
669
+
670
+ tag := underlyingStruct.Tag(i)
671
+ if tag != "" {
672
+ c.tsw.WriteLiterally("{ type: ")
673
+ c.writeTypeInfoObject(field.Type())
674
+ c.tsw.WriteLiterallyf(", tag: %q }", tag)
675
+ } else {
676
+ c.writeTypeInfoObject(field.Type())
677
+ }
678
+ }
679
+ c.tsw.WriteLiterally("}")
680
+ c.tsw.WriteLine("")
681
+ c.tsw.WriteLine(");")
682
+
683
+ c.tsw.Indent(-1)
684
+ c.tsw.WriteLine("}")
685
+ return nil
686
+ }
687
+
520
688
  // generateFlattenedInitTypeString generates a TypeScript type string for the
521
689
  // initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
522
690
  // The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
@@ -545,8 +713,7 @@ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named
545
713
  }
546
714
 
547
715
  // First add the direct fields and track embedded types
548
- for i := 0; i < underlying.NumFields(); i++ {
549
- field := underlying.Field(i)
716
+ for field := range underlying.Fields() {
550
717
  fieldName := field.Name()
551
718
 
552
719
  // Skip underscore fields
@@ -587,9 +754,7 @@ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named
587
754
  // not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
588
755
 
589
756
  // Add embedded types to the field map (these are the names of the embedded structs themselves)
590
- for embeddedName, embeddedTSType := range embeddedTypeMap {
591
- fieldMap[embeddedName] = embeddedTSType
592
- }
757
+ maps.Copy(fieldMap, embeddedTypeMap)
593
758
 
594
759
  var fieldNames []string
595
760
  for name := range fieldMap {
package/compiler/spec.go CHANGED
@@ -366,6 +366,20 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
366
366
 
367
367
  if recvTypeName == className {
368
368
  c.tsw.WriteLiterally("export function ")
369
+ var isAsync bool
370
+ if obj := c.pkg.TypesInfo.Defs[funcDecl.Name]; obj != nil {
371
+ isAsync = c.analysis.IsAsyncFunc(obj)
372
+ }
373
+ if !isAsync {
374
+ bodyNeedsAsync, err := c.funcBodyNeedsAsync(funcDecl, false)
375
+ if err != nil {
376
+ return err
377
+ }
378
+ isAsync = bodyNeedsAsync
379
+ }
380
+ if isAsync {
381
+ c.tsw.WriteLiterally("async ")
382
+ }
369
383
  c.tsw.WriteLiterally(className)
370
384
  c.tsw.WriteLiterally("_")
371
385
  c.tsw.WriteLiterally(funcDecl.Name.Name)
@@ -403,6 +417,9 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
403
417
  // Add return type
404
418
  if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
405
419
  c.tsw.WriteLiterally(": ")
420
+ if isAsync {
421
+ c.tsw.WriteLiterally("Promise<")
422
+ }
406
423
  if len(funcDecl.Type.Results.List) == 1 {
407
424
  c.WriteTypeExpr(funcDecl.Type.Results.List[0].Type)
408
425
  } else {
@@ -424,8 +441,15 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
424
441
  }
425
442
  c.tsw.WriteLiterally("]")
426
443
  }
444
+ if isAsync {
445
+ c.tsw.WriteLiterally(">")
446
+ }
427
447
  } else {
428
- c.tsw.WriteLiterally(": void")
448
+ if isAsync {
449
+ c.tsw.WriteLiterally(": Promise<void>")
450
+ } else {
451
+ c.tsw.WriteLiterally(": void")
452
+ }
429
453
  }
430
454
 
431
455
  c.tsw.WriteLine(" {")
@@ -476,6 +500,11 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
476
500
  default:
477
501
  // Check if this type has receiver methods
478
502
  if c.hasReceiverMethods(a.Name.Name) {
503
+ if named, ok := c.pkg.TypesInfo.Defs[a.Name].Type().(*types.Named); ok {
504
+ if _, isStruct := named.Underlying().(*types.Struct); isStruct {
505
+ return c.WriteNamedStructTypeSpec(a, named)
506
+ }
507
+ }
479
508
  return c.WriteNamedTypeWithMethods(a)
480
509
  }
481
510
 
@@ -560,8 +589,8 @@ func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.Interfac
560
589
  // Collect methods for the interface type
561
590
  var interfaceMethods []*types.Func
562
591
  if ifaceType != nil { // ifaceType is *types.Interface
563
- for i := range ifaceType.NumExplicitMethods() {
564
- interfaceMethods = append(interfaceMethods, ifaceType.ExplicitMethod(i))
592
+ for method := range ifaceType.ExplicitMethods() {
593
+ interfaceMethods = append(interfaceMethods, method)
565
594
  }
566
595
  // TODO: Handle embedded interface methods if necessary for full signature collection.
567
596
  // For now, explicit methods are covered.
@@ -313,8 +313,8 @@ func (c *GoToTSCompiler) writeMultiVarAssignFromCall(lhs []ast.Expr, callExpr *a
313
313
  if funType := c.pkg.TypesInfo.TypeOf(callExpr.Fun); funType != nil {
314
314
  if funcType, ok := funType.Underlying().(*types.Signature); ok {
315
315
  if funcType.Results() != nil && funcType.Results().Len() > 0 {
316
- for i := 0; i < funcType.Results().Len(); i++ {
317
- resultTypes = append(resultTypes, funcType.Results().At(i))
316
+ for v := range funcType.Results().Variables() {
317
+ resultTypes = append(resultTypes, v)
318
318
  }
319
319
  }
320
320
  }
@@ -2,6 +2,23 @@ package compiler
2
2
 
3
3
  import "go/types"
4
4
 
5
+ func qualifiedTypeName(namedType *types.Named) string {
6
+ if namedType == nil || namedType.Obj() == nil {
7
+ return ""
8
+ }
9
+
10
+ typeName := namedType.Obj().Name()
11
+ if pkg := namedType.Obj().Pkg(); pkg != nil {
12
+ switch {
13
+ case pkg.Name() == "main":
14
+ typeName = "main." + typeName
15
+ case pkg.Path() != "":
16
+ typeName = pkg.Path() + "." + typeName
17
+ }
18
+ }
19
+ return typeName
20
+ }
21
+
5
22
  // writeTypeInfoObject writes a TypeScript TypeInfo object literal for a given Go type.
6
23
  func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
7
24
  if typ == nil {
@@ -17,7 +34,7 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
17
34
  // For all other named types, output their name as a string literal.
18
35
  // This relies on the type being registered elsewhere (e.g., via registerStructType or registerInterfaceType)
19
36
  // so the TypeScript runtime can resolve the reference.
20
- c.tsw.WriteLiterallyf("%q", namedType.Obj().Name())
37
+ c.tsw.WriteLiterallyf("%q", qualifiedTypeName(namedType))
21
38
  }
22
39
  return // Return after handling the named type by reference.
23
40
  }
@@ -64,8 +81,8 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
64
81
  case *types.Interface: // Anonymous interface or underlying of a non-named type alias
65
82
  c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, methods: [")
66
83
  var methods []*types.Func
67
- for i := 0; i < t.NumExplicitMethods(); i++ {
68
- methods = append(methods, t.ExplicitMethod(i))
84
+ for method := range t.ExplicitMethods() {
85
+ methods = append(methods, method)
69
86
  }
70
87
  // TODO: Handle embedded methods for anonymous interfaces if needed.
71
88
  c.writeMethodSignatures(methods) // Calls writeMethodSignatures -> writeTypeInfoObject
@@ -47,12 +47,10 @@ func (c *GoToTSCompiler) constraintIncludesString(constraint *types.Interface) b
47
47
  return false // Pure method interface, no type terms
48
48
  }
49
49
  // For union constraints like []byte | string, check each term
50
- for i := 0; i < constraint.NumEmbeddeds(); i++ {
51
- embedded := constraint.EmbeddedType(i)
50
+ for embedded := range constraint.EmbeddedTypes() {
52
51
  // Check if embedded is a union
53
52
  if union, isUnion := embedded.(*types.Union); isUnion {
54
- for j := 0; j < union.Len(); j++ {
55
- term := union.Term(j)
53
+ for term := range union.Terms() {
56
54
  termType := term.Type()
57
55
  // Check if term is string or ~string
58
56
  if basic, isBasic := termType.Underlying().(*types.Basic); isBasic {
package/compiler/type.go CHANGED
@@ -605,8 +605,7 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
605
605
  c.tsw.Indent(1)
606
606
  c.tsw.WriteLine("") // Newline after opening brace, before the first method
607
607
 
608
- for i := 0; i < iface.NumExplicitMethods(); i++ {
609
- method := iface.ExplicitMethod(i)
608
+ for method := range iface.ExplicitMethods() {
610
609
  sig := method.Type().(*types.Signature)
611
610
 
612
611
  // Find corresponding ast.Field for comments if astNode is available
@@ -745,14 +744,14 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
745
744
 
746
745
  // Handle embedded types
747
746
  if iface.NumEmbeddeds() > 0 {
748
- for i := 0; i < iface.NumEmbeddeds(); i++ {
747
+ for etyp := range iface.EmbeddedTypes() {
749
748
  if firstPartWritten {
750
749
  c.tsw.WriteLiterally(" & ")
751
750
  } else {
752
751
  // This is the first part being written (no explicit methods, only embedded)
753
752
  firstPartWritten = true
754
753
  }
755
- embeddedType := iface.EmbeddedType(i)
754
+ embeddedType := etyp
756
755
  // When WriteGoType encounters an interface, it will call WriteInterfaceType
757
756
  // which will pass nil for astNode, so comments for deeply embedded interface literals
758
757
  // might not be available unless they are named types.