goscript 0.0.75 → 0.0.77

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 (95) hide show
  1. package/cmd/goscript/main.js +48 -12
  2. package/compiler/analysis.go +100 -33
  3. package/compiler/analysis_test.go +2 -7
  4. package/compiler/assignment.go +34 -12
  5. package/compiler/code-writer.go +2 -2
  6. package/compiler/compiler.go +4 -4
  7. package/compiler/composite-lit.go +4 -6
  8. package/compiler/constraint.go +2 -4
  9. package/compiler/expr-call-async.go +4 -0
  10. package/compiler/expr-call-helpers.go +98 -8
  11. package/compiler/expr-call-make.go +4 -4
  12. package/compiler/expr-call.go +3 -0
  13. package/compiler/expr-type.go +42 -0
  14. package/compiler/gs_dependencies_test.go +3 -14
  15. package/compiler/index.ts +20 -5
  16. package/compiler/protobuf.go +21 -21
  17. package/compiler/spec-struct.go +22 -30
  18. package/compiler/spec.go +2 -2
  19. package/compiler/stmt-assign.go +2 -2
  20. package/compiler/type-info.go +20 -3
  21. package/compiler/type-utils.go +2 -4
  22. package/compiler/type.go +3 -4
  23. package/dist/compiler/index.js +13 -4
  24. package/dist/compiler/index.js.map +1 -1
  25. package/dist/gs/builtin/builtin.d.ts +6 -0
  26. package/dist/gs/builtin/builtin.js +26 -0
  27. package/dist/gs/builtin/builtin.js.map +1 -1
  28. package/dist/gs/builtin/slice.js +2 -3
  29. package/dist/gs/builtin/slice.js.map +1 -1
  30. package/dist/gs/builtin/type.d.ts +1 -0
  31. package/dist/gs/builtin/type.js +8 -14
  32. package/dist/gs/builtin/type.js.map +1 -1
  33. package/dist/gs/bytes/buffer.gs.d.ts +1 -0
  34. package/dist/gs/bytes/buffer.gs.js +20 -0
  35. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  36. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +5 -0
  37. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +10 -0
  38. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -0
  39. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.d.ts +50 -0
  40. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js +221 -0
  41. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -0
  42. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.d.ts +1 -0
  43. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js +2 -0
  44. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js.map +1 -0
  45. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.d.ts +1 -0
  46. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js +2 -0
  47. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js.map +1 -0
  48. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.d.ts +56 -0
  49. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js +17 -0
  50. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js.map +1 -0
  51. package/dist/gs/io/fs/format.js +2 -6
  52. package/dist/gs/io/fs/format.js.map +1 -1
  53. package/dist/gs/io/fs/glob.js +18 -23
  54. package/dist/gs/io/fs/glob.js.map +1 -1
  55. package/dist/gs/path/match.js +9 -22
  56. package/dist/gs/path/match.js.map +1 -1
  57. package/dist/gs/reflect/index.d.ts +1 -1
  58. package/dist/gs/reflect/index.js +1 -1
  59. package/dist/gs/reflect/index.js.map +1 -1
  60. package/dist/gs/reflect/type.d.ts +1 -0
  61. package/dist/gs/reflect/type.js +52 -23
  62. package/dist/gs/reflect/type.js.map +1 -1
  63. package/dist/gs/strings/iter.js +1 -1
  64. package/dist/gs/strings/iter.js.map +1 -1
  65. package/dist/gs/strings/reader.js +1 -1
  66. package/dist/gs/strings/reader.js.map +1 -1
  67. package/dist/gs/strings/replace.js +9 -20
  68. package/dist/gs/strings/replace.js.map +1 -1
  69. package/dist/gs/time/time.js +2 -2
  70. package/dist/gs/time/time.js.map +1 -1
  71. package/go.mod +9 -10
  72. package/go.sum +20 -22
  73. package/gs/builtin/builtin.ts +29 -0
  74. package/gs/builtin/slice.ts +2 -2
  75. package/gs/builtin/type.ts +14 -14
  76. package/gs/bytes/buffer.gs.ts +21 -1
  77. package/gs/fmt/fmt.test.ts +1 -1
  78. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +14 -0
  79. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +238 -0
  80. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.ts +1 -0
  81. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +12 -0
  82. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.ts +1 -0
  83. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/meta.json +8 -0
  84. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.ts +94 -0
  85. package/gs/io/fs/format.ts +2 -5
  86. package/gs/io/fs/glob.ts +18 -21
  87. package/gs/path/match.ts +9 -22
  88. package/gs/reflect/index.ts +1 -0
  89. package/gs/reflect/type.ts +62 -25
  90. package/gs/strings/iter.ts +1 -1
  91. package/gs/strings/reader.ts +1 -1
  92. package/gs/strings/replace.ts +13 -18
  93. package/gs/time/time.ts +2 -2
  94. package/gs.go +8 -0
  95. package/package.json +20 -16
@@ -7,24 +7,60 @@ import { dirname } from 'node:path';
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
- // Go up two levels from cmd/goscript/ to the project root
10
+ // Go up two levels from cmd/goscript/ to the goscript module root
11
11
  const projectRoot = path.join(__dirname, '..', '..');
12
12
 
13
+ // The user's working directory (where they invoked the command)
14
+ const userCwd = process.cwd();
15
+
13
16
  // Get arguments passed to the script, excluding node executable and script path
14
17
  const args = process.argv.slice(2);
15
18
 
