goscript 0.0.24 → 0.0.25

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 (65) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +17 -1
  3. package/compiler/analysis.go +1 -1
  4. package/compiler/builtin_test.go +2 -14
  5. package/compiler/compiler.go +231 -11
  6. package/compiler/decl.go +7 -1
  7. package/compiler/expr-call.go +212 -2
  8. package/compiler/expr.go +46 -2
  9. package/compiler/field.go +4 -4
  10. package/compiler/stmt-range.go +204 -1
  11. package/compiler/type.go +47 -4
  12. package/dist/gs/builtin/builtin.d.ts +9 -0
  13. package/dist/gs/builtin/builtin.js +9 -0
  14. package/dist/gs/builtin/builtin.js.map +1 -1
  15. package/dist/gs/builtin/channel.d.ts +193 -0
  16. package/dist/gs/builtin/channel.js.map +1 -1
  17. package/dist/gs/builtin/defer.d.ts +38 -0
  18. package/dist/gs/builtin/defer.js.map +1 -1
  19. package/dist/gs/builtin/index.d.ts +1 -0
  20. package/dist/gs/builtin/index.js +2 -0
  21. package/dist/gs/builtin/index.js.map +1 -0
  22. package/dist/gs/builtin/io.d.ts +16 -0
  23. package/dist/gs/builtin/io.js.map +1 -1
  24. package/dist/gs/builtin/map.d.ts +33 -0
  25. package/dist/gs/builtin/map.js.map +1 -1
  26. package/dist/gs/builtin/slice.d.ts +173 -0
  27. package/dist/gs/builtin/slice.js.map +1 -1
  28. package/dist/gs/builtin/type.d.ts +203 -0
  29. package/dist/gs/builtin/type.js +1 -2
  30. package/dist/gs/builtin/type.js.map +1 -1
  31. package/dist/gs/builtin/varRef.d.ts +14 -0
  32. package/dist/gs/builtin/varRef.js.map +1 -1
  33. package/dist/gs/cmp/index.d.ts +4 -0
  34. package/dist/gs/cmp/index.js +27 -0
  35. package/dist/gs/cmp/index.js.map +1 -0
  36. package/dist/gs/context/context.d.ts +26 -0
  37. package/dist/gs/context/context.js +287 -37
  38. package/dist/gs/context/context.js.map +1 -1
  39. package/dist/gs/context/index.d.ts +1 -0
  40. package/dist/gs/internal/goarch/index.d.ts +6 -0
  41. package/dist/gs/internal/goarch/index.js +14 -0
  42. package/dist/gs/internal/goarch/index.js.map +1 -0
  43. package/dist/gs/iter/index.d.ts +1 -0
  44. package/dist/gs/iter/index.js +2 -0
  45. package/dist/gs/iter/index.js.map +1 -0
  46. package/dist/gs/iter/iter.d.ts +4 -0
  47. package/dist/gs/iter/iter.js +91 -0
  48. package/dist/gs/iter/iter.js.map +1 -0
  49. package/dist/gs/math/bits/index.d.ts +47 -0
  50. package/dist/gs/math/bits/index.js +298 -0
  51. package/dist/gs/math/bits/index.js.map +1 -0
  52. package/dist/gs/runtime/index.d.ts +1 -0
  53. package/dist/gs/runtime/runtime.d.ts +41 -0
  54. package/dist/gs/runtime/runtime.js.map +1 -1
  55. package/dist/gs/slices/index.d.ts +1 -0
  56. package/dist/gs/slices/index.js +2 -0
  57. package/dist/gs/slices/index.js.map +1 -0
  58. package/dist/gs/slices/slices.d.ts +8 -0
  59. package/dist/gs/slices/slices.js +20 -0
  60. package/dist/gs/slices/slices.js.map +1 -0
  61. package/dist/gs/time/index.d.ts +1 -0
  62. package/dist/gs/time/time.d.ts +57 -0
  63. package/dist/gs/time/time.js +103 -10
  64. package/dist/gs/time/time.js.map +1 -1
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -95,7 +95,7 @@ func main() {
95
95
 
96
96
  // Compile the desired Go package(s)
97
97
  // Replace "." with the specific Go import path of the package you want to compile
98
- if err := comp.CompilePackages(context.Background(), "your/go/package/path"); err != nil {
98
+ if _, err := comp.CompilePackages(context.Background(), "your/go/package/path"); err != nil {
99
99
  log.Fatalf("compilation failed: %v", err)
100
100
  }
101
101
 
@@ -62,6 +62,21 @@ var CompileCommands = []*cli.Command{{
62
62
  Destination: &cliCompilerBuildFlags,
63
63
  EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
64
64
  },
65
+ &cli.BoolFlag{
66
+ Name: "disable-emit-builtin",
67
+ Usage: "disable emitting built-in packages that have handwritten equivalents",
68
+ Destination: &cliCompilerConfig.DisableEmitBuiltin,
69
+ Value: false,
70
+ EnvVars: []string{"GOSCRIPT_DISABLE_EMIT_BUILTIN"},
71
+ },
72
+ &cli.BoolFlag{
73
+ Name: "all-dependencies",
74
+ Usage: "compile all dependencies of the requested packages",
75
+ Aliases: []string{"all-deps", "deps"},
76
+ Destination: &cliCompilerConfig.AllDependencies,
77
+ Value: false,
78
+ EnvVars: []string{"GOSCRIPT_ALL_DEPENDENCIES"},
79
+ },
65
80
  },
66
81
  }}
