goscript 0.0.50 → 0.0.51

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 (41) hide show
  1. package/compiler/analysis.go +18 -4
  2. package/compiler/compiler.go +8 -4
  3. package/compiler/expr-call-async.go +218 -13
  4. package/compiler/sanitize.go +1 -2
  5. package/compiler/spec-struct.go +3 -3
  6. package/compiler/spec.go +0 -21
  7. package/compiler/stmt-select.go +52 -1
  8. package/compiler/type.go +3 -3
  9. package/dist/gs/builtin/channel.d.ts +2 -2
  10. package/dist/gs/builtin/channel.js +12 -7
  11. package/dist/gs/builtin/channel.js.map +1 -1
  12. package/dist/gs/syscall/constants.d.ts +24 -0
  13. package/dist/gs/syscall/constants.js +27 -0
  14. package/dist/gs/syscall/constants.js.map +1 -0
  15. package/dist/gs/syscall/env.d.ts +6 -0
  16. package/dist/gs/syscall/env.js +43 -0
  17. package/dist/gs/syscall/env.js.map +1 -0
  18. package/dist/gs/syscall/errors.d.ts +111 -0
  19. package/dist/gs/syscall/errors.js +547 -0
  20. package/dist/gs/syscall/errors.js.map +1 -0
  21. package/dist/gs/syscall/fs.d.ts +29 -0
  22. package/dist/gs/syscall/fs.js +53 -0
  23. package/dist/gs/syscall/fs.js.map +1 -0
  24. package/dist/gs/syscall/index.d.ts +6 -80
  25. package/dist/gs/syscall/index.js +12 -168
  26. package/dist/gs/syscall/index.js.map +1 -1
  27. package/dist/gs/syscall/rawconn.d.ts +7 -0
  28. package/dist/gs/syscall/rawconn.js +19 -0
  29. package/dist/gs/syscall/rawconn.js.map +1 -0
  30. package/dist/gs/syscall/types.d.ts +12 -0
  31. package/dist/gs/syscall/types.js +2 -0
  32. package/dist/gs/syscall/types.js.map +1 -0
  33. package/gs/builtin/channel.ts +18 -12
  34. package/gs/syscall/constants.ts +29 -0
  35. package/gs/syscall/env.ts +47 -0
  36. package/gs/syscall/errors.ts +658 -0
  37. package/gs/syscall/fs.ts +62 -0
  38. package/gs/syscall/index.ts +12 -207
  39. package/gs/syscall/rawconn.ts +23 -0
  40. package/gs/syscall/types.ts +18 -0
  41. package/package.json +1 -1
@@ -911,6 +911,11 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
911
911
  return false
912
912
  }
913
913
 
914
+ case *ast.SelectStmt:
915
+ // Select statement with channel operations
916
+ hasAsync = true
917
+ return false
918
+
914
919
  case *ast.CallExpr:
915
920
  // Check if we're calling a function known to be async
916
921
  if funcIdent, ok := s.Fun.(*ast.Ident); ok {
@@ -928,8 +933,18 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
928
933
  // Get the type of the receiver
929
934
  if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
930
935
  if varObj, ok := obj.(*types.Var); ok {
931
- // Get the type name and package
932
- if namedType, ok := varObj.Type().(*types.Named); ok {
936
+ // Handle both direct named types and pointer to named types
937
+ var namedType *types.Named
938
+ switch t := varObj.Type().(type) {
939
+ case *types.Named:
940
+ namedType = t
941
+ case *types.Pointer:
942
+ if nt, isNamed := t.Elem().(*types.Named); isNamed {
943
+ namedType = nt
944
+ }
945
+ }
946
+
947
+ if namedType != nil {
933
948
  typeName := namedType.Obj().Name()
934
949
  methodName := selExpr.Sel.Name
935
950
 
@@ -944,13 +959,12 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
944
959
  return false
945
960
  }
946
961
  }
962
+ // Note: Local method async detection is handled during code generation to avoid circular dependencies
947
963
  }
948
964
  }
949
965
  }
950
966
  }
951
967
  }
952
-
953
- // TODO: Add detection of method calls on async types
954
968
  }
955
969
 
956
970
  return true
