goscript 0.0.25 → 0.0.28

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 (190) hide show
  1. package/README.md +4 -4
  2. package/cmd/goscript/cmd_compile.go +0 -3
  3. package/cmd/goscript/deps.go +11 -0
  4. package/compiler/analysis.go +259 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +201 -49
  8. package/compiler/compiler_test.go +53 -0
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +83 -0
  12. package/compiler/expr.go +1 -1
  13. package/compiler/protobuf.go +557 -0
  14. package/compiler/spec-struct.go +4 -0
  15. package/compiler/spec-value.go +11 -3
  16. package/compiler/spec.go +18 -1
  17. package/compiler/stmt-assign.go +35 -0
  18. package/compiler/type-assert.go +87 -0
  19. package/compiler/type.go +5 -2
  20. package/dist/gs/builtin/builtin.d.ts +19 -1
  21. package/dist/gs/builtin/builtin.js +85 -5
  22. package/dist/gs/builtin/builtin.js.map +1 -1
  23. package/dist/gs/builtin/channel.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +59 -26
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/cmp/index.js.map +1 -1
  28. package/dist/gs/context/context.d.ts +1 -1
  29. package/dist/gs/context/context.js +20 -11
  30. package/dist/gs/context/context.js.map +1 -1
  31. package/dist/gs/errors/errors.d.ts +7 -0
  32. package/dist/gs/errors/errors.js +190 -0
  33. package/dist/gs/errors/errors.js.map +1 -0
  34. package/dist/gs/errors/index.d.ts +1 -0
  35. package/dist/gs/errors/index.js +2 -0
  36. package/dist/gs/errors/index.js.map +1 -0
  37. package/dist/gs/internal/goarch/index.js +1 -1
  38. package/dist/gs/internal/goarch/index.js.map +1 -1
  39. package/dist/gs/io/index.d.ts +1 -0
  40. package/dist/gs/io/index.js +2 -0
  41. package/dist/gs/io/index.js.map +1 -0
  42. package/dist/gs/io/io.d.ts +107 -0
  43. package/dist/gs/io/io.js +385 -0
  44. package/dist/gs/io/io.js.map +1 -0
  45. package/dist/gs/iter/iter.js.map +1 -1
  46. package/dist/gs/math/bits/index.js +34 -32
  47. package/dist/gs/math/bits/index.js.map +1 -1
  48. package/dist/gs/runtime/runtime.d.ts +1 -0
  49. package/dist/gs/runtime/runtime.js +15 -18
  50. package/dist/gs/runtime/runtime.js.map +1 -1
  51. package/dist/gs/slices/slices.d.ts +1 -1
  52. package/dist/gs/slices/slices.js +1 -1
  53. package/dist/gs/slices/slices.js.map +1 -1
  54. package/dist/gs/strings/builder.d.ts +18 -0
  55. package/dist/gs/strings/builder.js +205 -0
  56. package/dist/gs/strings/builder.js.map +1 -0
  57. package/dist/gs/strings/clone.d.ts +1 -0
  58. package/dist/gs/strings/clone.js +16 -0
  59. package/dist/gs/strings/clone.js.map +1 -0
  60. package/dist/gs/strings/compare.d.ts +1 -0
  61. package/dist/gs/strings/compare.js +14 -0
  62. package/dist/gs/strings/compare.js.map +1 -0
  63. package/dist/gs/strings/index.d.ts +2 -0
  64. package/dist/gs/strings/index.js +3 -0
  65. package/dist/gs/strings/index.js.map +1 -0
  66. package/dist/gs/strings/iter.d.ts +8 -0
  67. package/dist/gs/strings/iter.js +160 -0
  68. package/dist/gs/strings/iter.js.map +1 -0
  69. package/dist/gs/strings/reader.d.ts +34 -0
  70. package/dist/gs/strings/reader.js +418 -0
  71. package/dist/gs/strings/reader.js.map +1 -0
  72. package/dist/gs/strings/replace.d.ts +106 -0
  73. package/dist/gs/strings/replace.js +1136 -0
  74. package/dist/gs/strings/replace.js.map +1 -0
  75. package/dist/gs/strings/search.d.ts +24 -0
  76. package/dist/gs/strings/search.js +169 -0
  77. package/dist/gs/strings/search.js.map +1 -0
  78. package/dist/gs/strings/strings.d.ts +47 -0
  79. package/dist/gs/strings/strings.js +418 -0
  80. package/dist/gs/strings/strings.js.map +1 -0
  81. package/dist/gs/stringslite/index.d.ts +1 -0
  82. package/dist/gs/stringslite/index.js +2 -0
  83. package/dist/gs/stringslite/index.js.map +1 -0
  84. package/dist/gs/stringslite/strings.d.ts +11 -0
  85. package/dist/gs/stringslite/strings.js +67 -0
  86. package/dist/gs/stringslite/strings.js.map +1 -0
  87. package/dist/gs/sync/index.d.ts +1 -0
  88. package/dist/gs/sync/index.js +2 -0
  89. package/dist/gs/sync/index.js.map +1 -0
  90. package/dist/gs/sync/sync.d.ts +79 -0
  91. package/dist/gs/sync/sync.js +392 -0
  92. package/dist/gs/sync/sync.js.map +1 -0
  93. package/dist/gs/time/time.js +7 -7
  94. package/dist/gs/time/time.js.map +1 -1
  95. package/dist/gs/unicode/index.d.ts +1 -0
  96. package/dist/gs/unicode/index.js +2 -0
  97. package/dist/gs/unicode/index.js.map +1 -0
  98. package/dist/gs/unicode/unicode.d.ts +105 -0
  99. package/dist/gs/unicode/unicode.js +332 -0
  100. package/dist/gs/unicode/unicode.js.map +1 -0
  101. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  102. package/dist/gs/unicode/utf8/index.js +3 -0
  103. package/dist/gs/unicode/utf8/index.js.map +1 -0
  104. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  105. package/dist/gs/unicode/utf8/utf8.js +196 -0
  106. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  107. package/dist/gs/unsafe/index.d.ts +1 -0
  108. package/dist/gs/unsafe/index.js +2 -0
  109. package/dist/gs/unsafe/index.js.map +1 -0
  110. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  111. package/dist/gs/unsafe/unsafe.js +44 -0
  112. package/dist/gs/unsafe/unsafe.js.map +1 -0
  113. package/go.mod +2 -1
  114. package/go.sum +6 -2
  115. package/gs/README.md +6 -0
  116. package/gs/builtin/builtin.ts +158 -0
  117. package/gs/builtin/channel.ts +683 -0
  118. package/gs/builtin/defer.ts +58 -0
  119. package/gs/builtin/index.ts +1 -0
  120. package/gs/builtin/io.ts +22 -0
  121. package/gs/builtin/map.ts +50 -0
  122. package/gs/builtin/slice.ts +1030 -0
  123. package/gs/builtin/type.ts +1106 -0
  124. package/gs/builtin/varRef.ts +25 -0
  125. package/gs/cmp/godoc.txt +8 -0
  126. package/gs/cmp/index.ts +29 -0
  127. package/gs/context/context.ts +401 -0
  128. package/gs/context/godoc.txt +69 -0
  129. package/gs/context/index.ts +1 -0
  130. package/gs/errors/errors.ts +223 -0
  131. package/gs/errors/godoc.txt +63 -0
  132. package/gs/errors/index.ts +1 -0
  133. package/gs/internal/goarch/godoc.txt +39 -0
  134. package/gs/internal/goarch/index.ts +18 -0
  135. package/gs/io/godoc.txt +61 -0
  136. package/gs/io/index.ts +1 -0
  137. package/gs/io/io.go +75 -0
  138. package/gs/io/io.ts +546 -0
  139. package/gs/iter/godoc.txt +203 -0
  140. package/gs/iter/index.ts +1 -0
  141. package/gs/iter/iter.ts +117 -0
  142. package/gs/math/bits/index.ts +356 -0
  143. package/gs/math/godoc.txt +76 -0
  144. package/gs/runtime/godoc.txt +331 -0
  145. package/gs/runtime/index.ts +1 -0
  146. package/gs/runtime/runtime.ts +178 -0
  147. package/gs/slices/godoc.txt +44 -0
  148. package/gs/slices/index.ts +1 -0
  149. package/gs/slices/slices.ts +22 -0
  150. package/gs/strings/builder.test.ts +121 -0
  151. package/gs/strings/builder.ts +223 -0
  152. package/gs/strings/clone.test.ts +43 -0
  153. package/gs/strings/clone.ts +17 -0
  154. package/gs/strings/compare.test.ts +84 -0
  155. package/gs/strings/compare.ts +13 -0
  156. package/gs/strings/godoc.txt +66 -0
  157. package/gs/strings/index.ts +2 -0
  158. package/gs/strings/iter.test.ts +343 -0
  159. package/gs/strings/iter.ts +171 -0
  160. package/gs/strings/reader.test.ts +243 -0
  161. package/gs/strings/reader.ts +451 -0
  162. package/gs/strings/replace.test.ts +181 -0
  163. package/gs/strings/replace.ts +1310 -0
  164. package/gs/strings/search.test.ts +214 -0
  165. package/gs/strings/search.ts +213 -0
  166. package/gs/strings/strings.test.ts +477 -0
  167. package/gs/strings/strings.ts +510 -0
  168. package/gs/stringslite/godoc.txt +17 -0
  169. package/gs/stringslite/index.ts +1 -0
  170. package/gs/stringslite/strings.ts +82 -0
  171. package/gs/sync/godoc.txt +21 -0
  172. package/gs/sync/index.ts +1 -0
  173. package/gs/sync/sync.go +64 -0
  174. package/gs/sync/sync.ts +449 -0
  175. package/gs/time/godoc.md +116 -0
  176. package/gs/time/godoc.txt +116 -0
  177. package/gs/time/index.ts +1 -0
  178. package/gs/time/time.ts +272 -0
  179. package/gs/unicode/godoc.txt +52 -0
  180. package/gs/unicode/index.ts +1 -0
  181. package/gs/unicode/unicode.go +38 -0
  182. package/gs/unicode/unicode.ts +418 -0
  183. package/gs/unicode/utf8/godoc.txt +22 -0
  184. package/gs/unicode/utf8/index.ts +2 -0
  185. package/gs/unicode/utf8/utf8.ts +227 -0
  186. package/gs/unsafe/godoc.txt +19 -0
  187. package/gs/unsafe/index.ts +1 -0
  188. package/gs/unsafe/unsafe.test.ts +68 -0
  189. package/gs/unsafe/unsafe.ts +77 -0
  190. package/package.json +6 -4
