goscript 0.0.57 → 0.0.59

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 (44) hide show
  1. package/README.md +40 -33
  2. package/compiler/analysis.go +115 -19
  3. package/compiler/assignment.go +163 -217
  4. package/compiler/compiler.go +35 -31
  5. package/compiler/composite-lit.go +233 -196
  6. package/compiler/constraint.go +88 -0
  7. package/compiler/decl.go +418 -7
  8. package/compiler/expr-call-async.go +20 -34
  9. package/compiler/expr-call-builtins.go +19 -0
  10. package/compiler/expr-call-helpers.go +0 -28
  11. package/compiler/expr-call-make.go +93 -343
  12. package/compiler/expr-call-type-conversion.go +221 -249
  13. package/compiler/expr-call.go +70 -69
  14. package/compiler/expr-selector.go +21 -24
  15. package/compiler/expr.go +3 -60
  16. package/compiler/protobuf.go +180 -36
  17. package/compiler/spec-value.go +132 -24
  18. package/compiler/spec.go +14 -55
  19. package/compiler/stmt-assign.go +338 -356
  20. package/compiler/stmt-range.go +4 -24
  21. package/compiler/stmt.go +99 -172
  22. package/compiler/type-utils.go +185 -0
  23. package/compiler/type.go +26 -80
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +3 -0
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/builtin/type.js +8 -2
  28. package/dist/gs/builtin/type.js.map +1 -1
  29. package/dist/gs/fmt/fmt.js +113 -16
  30. package/dist/gs/fmt/fmt.js.map +1 -1
  31. package/dist/gs/runtime/runtime.d.ts +1 -1
  32. package/dist/gs/runtime/runtime.js +1 -1
  33. package/dist/gs/slices/slices.d.ts +23 -0
  34. package/dist/gs/slices/slices.js +61 -0
  35. package/dist/gs/slices/slices.js.map +1 -1
  36. package/go.mod +8 -8
  37. package/go.sum +14 -14
  38. package/gs/builtin/slice.ts +5 -2
  39. package/gs/builtin/type.ts +13 -6
  40. package/gs/fmt/fmt.test.ts +176 -0
  41. package/gs/fmt/fmt.ts +109 -18
  42. package/gs/runtime/runtime.ts +1 -1
  43. package/gs/slices/slices.ts +68 -0
  44. package/package.json +3 -3
package/compiler/decl.go CHANGED
@@ -3,7 +3,9 @@ package compiler
3
3
  import (
4
4
  "fmt"
5
5
  "go/ast"
6
+ "go/token"
6
7
  "go/types"
8
+ "sort"
7
9
  )
8
10
 
9
11
  // WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
