goscript 0.0.49 → 0.0.50

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.
@@ -160,6 +160,7 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
160
160
  Defs: make(map[*ast.Ident]types.Object),
161
161
  Uses: make(map[*ast.Ident]types.Object),
162
162
  },
163
+ Fset: fset,
163
164
  }
164
165
 
165
166
  // Type check the package
@@ -170,10 +171,8 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
170
171
  }
171
172
  pkg.Types = typePkg
172
173
 
173
- // Run analysis
174
- analysis := NewAnalysis()
175
- cmap := ast.NewCommentMap(fset, file, file.Comments)
176
- AnalyzeFile(file, pkg, analysis, cmap)
174
+ // Run package-level analysis
175
+ analysis := AnalyzePackageFiles(pkg, nil)
177
176
 
178
177
  // Collect variable objects
179
178
  objects := make(map[string]types.Object)
@@ -269,18 +268,16 @@ func TestWrapperTypeDetection(t *testing.T) {
269
268
  t.Fatalf("Package has errors: %v", pkg.Errors[0])
270
269
  }
271
270
 
272
- // Run analysis on the first file
271
+ // Run package-level analysis
273
272
  if len(pkg.Syntax) == 0 {
274
273
  t.Fatal("No syntax files found")
275
274
  }
276
275
 
277
- analysis := NewAnalysis()
278
- cmap := ast.NewCommentMap(pkg.Fset, pkg.Syntax[0], pkg.Syntax[0].Comments)
279
- AnalyzeFile(pkg.Syntax[0], pkg, analysis, cmap)
276
+ analysis := AnalyzePackageFiles(pkg, nil)
280
277
 