@@ -4,15 +4,15 @@ import (
4
4
  "context"
5
5
  "fmt"
6
6
  "go/ast"
7
+ "go/constant"
7
8
  "go/token"
8
9
  "go/types"
9
10
  "os"
10
11
  "path/filepath"
11
12
  "slices"
13
+ "sort"
12
14
  "strings"
13
15
 
14
- "go/constant"
15
-
16
16
  gs "github.com/aperturerobotics/goscript"
17
17
  "github.com/sirupsen/logrus"
18
18
  "golang.org/x/tools/go/packages"
@@ -146,6 +146,18 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
146
146
 
147
147
  // Visit all imports, including standard library packages
148
148
  for _, imp := range pkg.Imports {
149
+ // Skip protobuf-go-lite packages and their dependencies
150
+ if isProtobufGoLitePackage(imp.PkgPath) {
151
+ c.le.Debugf("Skipping protobuf-go-lite package: %s", imp.PkgPath)
152
+ continue
153
+ }
154
+
155
+ // Skip packages that are only used by .pb.go files
156
+ if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
157
+ c.le.Debugf("Skipping package only used by .pb.go files: %s", imp.PkgPath)
158
+ continue
159
+ }
160
+
149
161
  visit(imp)
150
162
  }
151
163
  }
@@ -239,29 +251,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
239
251
  continue
