goscript 0.0.48 → 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.
Files changed (159) hide show
  1. package/cmd/goscript/deps.go +1 -4
  2. package/compiler/analysis.go +1120 -513
  3. package/compiler/analysis_test.go +113 -4
  4. package/compiler/compiler.go +88 -124
  5. package/compiler/decl.go +22 -0
  6. package/compiler/expr-call-async.go +46 -52
  7. package/compiler/expr-call-type-conversion.go +144 -59
  8. package/compiler/expr-call.go +235 -12
  9. package/compiler/expr.go +5 -82
  10. package/compiler/gs_dependencies_test.go +60 -1
  11. package/compiler/spec-value.go +73 -51
  12. package/compiler/spec.go +337 -151
  13. package/compiler/stmt-assign.go +7 -4
  14. package/compiler/stmt.go +250 -81
  15. package/compiler/type.go +13 -0
  16. package/dist/gs/builtin/builtin.d.ts +1 -5
  17. package/dist/gs/builtin/builtin.js +1 -34
  18. package/dist/gs/builtin/builtin.js.map +1 -1
  19. package/dist/gs/builtin/slice.js.map +1 -1
  20. package/dist/gs/context/context.d.ts +16 -18
  21. package/dist/gs/context/context.js +23 -13
  22. package/dist/gs/context/context.js.map +1 -1
  23. package/dist/gs/fmt/fmt.js +22 -4
  24. package/dist/gs/fmt/fmt.js.map +1 -1
  25. package/dist/gs/io/fs/fs.d.ts +6 -12
  26. package/dist/gs/io/fs/fs.js +52 -67
  27. package/dist/gs/io/fs/fs.js.map +1 -1
  28. package/dist/gs/os/index.d.ts +2 -1
  29. package/dist/gs/os/index.js +1 -1
  30. package/dist/gs/os/index.js.map +1 -1
  31. package/dist/gs/os/types_js.gs.d.ts +7 -1
  32. package/dist/gs/os/types_js.gs.js +16 -1
  33. package/dist/gs/os/types_js.gs.js.map +1 -1
  34. package/dist/gs/os/types_unix.gs.js +2 -2
  35. package/dist/gs/os/types_unix.gs.js.map +1 -1
  36. package/dist/gs/reflect/index.d.ts +3 -3
  37. package/dist/gs/reflect/index.js +2 -2
  38. package/dist/gs/reflect/index.js.map +1 -1
  39. package/dist/gs/reflect/map.js +2 -2
  40. package/dist/gs/reflect/map.js.map +1 -1
  41. package/dist/gs/reflect/type.d.ts +8 -9
  42. package/dist/gs/reflect/type.js +98 -103
  43. package/dist/gs/reflect/type.js.map +1 -1
  44. package/dist/gs/reflect/types.d.ts +1 -10
  45. package/dist/gs/reflect/types.js +3 -26
  46. package/dist/gs/reflect/types.js.map +1 -1
  47. package/dist/gs/reflect/value.js +23 -23
  48. package/dist/gs/reflect/value.js.map +1 -1
  49. package/dist/gs/reflect/visiblefields.js +3 -3
  50. package/dist/gs/reflect/visiblefields.js.map +1 -1
  51. package/dist/gs/time/time.d.ts +13 -23
  52. package/dist/gs/time/time.js +57 -75
  53. package/dist/gs/time/time.js.map +1 -1
  54. package/gs/builtin/builtin.ts +3 -47
  55. package/gs/builtin/slice.ts +1 -1
  56. package/gs/bytes/meta.json +10 -0
  57. package/gs/context/context.ts +63 -45
  58. package/gs/fmt/fmt.ts +22 -4
  59. package/gs/fmt/meta.json +5 -0
  60. package/gs/internal/meta.json +5 -0
  61. package/gs/io/fs/fs.ts +58 -73
  62. package/gs/io/meta.json +9 -0
  63. package/gs/maps/meta.json +6 -0
  64. package/gs/math/meta.json +5 -0
  65. package/gs/os/index.ts +8 -1
  66. package/gs/os/meta.json +15 -0
  67. package/gs/os/types_js.gs.ts +22 -1
  68. package/gs/os/types_unix.gs.ts +2 -2
  69. package/gs/path/meta.json +6 -0
  70. package/gs/reflect/function-types.test.ts +10 -10
  71. package/gs/reflect/index.ts +6 -6
  72. package/gs/reflect/map.ts +2 -2
  73. package/gs/reflect/meta.json +5 -0
  74. package/gs/reflect/type.ts +105 -105
  75. package/gs/reflect/types.ts +2 -28
  76. package/gs/reflect/value.ts +23 -23
  77. package/gs/reflect/visiblefields.ts +3 -3
  78. package/gs/strconv/meta.json +5 -0
  79. package/gs/strings/meta.json +9 -0
  80. package/gs/sync/meta.json +19 -0
  81. package/gs/time/time.ts +65 -84
  82. package/package.json +2 -2
  83. package/dist/gs/builtin/io.d.ts +0 -16
  84. package/dist/gs/builtin/io.js +0 -15
  85. package/dist/gs/builtin/io.js.map +0 -1
  86. package/dist/gs/internal/testlog/index.d.ts +0 -1
  87. package/dist/gs/internal/testlog/index.js +0 -5
  88. package/dist/gs/internal/testlog/index.js.map +0 -1
  89. package/dist/gs/maps/iter.gs.d.ts +0 -7
  90. package/dist/gs/maps/iter.gs.js +0 -65
  91. package/dist/gs/maps/iter.gs.js.map +0 -1
  92. package/dist/gs/maps/maps.gs.d.ts +0 -7
  93. package/dist/gs/maps/maps.gs.js +0 -79
  94. package/dist/gs/maps/maps.gs.js.map +0 -1
  95. package/dist/gs/reflect/abi.d.ts +0 -59
  96. package/dist/gs/reflect/abi.gs.d.ts +0 -59
  97. package/dist/gs/reflect/abi.gs.js +0 -79
  98. package/dist/gs/reflect/abi.gs.js.map +0 -1
  99. package/dist/gs/reflect/abi.js +0 -79
  100. package/dist/gs/reflect/abi.js.map +0 -1
  101. package/dist/gs/reflect/badlinkname.d.ts +0 -52
  102. package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
  103. package/dist/gs/reflect/badlinkname.gs.js +0 -72
  104. package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
  105. package/dist/gs/reflect/badlinkname.js +0 -72
  106. package/dist/gs/reflect/badlinkname.js.map +0 -1
  107. package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
  108. package/dist/gs/reflect/deepequal.gs.js +0 -308
  109. package/dist/gs/reflect/deepequal.gs.js.map +0 -1
  110. package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
  111. package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
  112. package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
  113. package/dist/gs/reflect/index.gs.d.ts +0 -1
  114. package/dist/gs/reflect/index.gs.js +0 -3
  115. package/dist/gs/reflect/index.gs.js.map +0 -1
  116. package/dist/gs/reflect/iter.gs.d.ts +0 -3
  117. package/dist/gs/reflect/iter.gs.js +0 -24
  118. package/dist/gs/reflect/iter.gs.js.map +0 -1
  119. package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
  120. package/dist/gs/reflect/makefunc.gs.js +0 -288
  121. package/dist/gs/reflect/makefunc.gs.js.map +0 -1
  122. package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
  123. package/dist/gs/reflect/map_swiss.gs.js +0 -70
  124. package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
  125. package/dist/gs/reflect/reflect.gs.d.ts +0 -132
  126. package/dist/gs/reflect/reflect.gs.js +0 -437
  127. package/dist/gs/reflect/reflect.gs.js.map +0 -1
  128. package/dist/gs/reflect/swapper.gs.d.ts +0 -1
  129. package/dist/gs/reflect/swapper.gs.js +0 -32
  130. package/dist/gs/reflect/swapper.gs.js.map +0 -1
  131. package/dist/gs/reflect/type.gs.d.ts +0 -4
  132. package/dist/gs/reflect/type.gs.js +0 -21
  133. package/dist/gs/reflect/type.gs.js.map +0 -1
  134. package/dist/gs/reflect/value.gs.d.ts +0 -4
  135. package/dist/gs/reflect/value.gs.js +0 -12
  136. package/dist/gs/reflect/value.gs.js.map +0 -1
  137. package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
  138. package/dist/gs/reflect/visiblefields.gs.js +0 -123
  139. package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
  140. package/dist/gs/stringslite/index.d.ts +0 -1
  141. package/dist/gs/stringslite/index.js +0 -2
  142. package/dist/gs/stringslite/index.js.map +0 -1
  143. package/dist/gs/stringslite/strings.d.ts +0 -11
  144. package/dist/gs/stringslite/strings.js +0 -67
  145. package/dist/gs/stringslite/strings.js.map +0 -1
  146. package/gs/bytes/metadata.go +0 -12
  147. package/gs/fmt/metadata.go +0 -7
  148. package/gs/internal/metadata.go +0 -7
  149. package/gs/io/io.go +0 -75
  150. package/gs/io/metadata.go +0 -11
  151. package/gs/maps/metadata.go +0 -8
  152. package/gs/math/metadata.go +0 -7
  153. package/gs/os/metadata.go +0 -17
  154. package/gs/path/metadata.go +0 -8
  155. package/gs/reflect/metadata.go +0 -7
  156. package/gs/strconv/metadata.go +0 -7
  157. package/gs/strings/metadata.go +0 -11
  158. package/gs/sync/metadata.go +0 -7
  159. package/gs/sync/sync.go +0 -64