67
82
 
@@ -75,5 +90,6 @@ func compilePackage(c *cli.Context) error {
75
90
  // build flags
76
91
  cliCompilerConfig.BuildFlags = slices.Clone(cliCompilerBuildFlags.Value())
77
92
 
78
- return cliCompiler.CompilePackages(context.Background(), pkgs...)
93
+ _, err := cliCompiler.CompilePackages(context.Background(), pkgs...)
94
+ return err
79
95
  }
@@ -553,7 +553,7 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
553
553
  // Standalone &x doesn't directly assign, but its usage in assignments
554
554
  // or function calls determines varRefing. Assignments are handled below.
555
555
  // Function calls like foo(&x) would require different tracking if needed.
556
- // For now, we focus on assignments as per the request.
556
+ // TODO: for now, we focus on assignments
557
557
  return v
558
558
 
559
559
  case *ast.CallExpr:
@@ -37,7 +37,7 @@ func TestEmitBuiltinOption(t *testing.T) {
37
37
  }
38
38
 
39
39
  // Compile a package that depends on builtin (time)
40
- err = compiler.CompilePackages(context.Background(), "time")
40
+ _, err = compiler.CompilePackages(context.Background(), "time")
41
41
  if err != nil {
42
42
  t.Fatalf("Compilation failed: %v", err)
43
43
  }
@@ -76,23 +76,11 @@ func TestEmitBuiltinOption(t *testing.T) {
76
76
  }
77
77
 
78
78
  // Compile a package that depends on builtin (time)
79
- err = compiler.CompilePackages(context.Background(), "time")
79
+ _, err = compiler.CompilePackages(context.Background(), "time")
80
80
  if err != nil {
81
81
  t.Fatalf("Compilation failed: %v", err)
82
82
  }
83
83
 
84
- // Check that the unsafe package was copied to the output
85
- unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
86
- if _, err := os.Stat(unsafePath); os.IsNotExist(err) {
87
- t.Errorf("unsafe package was not emitted when DisableEmitBuiltin=false")
88
- }
89
-
90
- // Also check for runtime package
91
- runtimePath := filepath.Join(outputDir, "@goscript/runtime")
92
- if _, err := os.Stat(runtimePath); os.IsNotExist(err) {
93
- t.Errorf("runtime package was not emitted when DisableEmitBuiltin=false")
94
- }
95
-
96
84
  // Time package should also have been emitted
97
85
  timePath := filepath.Join(outputDir, "@goscript/time")