@@ -433,7 +433,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
433
433
  switch d := decl.(type) {
434
434
  case *ast.FuncDecl:
435
435
  if d.Recv == nil && d.Name.IsExported() {
436
- valueSymbols = append(valueSymbols, d.Name.Name)
436
+ valueSymbols = append(valueSymbols, sanitizeIdentifier(d.Name.Name))
437
437
  }
438
438
  case *ast.GenDecl:
439
439
  for _, spec := range d.Specs {
@@ -443,17 +443,17 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
443
443
  // Check if this is a struct type
444
444
  if _, isStruct := s.Type.(*ast.StructType); isStruct {
445
445
  // Structs become TypeScript classes and need both type and value exports
446
- structSymbols = append(structSymbols, s.Name.Name)
446
+ structSymbols = append(structSymbols, sanitizeIdentifier(s.Name.Name))
447
447
  } else {
448
448
  // Other type declarations (interfaces, type definitions, type aliases)
449
449
  // become TypeScript types and must be exported with "export type"
450
- typeSymbols = append(typeSymbols, s.Name.Name)
450
+ typeSymbols = append(typeSymbols, sanitizeIdentifier(s.Name.Name))
451
451
  }
452
452
  }
453
453
  case *ast.ValueSpec:
454
454
  for _, name := range s.Names {
455
455
  if name.IsExported() {
456
- valueSymbols = append(valueSymbols, name.Name)
456
+ valueSymbols = append(valueSymbols, sanitizeIdentifier(name.Name))
457
457
  }
458
458
  }
459
459
  }
@@ -676,6 +676,10 @@ type GoToTSCompiler struct {
676
676
  pkg *packages.Package
677
677
 
678
678
  analysis *Analysis
679
+
680
+ // awaitedCalls tracks which call expressions have already been processed
681
+ // to avoid adding double await keywords
682
+ awaitedCalls map[*ast.CallExpr]bool
679
683
  }
680
684
 
681
685
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -2,6 +2,7 @@ package compiler
2
2
 
3
3
  import (
4
4
  "go/ast"
5
+ "go/token"
5
6
  "go/types"
6
7
  "strings"
7
8
  )