16
- // Construct the go run command with the absolute path to the goscript executable
17
- // Use path.join for robustness
18
- const goscriptCmd = `go run ./cmd/goscript`;
19
+ // Resolve --output and --dir paths relative to the user's cwd, since the
20
+ // go run process executes from the goscript module root, not the user's project.
21
+ const resolvedArgs = [];
22
+ let hasDir = false;
23
+ for (let i = 0; i < args.length; i++) {
24
+ const arg = args[i];
25
+ if (arg === '--dir') {
26
+ hasDir = true;
27
+ resolvedArgs.push(arg);
28
+ if (i + 1 < args.length) {
29
+ i++;
30
+ resolvedArgs.push(path.resolve(userCwd, args[i]));
31
+ }
32
+ } else if (arg.startsWith('--dir=')) {
33
+ hasDir = true;
34
+ const val = arg.slice('--dir='.length);
35
+ resolvedArgs.push('--dir=' + path.resolve(userCwd, val));
36
+ } else if (arg === '--output' || arg === '-o') {
37
+ resolvedArgs.push(arg);
38
+ if (i + 1 < args.length) {
39
+ i++;
40
+ resolvedArgs.push(path.resolve(userCwd, args[i]));
41
+ }
42
+ } else if (arg.startsWith('--output=')) {
43
+ const val = arg.slice('--output='.length);
44
+ resolvedArgs.push('--output=' + path.resolve(userCwd, val));
45
+ } else {
46
+ resolvedArgs.push(arg);
47
+ }
48
+ }
49
+
50
+ // Inject --dir with user's cwd if not explicitly provided, so the compiler
51
+ // loads packages from the user's project instead of the goscript module root.
52
+ if (!hasDir) {
53
+ resolvedArgs.push('--dir', userCwd);
54
+ }
19
55
 
20
- // Combine the goscript command with the arguments
21
- const command = `${goscriptCmd} ${args.join(" ")}`;
56
+ // Build the command: go run from the goscript module root
57
+ const command = `go run ./cmd/goscript ${resolvedArgs.join(' ')}`;
22
58
 
23
- // Execute the command
59
+ // Execute from the goscript module root so Go can resolve the module
24
60
  const child = spawn(command, {
25
- shell: true, // Use shell to correctly parse the command string
26
- stdio: 'inherit', // Inherit stdin, stdout, and stderr
27
- cwd: projectRoot, // Execute in the current working directory where the script is run
61
+ shell: true,
62
+ stdio: 'inherit',
63
+ cwd: projectRoot,
28
64
  });
29
65
 
30
66
  child.on('error', (error) => {
@@ -33,5 +69,5 @@ child.on('error', (error) => {
33
69
  });
34
70
 
35
71
  child.on('exit', (code) => {
36
- process.exit(code ?? 0); // Exit with the child process's exit code
37
- });
72
+ process.exit(code ?? 0);
73
+ });
@@ -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
  }
@@ -396,7 +396,37 @@ func (c *GoToTSCompiler) writeBlankIdentifierAssign(rhs ast.Expr) error {
396
396
 
397
397
  // writePointerDerefAssign handles assignments to dereferenced pointers (*p = val)
398
398
  func (c *GoToTSCompiler) writePointerDerefAssign(starExpr *ast.StarExpr, rhs ast.Expr, tok token.Token) error {
399
- // Write pointer dereference
399
+ // Check if RHS is a struct - if so, use $.assignStruct() to copy fields
400
+ if shouldApplyClone(c.pkg, rhs) {
401
+ // For struct assignments, use $.assignStruct(target, source)
402
+ // This copies all fields from source to target
403
+ c.tsw.WriteLiterally("$.assignStruct(")
404
+
405
+ // Write the pointer as the target
406
+ if ident, ok := starExpr.X.(*ast.Ident); ok {
407
+ obj := c.objectOfIdent(ident)
408
+ if obj != nil && c.analysis.NeedsVarRef(obj) {
409
+ c.WriteIdent(ident, true)
410
+ } else {
411
+ c.WriteIdent(ident, false)
412
+ }
413
+ } else {
414
+ if err := c.WriteValueExpr(starExpr.X); err != nil {
415
+ return err
416
+ }
417
+ }
418
+ c.tsw.WriteLiterally("!, ")
419
+
420
+ // Write the RHS with clone as the source
421
+ c.tsw.WriteLiterally("$.markAsStructValue(")
422
+ if err := c.WriteValueExpr(rhs); err != nil {
423
+ return err
424
+ }
425
+ c.tsw.WriteLiterally(".clone()))")
426
+ return nil
427
+ }
428
+
429
+ // For non-struct types, use the original p!.value = val approach
400
430
  if ident, ok := starExpr.X.(*ast.Ident); ok {
401
431
  obj := c.objectOfIdent(ident)
402
432
  if obj != nil && c.analysis.NeedsVarRef(obj) {
@@ -426,17 +456,9 @@ func (c *GoToTSCompiler) writePointerDerefAssign(starExpr *ast.StarExpr, rhs ast
426
456
  c.tsw.WriteLiterally(" ")
427
457
  }
428
458
 
429
- // Write RHS with cloning if needed
430
- if shouldApplyClone(c.pkg, rhs) {
431
- c.tsw.WriteLiterally("$.markAsStructValue(")
432
- if err := c.WriteValueExpr(rhs); err != nil {
433
- return err
434
- }
435
- c.tsw.WriteLiterally(".clone())")
436
- } else {
437
- if err := c.WriteValueExpr(rhs); err != nil {
438
- return err
439
- }
459
+ // Write RHS
460
+ if err := c.WriteValueExpr(rhs); err != nil {
461
+ return err
440
462
  }
441
463
 
442
464
  if tok == token.AND_NOT_ASSIGN {
@@ -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 {
@@ -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