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
@@ -240,6 +240,53 @@ func NewPackageAnalysis() *PackageAnalysis {
240
240
  }
241
241
  }
242
242
 
243
+ // collectZeroValueTypeNames records named struct types whose zero value emits a
244
+ // runtime constructor call.
245
+ func collectZeroValueTypeNames(typ types.Type, names map[string]struct{}) {
246
+ switch t := typ.(type) {
247
+ case *types.Array:
248
+ collectZeroValueTypeNames(t.Elem(), names)
249
+ case *types.Named:
250
+ if _, isStruct := t.Underlying().(*types.Struct); isStruct {
251
+ names[t.Obj().Name()] = struct{}{}
252
+ return
253
+ }
254
+ collectZeroValueTypeNames(t.Underlying(), names)
255
+ case *types.Alias:
256
+ collectZeroValueTypeNames(t.Underlying(), names)
257
+ }
258
+ }
259
+
260
+ // addTypeRefsFromZeroValue adds same-package type imports needed when code
261
+ // generation synthesizes a zero value without an explicit AST type reference.
262
+ func addTypeRefsFromZeroValue(analysis *PackageAnalysis, currentFileName string, typ types.Type, refs map[string][]string) {
263
+ typeNames := make(map[string]struct{})
264
+ collectZeroValueTypeNames(typ, typeNames)
265
+ if len(typeNames) == 0 {
266
+ return
267
+ }
268
+
269
+ currentFileTypes := analysis.TypeDefs[currentFileName]
270
+ for typeName := range typeNames {
271
+ if slices.Contains(currentFileTypes, typeName) {
272
+ continue
273
+ }
274
+
275
+ for sourceFile, types := range analysis.TypeDefs {
276
+ if sourceFile == currentFileName || !slices.Contains(types, typeName) {
277
+ continue
278
+ }
279
+
280
+ if refs[sourceFile] == nil {
281
+ refs[sourceFile] = []string{}
282
+ }
283
+ if !slices.Contains(refs[sourceFile], typeName) {
284
+ refs[sourceFile] = append(refs[sourceFile], typeName)
285
+ }
286
+ }
287
+ }
288
+ }
289
+
243
290
  // ensureNodeData ensures that NodeData exists for a given node and returns it
244
291
  func (a *Analysis) ensureNodeData(node ast.Node) *NodeInfo {
245
292
  if node == nil {
@@ -1148,8 +1195,7 @@ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) as
1148
1195
  }
1149
1196
 
1150
1197
  // For each method in the interface, check if the struct implements it