240
252
  }
241
253
 
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
-
265
254
  pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
266
255
  if err != nil {
267
256
  return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
@@ -326,6 +315,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
326
315
  }
327
316
  }
328
317
 
318
+ // Perform package-level analysis for auto-imports
319
+ packageAnalysis := AnalyzePackage(c.pkg)
320
+
329
321
  // Track all compiled files for later generating the index.ts
330
322
  compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
331
323
 
@@ -337,13 +329,35 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
337
329
  return err
338
330
  }
339
331
 
332
+ // Check if this is a .pb.go file that should be skipped
333
+ baseFileName := filepath.Base(fileName)
334
+ if strings.HasSuffix(baseFileName, ".pb.go") {
335
+ // Check if there's a corresponding .pb.ts file
336
+ pbTsFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb.ts"
337
+ packageDir := filepath.Dir(fileName)
338
+ pbTsPath := filepath.Join(packageDir, pbTsFileName)
339
+
340
+ if _, err := os.Stat(pbTsPath); err == nil {
341
+ // .pb.ts file exists, copy it instead of transpiling .pb.go
342
+ c.le.WithField("file", relWdFileName).Debug("found .pb.ts file, copying instead of transpiling .pb.go")
343
+
344
+ if err := c.copyProtobufTSFile(pbTsPath, pbTsFileName); err != nil {
345
+ return fmt.Errorf("failed to copy protobuf .pb.ts file: %w", err)
346
+ }
347
+
348
+ // Add the .pb file to our compiled files list for index generation
349
+ pbFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb"
350
+ compiledFiles = append(compiledFiles, pbFileName)
351
+ continue // Skip transpiling this .pb.go file
352
+ }
353
+ }
354
+
340
355
  c.le.WithField("file", relWdFileName).Debug("compiling file")