@@ -5,6 +5,7 @@ import (
5
5
  "go/parser"
6
6
  "go/token"
7
7
  "go/types"
8
+ "os"
8
9
  "testing"
9
10
 
10
11
  "golang.org/x/tools/go/packages"
@@ -159,6 +160,7 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
159
160
  Defs: make(map[*ast.Ident]types.Object),
160
161
  Uses: make(map[*ast.Ident]types.Object),
161
162
  },
163
+ Fset: fset,
162
164
  }
163
165
 
164
166
  // Type check the package
@@ -169,10 +171,8 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
169
171
  }
170
172
  pkg.Types = typePkg
171
173
 
172
- // Run analysis
173
- analysis := NewAnalysis()
174
- cmap := ast.NewCommentMap(fset, file, file.Comments)
175
- AnalyzeFile(file, pkg, analysis, cmap)
174
+ // Run package-level analysis
175
+ analysis := AnalyzePackageFiles(pkg, nil)
176
176
 
177
177
  // Collect variable objects
178
178
  objects := make(map[string]types.Object)
@@ -232,3 +232,112 @@ func main() {
232
232
  t.Log("")
233
233
  }
234
234
  }
235
+
236
+ // TestWrapperTypeDetection verifies that the analysis correctly identifies wrapper types
237
+ func TestWrapperTypeDetection(t *testing.T) {
238
+ // Use the actual compliance test case
239
+ testPath := "../compliance/tests/wrapper_type_args"
240
+
241
+ // Load the package using the packages config like the main compiler does
242
+ cfg := &packages.Config{
243
+ Mode: packages.NeedName |
244
+ packages.NeedFiles |
245
+ packages.NeedCompiledGoFiles |
246
+ packages.NeedImports |
247
+ packages.NeedDeps |
248
+ packages.NeedExportFile |
249
+ packages.NeedTypes |
250
+ packages.NeedSyntax |
251
+ packages.NeedTypesInfo |
252
+ packages.NeedTypesSizes,
253
+ Tests: false,
254
+ Env: append(os.Environ(), "GOOS=js", "GOARCH=wasm"),
255
+ }
256
+
257
+ pkgs, err := packages.Load(cfg, testPath)
258
+ if err != nil {
259
+ t.Fatalf("Failed to load test package: %v", err)
260
+ }
261
+
262
+ if len(pkgs) != 1 {
263
+ t.Fatalf("Expected 1 package, got %d", len(pkgs))
264
+ }
265
+
266
+ pkg := pkgs[0]
267
+ if len(pkg.Errors) > 0 {
268
+ t.Fatalf("Package has errors: %v", pkg.Errors[0])
269
+ }
270
+
271
+ // Run package-level analysis
272
+ if len(pkg.Syntax) == 0 {
273
+ t.Fatal("No syntax files found")
274
+ }
275
+
276
+ analysis := AnalyzePackageFiles(pkg, nil)
277
+
278
+ // Verify the NamedBasicTypes map was initialized
279
+ if analysis.NamedBasicTypes == nil {
280
+ t.Error("NamedBasicTypes map was not initialized")
281
+ }
282
+
283
+ // Test some type lookups to verify wrapper type detection works
284
+ // We'll check if MyMode (which has methods) is detected as a wrapper type
285
+ scope := pkg.Types.Scope()
286
+
287
+ // Check if MyMode is detected as a wrapper type
288
+ if obj := scope.Lookup("MyMode"); obj != nil {
289
+ if typeName, ok := obj.(*types.TypeName); ok {
290
+ isWrapper := analysis.IsNamedBasicType(typeName.Type())
291
+ if !isWrapper {
292
+ t.Errorf("MyMode should be detected as wrapper type, got %v", isWrapper)
293
+ }
294
+ t.Logf("MyMode wrapper detection: %v (correct)", isWrapper)
295
+ }
296
+ }
297
+
298
+ // Test that regular struct types are not detected as wrapper types
299
+ if obj := scope.Lookup("MyDir"); obj != nil {
300
+ if typeName, ok := obj.(*types.TypeName); ok {
301
+ isWrapper := analysis.IsNamedBasicType(typeName.Type())
302
+ if isWrapper {
303
+ t.Errorf("MyDir should not be detected as wrapper type, got %v", isWrapper)
304
+ }
305
+ t.Logf("MyDir wrapper detection: %v (correct)", isWrapper)
306
+ }
307
+ }
308
+
309
+ t.Logf("Analysis completed successfully with %d named basic types tracked", len(analysis.NamedBasicTypes))
310
+ }
311
+
312
+ // TestDiscoverGsPackages verifies that the discoverEmbeddedGsPackages function
313
+ // can find packages in the embedded gs/ directory
314
+ func TestDiscoverGsPackages(t *testing.T) {
315
+ analysis := NewAnalysis(nil)
316
+
317
+ // Test package discovery using the embedded filesystem
318
+ packages := analysis.discoverEmbeddedGsPackages()
319
+ t.Logf("Discovered %d packages:", len(packages))
320
+ for _, pkg := range packages {
321
+ t.Logf(" - %s", pkg)
322
+ }
323
+
324
+ // We should find at least some packages
325
+ if len(packages) == 0 {
326
+ t.Errorf("Expected to find at least one package in gs/ directory")
327
+ }
328
+
329
+ // Check for some known packages that should exist
330
+ expectedPackages := []string{"sync", "bytes", "strings"}
331
+ for _, expected := range expectedPackages {
332
+ found := false
333
+ for _, pkg := range packages {
334
+ if pkg == expected {
335
+ found = true
336
+ break
337
+ }
338
+ }
339
+ if !found {
340
+ t.Logf("Expected package '%s' not found in discovered packages: %v", expected, packages)
341
+ }
342
+ }
343
+ }
@@ -2,10 +2,10 @@ package compiler
2
2
 