@@ -17,33 +19,442 @@ import (
17
19
  // variables, or type definitions: It iterates through `d.Specs` and calls
18
20
  // `WriteSpec` for each specification.
19
21
  //
22
+ // Type declarations are sorted by dependencies to ensure referenced types are
23
+ // defined before types that reference them, avoiding initialization order issues.
20
24
  // A newline is added after each processed declaration or spec group for readability.
21
25
  // Unknown declaration types result in a printed diagnostic message.
22
26
  func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
27
+ // Separate type declarations from other declarations for dependency sorting
28
+ var typeSpecs []*ast.TypeSpec
29
+ var varSpecs []*ast.ValueSpec
30
+ var otherDecls []ast.Decl
31
+ var otherSpecs []ast.Spec
32
+
23
33
  for _, decl := range decls {
24
34
  switch d := decl.(type) {
25
35
  case *ast.FuncDecl:
26
36
  // Only handle top-level functions here. Methods are handled within WriteTypeSpec.
27
37
  if d.Recv == nil {
28
- if err := c.WriteFuncDeclAsFunction(d); err != nil {
29
- return err
30
- }
31
- c.tsw.WriteLine("") // Add space after function
38
+ otherDecls = append(otherDecls, d)
32
39
  }
33
40
  case *ast.GenDecl:
34
41
  for _, spec := range d.Specs {
35
- if err := c.WriteSpec(spec); err != nil {
36
- return err
42
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
43
+ typeSpecs = append(typeSpecs, typeSpec)
44
+ } else if varSpec, ok := spec.(*ast.ValueSpec); ok && d.Tok == token.VAR {
45
+ varSpecs = append(varSpecs, varSpec)
46
+ } else {
47
+ otherSpecs = append(otherSpecs, spec)
37
48
  }
38
- c.tsw.WriteLine("") // Add space after spec
39
49
  }
50
+ default:
51
+ otherDecls = append(otherDecls, d)
52
+ }
53
+ }
54
+
55
+ // Sort type declarations by dependencies
56
+ sortedTypeSpecs, err := c.sortTypeSpecsByDependencies(typeSpecs)
57
+ if err != nil {
58
+ // Surface the error instead of silently falling back
59
+ return fmt.Errorf("circular dependency detected sorting type declarations: %w", err)
60
+ }
61
+
62
+ // Sort variable declarations by type dependencies
63
+ sortedVarSpecs, err := c.sortVarSpecsByTypeDependencies(varSpecs, typeSpecs)
64
+ if err != nil {
65
+ return fmt.Errorf("failed to sort variable declarations: %w", err)
66
+ }
67
+
68
+ // Write non-type, non-var declarations first (imports, constants)
69
+ for _, spec := range otherSpecs {
70
+ if err := c.WriteSpec(spec); err != nil {
71
+ return err
72
+ }
73
+ c.tsw.WriteLine("") // Add space after spec
74
+ }
75
+
76
+ // Write sorted type declarations
77
+ for _, typeSpec := range sortedTypeSpecs {
78
+ if err := c.WriteSpec(typeSpec); err != nil {
79
+ return err
80
+ }
81
+ c.tsw.WriteLine("") // Add space after spec
82
+ }
83
+
84
+ // Write sorted variable declarations
85
+ for _, varSpec := range sortedVarSpecs {
86
+ if err := c.WriteSpec(varSpec); err != nil {
87
+ return err
88
+ }
89
+ c.tsw.WriteLine("") // Add space after spec
90
+ }
91
+
92
+ // Write function declarations last
93
+ for _, decl := range otherDecls {
94
+ switch d := decl.(type) {
95
+ case *ast.FuncDecl:
96
+ if err := c.WriteFuncDeclAsFunction(d); err != nil {
97
+ return err
98
+ }
99
+ c.tsw.WriteLine("") // Add space after function
40
100
  default:
41
101
  return fmt.Errorf("unknown decl: %#v", decl)
42
102
  }
43
103
  }
104
+
44
105
  return nil
45
106
  }
46
107
 