341
- if err := c.CompileFile(ctx, fileName, f); err != nil {
356
+ if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
342
357
  return err
343
358
  }
344
359
 
345
360
  // Add the base filename to our list for the index.ts generation
346
- baseFileName := filepath.Base(fileName)
347
361
  // Strip .go extension and add .gs
348
362
  gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
349
363
  compiledFiles = append(compiledFiles, gsFileName)
@@ -358,8 +372,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
358
372
  }
359
373
 
360
374
  // generateIndexFile creates an index.ts file in the package output directory
361
- // that re-exports all symbols from the compiled TypeScript files.
362
- // This ensures the package can be imported correctly by TypeScript modules.
375
+ // that re-exports only Go-exported symbols from the compiled TypeScript files.
376
+ // This ensures the package can be imported correctly by TypeScript modules
377
+ // while maintaining proper Go package boundaries.
363
378
  func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
364
379
  indexFilePath := filepath.Join(c.outputPath, "index.ts")
365
380
 
@@ -370,12 +385,128 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
370
385
  }
371
386
  defer indexFile.Close() //nolint:errcheck
372
387
 
373
- // Write the re-export statements for each compiled file
388
+ // Collect exported symbols from all files in the package
389
+ var exportedSymbols []string
390
+
391
+ // Iterate through all syntax files to find exported symbols
392
+ for i, syntax := range c.pkg.Syntax {
393
+ fileName := c.pkg.CompiledGoFiles[i]
394
+ baseFileName := filepath.Base(fileName)
395
+ gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
396
+
397
+ // Only include this file if it was compiled (in our compiledFiles list)
398
+ fileWasCompiled := false
399
+ for _, compiledFile := range compiledFiles {
400
+ if compiledFile == gsFileName {
401
+ fileWasCompiled = true
402
+ break
403
+ }
404
+ }
405
+ if !fileWasCompiled {
406
+ continue
407
+ }
408
+
409
+ // Analyze declarations in this file to find exported symbols
410
+ for _, decl := range syntax.Decls {
411
+ switch d := decl.(type) {
412
+ case *ast.FuncDecl:
413
+ // Only include top-level functions (not methods)
414
+ if d.Recv == nil && d.Name.IsExported() {
415
+ exportedSymbols = append(exportedSymbols, d.Name.Name)
416
+ }
417
+ case *ast.GenDecl:
418
+ for _, spec := range d.Specs {
419
+ switch s := spec.(type) {
420
+ case *ast.TypeSpec:
421
+ if s.Name.IsExported() {
422
+ exportedSymbols = append(exportedSymbols, s.Name.Name)
423
+ }
424
+ case *ast.ValueSpec:
425
+ for _, name := range s.Names {
426
+ if name.IsExported() {
427
+ exportedSymbols = append(exportedSymbols, name.Name)
428
+ }
429
+ }
430
+ }
431
+ }
432
+ }
433
+ }
434
+ }
435
+
436
+ // Remove duplicates and sort
437
+ symbolMap := make(map[string]bool)
438
+ for _, symbol := range exportedSymbols {
439
+ symbolMap[symbol] = true
440
+ }
441
+
442
+ var uniqueSymbols []string
443
+ for symbol := range symbolMap {
444
+ uniqueSymbols = append(uniqueSymbols, symbol)
445
+ }
446
+
447
+ // Sort for consistent output
448
+ sort.Strings(uniqueSymbols)
449
+
450
+ // Write selective re-exports for each compiled file
374
451
  for _, fileName := range compiledFiles {
375
- // Create the re-export line: export * from "./file.gs.js"
376
- exportLine := fmt.Sprintf("export * from \"./%s.js\"\n", fileName)
377
- if _, err := indexFile.WriteString(exportLine); err != nil {
378
- return err
452
+ // Check if this is a protobuf file
453
+ if strings.HasSuffix(fileName, ".pb") {
454
+ // For protobuf files, add a simple re-export
455
+ pbTsFileName := fileName + ".ts"
456
+ if err := c.writeProtobufExports(indexFile, fileName, pbTsFileName); err != nil {
457
+ return err
458
+ }
459
+ continue
460
+ }
461
+
462
+ // Find which symbols this file exports
463
+ var fileSymbols []string
464
+
465
+ // Find the corresponding syntax file
466
+ for i, syntax := range c.pkg.Syntax {
467
+ syntaxFileName := c.pkg.CompiledGoFiles[i]
468
+ syntaxBaseFileName := filepath.Base(syntaxFileName)
469
+ syntaxGsFileName := strings.TrimSuffix(syntaxBaseFileName, ".go") + ".gs"
470
+
471
+ if syntaxGsFileName != fileName {
472
+ continue
473
+ }
474
+
475
+ // Collect exported symbols from this specific file
476
+ for _, decl := range syntax.Decls {
477
+ switch d := decl.(type) {
478
+ case *ast.FuncDecl:
479
+ if d.Recv == nil && d.Name.IsExported() {
480
+ fileSymbols = append(fileSymbols, d.Name.Name)
481
+ }
482
+ case *ast.GenDecl:
483
+ for _, spec := range d.Specs {
484
+ switch s := spec.(type) {
485
+ case *ast.TypeSpec:
486
+ if s.Name.IsExported() {
487
+ fileSymbols = append(fileSymbols, s.Name.Name)
488
+ }
489
+ case *ast.ValueSpec:
490
+ for _, name := range s.Names {
491
+ if name.IsExported() {
492
+ fileSymbols = append(fileSymbols, name.Name)
493
+ }
494
+ }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ break
500
+ }
501
+
502
+ // Write selective export if this file has exported symbols
503
+ if len(fileSymbols) > 0 {
504
+ sort.Strings(fileSymbols)
505
+ exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
506
+ strings.Join(fileSymbols, ", "), fileName)
507
+ if _, err := indexFile.WriteString(exportLine); err != nil {
508
+ return err
509
+ }
379
510
  }
380
511
  }
381
512
 
@@ -388,7 +519,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
388
519
  // about varRefing, async functions, defer statements).
389
520
  // Then, it creates a `FileCompiler` instance for the file and invokes its
390
521
  // `Compile` method to generate the TypeScript code.
391
- func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File) error {
522
+ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, packageAnalysis *PackageAnalysis) error {
392
523
  // Create a new analysis instance for per-file data
393
524
  analysis := NewAnalysis()
394
525
 
@@ -398,7 +529,7 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
398
529
  // Analyze the file before compiling
399
530
  AnalyzeFile(syntax, p.pkg, analysis, cmap)
400
531
 
401
- fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis)
532
+ fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
402
533
  if err != nil {
403
534
  return err
404
535
  }
@@ -410,12 +541,13 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
410
541
  // initializes the `TSCodeWriter` for TypeScript code generation, and uses a
411
542
  // `GoToTSCompiler` to translate Go declarations and statements.
412
543
  type FileCompiler struct {
413
- compilerConfig *Config
414
- codeWriter *TSCodeWriter
415
- pkg *packages.Package
416
- ast *ast.File
417
- fullPath string
418
- Analysis *Analysis
544
+ compilerConfig *Config
545
+ codeWriter *TSCodeWriter
546
+ pkg *packages.Package
547
+ ast *ast.File
548
+ fullPath string
549
+ Analysis *Analysis
550
+ PackageAnalysis *PackageAnalysis
419
551
  }
420
552
 
421
553
  // NewFileCompiler creates a new `FileCompiler` for a specific Go file.
@@ -428,13 +560,15 @@ func NewFileCompiler(
428
560
  astFile *ast.File,
429
561
  fullPath string,
430
562
  analysis *Analysis,
563
+ packageAnalysis *PackageAnalysis,
431
564
  ) (*FileCompiler, error) {
432
565
  return &FileCompiler{
433
- compilerConfig: compilerConf,
434
- pkg: pkg,
435
- ast: astFile,
436
- fullPath: fullPath,
437
- Analysis: analysis,
566
+ compilerConfig: compilerConf,
567
+ pkg: pkg,
568
+ ast: astFile,
569
+ fullPath: fullPath,
570
+ Analysis: analysis,
571
+ PackageAnalysis: packageAnalysis,
438
572
  }, nil
439
573
  }
440
574
 
@@ -468,7 +602,26 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
468
602
 
469
603
  // Add import for the goscript runtime using namespace import and alias
470
604
  c.codeWriter.WriteLinef("import * as $ from %q;", "@goscript/builtin/builtin.js")
471
- c.codeWriter.WriteLine("") // Add a newline after the import
605
+
606
+ // Check if there are any .pb.go files in this package and add imports for them
607
+ if err := c.addProtobufImports(); err != nil {
608
+ return fmt.Errorf("failed to add protobuf imports: %w", err)
609
+ }
610
+
611
+ // Generate auto-imports for functions from other files in the same package
612
+ currentFileName := strings.TrimSuffix(filepath.Base(c.fullPath), ".go")
613
+ if imports := c.PackageAnalysis.FunctionCalls[currentFileName]; imports != nil {
614
+ for sourceFile, functions := range imports {
615
+ if len(functions) > 0 {
616
+ // Sort functions for consistent output
617
+ sort.Strings(functions)
618
+ c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
619
+ strings.Join(functions, ", "), sourceFile)
620
+ }
621
+ }
622
+ }
623
+
624
+ c.codeWriter.WriteLine("") // Add a newline after imports
472
625
 
473
626
  if err := goWriter.WriteDecls(f.Decls); err != nil {
474
627
  return fmt.Errorf("failed to write declarations: %w", err)
@@ -483,7 +636,6 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
483
636
  // decisions about code generation (e.g., varRefing, async behavior).
484
637
  type GoToTSCompiler struct {
485
638
  tsw *TSCodeWriter
486
-
487
639
  pkg *packages.Package
488
640
 
489
641
  analysis *Analysis
@@ -1,6 +1,7 @@
1
1
  package compiler_test
2
2
 
3
3
  import (
4
+ "context"
4
5
  "os"
5
6
  "os/exec"
6
7
  "path/filepath"
@@ -11,7 +12,9 @@ import (
11
12
  "sync/atomic"
12
13
  "testing"
13
14
 
15
+ "github.com/aperturerobotics/goscript/compiler"
14
16
  "github.com/aperturerobotics/goscript/compliance"
17
+ "github.com/sirupsen/logrus"
15
18
  )
16
19
 
17
20
  // NOTE: this is here instead of compliance/compliance_test.go so coverage ends up in this package.
@@ -137,3 +140,53 @@ func getParentGoModulePath() (string, error) {
137
140
  }
138
141
  return strings.TrimSpace(string(output)), nil
139
142
  }
143
+
144
+ func TestUnsafePackageCompilation(t *testing.T) {
145
+ // Create a temporary directory for the test output
146
+ tempDir, err := os.MkdirTemp("", "goscript-test-unsafe")
147
+ if err != nil {
148
+ t.Fatalf("Failed to create temp dir: %v", err)
149
+ }
150
+ defer os.RemoveAll(tempDir)
151
+
152
+ // Setup logger
153
+ log := logrus.New()
154
+ log.SetLevel(logrus.DebugLevel)
155
+ le := logrus.NewEntry(log)
156
+
157
+ // Test with AllDependencies=true and DisableEmitBuiltin=false to ensure handwritten packages are copied
158
+ config := &compiler.Config{
159
+ OutputPath: tempDir,
160
+ AllDependencies: true,
161
+ DisableEmitBuiltin: false, // This ensures handwritten packages are copied to output
162
+ }
163
+
164
+ comp, err := compiler.NewCompiler(config, le, nil)
165
+ if err != nil {
166
+ t.Fatalf("Failed to create compiler: %v", err)
167
+ }
168
+
169
+ // Try to compile a package that has dependencies that import unsafe
170
+ // We'll use "sync/atomic" which imports unsafe but doesn't have a handwritten equivalent
171
+ result, err := comp.CompilePackages(context.Background(), "sync/atomic")
172
+ // This should now succeed since we have a handwritten unsafe package
173
+ if err != nil {
174
+ t.Fatalf("Expected compilation to succeed with handwritten unsafe package, but it failed: %v", err)
175
+ }
176
+
177
+ // Verify that the unsafe package was copied (not compiled) since it has a handwritten equivalent
178
+ if !slices.Contains(result.CopiedPackages, "unsafe") {
179
+ t.Errorf("Expected unsafe package to be in CopiedPackages, but it wasn't. CopiedPackages: %v", result.CopiedPackages)
180
+ }
181
+
182
+ // Verify that sync/atomic was compiled
183
+ if !slices.Contains(result.CompiledPackages, "sync/atomic") {
184
+ t.Errorf("Expected sync/atomic package to be in CompiledPackages, but it wasn't. CompiledPackages: %v", result.CompiledPackages)
185
+ }
186
+
187
+ // Check that the unsafe package directory exists in the output
188
+ unsafePath := filepath.Join(tempDir, "@goscript/unsafe")
189
+ if _, err := os.Stat(unsafePath); os.IsNotExist(err) {
190
+ t.Errorf("unsafe package directory was not created at %s", unsafePath)
191
+ }
192
+ }
@@ -199,18 +199,34 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
199
199
  if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
200
200
  structType = underlyingStruct
201
201
  isStructLiteral = true
202
- // Named struct, use constructor
203
- c.tsw.WriteLiterally("new ")
204
- c.WriteTypeExpr(exp.Type)
202
+
203
+ // Check if this is a protobuf type
204
+ if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
205
+ if err != nil {
206
+ return err
207
+ }
208
+ } else {
209
+ // Named struct, use constructor
210
+ c.tsw.WriteLiterally("new ")
211
+ c.WriteTypeExpr(exp.Type)
212
+ }
205
213
  }
206
214
  } else if ptrType, ok := litType.(*types.Pointer); ok {
207
215
  if namedElem, ok := ptrType.Elem().(*types.Named); ok {
208
216
  if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
209
217
  structType = underlyingStruct
210
218
  isStructLiteral = true // Treat pointer-to-struct literal similarly
211
- // Named struct pointer, use constructor
212
- c.tsw.WriteLiterally("new ")
213
- c.WriteTypeExpr(exp.Type)
219
+
220
+ // Check if this is a protobuf type
221
+ if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
222
+ if err != nil {
223
+ return err
224
+ }
225
+ } else {
226
+ // Named struct pointer, use constructor
227
+ c.tsw.WriteLiterally("new ")
228
+ c.WriteTypeExpr(exp.Type)
229
+ }
214
230
  }
215
231
  }