3
3
  import (
4
4
  "context"
5
+ "encoding/json"
5
6
  "fmt"
6
7
  "go/ast"
7
8
  "go/constant"
8
- "go/parser"
9
9
  "go/token"
10
10
  "go/types"
11
11
  "os"
@@ -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
@@ -644,9 +676,6 @@ type GoToTSCompiler struct {
644
676
  pkg *packages.Package
645
677
 
646
678
  analysis *Analysis
647
-
648
- // shadowingContext tracks temporary variable mappings when we're in a shadowing context
649
- shadowingContext map[string]string
650
679
  }
651
680
 
652
681
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -654,10 +683,9 @@ type GoToTSCompiler struct {
654
683
  // analysis results (`Analysis`) to guide the translation process.
655
684
  func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
656
685
  return &GoToTSCompiler{
657
- tsw: tsw,
658
- pkg: pkg,
659
- analysis: analysis,
660
- shadowingContext: make(map[string]string),
686
+ tsw: tsw,
687
+ pkg: pkg,
688
+ analysis: analysis,
661
689
  }
662
690
  }
663
691
 
@@ -679,9 +707,9 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
679
707
  return
680
708
  }
681
709
 
682
- // Check if we're in a shadowing context and should use a temporary variable
683
- if tempVarName, exists := c.shadowingContext[exp.Name]; exists {
684
- c.tsw.WriteLiterally(c.sanitizeIdentifier(tempVarName))
710
+ // Check if this identifier has a pre-computed mapping (e.g., wrapper function receiver)
711
+ if mappedName := c.analysis.GetIdentifierMapping(exp); mappedName != "" {
712
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(mappedName))
685
713
  return
686
714
  }