@@ -9,10 +10,19 @@ import (
9
10
  // writeAsyncCallIfNeeded writes the await prefix for async function or method calls
10
11
  // Returns true if await was written, false otherwise
11
12
  func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
13
+ // Track if we've already processed this call expression to avoid double await
14
+ if c.awaitedCalls == nil {
15
+ c.awaitedCalls = make(map[*ast.CallExpr]bool)
16
+ }
17
+
18
+ if c.awaitedCalls[exp] {
19
+ return false // Already processed this call
20
+ }
12
21
  switch fun := exp.Fun.(type) {
13
22
  case *ast.Ident:
14
23
  // Function call (e.g., func())
15
24
  if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil && c.analysis.IsAsyncFunc(obj) {
25
+ c.awaitedCalls[exp] = true
16
26
  c.tsw.WriteLiterally("await ")
17
27
  return true
18
28
  }
@@ -20,8 +30,23 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
20
30
 
21
31
  case *ast.SelectorExpr:
22
32
  // Method call (e.g., obj.method())
23
- ident, ok := fun.X.(*ast.Ident)
24
- if !ok {
33
+ var ident *ast.Ident
34
+ var identOk bool
35
+
36
+ // Handle both direct identifiers and pointer dereferences
37
+ switch x := fun.X.(type) {
38
+ case *ast.Ident:
39
+ ident, identOk = x, true
40
+ case *ast.StarExpr:
41
+ // Handle pointer dereference: (*p).method() or p.method() where p is a pointer
42
+ if id, isIdent := x.X.(*ast.Ident); isIdent {
43
+ ident, identOk = id, true
44
+ }
45
+ default:
46
+ return false
47
+ }
48
+
49
+ if !identOk {
25
50
  return false
26
51
  }
27
52
 
@@ -37,8 +62,20 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
37
62
  }
38
63
 
39
64
  // Get the type name and package
40
- namedType, ok := varObj.Type().(*types.Named)
41
- if !ok {
65
+ var namedType *types.Named
66
+ var namedTypeOk bool
67
+
68
+ // Handle both direct named types and pointer to named types
69
+ switch t := varObj.Type().(type) {
70
+ case *types.Named:
71
+ namedType, namedTypeOk = t, true
72
+ case *types.Pointer:
73
+ if nt, isNamed := t.Elem().(*types.Named); isNamed {
74
+ namedType, namedTypeOk = nt, true
75
+ }
76
+ }
77
+
78
+ if !namedTypeOk {
42
79
  return false
43
80
  }
44
81
 
@@ -47,23 +84,191 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
47
84
 
48
85
  // Check if the type is from an imported package
49
86
  typePkg := namedType.Obj().Pkg()
50
- if typePkg == nil || typePkg == c.pkg.Types {
51
- return false
87
+ if typePkg != nil && typePkg != c.pkg.Types {
88
+ // Use the full package path from the type information (not just the package name)
89
+ pkgPath := typePkg.Path()
90
+
91
+ // Check if this method is async based on metadata
92
+ if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
93
+ c.awaitedCalls[exp] = true
94
+ c.tsw.WriteLiterally("await ")
95
+ return true
96
+ }
97
+ } else {
98
+ // For local types, check if the method contains async operations at runtime
99
+ // to avoid circular dependencies during analysis
100
+ if c.isLocalMethodAsync(namedType, methodName) {
101
+ c.awaitedCalls[exp] = true
102
+ c.tsw.WriteLiterally("await ")
103
+ return true
104
+ }
52
105
  }
106
+ return false
53
107
 
54
- // Use the full package path from the type information (not just the package name)
55
- pkgPath := typePkg.Path()
108
+ default:
109
+ return false
110
+ }
111
+ }
56
112
 
57
- // Check if this method is async based on metadata
58
- if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
59
- c.tsw.WriteLiterally("await ")
60
- return true
113
+ // isLocalMethodAsync checks if a local method contains async operations by inspecting its body
114
+ func (c *GoToTSCompiler) isLocalMethodAsync(namedType *types.Named, methodName string) bool {
115
+ // Find the method in the named type
116
+ for i := 0; i < namedType.NumMethods(); i++ {
117
+ method := namedType.Method(i)
118
+ if method.Name() == methodName {
119
+ // Find the method declaration in the AST
120
+ methodDecl := c.findMethodDecl(namedType, methodName)
121
+ if methodDecl != nil && methodDecl.Body != nil {
122
+ // Check if the method body contains async operations
123
+ return c.containsAsyncOperationsRuntime(methodDecl.Body)
124
+ }
125
+ break
61
126
  }
127
+ }
128
+ return false
129
+ }
130
+
131
+ // findMethodDecl finds the AST declaration for a method on a named type
132
+ func (c *GoToTSCompiler) findMethodDecl(namedType *types.Named, methodName string) *ast.FuncDecl {
133
+ // Search through all files in the package for the method declaration
134
+ for _, file := range c.pkg.Syntax {
135
+ for _, decl := range file.Decls {
136
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
137
+ // Check if this is a method with the right name
138
+ if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
139
+ // Check if the receiver type matches
140
+ if c.isReceiverOfType(funcDecl, namedType) {
141
+ return funcDecl
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ return nil
148
+ }
149
+
150
+ // isReceiverOfType checks if a function declaration is a method of the given named type
151
+ func (c *GoToTSCompiler) isReceiverOfType(funcDecl *ast.FuncDecl, namedType *types.Named) bool {
152
+ if funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
62
153
  return false
154
+ }
63
155
 
64
- default:
156
+ recvField := funcDecl.Recv.List[0]
157
+ if len(recvField.Names) == 0 {
65
158
  return false
66
159
  }
160
+
161
+ recvIdent := recvField.Names[0]
162
+ if obj := c.pkg.TypesInfo.Defs[recvIdent]; obj != nil {
163
+ if varObj, ok := obj.(*types.Var); ok {
164
+ // Handle both direct named types and pointer to named types
165
+ var receiverNamedType *types.Named
166
+ switch t := varObj.Type().(type) {
167
+ case *types.Named:
168
+ receiverNamedType = t
169
+ case *types.Pointer:
170
+ if nt, isNamed := t.Elem().(*types.Named); isNamed {
171
+ receiverNamedType = nt
172
+ }
173
+ }
174
+
175
+ if receiverNamedType != nil {
176
+ // For generic types, compare the origin types and names
177
+ // since instantiated generics have different type objects
178
+ targetObj := namedType.Obj()
179
+ receiverObj := receiverNamedType.Obj()
180
+
181
+ // Compare by name and package path
182
+ return targetObj.Name() == receiverObj.Name() &&
183
+ targetObj.Pkg() == receiverObj.Pkg()
184
+ }
185
+ }
186
+ }
187
+
188
+ return false
189
+ }
190
+
191
+ // containsAsyncOperationsRuntime checks if a node contains async operations at runtime
192
+ func (c *GoToTSCompiler) containsAsyncOperationsRuntime(node ast.Node) bool {
193
+ var hasAsync bool
194
+
195
+ ast.Inspect(node, func(n ast.Node) bool {
196
+ if n == nil {
197
+ return false
198
+ }
199
+
200
+ switch s := n.(type) {
201
+ case *ast.SendStmt:
202
+ // Channel send operation (ch <- value)
203
+ hasAsync = true
204
+ return false
205
+
206
+ case *ast.UnaryExpr:
207
+ // Channel receive operation (<-ch)
208
+ if s.Op == token.ARROW {
209
+ hasAsync = true
210
+ return false
211
+ }
212
+
213
+ case *ast.SelectStmt:
214
+ // Select statement with channel operations
215
+ hasAsync = true
216
+ return false
217
+
218
+ case *ast.CallExpr:
219
+ // Check if we're calling a function known to be async
220
+ if funcIdent, ok := s.Fun.(*ast.Ident); ok {
221
+ // Get the object for this function call
222
+ if obj := c.pkg.TypesInfo.Uses[funcIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
223
+ hasAsync = true
224
+ return false
225
+ }
226
+ }
227
+
228
+ // Check for method calls on imported types (similar to analysis.go logic)
229
+ if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
230
+ if ident, ok := selExpr.X.(*ast.Ident); ok {
231
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
232
+ if varObj, ok := obj.(*types.Var); ok {
233
+ var namedType *types.Named
234
+ switch t := varObj.Type().(type) {
235
+ case *types.Named:
236
+ namedType = t
237
+ case *types.Pointer:
238
+ if nt, isNamed := t.Elem().(*types.Named); isNamed {
239
+ namedType = nt
240
+ }
241
+ }
242
+
243
+ if namedType != nil {
244
+ typeName := namedType.Obj().Name()
245
+ methodName := selExpr.Sel.Name
246
+
247
+ // Check if the type is from an imported package
248
+ if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != c.pkg.Types {
249
+ pkgPath := typePkg.Path()
250
+ if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
251
+ hasAsync = true
252
+ return false
253
+ }
254
+ } else {
255
+ // For local types, recursively check
256
+ if c.isLocalMethodAsync(namedType, methodName) {
257
+ hasAsync = true
258
+ return false
259
+ }
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ return true
269
+ })
270
+
271
+ return hasAsync
67
272
  }
68
273
 
69
274
  // addNonNullAssertion adds ! for function calls that might return null
@@ -11,8 +11,7 @@ func sanitizeIdentifier(name string) string {
11
11
 
12
12
  // Handle TypeScript built-in types that conflict with Go type parameter names
13
13
  builtinTypes := map[string]string{
14
- "Map": "MapType",
15
- // Add other built-in types as needed
14
+ "Promise": "PromiseType",
16
15
  }
17
16
 
18
17
  if replacement, exists := builtinTypes[name]; exists {
@@ -37,7 +37,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
37
37
  c.tsw.WriteLine("{")
38
38
  c.tsw.Indent(1)
39
39
 
40
- className := a.Name.Name
40
+ className := sanitizeIdentifier(a.Name.Name)
41
41
 
42
42
  goStructType, ok := c.pkg.TypesInfo.Defs[a.Name].Type().(*types.Named)
43
43
  if !ok {
@@ -231,10 +231,10 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
231
231
  // Check for both simple identifiers (Pair) and generic types (Pair[T])
232
232
  var recvTypeName string
233
233
  if ident, ok := recvType.(*ast.Ident); ok {
234
- recvTypeName = ident.Name
234
+ recvTypeName = sanitizeIdentifier(ident.Name)
235
235
  } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
236
236
  if ident, ok := indexExpr.X.(*ast.Ident); ok {
237
- recvTypeName = ident.Name
237
+ recvTypeName = sanitizeIdentifier(ident.Name)
238
238
  }
239
239
  }
240
240
 
package/compiler/spec.go CHANGED
@@ -136,11 +136,6 @@ func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldTyp
136
136
 
137
137
  c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
138
138
 
139
- // Debug for Mode field specifically
140
- if fieldName == "Mode" {
141
- fmt.Printf("DEBUG Mode field: Type=%T, String=%s\n", fieldType, fieldType.String())
142
- }
143
-
144
139
  // Priority 1: Check if this is a wrapper type
145
140
  if c.analysis.IsNamedBasicType(fieldType) {
146
141
  // For wrapper types, use the zero value of the underlying type with type casting
@@ -161,35 +156,22 @@ func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldTyp
161
156
 
162
157
  // Priority 2: Handle imported types with basic underlying types (like os.FileMode)
163
158
  if c.isImportedBasicType(fieldType) {
164
- if fieldName == "Mode" {
165
- fmt.Printf("DEBUG Mode field: Using imported basic type zero value\n")
166
- }
167
159
  c.writeImportedBasicTypeZeroValue(fieldType)
168
160
  return
169
161
  }
170
162
 
171
163
  // Priority 3: Handle named types
172
164
  if named, isNamed := fieldType.(*types.Named); isNamed {
173
- if fieldName == "Mode" {
174
- fmt.Printf("DEBUG Mode field: Using named type zero value\n")
175
- }
176
165
  c.writeNamedTypeZeroValue(named)
177
166
  return
178
167
  }
179
168
 
180
169
  // Priority 4: Handle type aliases
181
170
  if alias, isAlias := fieldType.(*types.Alias); isAlias {
182
- if fieldName == "Mode" {
183
- fmt.Printf("DEBUG Mode field: Using type alias zero value\n")
184
- }
185
171
  c.writeTypeAliasZeroValue(alias, astType)
186
172
  return
187
173
  }
188
174
 
189
- // Default: use WriteZeroValueForType
190
- if fieldName == "Mode" {
191
- fmt.Printf("DEBUG Mode field: Using default WriteZeroValueForType\n")
192
- }
193
175
  c.WriteZeroValueForType(fieldType)
194
176
  }
195
177
 
@@ -241,7 +223,6 @@ func (c *GoToTSCompiler) isImportedBasicType(fieldType types.Type) bool {
241
223
  func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) {
242
224
  if named, ok := fieldType.(*types.Named); ok {
243
225
  underlying := named.Underlying()
244
- fmt.Printf("DEBUG writeImportedBasicTypeZeroValue: Named type, underlying=%T\n", underlying)
245
226
  // Write zero value of underlying type with type casting
246
227
  c.WriteZeroValueForType(underlying)
247
228
  c.tsw.WriteLiterally(" as ")
@@ -251,7 +232,6 @@ func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) {
251
232
 
252
233
  if alias, ok := fieldType.(*types.Alias); ok {
253
234
  underlying := alias.Underlying()
254
- fmt.Printf("DEBUG writeImportedBasicTypeZeroValue: Alias type, underlying=%T\n", underlying)
255
235
  // Write zero value of underlying type with type casting
256
236
  c.WriteZeroValueForType(underlying)
257
237
  c.tsw.WriteLiterally(" as ")
@@ -260,7 +240,6 @@ func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) {
260
240
  }
261
241
 
262
242
  // Fallback (should not happen if isImportedBasicType was correct)
263
- fmt.Printf("DEBUG writeImportedBasicTypeZeroValue: Fallback path\n")
264
243
  c.WriteZeroValueForType(fieldType)
265
244
  }
266
245
 
@@ -8,6 +8,18 @@ import (
8
8
  "github.com/pkg/errors"
9
9
  )
10
10
 
11
+ // caseEndsWithReturn checks if a case body ends with a return statement
12
+ func (c *GoToTSCompiler) caseEndsWithReturn(body []ast.Stmt) bool {
13
+ if len(body) == 0 {
14
+ return false
15
+ }
16
+
17
+ // Check if the last statement is a return statement
18
+ lastStmt := body[len(body)-1]
19
+ _, isReturn := lastStmt.(*ast.ReturnStmt)
20
+ return isReturn
21
+ }
22
+
11
23
  // WriteStmtSelect translates a Go `select` statement into an asynchronous
12
24
  // TypeScript operation using the `$.selectStatement` runtime helper.
13
25
  // Go's `select` provides non-deterministic choice over channel operations.
@@ -38,8 +50,29 @@ func (c *GoToTSCompiler) WriteStmtSelect(exp *ast.SelectStmt) error {
38
50
  // Variable to track whether we have a default case
39
51
  hasDefault := false
40
52
 
53
+ // Analyze if all cases end with return statements
54
+ allCasesReturn := true
55
+ for _, stmt := range exp.Body.List {
56
+ if commClause, ok := stmt.(*ast.CommClause); ok {
57
+ if commClause.Comm == nil {
58
+ // Default case - check if it ends with return
59
+ if !c.caseEndsWithReturn(commClause.Body) {
60
+ allCasesReturn = false
61
+ }
62
+ } else {
63
+ // Regular case - check if it ends with return
64
+ if !c.caseEndsWithReturn(commClause.Body) {
65
+ allCasesReturn = false
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ // Generate unique variable names for this select statement
72
+ selectID := fmt.Sprintf("%d", exp.Pos()) // Use AST position for uniqueness
73
+
41
74
  // Start the selectStatement call and the array literal
42
- c.tsw.WriteLiterally("await $.selectStatement(")
75
+ c.tsw.WriteLiterallyf("const [_selectHasReturn%s, _selectValue%s] = await $.selectStatement(", selectID, selectID)
43
76
  c.tsw.WriteLine("[") // Put bracket on new line
44
77
  c.tsw.Indent(1)
45
78
 
@@ -207,5 +240,23 @@ func (c *GoToTSCompiler) WriteStmtSelect(exp *ast.SelectStmt) error {
207
240
  c.tsw.WriteLiterally(")")
208
241
  c.tsw.WriteLine("")
209
242
 
243
+ // Add code to handle the return value from selectStatement
244
+ c.tsw.WriteLiterallyf("if (_selectHasReturn%s) {", selectID)
245
+ c.tsw.WriteLine("")
246
+ c.tsw.Indent(1)
247
+ c.tsw.WriteLiterallyf("return _selectValue%s!", selectID)
248
+ c.tsw.WriteLine("")
249
+ c.tsw.Indent(-1)
250
+ c.tsw.WriteLine("}")
251
+
252
+ // If all cases return, add a TypeScript-satisfying fallback return
253
+ if allCasesReturn {
254
+ c.tsw.WriteLine("// All cases should return, this fallback should never execute")
255
+ c.tsw.WriteLine("throw new Error('Unexpected: select statement did not return when all cases should return')")
256
+ } else {
257
+ c.tsw.WriteLiterallyf("// If _selectHasReturn%s is false, continue execution", selectID)
258
+ c.tsw.WriteLine("")
259
+ }
260
+
210
261
  return nil
211
262
  }
package/compiler/type.go CHANGED
@@ -278,7 +278,7 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
278
278
  // Write the qualified name: importAlias.TypeName
279
279
  c.tsw.WriteLiterally(importAlias)
280
280
  c.tsw.WriteLiterally(".")
281
- c.tsw.WriteLiterally(t.Obj().Name())
281
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(t.Obj().Name()))
282
282
 
283
283
  // For generic types, include type arguments
284
284
  if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 {
@@ -306,7 +306,7 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
306
306
  // Write the qualified name: importAlias.TypeName
307
307
  c.tsw.WriteLiterally(importAlias)
308
308
  c.tsw.WriteLiterally(".")
309
- c.tsw.WriteLiterally(t.Obj().Name())
309
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(t.Obj().Name()))
310
310
 
311
311
  // For generic types, include type arguments
312
312
  if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 {
@@ -330,7 +330,7 @@ func (c *GoToTSCompiler) WriteNamedType(t *types.Named) {
330
330
  }
331
331
 
332
332
  // Use Obj().Name() for the original defined name (local types or unmatched imports)
333
- c.tsw.WriteLiterally(t.Obj().Name())
333
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(t.Obj().Name()))
334
334
 
335
335
  // For generic types, include type arguments
336
336
  if t.TypeArgs() != nil && t.TypeArgs().Len() > 0 {
@@ -77,7 +77,7 @@ export interface SelectCase<T> {
77
77
  isSend: boolean;
78
78
  channel: Channel<any> | ChannelRef<any> | null;
79
79
  value?: any;
80
- onSelected?: (result: SelectResult<T>) => Promise<void>;
80
+ onSelected?: (result: SelectResult<T>) => Promise<any>;
81
81
  }
82
82
  /**
83
83
  * Helper for 'select' statements. Takes an array of select cases
@@ -87,7 +87,7 @@ export interface SelectCase<T> {
87
87
  * @param hasDefault Whether there is a default case
88
88
  * @returns A promise that resolves with the result of the selected case
89
89
  */
90
- export declare function selectStatement<T>(cases: SelectCase<T>[], hasDefault?: boolean): Promise<void>;
90
+ export declare function selectStatement<T, V = void>(cases: SelectCase<T>[], hasDefault?: boolean): Promise<[boolean, V]>;
91
91
  /**
92
92
  * Helper function for channel send operations that handles nil channels correctly.
93
93
  * In Go, sending to a nil channel blocks forever.
@@ -41,13 +41,15 @@ export async function selectStatement(cases, hasDefault = false) {
41
41
  if (selectedCase.isSend) {
42
42
  const result = await selectedCase.channel.selectSend(selectedCase.value, selectedCase.id);
43
43
  if (selectedCase.onSelected) {
44
- await selectedCase.onSelected(result); // Await the handler
44
+ const handlerResult = await selectedCase.onSelected(result);
45
+ return [handlerResult !== undefined, handlerResult];
45
46
  }
46
47
  }
47
48
  else {
48
49
  const result = await selectedCase.channel.selectReceive(selectedCase.id);
49
50
  if (selectedCase.onSelected) {
50
- await selectedCase.onSelected(result); // Await the handler
51
+ const handlerResult = await selectedCase.onSelected(result);
52
+ return [handlerResult !== undefined, handlerResult];
51
53
  }
52
54
  }
53
55
  }
@@ -55,7 +57,7 @@ export async function selectStatement(cases, hasDefault = false) {
55
57
  // This case should ideally not happen if channel is required for non-default cases
56
58
  console.error('Selected case without a channel:', selectedCase);
57
59
  }
58
- return; // Return after executing a ready case
60
+ return [false, undefined]; // Return after executing a ready case
59
61
  }
60
62
  // 2. If no operations are ready and there's a default case, select default
61
63
  if (hasDefault) {
@@ -63,13 +65,14 @@ export async function selectStatement(cases, hasDefault = false) {
63
65
  const defaultCase = cases.find((c) => c.id === -1);
64
66
  if (defaultCase && defaultCase.onSelected) {
65
67
  // Execute the onSelected handler for the default case
66
- await defaultCase.onSelected({
68
+ const handlerResult = await defaultCase.onSelected({
67
69
  value: undefined,
68
70
  ok: false,
69
71
  id: -1,
70
- }); // Await the handler
72
+ });
73
+ return [handlerResult !== undefined, handlerResult];
71
74
  }
72
- return; // Return after executing the default case
75
+ return [false, undefined]; // Return after executing the default case
73
76
  }
74
77
  // 3. If no operations are ready and no default case, block until one is ready
75
78
  // Use Promise.race on the blocking promises
@@ -94,9 +97,11 @@ export async function selectStatement(cases, hasDefault = false) {
94
97
  // Execute onSelected handler for the selected case
95
98
  const selectedCase = cases.find((c) => c.id === result.id);
96
99
  if (selectedCase && selectedCase.onSelected) {
97
- await selectedCase.onSelected(result); // Await the handler
100
+ const handlerResult = await selectedCase.onSelected(result);
101
+ return [handlerResult !== undefined, handlerResult];
98
102
  }
99
103
  // No explicit return needed here, as the function will implicitly return after the await
104
+ return [false, undefined];
100
105
  }
101
106
  /**
102
107
  * Helper function for channel send operations that handles nil channels correctly.