98
86
  if _, err := os.Stat(timePath); os.IsNotExist(err) {
@@ -11,6 +11,8 @@ import (
11
11
  "slices"
12
12
  "strings"
13
13
 
14
+ "go/constant"
15
+
14
16
  gs "github.com/aperturerobotics/goscript"
15
17
  "github.com/sirupsen/logrus"
16
18
  "golang.org/x/tools/go/packages"
@@ -72,6 +74,16 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
72
74
  return &Compiler{config: *conf, le: le, opts: *opts}, nil
73
75
  }
74
76
 
77
+ // CompilationResult contains information about what was compiled
78
+ type CompilationResult struct {
79
+ // CompiledPackages contains the package paths of all packages that were actually compiled to TypeScript
80
+ CompiledPackages []string
81
+ // CopiedPackages contains the package paths of all packages that were copied from handwritten sources
82
+ CopiedPackages []string
83
+ // OriginalPackages contains the package paths that were explicitly requested for compilation
84
+ OriginalPackages []string
85
+ }
86
+
75
87
  // CompilePackages loads Go packages based on the provided patterns and
76
88
  // then compiles each loaded package into TypeScript. It uses the context for
77
89
  // cancellation and applies the compiler's configured options during package loading.
@@ -79,7 +91,8 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
79
91
  // invokes its `Compile` method.
80
92
  // If c.config.AllDependencies is true, it will also compile all dependencies
81
93
  // of the requested packages, including standard library dependencies.
82
- func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) error {
94
+ // Returns a CompilationResult with information about what was compiled.
95
+ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*CompilationResult, error) {
83
96
  opts := c.opts
84
97
  opts.Context = ctx
85
98
 
@@ -87,7 +100,7 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
87
100
  opts.Mode |= packages.NeedImports
88
101
  pkgs, err := packages.Load(&opts, patterns...)
89
102
  if err != nil {
90
- return fmt.Errorf("failed to load packages: %w", err)
103
+ return nil, fmt.Errorf("failed to load packages: %w", err)
91
104
  }
92
105
 
93
106
  // build a list of packages that patterns matched
@@ -96,12 +109,23 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
96
109
  patternPkgPaths = append(patternPkgPaths, pkg.PkgPath)
97
110
  }
98
111
 
112
+ result := &CompilationResult{
113
+ OriginalPackages: patternPkgPaths,
114
+ }
115
+
99
116
  // If AllDependencies is true, we need to collect all dependencies