108
+ // sortTypeSpecsByDependencies performs a topological sort of type specifications
109
+ // based on their dependencies to ensure referenced types are defined before
110
+ // types that reference them.
111
+ func (c *GoToTSCompiler) sortTypeSpecsByDependencies(typeSpecs []*ast.TypeSpec) ([]*ast.TypeSpec, error) {
112
+ if len(typeSpecs) <= 1 {
113
+ return typeSpecs, nil
114
+ }
115
+
116
+ // Build dependency graph
117
+ dependencies := make(map[string][]string) // typeName -> list of types it depends on
118
+ typeSpecMap := make(map[string]*ast.TypeSpec)
119
+
120
+ // First pass: collect all type names
121
+ for _, typeSpec := range typeSpecs {
122
+ typeName := typeSpec.Name.Name
123
+ typeSpecMap[typeName] = typeSpec
124
+ dependencies[typeName] = []string{}
125
+ }
126
+
127
+ // Second pass: analyze dependencies
128
+ for _, typeSpec := range typeSpecs {
129
+ typeName := typeSpec.Name.Name
130
+ deps := c.extractTypeDependencies(typeSpec.Type, typeSpecMap)
131
+ dependencies[typeName] = deps
132
+ }
133
+
134
+ // Perform topological sort
135
+ sorted, err := c.topologicalSort(dependencies)
136
+ if err != nil {
137
+ return nil, err
138
+ }
139
+
140
+ // Build result in sorted order
141
+ var result []*ast.TypeSpec
142
+ for _, typeName := range sorted {
143
+ if typeSpec, exists := typeSpecMap[typeName]; exists {
144
+ result = append(result, typeSpec)
145
+ }
146
+ }
147
+
148
+ return result, nil
149
+ }
150
+
151
+ // extractTypeDependencies extracts only the dependencies that cause TypeScript initialization order issues.
152
+ // These are dependencies where the constructor of one type directly instantiates another type.
153
+ //
154
+ // TRUE dependencies (cause initialization issues):
155
+ // - Direct struct fields (non-pointer): type A struct { b B } -> A constructor calls new B()
156
+ // - Embedded struct fields: type A struct { B } -> A constructor calls new B()
157
+ // - Direct type aliases: type A B -> A directly wraps B
158
+ // - Array/slice of structs: type A []B -> needs B for default values
159
+ //
160
+ // FALSE dependencies (don't cause initialization issues):
161
+ // - Pointer fields: type A struct { b *B } -> just stores reference, no constructor call
162
+ // - Interface fields: type A struct { b SomeInterface } -> stores interface, no concrete instantiation
163
+ // - Map types: type A map[K]V -> map initialized empty, no constructor calls
164
+ // - Array/slice of pointers: type A []*B -> array of pointers, no constructor calls
165
+ func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
166
+ var deps []string
167
+
168
+ switch t := typeExpr.(type) {
169
+ case *ast.Ident:
170
+ // Direct type reference (e.g., type MyType OtherType)
171
+ if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
172
+ deps = append(deps, t.Name)
173
+ }
174
+
175
+ case *ast.StructType:
176
+ // Struct type - check field types, but be more selective
177
+ if t.Fields != nil {
178
+ for _, field := range t.Fields.List {
179
+ fieldDeps := c.extractStructFieldDependencies(field.Type, typeSpecMap)
180
+ deps = append(deps, fieldDeps...)
181
+ }
182
+ }
183
+
184
+ case *ast.ArrayType:
185
+ // Array/slice type - only depends on element type if element is a struct (not pointer)
186
+ if !c.isPointerType(t.Elt) {
187
+ elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
188
+ deps = append(deps, elemDeps...)
189
+ }
190
+ // Arrays of pointers don't create initialization dependencies
191
+
192
+ case *ast.StarExpr:
193
+ // Pointer types don't create initialization dependencies
194
+ // The pointed-to type doesn't need to be initialized when creating a pointer field
195
+
196
+ case *ast.MapType:
197
+ // Map types don't create initialization dependencies
198
+ // Maps are initialized empty, no constructor calls needed
199
+
200
+ case *ast.InterfaceType:
201
+ // Interface types don't create initialization dependencies
202
+
203
+ case *ast.FuncType:
204
+ // Function types don't create initialization dependencies
205
+
206
+ case *ast.SelectorExpr:
207
+ // External package types don't create local dependencies
208
+
209
+ // Add other type expressions as needed
210
+ }
211
+
212
+ // Sort dependencies for deterministic output
213
+ sort.Strings(deps)
214
+ return deps
215
+ }
216
+
217
+ // extractStructFieldDependencies extracts dependencies from struct field types
218
+ func (c *GoToTSCompiler) extractStructFieldDependencies(fieldType ast.Expr, typeSpecMap map[string]*ast.TypeSpec) []string {
219
+ var deps []string
220
+
221
+ switch t := fieldType.(type) {
222
+ case *ast.Ident:
223
+ // Direct field type: struct { b B } - this requires B to be initialized
224
+ if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
225
+ deps = append(deps, t.Name)
226
+ }
227
+
228
+ case *ast.StarExpr:
229
+ // Pointer field: struct { b *B } - this doesn't require B initialization
230
+ // Pointers are just references, no constructor call needed
231
+
232
+ case *ast.ArrayType:
233
+ // Array field: struct { b []B } or struct { b [5]B }
234
+ // Only create dependency if element type is not a pointer
235
+ if !c.isPointerType(t.Elt) {
236
+ elemDeps := c.extractTypeDependencies(t.Elt, typeSpecMap)
237
+ deps = append(deps, elemDeps...)
238
+ }
239
+
240
+ case *ast.MapType:
241
+ // Map field: struct { b map[K]V } - maps don't require initialization dependencies
242
+
243
+ case *ast.InterfaceType:
244
+ // Interface field: struct { b SomeInterface } - no concrete type dependency
245
+
246
+ case *ast.FuncType:
247
+ // Function field: struct { b func() } - no dependency
248
+
249
+ // Handle other field types as needed
250
+ }
251
+
252
+ return deps
253
+ }
254
+
255
+ // sortVarSpecsByTypeDependencies sorts variable declarations based on their type dependencies
256
+ func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
257
+ if len(varSpecs) <= 1 {
258
+ return varSpecs, nil
259
+ }
260
+
261
+ // Build type name map
262
+ typeSpecMap := make(map[string]*ast.TypeSpec)
263
+ for _, typeSpec := range typeSpecs {
264
+ typeSpecMap[typeSpec.Name.Name] = typeSpec
265
+ }
266
+
267
+ // Group variables by dependency status with names for sorting
268
+ type namedVarSpec struct {
269
+ spec *ast.ValueSpec
270
+ name string
271
+ }
272
+
273
+ var independentVars []namedVarSpec
274
+ var dependentVars []namedVarSpec
275
+
276
+ for _, varSpec := range varSpecs {
277
+ // Get variable name for sorting
278
+ varName := ""
279
+ if len(varSpec.Names) > 0 {
280
+ varName = varSpec.Names[0].Name
281
+ }
282
+
283
+ hasDependency := false
284
+
285
+ // Check type annotation
286
+ if varSpec.Type != nil {
287
+ deps := c.extractTypeDependencies(varSpec.Type, typeSpecMap)
288
+ if len(deps) > 0 {
289
+ hasDependency = true
290
+ }
291
+ }
292
+
293
+ // Check initializer expressions for type usage
294
+ if !hasDependency {
295
+ for _, value := range varSpec.Values {
296
+ if c.hasTypeReferences(value, typeSpecMap) {
297
+ hasDependency = true
298
+ break
299
+ }
300
+ }
301
+ }
302
+
303
+ namedVar := namedVarSpec{spec: varSpec, name: varName}
304
+ if hasDependency {
305
+ dependentVars = append(dependentVars, namedVar)
306
+ } else {
307
+ independentVars = append(independentVars, namedVar)
308
+ }
309
+ }
310
+
311
+ // Sort both groups by name for deterministic output
312
+ sort.Slice(independentVars, func(i, j int) bool {
313
+ return independentVars[i].name < independentVars[j].name
314
+ })
315
+ sort.Slice(dependentVars, func(i, j int) bool {
316
+ return dependentVars[i].name < dependentVars[j].name
317
+ })
318
+
319
+ // Return independent variables first, then dependent ones
320
+ result := make([]*ast.ValueSpec, 0, len(varSpecs))
321
+ for _, namedVar := range independentVars {
322
+ result = append(result, namedVar.spec)
323
+ }
324
+ for _, namedVar := range dependentVars {
325
+ result = append(result, namedVar.spec)
326
+ }
327
+
328
+ return result, nil
329
+ }
330
+
331
+ // hasTypeReferences checks if an expression contains references to local types
332
+ func (c *GoToTSCompiler) hasTypeReferences(expr ast.Expr, typeSpecMap map[string]*ast.TypeSpec) bool {
333
+ hasRef := false
334
+
335
+ ast.Inspect(expr, func(n ast.Node) bool {
336
+ switch t := n.(type) {
337
+ case *ast.CallExpr:
338
+ // Check function calls like new Message(), makeChannel<Message>()
339
+ if ident, ok := t.Fun.(*ast.Ident); ok {
340
+ if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
341
+ hasRef = true
342
+ return false
343
+ }
344
+ }
345
+ // Check type arguments in generic calls
346
+ if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
347
+ if c.hasTypeReferences(funcType.Index, typeSpecMap) {
348
+ hasRef = true
349
+ return false
350
+ }
351
+ }
352
+ case *ast.CompositeLit:
353
+ // Check composite literals like Message{...}
354
+ if ident, ok := t.Type.(*ast.Ident); ok {
355
+ if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
356
+ hasRef = true
357
+ return false
358
+ }
359
+ }
360
+ case *ast.Ident:
361
+ // Check direct type references
362
+ if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
363
+ hasRef = true
364
+ return false
365
+ }
366
+ }
367
+ return !hasRef // Stop walking if we found a reference
368
+ })
369
+
370
+ return hasRef
371
+ }
372
+
373
+ // topologicalSort performs a topological sort of the dependency graph
374
+ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]string, error) {
375
+ // Kahn's algorithm for topological sorting with deterministic ordering
376
+ inDegree := make(map[string]int)
377
+ graph := make(map[string][]string)
378
+
379
+ // Initialize in-degree counts and reverse graph
380
+ for node := range dependencies {
381
+ inDegree[node] = 0
382
+ graph[node] = []string{}
383
+ }
384
+
385
+ // Build reverse graph and count in-degrees
386
+ for node, deps := range dependencies {
387
+ // Sort dependencies for consistent output
388
+ sortedDeps := make([]string, len(deps))
389
+ copy(sortedDeps, deps)
390
+ sort.Strings(sortedDeps)
391
+
392
+ for _, dep := range sortedDeps {
393
+ if _, exists := inDegree[dep]; exists {
394
+ graph[dep] = append(graph[dep], node)
395
+ inDegree[node]++
396
+ }
397
+ }
398
+ }
399
+
400
+ // Sort neighbors in graph for consistency
401
+ for node := range graph {
402
+ sort.Strings(graph[node])
403
+ }
404
+
405
+ // Find nodes with no incoming edges and sort them
406
+ var queue []string
407
+ for node, degree := range inDegree {
408
+ if degree == 0 {
409
+ queue = append(queue, node)
410
+ }
411
+ }
412
+ sort.Strings(queue) // Sort initial queue for deterministic output
413
+
414
+ var result []string
415
+
416
+ for len(queue) > 0 {
417
+ // Remove node from queue (already sorted)
418
+ current := queue[0]
419
+ queue = queue[1:]
420
+ result = append(result, current)
421
+
422
+ // Collect new zero-degree nodes
423
+ var newZeroNodes []string
424
+ for _, neighbor := range graph[current] {
425
+ inDegree[neighbor]--
426
+ if inDegree[neighbor] == 0 {
427
+ newZeroNodes = append(newZeroNodes, neighbor)
428
+ }
429
+ }
430
+
431
+ // Sort new zero-degree nodes and add to queue
432
+ sort.Strings(newZeroNodes)
433
+ queue = append(queue, newZeroNodes...)
434
+ }
435
+
436
+ // Check for cycles
437
+ if len(result) != len(dependencies) {
438
+ // Find the remaining nodes to help debug the circular dependency
439
+ processed := make(map[string]bool)
440
+ for _, name := range result {
441
+ processed[name] = true
442
+ }
443
+
444
+ var remaining []string
445
+ for name := range dependencies {
446
+ if !processed[name] {
447
+ remaining = append(remaining, name)
448
+ }
449
+ }
450
+ sort.Strings(remaining)
451
+
452
+ return nil, fmt.Errorf("circular dependency detected in type declarations. Remaining types: %v", remaining)
453
+ }
454
+
455
+ return result, nil
456
+ }
457
+
47
458
  // WriteFuncDeclAsFunction translates a Go function declaration (`ast.FuncDecl`)