687
715
 
@@ -1036,97 +1064,33 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
1036
1064
  // GsPackageMetadata holds metadata about a gs/ package
1037
1065
  type GsPackageMetadata struct {
1038
1066
  // Dependencies lists the import paths that this gs/ package requires
1039
- Dependencies []string
1067
+ Dependencies []string `json:"dependencies,omitempty"`
1068
+ AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
1040
1069
  }
1041
1070
 
1042
- // ReadGsPackageMetadata reads dependency metadata from .go files in a gs/ package
1043
- // It looks for a var variable named "GsDependencies" which should be a slice of strings
1044
- // containing the import paths that this package depends on.
1071
+ // ReadGsPackageMetadata reads dependency metadata from meta.json file in a gs/ package
1045
1072
  func (c *Compiler) ReadGsPackageMetadata(gsSourcePath string) (*GsPackageMetadata, error) {
1046
1073
  metadata := &GsPackageMetadata{
1047
1074
  Dependencies: []string{},
1075
+ AsyncMethods: make(map[string]bool),
1048
1076
  }
1049
1077
 
1050
- // Check if there are any .go files in the gs package directory
1051
- entries, err := gs.GsOverrides.ReadDir(gsSourcePath)
1078
+ // Try to read meta.json file
1079
+ metaFilePath := filepath.Join(gsSourcePath, "meta.json")
1080
+ content, err := gs.GsOverrides.ReadFile(metaFilePath)
1052
1081
  if err != nil {
1053
- return metadata, nil // No metadata files, return empty metadata
1082
+ // No meta.json file found, return empty metadata
1083
+ return metadata, nil
1054
1084
  }
1055
1085
 
1056
- // Look for .go files containing metadata
1057
- for _, entry := range entries {
1058
- if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".go") {
1059
- metadataFilePath := filepath.Join(gsSourcePath, entry.Name())
1060
-
1061
- // Read the .go file content
1062
- content, err := gs.GsOverrides.ReadFile(metadataFilePath)
1063
- if err != nil {
1064
- continue // Skip files we can't read
1065
- }
1066
-
1067
- // Parse the file to extract metadata
1068
- if deps, err := c.extractDependenciesFromGoFile(content); err == nil {
1069
- metadata.Dependencies = append(metadata.Dependencies, deps...)
1070
- }
1071
- }
1086
+ // Parse the JSON content
1087
+ if err := json.Unmarshal(content, metadata); err != nil {
1088
+ return metadata, fmt.Errorf("failed to parse meta.json in %s: %w", gsSourcePath, err)
1072
1089
  }
1073
1090
 
1074
1091
  return metadata, nil
1075
1092
  }