216
232
  } else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
@@ -339,7 +355,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
339
355
  if firstFieldWritten {
340
356
  c.tsw.WriteLiterally(", ")
341
357
  }
342
- c.tsw.WriteLiterally(keyName)
358
+
359
+ // Convert field name for protobuf types
360
+ fieldName := c.convertProtobufFieldNameInLiteral(keyName, litType)
361
+
362
+ c.tsw.WriteLiterally(fieldName)
343
363
  c.tsw.WriteLiterally(": ")
344
364
  if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
345
365
  return err
@@ -538,7 +558,11 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
538
558
  if firstFieldWritten {
539
559
  c.tsw.WriteLiterally(", ")
540
560
  }
541
- c.tsw.WriteLiterally(keyName)
561
+
562
+ // Convert field name for protobuf types
563
+ fieldName := c.convertProtobufFieldNameInLiteral(keyName, structType.Underlying())
564
+
565
+ c.tsw.WriteLiterally(fieldName)
542
566
  c.tsw.WriteLiterally(": ")
543
567
  if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
544
568
  return err
package/compiler/decl.go CHANGED
@@ -68,11 +68,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
68
68
  c.WriteDoc(decl.Doc)
69
69
  }
70
70
 
71
- // Exported functions start with uppercase in Go, or special-case "main" entry point
72
- isExported := decl.Name.IsExported() || decl.Name.Name == "main"
73
- if isExported {
74
- c.tsw.WriteLiterally("export ")
75
- }
71
+ // Export all functions for intra-package visibility
72
+ // This allows other files in the same package to import functions
73
+ c.tsw.WriteLiterally("export ")
76
74
 