100
117
  if c.config.AllDependencies {
101
118
  // Create a set to track processed packages by their ID
102
119
  processed := make(map[string]bool)
103
120
  var allPkgs []*packages.Package
104
121
 
122
+ // Helper function to check if a package has a handwritten equivalent
123
+ hasHandwrittenEquivalent := func(pkgPath string) bool {
124
+ gsSourcePath := "gs/" + pkgPath
125
+ _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
126
+ return gsErr == nil
127
+ }
128
+
105
129
  // Visit all packages and their dependencies
106
130
  var visit func(pkg *packages.Package)
107
131
  visit = func(pkg *packages.Package) {
@@ -109,8 +133,17 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
109
133
  return
110
134
  }
111
135
  processed[pkg.ID] = true
136
+
137
+ // Add this package to the list of all packages
112
138
  allPkgs = append(allPkgs, pkg)
113
139
 
140
+ // Check if this package has a handwritten equivalent
141
+ if hasHandwrittenEquivalent(pkg.PkgPath) {
142
+ // Add this package but don't visit its dependencies
143
+ c.le.Debugf("Skipping dependencies of handwritten package: %s", pkg.PkgPath)
144
+ return
145
+ }
146
+
114
147
  // Visit all imports, including standard library packages
115
148
  for _, imp := range pkg.Imports {
116
149
  visit(imp)
@@ -146,18 +179,31 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
146
179
  */
147
180
  }
148
181
 
182
+ // If DisableEmitBuiltin is false, we need to copy the builtin package to the output directory
183
+ if !c.config.DisableEmitBuiltin {
184
+ c.le.Infof("Copying builtin package to output directory")
185
+ builtinPath := "gs/builtin"
186
+ outputPath := ComputeModulePath(c.config.OutputPath, "builtin")
187
+ if err := c.copyEmbeddedPackage(builtinPath, outputPath); err != nil {
188
+ return nil, fmt.Errorf("failed to copy builtin package to output directory: %w", err)
189
+ }
190
+ result.CopiedPackages = append(result.CopiedPackages, "builtin")
191
+ }
192
+
149
193
  // Compile all packages
150
194
  for _, pkg := range pkgs {
151
195
  // Check if the package has a handwritten equivalent
196
+ // If the package was explicitly requested, skip this logic
152
197
  if !slices.Contains(patternPkgPaths, pkg.PkgPath) {
153
198
  gsSourcePath := "gs/" + pkg.PkgPath
154
199
  _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
155
200
  if gsErr != nil && !os.IsNotExist(gsErr) {
156
- return gsErr
201
+ return nil, gsErr
157
202
  }
158
203
  if gsErr == nil {
159
204
  if c.config.DisableEmitBuiltin {
160
205
  c.le.Infof("Skipping compilation for overridden package %s", pkg.PkgPath)
206
+ result.CopiedPackages = append(result.CopiedPackages, pkg.PkgPath)
161
207
  continue
162
208
  } else {
163
209
  // If DisableEmitBuiltin is false, we need to copy the handwritten package to the output directory
@@ -168,19 +214,20 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
168
214
 
169
215
  // Remove existing directory if it exists
170
216
  if err := os.RemoveAll(outputPath); err != nil {
171
- return fmt.Errorf("failed to remove existing output directory for %s: %w", pkg.PkgPath, err)
217
+ return nil, fmt.Errorf("failed to remove existing output directory for %s: %w", pkg.PkgPath, err)
172
218
  }
173
219
 
174
220
  // Create the output directory
175
221
  if err := os.MkdirAll(outputPath, 0o755); err != nil {
176
- return fmt.Errorf("failed to create output directory for %s: %w", pkg.PkgPath, err)
222
+ return nil, fmt.Errorf("failed to create output directory for %s: %w", pkg.PkgPath, err)
177
223
  }
178
224
 
179
225
  // Copy files from embedded FS to output directory
180
226
  if err := c.copyEmbeddedPackage(gsSourcePath, outputPath); err != nil {
181
- return fmt.Errorf("failed to copy embedded package %s: %w", pkg.PkgPath, err)
227
+ return nil, fmt.Errorf("failed to copy embedded package %s: %w", pkg.PkgPath, err)
182
228
  }
183
229
 
230
+ result.CopiedPackages = append(result.CopiedPackages, pkg.PkgPath)
184
231
  continue
185
232
  }
186
233
  }
@@ -192,17 +239,42 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
192
239
  continue
193
240
  }
194
241
 
242
+ // Check if this is the unsafe package, which is not supported in GoScript
243
+ if pkg.PkgPath == "unsafe" {
244
+ // Find which package depends on unsafe by looking at the import graph
245
+ var dependentPackages []string
246
+ for _, otherPkg := range pkgs {
247
+ if otherPkg.PkgPath != "unsafe" {
248
+ for importPath := range otherPkg.Imports {
249
+ if importPath == "unsafe" {
250
+ dependentPackages = append(dependentPackages, otherPkg.PkgPath)
251
+ break
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ dependentList := "unknown package"
258
+ if len(dependentPackages) > 0 {
259
+ dependentList = strings.Join(dependentPackages, ", ")
260
+ }
261
+
262
+ return nil, fmt.Errorf("cannot compile package 'unsafe': GoScript does not support the unsafe package due to its low-level memory operations that are incompatible with TypeScript/JavaScript. This package is required by: %s. Consider using alternative approaches that don't require unsafe operations", dependentList)
263
+ }
264
+
195
265
  pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
196
266
  if err != nil {
197
- return fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
267
+ return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
198
268
  }
199
269
 
200
270
  if err := pkgCompiler.Compile(ctx); err != nil {
201
- return fmt.Errorf("failed to compile package %s: %w", pkg.PkgPath, err)
271
+ return nil, fmt.Errorf("failed to compile package %s: %w", pkg.PkgPath, err)
202
272
  }
273
+
274
+ result.CompiledPackages = append(result.CompiledPackages, pkg.PkgPath)
203
275
  }
204
276
 
205
- return nil
277
+ return result, nil
206
278
  }
207
279
 
208
280
  // PackageCompiler is responsible for compiling an entire Go package into
@@ -433,6 +505,7 @@ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analy
433
505
  // WriteIdent translates a Go identifier (`ast.Ident`) used as a value (e.g.,
434
506
  // variable, function name) into its TypeScript equivalent.
435
507
  // - If the identifier is `nil`, it writes `null`.
508
+ // - If the identifier refers to a constant, it writes the constant's evaluated value.
436
509
  // - Otherwise, it writes the identifier's name.
437
510
  // - If `accessVarRefedValue` is true and the analysis (`c.analysis.NeedsVarRefAccess`)
438
511
  // indicates the variable is variable referenced, `.value` is appended to access the contained value.
@@ -452,8 +525,22 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
452
525
  obj = c.pkg.TypesInfo.Defs[exp]
453
526
  }
454
527
 
455
- // Write the identifier name first
456
- c.tsw.WriteLiterally(exp.Name)
528
+ // Check if this identifier refers to a constant
529
+ if obj != nil {
530
+ if constObj, isConst := obj.(*types.Const); isConst {
531
+ // Only evaluate constants from the current package being compiled
532
+ // Don't evaluate constants from imported packages (they should use their exported names)
533
+ // Special case: predeclared constants like iota have a nil package, so we should evaluate them
534
+ if constObj.Pkg() == c.pkg.Types || constObj.Pkg() == nil {
535
+ // Write the constant's evaluated value instead of the identifier name
536
+ c.writeConstantValue(constObj)
537
+ return
538
+ }
539
+ }
540
+ }
541
+
542
+ // Write the identifier name first, sanitizing if it's a reserved word
543
+ c.tsw.WriteLiterally(c.sanitizeIdentifier(exp.Name))
457
544
 
458
545
  // Determine if we need to access .value based on analysis data
459
546
  if obj != nil && accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
@@ -652,9 +739,142 @@ func (c *GoToTSCompiler) WriteDoc(doc *ast.CommentGroup) {
652
739
  }
653
740
  }
654
741
 
742
+ // sanitizeIdentifier checks if an identifier is a JavaScript/TypeScript reserved word
743
+ // and prefixes it with an underscore if it is. This prevents compilation errors
744
+ // when Go identifiers conflict with JS/TS keywords.
745
+ func (c *GoToTSCompiler) sanitizeIdentifier(name string) string {
746
+ // Don't sanitize boolean literals - they are valid in both Go and JS/TS
747
+ if name == "true" || name == "false" {
748
+ return name
749
+ }
750
+
751
+ // List of JavaScript/TypeScript reserved words that could conflict
752
+ reservedWords := map[string]bool{
753
+ "abstract": true,
754
+ "any": true,
755
+ "as": true,
756
+ "asserts": true,
757
+ "async": true,
758
+ "await": true,
759
+ "boolean": true,
760
+ "break": true,
761
+ "case": true,
762
+ "catch": true,
763
+ "class": true,
764
+ "const": true,
765
+ "constructor": true,
766
+ "continue": true,
767
+ "debugger": true,
768
+ "declare": true,
769
+ "default": true,
770
+ "delete": true,
771
+ "do": true,
772
+ "else": true,
773
+ "enum": true,
774
+ "export": true,
775
+ "extends": true,
776
+ "finally": true,
777
+ "for": true,
778
+ "from": true,
779
+ "function": true,
780
+ "get": true,
781
+ "if": true,
782
+ "implements": true,
783
+ "import": true,
784
+ "in": true,
785
+ "instanceof": true,
786
+ "interface": true,
787
+ "is": true,
788
+ "keyof": true,
789
+ "let": true,
790
+ "module": true,
791
+ "namespace": true,
792
+ "never": true,
793
+ "new": true,
794
+ "null": true,
795
+ "number": true,
796
+ "object": true,
797
+ "of": true,
798
+ "package": true,
799
+ "private": true,
800
+ "protected": true,
801
+ "public": true,
802
+ "readonly": true,
803
+ "require": true,
804
+ "return": true,
805
+ "set": true,
806
+ "static": true,
807
+ "string": true,
808
+ "super": true,
809
+ "switch": true,
810
+ "symbol": true,
811
+ "this": true,
812
+ "throw": true,
813
+ "try": true,
814
+ "type": true,
815
+ "typeof": true,
816
+ "undefined": true,
817
+ "unique": true,
818
+ "unknown": true,
819
+ "var": true,
820
+ "void": true,
821
+ "while": true,
822
+ "with": true,
823
+ "yield": true,
824
+ }
825
+
826
+ if reservedWords[name] {
827
+ return "_" + name
828
+ }
829
+ return name
830
+ }
831
+
832
+ // writeConstantValue writes the evaluated value of a Go constant to TypeScript.
833
+ // It handles different constant types (integer, float, string, boolean, complex)
834
+ // and writes the appropriate TypeScript literal.
835
+ func (c *GoToTSCompiler) writeConstantValue(constObj *types.Const) {
836
+ val := constObj.Val()
837
+
838
+ switch val.Kind() {
839
+ case constant.Int:
840
+ // For integer constants, write the string representation
841
+ c.tsw.WriteLiterally(val.String())
842
+ case constant.Float:
843
+ // For float constants, write the string representation
844
+ c.tsw.WriteLiterally(val.String())
845
+ case constant.String:
846
+ // For string constants, write as a quoted string literal
847
+ c.tsw.WriteLiterally(val.String()) // val.String() already includes quotes
848
+ case constant.Bool:
849
+ // For boolean constants, write true/false
850
+ if constant.BoolVal(val) {
851
+ c.tsw.WriteLiterally("true")
852
+ } else {
853
+ c.tsw.WriteLiterally("false")
854
+ }
855
+ case constant.Complex:
856
+ // For complex constants, we need to handle them specially
857
+ // For now, write as a comment indicating unsupported
858
+ c.tsw.WriteLiterally("/* complex constant: " + val.String() + " */")
859
+ default:
860
+ // For unknown constant types, write as a comment
861
+ c.tsw.WriteLiterally("/* unknown constant: " + val.String() + " */")
862
+ }
863
+ }
864
+
655
865
  // copyEmbeddedPackage recursively copies files from an embedded FS path to a filesystem directory.
656
866
  // It handles both regular files and directories.
657
867
  func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) error {
868
+ // Remove the output path if it exists
869
+ if err := os.RemoveAll(outputPath); err != nil {
870
+ return fmt.Errorf("failed to remove output directory %s: %w", outputPath, err)
871
+ }
872
+
873
+ // Create the output path
874
+ if err := os.MkdirAll(outputPath, 0o755); err != nil {
875
+ return fmt.Errorf("failed to create output directory %s: %w", outputPath, err)
876
+ }
877
+
658
878
  // List the entries in the embedded path
659
879
  entries, err := gs.GsOverrides.ReadDir(embeddedPath)
660
880
  if err != nil {
package/compiler/decl.go CHANGED
@@ -37,7 +37,7 @@ func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
37
37
  c.tsw.WriteLine("") // Add space after spec
38
38
  }
39
39
  default:
40
- fmt.Printf("unknown decl: %#v\n", decl)
40
+ return fmt.Errorf("unknown decl: %#v", decl)
41
41
  }
42
42
  }
43
43
  return nil
@@ -79,6 +79,12 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
79
79
  if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
80
80
  isAsync = c.analysis.IsAsyncFunc(obj)
81
81
  }
82
+
83
+ // Always make main function async (only in main package)
84
+ if decl.Name.Name == "main" && c.pkg.Name == "main" {
85
+ isAsync = true
86
+ }
87
+
82
88
  if isAsync {
83
89
  c.tsw.WriteLiterally("async ")
84
90
  }