1076
1093
 
1077
- // extractDependenciesFromGoFile parses a .go file and extracts the GsDependencies var
1078
- func (c *Compiler) extractDependenciesFromGoFile(content []byte) ([]string, error) {
1079
- // Parse the Go file
1080
- fset := token.NewFileSet()
1081
- file, err := parser.ParseFile(fset, "metadata.go", content, 0)
1082
- if err != nil {
1083
- return nil, err
1084
- }
1085
-
1086
- var dependencies []string
1087
-
1088
- // Look for var declarations
1089
- for _, decl := range file.Decls {
1090
- if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
1091
- for _, spec := range genDecl.Specs {
1092
- if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1093
- for i, name := range valueSpec.Names {
1094
- if name.Name == "GsDependencies" {
1095
- // Found the GsDependencies var, extract its value
1096
- if i < len(valueSpec.Values) {
1097
- if deps := c.extractStringSliceFromExpr(valueSpec.Values[i]); deps != nil {
1098
- dependencies = append(dependencies, deps...)
1099
- }
1100
- }
1101
- }
1102
- }
1103
- }
1104
- }
1105
- }
1106
- }
1107
-
1108
- return dependencies, nil
1109
- }
1110
-
1111
- // extractStringSliceFromExpr extracts string values from a composite literal expression
1112
- func (c *Compiler) extractStringSliceFromExpr(expr ast.Expr) []string {
1113
- var result []string
1114
-
1115
- if compLit, ok := expr.(*ast.CompositeLit); ok {
1116
- for _, elt := range compLit.Elts {
1117
- if basicLit, ok := elt.(*ast.BasicLit); ok && basicLit.Kind == token.STRING {
1118
- // Remove quotes from string literal
1119
- value := basicLit.Value
1120
- if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' {
1121
- result = append(result, value[1:len(value)-1])
1122
- }
1123
- }
1124
- }
1125
- }
1126
-
1127
- return result
1128
- }
1129
-
1130
1094
  // copyGsPackageWithDependencies copies a gs/ package and all its dependencies recursively
1131
1095
  // It tracks already processed packages to avoid infinite loops and duplicate work
1132
1096
  func (c *Compiler) copyGsPackageWithDependencies(packagePath string, processedPackages map[string]bool, result *CompilationResult) error {
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