48
459
  // that does not have a receiver (i.e., it's a regular function, not a method)
49
460
  // into a TypeScript function.
@@ -6,17 +6,13 @@ import (
6
6
  "strings"
7
7
  )
8
8
 
9
- // writeAsyncCallIfNeeded writes the await prefix for async function or method calls
10
- // Returns true if await was written, false otherwise
11
- func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
9
+ // isCallExprAsync determines if a CallExpr represents an async function/method call
10
+ func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
12
11
  switch fun := exp.Fun.(type) {
13
12
  case *ast.Ident:
14
13
  // Function call (e.g., func())
15
- if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil {
16
- if c.analysis.IsAsyncFunc(obj) {
17
- c.tsw.WriteLiterally("await ")
18
- return true
19
- }
14
+ if obj := c.objectOfIdent(fun); obj != nil {
15
+ return c.analysis.IsAsyncFunc(obj)
20
16
  }
21
17
  return false
22
18
 
@@ -29,13 +25,13 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
29
25
  switch x := fun.X.(type) {
30
26
  case *ast.Ident:
31
27
  // Direct identifier: obj.method()
32
- obj = c.pkg.TypesInfo.Uses[x]
28
+ obj = c.objectOfIdent(x)
33
29
  objOk = obj != nil
34
30
 
35
31
  case *ast.StarExpr:
36
32
  // Pointer dereference: (*p).method() or p.method() where p is a pointer
37
33
  if id, isIdent := x.X.(*ast.Ident); isIdent {
38
- obj = c.pkg.TypesInfo.Uses[id]
34
+ obj = c.objectOfIdent(id)
39
35
  objOk = obj != nil
40
36
  }
41
37
 
@@ -43,8 +39,6 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
43
39
  // Field access: obj.field.method()
44
40
  // Get the type of the field access expression
45
41
  if fieldType := c.pkg.TypesInfo.TypeOf(x); fieldType != nil {
46
- // For field access, we create a synthetic object representing the field type
47
- // We'll handle this case below when we determine the method's type
48
42
  objOk = true
49
43
  }
50
44
 
@@ -61,13 +55,7 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
61
55
  if pkgName, isPkg := obj.(*types.PkgName); isPkg {
62
56
  methodName := fun.Sel.Name
63
57
  pkgPath := pkgName.Imported().Path()
64
-
65
- // Check if this package-level function is async (empty TypeName)
66
- if c.analysis.IsMethodAsync(pkgPath, "", methodName) {
67
- c.tsw.WriteLiterally("await ")
68
- return true
69
- }
70
- return false
58
+ return c.analysis.IsMethodAsync(pkgPath, "", methodName)
71
59
  }
72
60
  }
73
61
 
@@ -83,7 +71,6 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
83
71
  }
84
72
  } else {
85
73
  // Field access case: obj.field.method()
86
- // Get the type of the field access expression
87
74
  targetType = c.pkg.TypesInfo.TypeOf(fun.X)
88
75
  if targetType == nil {
89
76
  return false
@@ -94,12 +81,7 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
94
81
 
95
82
  // Check if target type is an interface
96
83
  if interfaceType, isInterface := targetType.Underlying().(*types.Interface); isInterface {
97
- // Interface method call: use interface method async analysis
98
- if c.analysis.IsInterfaceMethodAsync(interfaceType, methodName) {
99
- c.tsw.WriteLiterally("await ")
100
- return true
101
- }
102
- return false
84
+ return c.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
103
85
  }
104
86
 
105
87
  // Get the named type from the target type
@@ -128,22 +110,26 @@ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
128
110
  if typePkg != nil {
129
111
  pkgPath = typePkg.Path()
130
112
  } else {
131
- // Fallback to current package
132
113
  pkgPath = c.pkg.Types.Path()
133
114
  }
134
115
 
135
- // Check if this method is async using unified analysis
136
- if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
137
- c.tsw.WriteLiterally("await ")
138
- return true
139
- }
140
- return false
116
+ return c.analysis.IsMethodAsync(pkgPath, typeName, methodName)
141
117
 
142
118
  default:
143
119
  return false
144
120
  }
145
121
  }
146
122
 
123
+ // writeAsyncCallIfNeeded writes the await prefix for async function or method calls
124
+ // Returns true if await was written, false otherwise
125
+ func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
126
+ if c.isCallExprAsync(exp) {
127
+ c.tsw.WriteLiterally("await ")
128
+ return true
129
+ }
130
+ return false
131
+ }
132
+
147
133
  // addNonNullAssertion adds ! for function calls that might return null
148
134
  func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
149
135
  if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
@@ -151,7 +137,7 @@ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
151
137
  // Check if this is a function parameter identifier that needs not-null assertion
152
138
  if ident, isIdent := expFun.(*ast.Ident); isIdent {
153
139
  // Check if this identifier is a function parameter
154
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
140
+ if obj := c.objectOfIdent(ident); obj != nil {
155
141
  if _, isVar := obj.(*types.Var); isVar {
156
142
  // This is a variable (including function parameters)
157
143
  // Function parameters that are function types need ! assertion
@@ -8,6 +8,25 @@ import (
8
8
  "github.com/pkg/errors"
9
9
  )
10
10
 
11
+ // builtinFunctions is the definitive list of Go builtin functions handled by the compiler
12
+ var builtinFunctions = map[string]bool{
13
+ "len": true,
14
+ "cap": true,
15
+ "make": true,
16
+ "new": true,
17
+ "append": true,
18
+ "copy": true,
19
+ "delete": true,
20
+ "complex": true,
21
+ "real": true,
22
+ "imag": true,
23
+ "close": true,
24
+ "panic": true,
25
+ "recover": true,
26
+ "print": true,
27
+ "println": true,
28
+ }
29
+
11
30
  // writeBuiltinFunction handles built-in Go functions
12
31
  func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
13
32
  switch funName {
@@ -7,34 +7,6 @@ import (
7
7
  "github.com/pkg/errors"
8
8
  )
9
9
 
10
- // isByteSliceType checks if a type is []byte (slice of uint8)
11
- func (c *GoToTSCompiler) isByteSliceType(t types.Type) bool {
12
- if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
13
- if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
14
- return true
15
- }
16
- }
17
- return false
18
- }
19
-
20
- // isRuneSliceType checks if a type is []rune (slice of int32)
21
- func (c *GoToTSCompiler) isRuneSliceType(t types.Type) bool {
22
- if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
23
- if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Int32 {
24
- return true
25
- }
26
- }
27
- return false
28
- }
29
-
30
- // isStringType checks if a type is string
31
- func (c *GoToTSCompiler) isStringType(t types.Type) bool {
32
- if basic, isBasic := t.Underlying().(*types.Basic); isBasic {
33
- return basic.Kind() == types.String || basic.Kind() == types.UntypedString
34
- }
35
- return false
36
- }
37
-
38
10
  // writeByteSliceCreation handles the creation of []byte slices with proper Uint8Array handling
39
11
  func (c *GoToTSCompiler) writeByteSliceCreation(lengthArg, capacityArg interface{}) error {
40
12
  return c.writeSliceCreationForType(lengthArg, capacityArg, true)