77
75
  // Check if this function is async using the analysis data
78
76
  var isAsync bool
@@ -233,7 +231,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
233
231
  if recvName != "_" {
234
232
  c.tsw.WriteLine("{")
235
233
  c.tsw.Indent(1)
236
- c.tsw.WriteLinef("const %s = this", recvName)
234
+ // Sanitize the receiver name to avoid conflicts with TypeScript reserved words
235
+ sanitizedRecvName := c.sanitizeIdentifier(recvName)
236
+ c.tsw.WriteLinef("const %s = this", sanitizedRecvName)
237
237
 
238
238
  // Add using statement if needed
239
239
  if c.analysis.NeedsDefer(decl.Body) {
@@ -5,6 +5,7 @@ import (
5
5
  "go/ast"
6
6
  "go/token"
7
7
  "go/types"
8
+ "strings"
8
9
 
9
10
  "github.com/pkg/errors"
10
11
  )
@@ -38,6 +39,11 @@ import (
38
39
  func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
39
40
  expFun := exp.Fun
40
41
 
42
+ // Handle protobuf method calls
43
+ if handled, err := c.writeProtobufMethodCall(exp); handled {
44
+ return err
45
+ }
46
+
41
47
  // Handle any type conversion with nil argument
42
48
  if len(exp.Args) == 1 {
43
49
  if nilIdent, isIdent := exp.Args[0].(*ast.Ident); isIdent && nilIdent.Name == "nil" {
@@ -325,6 +331,53 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
325
331
  }
326
332
  }
327
333
  }
334
+ } else {
335
+ // Handle named types with slice underlying types: make(NamedSliceType, len, cap)
336
+ // This handles cases like: type appendSliceWriter []byte; make(appendSliceWriter, 0, len(s))
337
+ namedType := typeName.Type()
338
+ if sliceType, isSlice := namedType.Underlying().(*types.Slice); isSlice {
339
+ goElemType := sliceType.Elem()
340
+
341
+ // Check if it's a named type with []byte underlying type
342
+ if basicElem, isBasic := goElemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
343
+ c.tsw.WriteLiterally("new Uint8Array(")
344
+ if len(exp.Args) >= 2 {
345
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
346
+ return err
347
+ }
348
+ // Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
349
+ } else {
350
+ // If no length is provided, default to 0
351
+ c.tsw.WriteLiterally("0")
352
+ }
353
+ c.tsw.WriteLiterally(")")
354
+ return nil // Handled make for named []byte type
355
+ }
356
+
357
+ // Handle other named slice types
358
+ c.tsw.WriteLiterally("$.makeSlice<")
359
+ c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
360
+ c.tsw.WriteLiterally(">(")
361
+
362
+ if len(exp.Args) >= 2 {
363
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
364
+ return err
365
+ }
366
+ if len(exp.Args) == 3 {
367
+ c.tsw.WriteLiterally(", ")
368
+ if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
369
+ return err
370
+ }
371
+ } else if len(exp.Args) > 3 {
372
+ return errors.New("makeSlice expects 2 or 3 arguments")
373
+ }
374
+ } else {
375
+ // If no length is provided, default to 0
376
+ c.tsw.WriteLiterally("0")
377
+ }
378
+ c.tsw.WriteLiterally(")")
379
+ return nil // Handled make for named slice type
380
+ }
328
381
  }
329
382
  }
330
383
  }
@@ -554,6 +607,36 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
554
607
  return nil // Handled regular function call
555
608
  }
556
609
  } else {
610
+ // Check if this is an async method call (e.g., mu.Lock())
611
+ if selExpr, ok := expFun.(*ast.SelectorExpr); ok {
612
+ // Check if this is a method call on a variable (e.g., mu.Lock())
613
+ if ident, ok := selExpr.X.(*ast.Ident); ok {
614
+ // Get the type of the receiver
615
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
616
+ if varObj, ok := obj.(*types.Var); ok {
617
+ // Get the type name and package
618
+ if namedType, ok := varObj.Type().(*types.Named); ok {
619
+ typeName := namedType.Obj().Name()
620
+ methodName := selExpr.Sel.Name
621
+
622
+ // Check if the type is from an imported package
623
+ if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != c.pkg.Types {
624
+ pkgPath := typePkg.Path()
625
+ // Extract package name from path (e.g., "sync" from "github.com/.../gs/sync")
626
+ parts := strings.Split(pkgPath, "/")
627
+ pkgName := parts[len(parts)-1]
628
+
629
+ // Check if this method is async based on metadata
630
+ if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
631
+ c.tsw.WriteLiterally("await ")
632
+ }
633
+ }
634
+ }
635
+ }
636
+ }
637
+ }
638
+ }
639
+
557
640
  // If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
558
641
  if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
559
642
  c.tsw.WriteLiterally("(")
package/compiler/expr.go CHANGED
@@ -388,7 +388,7 @@ func (c *GoToTSCompiler) WriteBinaryExpr(exp *ast.BinaryExpr) error {
388
388
  isBitwise = true
389
389
  }
390
390
 
391
- // Special handling for large bit shift expressions that would overflow in JavaScript
391
+ // Handle large bit shift expressions that would overflow in JavaScript
392
392
  if exp.Op == token.SHL {
393
393
  // Check if this is 1 << 63 pattern
394
394
  if leftLit, leftIsLit := exp.X.(*ast.BasicLit); leftIsLit && leftLit.Value == "1" {