281
- // Verify the WrapperTypes map was initialized
282
- if analysis.WrapperTypes == nil {
283
- t.Error("WrapperTypes map was not initialized")
278
+ // Verify the NamedBasicTypes map was initialized
279
+ if analysis.NamedBasicTypes == nil {
280
+ t.Error("NamedBasicTypes map was not initialized")
284
281
  }
285
282
 
286
283
  // Test some type lookups to verify wrapper type detection works
@@ -290,7 +287,7 @@ func TestWrapperTypeDetection(t *testing.T) {
290
287
  // Check if MyMode is detected as a wrapper type
291
288
  if obj := scope.Lookup("MyMode"); obj != nil {
292
289
  if typeName, ok := obj.(*types.TypeName); ok {
293
- isWrapper := analysis.IsWrapperType(typeName.Type())
290
+ isWrapper := analysis.IsNamedBasicType(typeName.Type())
294
291
  if !isWrapper {
295
292
  t.Errorf("MyMode should be detected as wrapper type, got %v", isWrapper)
296
293
  }
@@ -301,7 +298,7 @@ func TestWrapperTypeDetection(t *testing.T) {
301
298
  // Test that regular struct types are not detected as wrapper types
302
299
  if obj := scope.Lookup("MyDir"); obj != nil {
303
300
  if typeName, ok := obj.(*types.TypeName); ok {
304
- isWrapper := analysis.IsWrapperType(typeName.Type())
301
+ isWrapper := analysis.IsNamedBasicType(typeName.Type())
305
302
  if isWrapper {
306
303
  t.Errorf("MyDir should not be detected as wrapper type, got %v", isWrapper)
307
304
  }
@@ -309,13 +306,13 @@ func TestWrapperTypeDetection(t *testing.T) {
309
306
  }
310
307
  }
311
308
 
312
- t.Logf("Analysis completed successfully with %d wrapper types tracked", len(analysis.WrapperTypes))
309
+ t.Logf("Analysis completed successfully with %d named basic types tracked", len(analysis.NamedBasicTypes))
313
310
  }
314
311
 
315
312
  // TestDiscoverGsPackages verifies that the discoverEmbeddedGsPackages function
316
313
  // can find packages in the embedded gs/ directory
317
314
  func TestDiscoverGsPackages(t *testing.T) {
318
- analysis := NewAnalysis()
315
+ analysis := NewAnalysis(nil)
319
316
 
320
317
  // Test package discovery using the embedded filesystem
321
318
  packages := analysis.discoverEmbeddedGsPackages()
@@ -72,7 +72,11 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
72
72
  packages.NeedTypesInfo |
73
73
  packages.NeedTypesSizes
74
74
 
75
- return &Compiler{config: *conf, le: le, opts: *opts}, nil
75
+ return &Compiler{
76
+ config: *conf,
77
+ le: le,
78
+ opts: *opts,
79
+ }, nil
76
80
  }
77
81
 
78
82
  // CompilationResult contains information about what was compiled
@@ -141,7 +145,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
141
145
  // Check if this package has a handwritten equivalent
142
146
  if hasHandwrittenEquivalent(pkg.PkgPath) {
143
147
  // Add this package but don't visit its dependencies
144
- // c.le.Debugf("Skipping dependencies of handwritten package: %s", pkg.PkgPath)
145
148
  return
146
149
  }
147
150
 
@@ -149,13 +152,11 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
149
152
  for _, imp := range pkg.Imports {
150
153
  // Skip protobuf-go-lite packages and their dependencies
151
154
  if isProtobufGoLitePackage(imp.PkgPath) {
152
- // c.le.Debugf("Skipping protobuf-go-lite package: %s", imp.PkgPath)
153
155
  continue
154
156
  }
155
157
 
156
158
  // Skip packages that are only used by .pb.go files
157
159
  if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
158
- // c.le.Debugf("Skipping package only used by .pb.go files: %s", imp.PkgPath)
159
160
  continue
160
161
  }
161
162
 
@@ -168,28 +169,39 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
168
169
  visit(pkg)
169
170
  }
170
171
 
171
- // Replace pkgs with all packages
172
- pkgs = allPkgs
172
+ // Now we have collected all dependencies, but they only have minimal information.
173
+ // We need to reload them with complete type information for compilation.
174
+ // Collect all package paths from the dependency graph
175
+ var pkgPaths []string
176
+ pkgPathSet := make(map[string]bool) // Use set to avoid duplicates
173
177
 
174
- /*
175
- // Now load all packages with full mode to get complete type information
176
- var pkgPaths []string
177
- for _, pkg := range pkgs {
178
- if pkg.PkgPath != "" {
179
- pkgPaths = append(pkgPaths, pkg.PkgPath)
180
- }
178
+ for _, pkg := range allPkgs {
179
+ if pkg.PkgPath != "" && !pkgPathSet[pkg.PkgPath] {
180
+ pkgPaths = append(pkgPaths, pkg.PkgPath)
181
+ pkgPathSet[pkg.PkgPath] = true
181
182
  }
183
+ }
184
+
185
+ // Reload all collected packages with complete type information
186
+ if len(pkgPaths) > 0 {
187
+ c.le.Debugf("Reloading %d packages with complete type information", len(pkgPaths))
182
188
 
183
- // Reload all packages with full mode
184
- // TODO: Can we get rid of this? This would be very slow!
185
189
  fullOpts := c.opts
186
190
  fullOpts.Context = ctx
191
+ // Use LoadAllSyntax to get complete type information, syntax trees, and type checking
187
192
  fullOpts.Mode = packages.LoadAllSyntax
188
- pkgs, err = packages.Load(&fullOpts, pkgPaths...)
193
+
194
+ reloadedPkgs, err := packages.Load(&fullOpts, pkgPaths...)
189
195
  if err != nil {
190
- return fmt.Errorf("failed to reload packages with full mode: %w", err)
196
+ return nil, fmt.Errorf("failed to reload packages with complete type information: %w", err)
191
197
  }
192
- */
198
+
199
+ // Replace the minimal packages with the fully loaded ones
200
+ pkgs = reloadedPkgs
201
+ } else {
202
+ // No packages to reload, use the original set
203
+ pkgs = allPkgs
204
+ }
193
205
  }
194
206
 
195
207
  // If DisableEmitBuiltin is false, we need to copy the builtin package to the output directory
@@ -206,6 +218,12 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
206
218
  // Track which gs packages have been processed to avoid duplicates
207
219
  processedGsPackages := make(map[string]bool)
208
220
 
221
+ // Create a map of all loaded packages for dependency analysis
222
+ allPackages := make(map[string]*packages.Package)
223
+ for _, pkg := range pkgs {
224
+ allPackages[pkg.PkgPath] = pkg
225
+ }
226
+
209
227
  // Compile all packages
210
228
  for _, pkg := range pkgs {
211
229
  // Check if the package has a handwritten equivalent
@@ -237,7 +255,7 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
237
255
  continue
238
256
  }
239
257
 
240
- pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
258
+ pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg, allPackages)
241
259
  if err != nil {
242
260
  return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
243
261
  }
@@ -262,6 +280,7 @@ type PackageCompiler struct {
262
280
  compilerConf *Config
263
281
  outputPath string
264
282
  pkg *packages.Package
283
+ allPackages map[string]*packages.Package
265
284
  }
266
285
 
267
286
  // NewPackageCompiler creates a new `PackageCompiler` for a given Go package.
@@ -272,12 +291,14 @@ func NewPackageCompiler(
272
291
  le *logrus.Entry,
273
292
  compilerConf *Config,
274
293
  pkg *packages.Package,
294
+ allPackages map[string]*packages.Package,
275
295
  ) (*PackageCompiler, error) {
276
296
  res := &PackageCompiler{
277
297
  le: le,
278
298
  pkg: pkg,
279
299
  compilerConf: compilerConf,
280
300
  outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
301
+ allPackages: allPackages,
281
302
  }
282
303
 
283
304
  return res, nil
@@ -304,7 +325,10 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
304
325
  }
305
326
 
306
327
  // Perform package-level analysis for auto-imports
307
- packageAnalysis := AnalyzePackage(c.pkg)
328
+ packageAnalysis := AnalyzePackageImports(c.pkg)
329
+
330
+ // Perform comprehensive package-level analysis for code generation
331
+ analysis := AnalyzePackageFiles(c.pkg, c.allPackages)
308
332
 
309
333
  // Track all compiled files for later generating the index.ts
310
334
  compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
@@ -342,7 +366,7 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
342
366
 
343
367
  // log just the filename
344
368
  c.le.Debugf("GS: %s", filepath.Base(fileName))
345
- if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
369
+ if err := c.CompileFile(ctx, fileName, f, analysis, packageAnalysis); err != nil {
346
370
  return err
347
371
  }
348
372
 
@@ -392,6 +416,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
392
416
  // Find which symbols this file exports
393
417
  var valueSymbols []string
394
418
  var typeSymbols []string
419
+ var structSymbols []string // New: Track structs separately
395
420
 
396
421
  // Find the corresponding syntax file
397
422
  for i, syntax := range c.pkg.Syntax {
@@ -415,9 +440,15 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
415
440
  switch s := spec.(type) {
416
441
  case *ast.TypeSpec:
417
442
  if s.Name.IsExported() {
418
- // All type declarations (interfaces, structs, type definitions, type aliases)
419
- // become TypeScript types and must be exported with "export type"
420
- typeSymbols = append(typeSymbols, s.Name.Name)
443
+ // Check if this is a struct type
444
+ if _, isStruct := s.Type.(*ast.StructType); isStruct {
445
+ // Structs become TypeScript classes and need both type and value exports
446
+ structSymbols = append(structSymbols, s.Name.Name)
447
+ } else {
448
+ // Other type declarations (interfaces, type definitions, type aliases)
449
+ // become TypeScript types and must be exported with "export type"
450
+ typeSymbols = append(typeSymbols, s.Name.Name)
451
+ }
421
452
  }
422
453
  case *ast.ValueSpec:
423
454
  for _, name := range s.Names {
@@ -442,6 +473,17 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
442
473
  }
443
474
  }
444
475
 
476
+ // Write struct exports (both as types and values)
477
+ if len(structSymbols) > 0 {
478
+ sort.Strings(structSymbols)
479
+ // Export classes as values (which makes them available as both types and values in TypeScript)
480
+ exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
481
+ strings.Join(structSymbols, ", "), fileName)
482
+ if _, err := indexFile.WriteString(exportLine); err != nil {
483
+ return err
484
+ }
485
+ }
486
+
445
487
  if len(typeSymbols) > 0 {
446
488
  sort.Strings(typeSymbols)
447
489
  exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
@@ -456,21 +498,11 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
456
498
  }
457
499
 
458
500
  // CompileFile handles the compilation of a single Go source file to TypeScript.
459
- // It first performs a pre-compilation analysis of the file using `AnalyzeFile`
460
- // to gather information necessary for accurate TypeScript generation (e.g.,
461
- // about varRefing, async functions, defer statements).
501
+ // It uses the pre-computed package-level analysis for accurate TypeScript generation
502
+ // (e.g., about varRefing, async functions, defer statements, receiver usage across files).
462
503
  // Then, it creates a `FileCompiler` instance for the file and invokes its
463
504
  // `Compile` method to generate the TypeScript code.
464
- func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, packageAnalysis *PackageAnalysis) error {
465
- // Create a new analysis instance for per-file data
466
- analysis := NewAnalysis()
467
-
468
- // Create comment map for the file
469
- cmap := ast.NewCommentMap(p.pkg.Fset, syntax, syntax.Comments)
470
-
471
- // Analyze the file before compiling
472
- AnalyzeFile(syntax, p.pkg, analysis, cmap)
473
-
505
+ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, analysis *Analysis, packageAnalysis *PackageAnalysis) error {
474
506
  fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
475
507
  if err != nil {
476
508
  return err
package/compiler/decl.go CHANGED
@@ -3,6 +3,7 @@ package compiler
3
3
  import (
4
4
  "fmt"
5
5
  "go/ast"
6
+ "go/types"
6
7
  )
7
8
 
8
9
  // WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
@@ -193,6 +194,27 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
193
194
  var isAsync bool
194
195
  if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
195
196
  isAsync = c.analysis.IsAsyncFunc(obj)
197
+
198
+ // Check if this method must be async due to interface constraints
199
+ if !isAsync && decl.Recv != nil && len(decl.Recv.List) > 0 {
200
+ // Get the receiver type
201
+ receiverType := decl.Recv.List[0].Type
202
+ if starExpr, ok := receiverType.(*ast.StarExpr); ok {
203
+ receiverType = starExpr.X
204
+ }
205
+
206
+ if ident, ok := receiverType.(*ast.Ident); ok {
207
+ // Get the named type for this receiver
208
+ if receiverObj := c.pkg.TypesInfo.Uses[ident]; receiverObj != nil {
209
+ if namedType, ok := receiverObj.Type().(*types.Named); ok {
210
+ // Check if this method must be async due to interface constraints
211
+ if c.analysis.MustBeAsyncDueToInterface(namedType, decl.Name.Name) {
212
+ isAsync = true
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
196
218
  }
197
219
 
198
220
  // Methods are typically public in the TS output
@@ -6,70 +6,64 @@ import (
6
6
  "strings"
7
7
  )
8
8
 
9
- // writeAsyncCall writes the await prefix for async function calls
10
- func (c *GoToTSCompiler) writeAsyncCall(exp *ast.CallExpr, funIdent *ast.Ident) bool {
11
- if funIdent == nil {
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 {
12
+ switch fun := exp.Fun.(type) {
13
+ case *ast.Ident:
14
+ // Function call (e.g., func())
15
+ if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil && c.analysis.IsAsyncFunc(obj) {
16
+ c.tsw.WriteLiterally("await ")
17
+ return true
18
+ }
12
19
  return false
13
- }
14
-
15
- // Check if this is an async function call
16
- if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
17
- c.tsw.WriteLiterally("await ")
18
- return true
19
- }
20
20
 
21
- return false
22
- }
21
+ case *ast.SelectorExpr:
22
+ // Method call (e.g., obj.method())
23
+ ident, ok := fun.X.(*ast.Ident)
24
+ if !ok {
25
+ return false
26
+ }
23
27
 
24
- // writeAsyncMethodCall writes the await prefix for async method calls
25
- func (c *GoToTSCompiler) writeAsyncMethodCall(exp *ast.CallExpr) bool {
26
- selExpr, ok := exp.Fun.(*ast.SelectorExpr)
27
- if !ok {
28
- return false
29
- }
28
+ // Get the type of the receiver
29
+ obj := c.pkg.TypesInfo.Uses[ident]
30
+ if obj == nil {
31
+ return false
32
+ }
30
33
 
31
- // Check if this is a method call on a variable (e.g., mu.Lock())
32
- ident, ok := selExpr.X.(*ast.Ident)
33
- if !ok {
34
- return false
35
- }
34
+ varObj, ok := obj.(*types.Var)
35
+ if !ok {
36
+ return false
37
+ }
36
38
 
37
- // Get the type of the receiver
38
- obj := c.pkg.TypesInfo.Uses[ident]
39
- if obj == nil {
40
- return false
41
- }
39
+ // Get the type name and package
40
+ namedType, ok := varObj.Type().(*types.Named)
41
+ if !ok {
42
+ return false
43
+ }
42
44
 
43
- varObj, ok := obj.(*types.Var)
44
- if !ok {
45
- return false
46
- }
45
+ typeName := namedType.Obj().Name()
46
+ methodName := fun.Sel.Name
47
47
 
48
- // Get the type name and package
49
- namedType, ok := varObj.Type().(*types.Named)
50
- if !ok {
51
- return false
52
- }
48
+ // Check if the type is from an imported package
49
+ typePkg := namedType.Obj().Pkg()
50
+ if typePkg == nil || typePkg == c.pkg.Types {
51
+ return false
52
+ }
53
53
 
54
- typeName := namedType.Obj().Name()
55
- methodName := selExpr.Sel.Name
54
+ // Use the full package path from the type information (not just the package name)
55
+ pkgPath := typePkg.Path()
56
56
 
57
- // Check if the type is from an imported package
58
- typePkg := namedType.Obj().Pkg()
59
- if typePkg == nil || typePkg == c.pkg.Types {
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
61
+ }
60
62
  return false
61
- }
62
63
 
63
- // Use the actual package name from the type information
64
- pkgName := typePkg.Name()
65
-
66
- // Check if this method is async based on metadata
67
- if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
68
- c.tsw.WriteLiterally("await ")
69
- return true
64
+ default:
65
+ return false
70
66
  }
71
-
72
- return false
73
67
  }
74
68
 
75
69
  // addNonNullAssertion adds ! for function calls that might return null
@@ -81,7 +81,7 @@ func (c *GoToTSCompiler) writeArrayTypeConversion(exp *ast.CallExpr) (handled bo
81
81
  // Check if the argument is a named type with a slice underlying type
82
82
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
83
83
  // Check if the named type has receiver methods (is a wrapper type)
84
- if c.analysis.IsWrapperType(namedArgType) {
84
+ if c.analysis.IsNamedBasicType(namedArgType) {
85
85
  // Check if the underlying type matches the target slice type
86
86
  if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
87
87
  // Get the target slice type
@@ -216,7 +216,7 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
216
216
  if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
217
217
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
218
218
  // Check if the argument type is a wrapper type
219
- if c.analysis.IsWrapperType(namedArgType) {
219
+ if c.analysis.IsNamedBasicType(namedArgType) {
220
220
  // Check if we're converting to the underlying type
221
221
  targetType := typeName.Type()
222
222
  underlyingType := namedArgType.Underlying()
@@ -247,7 +247,7 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
247
247
  return true, nil
248
248
  } else {
249
249
  // Check if this is a wrapper type
250
- isWrapperType := c.analysis.IsWrapperType(typeName.Type())
250
+ isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
251
251
  if isWrapperType {
252
252
  // For wrapper types, use type casting instead of constructor calls
253
253
  c.tsw.WriteLiterally("(")
@@ -288,8 +288,6 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
288
288
  c.tsw.WriteLiterally(funIdent.String())
289
289
  c.tsw.WriteLiterally("(")
290
290
 
291
- fmt.Printf("DEBUG: Type conversion constructor for %s\n", funIdent.String())
292
-
293
291
  // Use auto-wrapping for the constructor argument
294
292
  // The constructor parameter type is the underlying type of the named type
295
293
  // For MyMode (which is type MyMode os.FileMode), the constructor expects os.FileMode
@@ -299,8 +297,6 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
299
297
  constructorParamType = namedType.Underlying()
300
298
  }
301
299
 
302
- fmt.Printf("DEBUG: Constructor param type: %v\n", constructorParamType)
303
-
304
300
  if err := c.writeAutoWrappedArgument(exp.Args[0], constructorParamType); err != nil {
305
301
  return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
306
302
  }
@@ -376,7 +372,7 @@ func (c *GoToTSCompiler) writeIntConversion(exp *ast.CallExpr) error {
376
372
  if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
377
373
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
378
374
  // Check if the argument type is a wrapper type
379
- if c.analysis.IsWrapperType(namedArgType) {
375
+ if c.analysis.IsNamedBasicType(namedArgType) {
380
376
  // Check if we're converting to int (the underlying type)
381
377
  if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
382
378
  // This is a conversion from a wrapper type to int
@@ -413,7 +409,7 @@ func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selecto
413
409
  if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
414
410
  if namedArgType, isNamed := argType.(*types.Named); isNamed {
415
411
  // Check if the argument type is a wrapper type
416
- if c.analysis.IsWrapperType(namedArgType) {
412
+ if c.analysis.IsNamedBasicType(namedArgType) {
417
413
  // Check if we're converting to the underlying type
418
414
  targetType := typeName.Type()
419
415
  underlyingType := namedArgType.Underlying()
@@ -444,7 +440,7 @@ func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selecto
444
440
  return true, nil
445
441
  } else {
446
442
  // Check if this is a wrapper type
447
- isWrapperType := c.analysis.IsWrapperType(typeName.Type())
443
+ isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
448
444
  if isWrapperType {
449
445
  // For wrapper types, use type casting instead of constructor calls
450
446
  c.tsw.WriteLiterally("(")
@@ -74,7 +74,7 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
74
74
  }
75
75
 
76
76
  // Check if this is an async function call
77
- _ = c.writeAsyncCall(exp, funIdent)
77
+ c.writeAsyncCallIfNeeded(exp)
78
78
 
79
79
  // Not a special built-in, treat as a regular function call
80
80
  if err := c.WriteValueExpr(expFun); err != nil {
@@ -99,7 +99,7 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
99
99
 
100
100
  // Handle non-identifier function expressions (method calls, function literals, etc.)
101
101
  // Check if this is an async method call (e.g., mu.Lock())
102
- _ = c.writeAsyncMethodCall(exp)
102
+ c.writeAsyncCallIfNeeded(exp)
103
103
 
104
104
  // If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
105
105
  if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
@@ -169,7 +169,7 @@ func (c *GoToTSCompiler) writeCallArguments(exp *ast.CallExpr) error {
169
169
  func (c *GoToTSCompiler) writeArgumentWithTypeHandling(arg ast.Expr, funcSig *types.Signature, argIndex int) error {
170
170
  if funcSig != nil && argIndex < funcSig.Params().Len() {
171
171
  paramType := funcSig.Params().At(argIndex).Type()
172
- isWrapper := c.analysis.IsWrapperType(paramType)
172
+ isWrapper := c.analysis.IsNamedBasicType(paramType)
173
173
 
174
174
  if isWrapper {
175
175
  // For wrapper types (now type aliases), no auto-wrapping is needed
@@ -234,7 +234,7 @@ func (c *GoToTSCompiler) getImportAlias(pkgPath string) string {
234
234
  func (c *GoToTSCompiler) writeAutoWrappedArgument(arg ast.Expr, expectedType types.Type) error {
235
235
  // For wrapper types (now type aliases), no auto-wrapping is needed
236
236
  // Just use type casting if the types don't match exactly
237
- if c.analysis.IsWrapperType(expectedType) {
237
+ if c.analysis.IsNamedBasicType(expectedType) {
238
238
  argType := c.pkg.TypesInfo.TypeOf(arg)
239
239
 
240
240
  // Only add type casting if needed
@@ -271,39 +271,70 @@ func (c *GoToTSCompiler) writeWrapperTypeMethodCall(exp *ast.CallExpr, selectorE
271
271
  return false, nil
272
272
  }
273
273
 
274
- // Check if this is a wrapper type
275
- if !c.analysis.IsWrapperType(baseType) {
274
+ // Check if this is a wrapper type using the analysis
275
+ isWrapperType := c.analysis.IsNamedBasicType(baseType)
276
+
277
+ // Special handling for type aliases to basic types that might have wrapper functions
278
+ // Even if IsNamedBasicType returns false, we should check for known wrapper type patterns
279
+ var typeName string
280
+ if !isWrapperType {
281
+ // Check if this is a type alias to a basic type that might have wrapper methods
282
+ if aliasType, ok := baseType.(*types.Alias); ok {
283
+ if aliasType.Obj() != nil && aliasType.Obj().Pkg() != nil {
284
+ // Check if the underlying type is a basic type
285
+ underlying := aliasType.Underlying()
286
+ if _, isBasic := underlying.(*types.Basic); isBasic {
287
+ // This is a type alias to a basic type - treat it as a potential wrapper type
288
+ isWrapperType = true
289
+ if aliasType.Obj().Pkg() != c.pkg.Types {
290
+ // Imported type alias like os.FileMode
291
+ if importAlias := c.getImportAlias(aliasType.Obj().Pkg().Path()); importAlias != "" {
292
+ typeName = importAlias + "." + aliasType.Obj().Name()
293
+ } else {
294
+ typeName = aliasType.Obj().Name()
295
+ }
296
+ } else {
297
+ // Local type alias
298
+ typeName = aliasType.Obj().Name()
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+
305
+ if !isWrapperType {
276
306
  return false, nil
277
307
  }
278
308
 
279
- // Get the type name for the function call
280
- var typeName string
281
- if namedType, ok := baseType.(*types.Named); ok {
282
- if obj := namedType.Obj(); obj != nil {
283
- if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
284
- // Imported type like os.FileMode
285
- if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
286
- typeName = importAlias + "." + obj.Name()
309
+ // Get the type name for the function call if not already set
310
+ if typeName == "" {
311
+ if namedType, ok := baseType.(*types.Named); ok {
312
+ if obj := namedType.Obj(); obj != nil {
313
+ if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
314
+ // Imported type like os.FileMode
315
+ if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
316
+ typeName = importAlias + "." + obj.Name()
317
+ } else {
318
+ typeName = obj.Name()
319
+ }
287
320
  } else {
321
+ // Local type
288
322
  typeName = obj.Name()
289
323
  }
290
- } else {
291
- // Local type
292
- typeName = obj.Name()
293
324
  }
294
- }
295
- } else if aliasType, ok := baseType.(*types.Alias); ok {
296
- if obj := aliasType.Obj(); obj != nil {
297
- if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
298
- // Imported type alias
299
- if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
300
- typeName = importAlias + "." + obj.Name()
325
+ } else if aliasType, ok := baseType.(*types.Alias); ok {
326
+ if obj := aliasType.Obj(); obj != nil {
327
+ if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
328
+ // Imported type alias
329
+ if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
330
+ typeName = importAlias + "." + obj.Name()
331
+ } else {
332
+ typeName = obj.Name()
333
+ }
301
334
  } else {
335
+ // Local type alias
302
336
  typeName = obj.Name()
303
337
  }
304
- } else {
305
- // Local type alias
306
- typeName = obj.Name()
307
338
  }
308
339
  }
309
340
  }
@@ -237,7 +237,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
237
237
  // Check if this is a named type with methods and the initializer is a basic value
238
238
  if namedType, isNamed := goType.(*types.Named); isNamed {
239
239
  // Check if this is a wrapper type first
240
- isWrapperType := c.analysis.IsWrapperType(namedType)
240
+ isWrapperType := c.analysis.IsNamedBasicType(namedType)
241
241
  if isWrapperType {
242
242
  // For wrapper types, no constructor wrapping needed
243
243
  if shouldApplyClone(c.pkg, initializerExpr) {
@@ -334,7 +334,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
334
334
  // No initializer, use the zero value directly
335
335
  // Check if this is a wrapper type first
336
336
  if namedType, isNamed := goType.(*types.Named); isNamed {
337
- isWrapperType := c.analysis.IsWrapperType(namedType)
337
+ isWrapperType := c.analysis.IsNamedBasicType(namedType)
338
338
  if isWrapperType {
339
339
  // For wrapper types, just use zero value directly
340
340
  c.WriteZeroValueForType(goType)