1151
- for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
1152
- interfaceMethod := interfaceType.ExplicitMethod(i)
1198
+ for interfaceMethod := range interfaceType.ExplicitMethods() {
1153
1199
 
1154
1200
  // Find the corresponding method in the struct type
1155
1201
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
@@ -1417,8 +1463,7 @@ func (a *Analysis) addImportsForPromotedMethods(pkg *packages.Package) {
1417
1463
  }
1418
1464
 
1419
1465
  // Look for embedded fields
1420
- for i := 0; i < structType.NumFields(); i++ {
1421
- field := structType.Field(i)
1466
+ for field := range structType.Fields() {
1422
1467
  if !field.Embedded() {
1423
1468
  continue
1424
1469
  }
@@ -1442,8 +1487,7 @@ func (a *Analysis) addImportsForPromotedMethods(pkg *packages.Package) {
1442
1487
  embeddedMethodSet := types.NewMethodSet(methodSetType)
1443
1488
 
1444
1489
  // Scan all methods in the method set
1445
- for j := 0; j < embeddedMethodSet.Len(); j++ {
1446
- selection := embeddedMethodSet.At(j)
1490
+ for selection := range embeddedMethodSet.Methods() {
1447
1491
  method := selection.Obj()
1448
1492
  sig, ok := method.Type().(*types.Signature)
1449
1493
  if !ok {
@@ -1452,16 +1496,14 @@ func (a *Analysis) addImportsForPromotedMethods(pkg *packages.Package) {
1452
1496
 
1453
1497
  // Scan parameters
1454
1498
  if sig.Params() != nil {
1455
- for k := 0; k < sig.Params().Len(); k++ {
1456
- param := sig.Params().At(k)
1499
+ for param := range sig.Params().Variables() {
1457
1500
  a.collectPackageFromType(param.Type(), pkg.Types, packagesToAdd)
1458
1501
  }
1459
1502
  }
1460
1503
 
1461
1504
  // Scan results
1462
1505
  if sig.Results() != nil {
1463
- for k := 0; k < sig.Results().Len(); k++ {
1464
- result := sig.Results().At(k)
1506
+ for result := range sig.Results().Variables() {
1465
1507
  a.collectPackageFromType(result.Type(), pkg.Types, packagesToAdd)
1466
1508
  }
1467
1509
  }
@@ -1494,17 +1536,16 @@ func (a *Analysis) collectPackageFromType(t types.Type, currentPkg *types.Packag
1494
1536
  }
1495
1537
  // Check type arguments for generics
1496
1538
  if typ.TypeArgs() != nil {
1497
- for i := 0; i < typ.TypeArgs().Len(); i++ {
1498
- a.collectPackageFromType(typ.TypeArgs().At(i), currentPkg, packagesToAdd)
1539
+ for t := range typ.TypeArgs().Types() {
1540
+ a.collectPackageFromType(t, currentPkg, packagesToAdd)
1499
1541
  }
1500
1542
  }
1501
1543
  case *types.Interface:
1502
1544
  // For interfaces, we need to check embedded interfaces and method signatures
1503
- for i := 0; i < typ.NumEmbeddeds(); i++ {
1504
- a.collectPackageFromType(typ.EmbeddedType(i), currentPkg, packagesToAdd)
1545
+ for etyp := range typ.EmbeddedTypes() {
1546
+ a.collectPackageFromType(etyp, currentPkg, packagesToAdd)
1505
1547
  }
1506
- for i := 0; i < typ.NumExplicitMethods(); i++ {
1507
- method := typ.ExplicitMethod(i)
1548
+ for method := range typ.ExplicitMethods() {
1508
1549
  a.collectPackageFromType(method.Type(), currentPkg, packagesToAdd)
1509
1550
  }
1510
1551
  case *types.Pointer:
@@ -1521,14 +1562,14 @@ func (a *Analysis) collectPackageFromType(t types.Type, currentPkg *types.Packag
1521
1562
  case *types.Signature:
1522
1563
  // Collect from parameters
1523
1564
  if typ.Params() != nil {
1524
- for i := 0; i < typ.Params().Len(); i++ {
1525
- a.collectPackageFromType(typ.Params().At(i).Type(), currentPkg, packagesToAdd)
1565
+ for v := range typ.Params().Variables() {
1566
+ a.collectPackageFromType(v.Type(), currentPkg, packagesToAdd)
1526
1567
  }
1527
1568
  }
1528
1569
  // Collect from results
1529
1570
  if typ.Results() != nil {
1530
- for i := 0; i < typ.Results().Len(); i++ {
1531
- a.collectPackageFromType(typ.Results().At(i).Type(), currentPkg, packagesToAdd)
1571
+ for v := range typ.Results().Variables() {
1572
+ a.collectPackageFromType(v.Type(), currentPkg, packagesToAdd)
1532
1573
  }
1533
1574
  }
1534
1575
  }
@@ -1693,6 +1734,37 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1693
1734
  }
1694
1735
  }
1695
1736
  }
1737
+
1738
+ if callExpr, ok := n.(*ast.CallExpr); ok {
1739
+ if funIdent, ok := callExpr.Fun.(*ast.Ident); ok && funIdent.Name == "make" && len(callExpr.Args) > 0 {
1740
+ if typ := pkg.TypesInfo.TypeOf(callExpr.Args[0]); typ != nil {
1741
+ if chanType, ok := typ.Underlying().(*types.Chan); ok {
1742
+ addTypeRefsFromZeroValue(analysis, baseFileName, chanType.Elem(), typeRefsFromOtherFiles)
1743
+ }
1744
+ }
1745
+ }
1746
+ }
1747
+
1748
+ if indexExpr, ok := n.(*ast.IndexExpr); ok {
1749
+ if tv, ok := pkg.TypesInfo.Types[indexExpr.X]; ok {
1750
+ if mapType, ok := tv.Type.Underlying().(*types.Map); ok {
1751
+ addTypeRefsFromZeroValue(analysis, baseFileName, mapType.Elem(), typeRefsFromOtherFiles)
1752
+ return true
1753
+ }
1754
+
1755
+ if typeParam, ok := tv.Type.(*types.TypeParam); ok {
1756
+ constraint := typeParam.Constraint()
1757
+ if constraint == nil {
1758
+ return true
1759
+ }
1760
+ if iface, ok := constraint.Underlying().(*types.Interface); ok && hasMapConstraint(iface) {
1761
+ if mapValueType := getMapValueTypeFromConstraint(iface); mapValueType != nil {
1762
+ addTypeRefsFromZeroValue(analysis, baseFileName, mapValueType, typeRefsFromOtherFiles)
1763
+ }
1764
+ }
1765
+ }
1766
+ }
1767
+ }
1696
1768
  return true
1697
1769
  })
1698
1770
 
@@ -1830,8 +1902,8 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1830
1902
  // Check if this type has the method being called
1831
1903
  methodName := selectorExpr.Sel.Name
1832
1904
  found := false
1833
- for j := 0; j < namedType.NumMethods(); j++ {
1834
- if namedType.Method(j).Name() == methodName {
1905
+ for method := range namedType.Methods() {
1906
+ if method.Name() == methodName {
1835
1907
  found = true
1836
1908
  break
1837
1909
  }
@@ -2351,8 +2423,7 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
2351
2423
  // findStructMethod finds a method with the given name on a named type
2352
2424
  func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
2353
2425
  // Check methods directly on the type
2354
- for i := 0; i < namedType.NumMethods(); i++ {
2355
- method := namedType.Method(i)
2426
+ for method := range namedType.Methods() {
2356
2427
  if method.Name() == methodName {
2357
2428
  return method
2358
2429
  }
@@ -2455,8 +2526,7 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
2455
2526
  }
2456
2527
 
2457
2528
  // Track implementations for all interface methods
2458
- for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
2459
- interfaceMethod := interfaceType.ExplicitMethod(j)
2529
+ for interfaceMethod := range interfaceType.ExplicitMethods() {
2460
2530
 
2461
2531
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2462
2532
  if structMethod != nil {
@@ -2514,8 +2584,7 @@ func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
2514
2584
  }
2515
2585
 
2516
2586
  // Track implementations for all interface methods
2517
- for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
2518
- interfaceMethod := interfaceType.ExplicitMethod(j)
2587
+ for interfaceMethod := range interfaceType.ExplicitMethods() {
2519
2588
 
2520
2589
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2521
2590
  if structMethod != nil {
@@ -2659,8 +2728,7 @@ func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceT
2659
2728
  // trackImplementation records that a named type implements an interface
2660
2729
  func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
2661
2730
  // For each method in the interface, find the corresponding implementation
2662
- for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
2663
- interfaceMethod := interfaceType.ExplicitMethod(i)
2731
+ for interfaceMethod := range interfaceType.ExplicitMethods() {
2664
2732
 
2665
2733
  // Find the method in the implementing type
2666
2734
  structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
@@ -2672,8 +2740,7 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
2672
2740
 
2673
2741
  // findMethodInType finds a method with the given name in a named type
2674
2742
  func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
2675
- for i := 0; i < namedType.NumMethods(); i++ {
2676
- method := namedType.Method(i)
2743
+ for method := range namedType.Methods() {
2677
2744
  if method.Name() == methodName {
2678
2745
  return method
2679
2746
  }
@@ -2696,7 +2763,7 @@ func (v *analysisVisitor) analyzeAllMethodsAsync() {
2696
2763
  // We need to iterate multiple times because methods in cycles can call each other,
2697
2764
  // and we need to propagate async status until no changes occur
2698
2765
  maxIterations := 10
2699
- for iteration := 0; iteration < maxIterations; iteration++ {
2766
+ for range maxIterations {
2700
2767
  changed := false
2701
2768
 
2702
2769
  for _, methodKey := range cycles {
@@ -6,6 +6,7 @@ import (
6
6
  "go/token"
7
7
  "go/types"
8
8
  "os"
9
+ "slices"
9
10
  "testing"
10
11
 
11
12
  "golang.org/x/tools/go/packages"
@@ -329,13 +330,7 @@ func TestDiscoverGsPackages(t *testing.T) {
329
330
  // Check for some known packages that should exist
330
331
  expectedPackages := []string{"sync", "bytes", "strings"}
331
332
  for _, expected := range expectedPackages {
332
- found := false
333
- for _, pkg := range packages {
334
- if pkg == expected {
335
- found = true
336
- break
337
- }
338
- }
333
+ found := slices.Contains(packages, expected)
339
334
  if !found {
340
335
  t.Logf("Expected package '%s' not found in discovered packages: %v", expected, packages)
341
336
  }
@@ -59,8 +59,8 @@ func (w *TSCodeWriter) WriteImport(symbolName, importPath string) {
59
59
 
60
60
  // WriteCommentLine writes a comment as a // line.
61
61
  func (w *TSCodeWriter) WriteCommentLine(commentText string) {
62
- lines := strings.Split(commentText, "\n")
63
- for _, line := range lines {
62
+ lines := strings.SplitSeq(commentText, "\n")
63
+ for line := range lines {
64
64
  w.WriteLinef("// %s", line)
65
65
  }
66
66
  }
@@ -414,9 +414,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
414
414
 
415
415
  // Check if this is a .pb.go file that should be skipped
416
416
  baseFileName := filepath.Base(fileName)
417
- if strings.HasSuffix(baseFileName, ".pb.go") {
417
+ if before, ok := strings.CutSuffix(baseFileName, ".pb.go"); ok {
418
418
  // Check if there's a corresponding .pb.ts file
419
- pbTsFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb.ts"
419
+ pbTsFileName := before + ".pb.ts"
420
420
  packageDir := filepath.Dir(fileName)
421
421
  pbTsPath := filepath.Join(packageDir, pbTsFileName)
422
422
 
@@ -1131,10 +1131,10 @@ func (c *GoToTSCompiler) WriteDoc(doc *ast.CommentGroup) {
1131
1131
  // Preserve original comment style (// or /*)
1132
1132
  if strings.HasPrefix(comment.Text, "//") {
1133
1133
  c.tsw.WriteLine(comment.Text)
1134
- } else if strings.HasPrefix(comment.Text, "/*") {
1134
+ } else if after, ok := strings.CutPrefix(comment.Text, "/*"); ok {
1135
1135
  // Write block comments potentially spanning multiple lines
1136
1136
  // Remove /* and */, then split by newline
1137
- content := strings.TrimSuffix(strings.TrimPrefix(comment.Text, "/*"), "*/")
1137
+ content := strings.TrimSuffix(after, "*/")
1138
1138
  lines := strings.Split(content, "\n") // Use \n as Split expects a separator string
1139
1139
 
1140
1140
  if len(lines) == 1 && !strings.Contains(lines[0], "\n") { // Check again for internal newlines just in case
@@ -107,7 +107,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
107
107
  // Use type info to get array length and element type
108
108
  var arrayLen int
109
109
  var elemType ast.Expr
110
- var goElemType interface{}
110
+ var goElemType any
111
111
  if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
112
112
  if at, ok := typ.Underlying().(*types.Array); ok {
113
113
  arrayLen = int(at.Len())
@@ -534,7 +534,7 @@ func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
534
534
  // evaluateConstantExpr attempts to evaluate a Go expression as a compile-time constant.
535
535
  // It returns the constant value if successful, or nil if the expression is not a constant.
536
536
  // This is used for evaluating array literal keys that are constant expressions.
537
- func (c *GoToTSCompiler) evaluateConstantExpr(expr ast.Expr) interface{} {
537
+ func (c *GoToTSCompiler) evaluateConstantExpr(expr ast.Expr) any {
538
538
  // Use the type checker's constant evaluation
539
539
  if tv, ok := c.pkg.TypesInfo.Types[expr]; ok && tv.Value != nil {
540
540
  // The expression has a constant value
@@ -575,8 +575,7 @@ func (c *GoToTSCompiler) categorizeStructFields(
575
575
  explicitEmbedded = make(map[string]ast.Expr)
576
576
 
577
577
  // Pre-populate embeddedFields map keys using the correct property name
578
- for i := 0; i < structType.NumFields(); i++ {
579
- field := structType.Field(i)
578
+ for field := range structType.Fields() {
580
579
  if field.Anonymous() {
581
580
  fieldType := field.Type()
582
581
  if ptr, ok := fieldType.(*types.Pointer); ok {
@@ -611,8 +610,7 @@ func (c *GoToTSCompiler) categorizeStructFields(
611
610
  }
612
611
 
613
612
  isDirectField := false
614
- for i := range structType.NumFields() {
615
- field := structType.Field(i)
613
+ for field := range structType.Fields() {
616
614
  if field.Name() == keyName {
617
615
  isDirectField = true
618
616
  directFields[keyName] = kv.Value
@@ -16,11 +16,9 @@ type ConstraintInfo struct {
16
16
  func analyzeConstraint(iface *types.Interface) ConstraintInfo {
17
17
  info := ConstraintInfo{}
18
18
 
19
- for i := 0; i < iface.NumEmbeddeds(); i++ {
20
- embedded := iface.EmbeddedType(i)
19
+ for embedded := range iface.EmbeddedTypes() {
21
20
  if union, ok := embedded.(*types.Union); ok {
22
- for j := 0; j < union.Len(); j++ {
23
- term := union.Term(j)
21
+ for term := range union.Terms() {
24
22
  checkType(term.Type(), &info)
25
23
  }
26
24
  } else {
package/compiler/decl.go CHANGED
@@ -1,6 +1,7 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
+ "bytes"
4
5
  "fmt"
5
6
  "go/ast"
6
7
  "go/token"
@@ -677,6 +678,14 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
677
678
  }
678
679
  }
679
680
 
681
+ if !isAsync {
682
+ bodyNeedsAsync, err := c.funcBodyNeedsAsync(decl, true)
683
+ if err != nil {
684
+ return false, err
685
+ }
686
+ isAsync = bodyNeedsAsync
687
+ }
688
+
680
689
  // Methods are typically public in the TS output
681
690
  c.tsw.WriteLiterally("public ")
682
691
 
@@ -744,6 +753,31 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
744
753
  return isAsync, nil
745
754
  }
746
755
 
756
+ // funcBodyNeedsAsync checks whether emitting a function body would generate await.
757
+ func (c *GoToTSCompiler) funcBodyNeedsAsync(decl *ast.FuncDecl, isMethod bool) (bool, error) {
758
+ if decl.Body == nil {
759
+ return false, nil
760
+ }
761
+
762
+ var body strings.Builder
763
+ writer := NewTSCodeWriter(&body)
764
+ tempCompiler := NewGoToTSCompiler(writer, c.pkg, c.analysis, c.currentFilePath)
765
+
766
+ if isMethod {
767
+ if err := tempCompiler.writeMethodBodyWithReceiverBinding(decl, "this"); err != nil {
768
+ return false, err
769
+ }
770
+ } else {
771
+ for _, stmt := range decl.Body.List {
772
+ if err := tempCompiler.WriteStmt(stmt); err != nil {
773
+ return false, err
774
+ }
775
+ }
776
+ }
777
+
778
+ return bytes.Contains([]byte(body.String()), []byte("await")), nil
779
+ }
780
+
747
781
  // writeMethodBodyWithReceiverBinding writes the method body with optional receiver binding
748
782
  // receiverTarget should be "this" for struct methods or "this._value" for named type methods
749
783
  func (c *GoToTSCompiler) writeMethodBodyWithReceiverBinding(decl *ast.FuncDecl, receiverTarget string) error {
@@ -169,6 +169,10 @@ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
169
169
  c.tsw.WriteLiterally("!")
170
170
  }
171
171
  }
172
+ } else if _, isParenExpr := expFun.(*ast.ParenExpr); isParenExpr {
173
+ // Parenthesized function expressions often come from pointer dereferences
174
+ // like (*rel)(), which remain nullable in TypeScript.
175
+ c.tsw.WriteLiterally("!")
172
176
  } else if _, isNamed := funType.(*types.Named); isNamed {
173
177
  c.tsw.WriteLiterally("!")
174
178
  }
@@ -8,12 +8,12 @@ import (
8
8
  )
9
9
 
10
10
  // writeByteSliceCreation handles the creation of []byte slices with proper Uint8Array handling
11
- func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg interface{}) error {
11
+ func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg any) error {
12
12
  return c.writeSliceCreationForType(lengthArg, capacityArg, true)
13
13
  }
14
14
 
15
15
  // writeSliceCreationForType handles slice creation with special handling for byte slices
16
- func (c *GoToTSCompiler) writeSliceCreationForType(lengthArg, capacityArg interface{}, isByteSlice bool) error {
16
+ func (c *GoToTSCompiler) writeSliceCreationForType(lengthArg, capacityArg any, isByteSlice bool) error {
17
17
  hasCapacity := capacityArg != nil
18
18
 
19
19
  if isByteSlice && !hasCapacity {
@@ -52,7 +52,7 @@ func (c *GoToTSCompiler) writeSliceCreationForType(lengthArg, capacityArg interf
52
52
  }
53
53
 
54
54
  // writeGenericSliceCreation handles the creation of generic slices with proper type hints
55
- func (c *GoToTSCompiler) writeGenericSliceCreation(elemType types.Type, lengthArg, capacityArg interface{}) error {
55
+ func (c *GoToTSCompiler) writeGenericSliceCreation(elemType types.Type, lengthArg, capacityArg any) error {
56
56
  hasCapacity := capacityArg != nil
57
57
 
58
58
  c.tsw.WriteLiterally("$.makeSlice<")
@@ -90,7 +90,7 @@ func (c *GoToTSCompiler) writeSliceTypeHint(elemType types.Type, hasCapacity boo
90
90
  }
91
91
 
92
92
  // writeExprOrDefault writes an expression if it's not nil, otherwise writes a default value
93
- func (c *GoToTSCompiler) writeExprOrDefault(expr interface{}, defaultValue string) error {
93
+ func (c *GoToTSCompiler) writeExprOrDefault(expr any, defaultValue string) error {
94
94
  if expr == nil {
95
95
  c.tsw.WriteLiterally(defaultValue)
96
96
  return nil
@@ -149,6 +149,14 @@ func (c *GoToTSCompiler) writeReflectTypeFor(exp *ast.CallExpr, selectorExpr *as
149
149
  typeArg := instance.TypeArgs.At(0)
150
150
  // fmt.Printf("DEBUG: Type argument: %v\n", typeArg)
151
151
 
152
+ if named, ok := typeArg.(*types.Named); ok {
153
+ if _, isInterface := named.Underlying().(*types.Interface); isInterface {
154
+ typeName := qualifiedTypeName(named)
155
+ c.tsw.WriteLiterally("reflect.getInterfaceLiteralTypeByName(\"" + typeName + "\")")
156
+ return true, nil
157
+ }
158
+ }
159
+
152
160
  // Generate TypeScript code to create a Type for this type
153
161
  if err := c.writeTypeForTypeArg(typeArg); err != nil {
154
162
  return true, err
@@ -157,6 +165,54 @@ func (c *GoToTSCompiler) writeReflectTypeFor(exp *ast.CallExpr, selectorExpr *as
157
165
  return true, nil
158
166
  }
159
167
 
168
+ // writeReflectTypeAssert handles reflect.TypeAssert[T](v) calls.
169
+ func (c *GoToTSCompiler) writeReflectTypeAssert(exp *ast.CallExpr, selectorExpr *ast.SelectorExpr) (handled bool, err error) {
170
+ if selectorExpr.Sel.Name != "TypeAssert" {
171
+ return false, nil
172
+ }
173
+
174
+ xIdent, ok := selectorExpr.X.(*ast.Ident)
175
+ if !ok {
176
+ return false, nil
177
+ }
178
+
179
+ obj := c.objectOfIdent(xIdent)
180
+ if obj == nil {
181
+ return false, nil
182
+ }
183
+
184
+ pkgName, ok := obj.(*types.PkgName)
185
+ if !ok || pkgName.Imported().Path() != "reflect" {
186
+ return false, nil
187
+ }
188
+
189
+ if len(exp.Args) != 1 {
190
+ return false, errors.New("reflect.TypeAssert called with unexpected argument count")
191
+ }
192
+
193
+ if c.pkg.TypesInfo.Instances == nil {
194
+ return false, errors.New("reflect.TypeAssert called but no type instances available")
195
+ }
196
+
197
+ instance, hasInstance := c.pkg.TypesInfo.Instances[selectorExpr.Sel]
198
+ if !hasInstance || instance.TypeArgs == nil || instance.TypeArgs.Len() == 0 {
199
+ return false, errors.New("reflect.TypeAssert called without type arguments")
200
+ }
201
+
202
+ typeArg := instance.TypeArgs.At(0)
203
+
204
+ c.tsw.WriteLiterally("$.typeAssertTuple<")
205
+ c.WriteGoType(typeArg, GoTypeContextGeneral)
206
+ c.tsw.WriteLiterally(">(")
207
+ if err := c.WriteValueExpr(exp.Args[0]); err != nil {
208
+ return true, err
209
+ }
210
+ c.tsw.WriteLiterally(".Interface(), ")
211
+ c.writeTypeInfoObject(typeArg)
212
+ c.tsw.WriteLiterally(")")
213
+ return true, nil
214
+ }
215
+
160
216
  // writeTypeForTypeArg generates TypeScript code to create a reflect.Type for the given Go type
161
217
  func (c *GoToTSCompiler) writeTypeForTypeArg(t types.Type) error {
162
218
  // Handle basic types
@@ -175,12 +231,46 @@ func (c *GoToTSCompiler) writeTypeForTypeArg(t types.Type) error {
175
231
  c.tsw.WriteLiterally(")")
176
232
  return nil
177
233
  case *types.Slice:
178
- // For slice types, use TypeOf with an empty slice
179
- c.tsw.WriteLiterally("reflect.TypeOf([])")
234
+ c.tsw.WriteLiterally("reflect.SliceOf(")
235
+ if err := c.writeTypeForTypeArg(underlying.Elem()); err != nil {
236
+ return err
237
+ }
238
+ c.tsw.WriteLiterally(")")
180
239
  return nil
181
240
  case *types.Array:
182
- // For array types, use TypeOf with an empty array
183
- c.tsw.WriteLiterally("reflect.TypeOf([])")
241
+ c.tsw.WriteLiterally("reflect.ArrayOf(")
242
+ c.tsw.WriteLiterallyf("%d, ", underlying.Len())
243
+ if err := c.writeTypeForTypeArg(underlying.Elem()); err != nil {
244
+ return err
245
+ }
246
+ c.tsw.WriteLiterally(")")
247
+ return nil
248
+ case *types.Map:
249
+ c.tsw.WriteLiterally("reflect.MapOf(")
250
+ if err := c.writeTypeForTypeArg(underlying.Key()); err != nil {
251
+ return err
252
+ }
253
+ c.tsw.WriteLiterally(", ")
254
+ if err := c.writeTypeForTypeArg(underlying.Elem()); err != nil {
255
+ return err
256
+ }
257
+ c.tsw.WriteLiterally(")")
258
+ return nil
259
+ case *types.Chan:
260
+ c.tsw.WriteLiterally("reflect.ChanOf(")
261
+ switch underlying.Dir() {
262
+ case types.RecvOnly:
263
+ c.tsw.WriteLiterally("reflect.RecvDir")
264
+ case types.SendOnly:
265
+ c.tsw.WriteLiterally("reflect.SendDir")
266
+ default:
267
+ c.tsw.WriteLiterally("reflect.BothDir")
268
+ }
269
+ c.tsw.WriteLiterally(", ")
270
+ if err := c.writeTypeForTypeArg(underlying.Elem()); err != nil {
271
+ return err
272
+ }
273
+ c.tsw.WriteLiterally(")")
184
274
  return nil
185
275
  case *types.Struct:
186
276
  // For struct types, use TypeOf with zero value
@@ -79,7 +79,7 @@ func (c *GoToTSCompiler) writeMakeSlice(sliceType *types.Slice, exp *ast.CallExp
79
79
 
80
80
  // Check if it's []byte
81
81
  if c.isByteSliceType(sliceType) {
82
- var lengthArg, capacityArg interface{}
82
+ var lengthArg, capacityArg any
83
83
  if len(exp.Args) >= 2 {
84
84
  lengthArg = exp.Args[1]
85
85
  }
@@ -90,7 +90,7 @@ func (c *GoToTSCompiler) writeMakeSlice(sliceType *types.Slice, exp *ast.CallExp
90
90
  }
91
91
 
92
92
  // Handle other slice types
93
- var lengthArg, capacityArg interface{}
93
+ var lengthArg, capacityArg any
94
94
  if len(exp.Args) >= 2 {
95
95
  lengthArg = exp.Args[1]
96
96
  }
@@ -193,7 +193,7 @@ func (c *GoToTSCompiler) WriteCallExprMake(exp *ast.CallExpr) error {
193
193
  if elemType != nil {
194
194
  // Check if it's make(S, ...) where S constrains to []byte
195
195
  if c.isByteSliceType(types.NewSlice(elemType)) {
196
- var lengthArg, capacityArg interface{}
196
+ var lengthArg, capacityArg any
197
197
  if len(exp.Args) >= 2 {
198
198
  lengthArg = exp.Args[1]
199
199
  }
@@ -203,7 +203,7 @@ func (c *GoToTSCompiler) WriteCallExprMake(exp *ast.CallExpr) error {
203
203
  return c.writeByteSliceCreation(lengthArg, capacityArg)
204
204
  }
205
205
 
206
- var lengthArg, capacityArg interface{}
206
+ var lengthArg, capacityArg any
207
207
  if len(exp.Args) >= 2 {
208
208
  lengthArg = exp.Args[1]
209
209
  }
@@ -100,6 +100,9 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
100
100
  // Handle reflect.TypeFor[T]() - Fun is IndexExpr where X is SelectorExpr
101
101
  if indexExpr, ok := expFun.(*ast.IndexExpr); ok {
102
102
  if selectorExpr, ok := indexExpr.X.(*ast.SelectorExpr); ok {
103
+ if handled, err := c.writeReflectTypeAssert(exp, selectorExpr); handled {
104
+ return err
105
+ }
103
106
  if handled, err := c.writeReflectTypeFor(exp, selectorExpr); handled {
104
107
  return err
105
108
  }
@@ -20,6 +20,48 @@ import (
20
20
  // - Interface types -> TypeScript interface types or "any"
21
21
  // - Function types -> TypeScript function signatures
22
22
  func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
23
+ // Handle qualified generic type references (e.g., pkg.Type[T]) preserving the package alias.
24
+ if indexExpr, ok := a.(*ast.IndexExpr); ok {
25
+ if selectorExpr, ok := indexExpr.X.(*ast.SelectorExpr); ok {
26
+ if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {
27
+ if obj := c.pkg.TypesInfo.Uses[pkgIdent]; obj != nil {
28
+ if _, isPkg := obj.(*types.PkgName); isPkg {
29
+ c.tsw.WriteLiterally(pkgIdent.Name)
30
+ c.tsw.WriteLiterally(".")
31
+ c.tsw.WriteLiterally(selectorExpr.Sel.Name)
32
+ c.tsw.WriteLiterally("<")
33
+ c.WriteTypeExpr(indexExpr.Index)
34
+ c.tsw.WriteLiterally(">")
35
+ return
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ if indexListExpr, ok := a.(*ast.IndexListExpr); ok {
43
+ if selectorExpr, ok := indexListExpr.X.(*ast.SelectorExpr); ok {
44
+ if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {
45
+ if obj := c.pkg.TypesInfo.Uses[pkgIdent]; obj != nil {
46
+ if _, isPkg := obj.(*types.PkgName); isPkg {
47
+ c.tsw.WriteLiterally(pkgIdent.Name)
48
+ c.tsw.WriteLiterally(".")
49
+ c.tsw.WriteLiterally(selectorExpr.Sel.Name)
50
+ c.tsw.WriteLiterally("<")
51
+ for i, index := range indexListExpr.Indices {
52
+ if i > 0 {
53
+ c.tsw.WriteLiterally(", ")
54
+ }
55
+ c.WriteTypeExpr(index)
56
+ }
57
+ c.tsw.WriteLiterally(">")
58
+ return
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+
23
65
  // Handle selector expressions (e.g., os.FileInfo) specially to preserve qualified names
24
66
  if selectorExpr, ok := a.(*ast.SelectorExpr); ok {
25
67
  if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {