goscript 0.0.26 → 0.0.29

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 (228) 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 +298 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +200 -68
  8. package/compiler/compiler_test.go +17 -24
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +170 -15
  12. package/compiler/expr-selector.go +100 -0
  13. package/compiler/expr.go +1 -1
  14. package/compiler/protobuf.go +557 -0
  15. package/compiler/spec-struct.go +4 -0
  16. package/compiler/spec-value.go +89 -10
  17. package/compiler/spec.go +254 -1
  18. package/compiler/stmt-assign.go +35 -0
  19. package/compiler/type-assert.go +87 -0
  20. package/compiler/type.go +4 -1
  21. package/dist/gs/builtin/builtin.d.ts +20 -1
  22. package/dist/gs/builtin/builtin.js +95 -4
  23. package/dist/gs/builtin/builtin.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +21 -2
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/errors/errors.d.ts +5 -6
  28. package/dist/gs/errors/errors.js.map +1 -1
  29. package/dist/gs/internal/oserror/errors.d.ts +6 -0
  30. package/dist/gs/internal/oserror/errors.js +7 -0
  31. package/dist/gs/internal/oserror/errors.js.map +1 -0
  32. package/dist/gs/internal/oserror/index.d.ts +1 -0
  33. package/dist/gs/internal/oserror/index.js +2 -0
  34. package/dist/gs/internal/oserror/index.js.map +1 -0
  35. package/dist/gs/io/fs/format.d.ts +3 -0
  36. package/dist/gs/io/fs/format.js +56 -0
  37. package/dist/gs/io/fs/format.js.map +1 -0
  38. package/dist/gs/io/fs/fs.d.ts +79 -0
  39. package/dist/gs/io/fs/fs.js +200 -0
  40. package/dist/gs/io/fs/fs.js.map +1 -0
  41. package/dist/gs/io/fs/glob.d.ts +10 -0
  42. package/dist/gs/io/fs/glob.js +141 -0
  43. package/dist/gs/io/fs/glob.js.map +1 -0
  44. package/dist/gs/io/fs/index.d.ts +8 -0
  45. package/dist/gs/io/fs/index.js +9 -0
  46. package/dist/gs/io/fs/index.js.map +1 -0
  47. package/dist/gs/io/fs/readdir.d.ts +7 -0
  48. package/dist/gs/io/fs/readdir.js +152 -0
  49. package/dist/gs/io/fs/readdir.js.map +1 -0
  50. package/dist/gs/io/fs/readfile.d.ts +6 -0
  51. package/dist/gs/io/fs/readfile.js +118 -0
  52. package/dist/gs/io/fs/readfile.js.map +1 -0
  53. package/dist/gs/io/fs/stat.d.ts +6 -0
  54. package/dist/gs/io/fs/stat.js +87 -0
  55. package/dist/gs/io/fs/stat.js.map +1 -0
  56. package/dist/gs/io/fs/sub.d.ts +6 -0
  57. package/dist/gs/io/fs/sub.js +172 -0
  58. package/dist/gs/io/fs/sub.js.map +1 -0
  59. package/dist/gs/io/fs/walk.d.ts +7 -0
  60. package/dist/gs/io/fs/walk.js +76 -0
  61. package/dist/gs/io/fs/walk.js.map +1 -0
  62. package/dist/gs/io/index.d.ts +1 -0
  63. package/dist/gs/io/index.js +2 -0
  64. package/dist/gs/io/index.js.map +1 -0
  65. package/dist/gs/io/io.d.ts +107 -0
  66. package/dist/gs/io/io.js +385 -0
  67. package/dist/gs/io/io.js.map +1 -0
  68. package/dist/gs/path/index.d.ts +2 -0
  69. package/dist/gs/path/index.js +3 -0
  70. package/dist/gs/path/index.js.map +1 -0
  71. package/dist/gs/path/match.d.ts +6 -0
  72. package/dist/gs/path/match.js +281 -0
  73. package/dist/gs/path/match.js.map +1 -0
  74. package/dist/gs/path/path.d.ts +7 -0
  75. package/dist/gs/path/path.js +256 -0
  76. package/dist/gs/path/path.js.map +1 -0
  77. package/dist/gs/strings/builder.d.ts +18 -0
  78. package/dist/gs/strings/builder.js +205 -0
  79. package/dist/gs/strings/builder.js.map +1 -0
  80. package/dist/gs/strings/clone.d.ts +1 -0
  81. package/dist/gs/strings/clone.js +16 -0
  82. package/dist/gs/strings/clone.js.map +1 -0
  83. package/dist/gs/strings/compare.d.ts +1 -0
  84. package/dist/gs/strings/compare.js +14 -0
  85. package/dist/gs/strings/compare.js.map +1 -0
  86. package/dist/gs/strings/index.d.ts +2 -0
  87. package/dist/gs/strings/index.js +3 -0
  88. package/dist/gs/strings/index.js.map +1 -0
  89. package/dist/gs/strings/iter.d.ts +8 -0
  90. package/dist/gs/strings/iter.js +160 -0
  91. package/dist/gs/strings/iter.js.map +1 -0
  92. package/dist/gs/strings/reader.d.ts +34 -0
  93. package/dist/gs/strings/reader.js +418 -0
  94. package/dist/gs/strings/reader.js.map +1 -0
  95. package/dist/gs/strings/replace.d.ts +106 -0
  96. package/dist/gs/strings/replace.js +1136 -0
  97. package/dist/gs/strings/replace.js.map +1 -0
  98. package/dist/gs/strings/search.d.ts +24 -0
  99. package/dist/gs/strings/search.js +169 -0
  100. package/dist/gs/strings/search.js.map +1 -0
  101. package/dist/gs/strings/strings.d.ts +47 -0
  102. package/dist/gs/strings/strings.js +418 -0
  103. package/dist/gs/strings/strings.js.map +1 -0
  104. package/dist/gs/stringslite/index.d.ts +1 -0
  105. package/dist/gs/stringslite/index.js +2 -0
  106. package/dist/gs/stringslite/index.js.map +1 -0
  107. package/dist/gs/stringslite/strings.d.ts +11 -0
  108. package/dist/gs/stringslite/strings.js +67 -0
  109. package/dist/gs/stringslite/strings.js.map +1 -0
  110. package/dist/gs/sync/index.d.ts +1 -0
  111. package/dist/gs/sync/index.js +2 -0
  112. package/dist/gs/sync/index.js.map +1 -0
  113. package/dist/gs/sync/sync.d.ts +79 -0
  114. package/dist/gs/sync/sync.js +392 -0
  115. package/dist/gs/sync/sync.js.map +1 -0
  116. package/dist/gs/time/time.d.ts +11 -2
  117. package/dist/gs/time/time.js +337 -12
  118. package/dist/gs/time/time.js.map +1 -1
  119. package/dist/gs/unicode/index.d.ts +1 -0
  120. package/dist/gs/unicode/index.js +2 -0
  121. package/dist/gs/unicode/index.js.map +1 -0
  122. package/dist/gs/unicode/unicode.d.ts +105 -0
  123. package/dist/gs/unicode/unicode.js +332 -0
  124. package/dist/gs/unicode/unicode.js.map +1 -0
  125. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  126. package/dist/gs/unicode/utf8/index.js +3 -0
  127. package/dist/gs/unicode/utf8/index.js.map +1 -0
  128. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  129. package/dist/gs/unicode/utf8/utf8.js +196 -0
  130. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  131. package/dist/gs/unsafe/index.d.ts +1 -0
  132. package/dist/gs/unsafe/index.js +2 -0
  133. package/dist/gs/unsafe/index.js.map +1 -0
  134. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  135. package/dist/gs/unsafe/unsafe.js +44 -0
  136. package/dist/gs/unsafe/unsafe.js.map +1 -0
  137. package/go.mod +2 -1
  138. package/go.sum +6 -2
  139. package/gs/README.md +6 -0
  140. package/gs/builtin/builtin.ts +171 -0
  141. package/gs/builtin/channel.ts +683 -0
  142. package/gs/builtin/defer.ts +58 -0
  143. package/gs/builtin/index.ts +1 -0
  144. package/gs/builtin/io.ts +22 -0
  145. package/gs/builtin/map.ts +50 -0
  146. package/gs/builtin/slice.ts +1030 -0
  147. package/gs/builtin/type.ts +1106 -0
  148. package/gs/builtin/varRef.ts +25 -0
  149. package/gs/cmp/godoc.txt +8 -0
  150. package/gs/cmp/index.ts +29 -0
  151. package/gs/context/context.ts +401 -0
  152. package/gs/context/godoc.txt +69 -0
  153. package/gs/context/index.ts +1 -0
  154. package/gs/errors/errors.ts +223 -0
  155. package/gs/errors/godoc.txt +63 -0
  156. package/gs/errors/index.ts +1 -0
  157. package/gs/internal/goarch/godoc.txt +39 -0
  158. package/gs/internal/goarch/index.ts +18 -0
  159. package/gs/internal/oserror/errors.ts +14 -0
  160. package/gs/internal/oserror/index.ts +1 -0
  161. package/gs/io/fs/format.ts +65 -0
  162. package/gs/io/fs/fs.ts +359 -0
  163. package/gs/io/fs/glob.ts +167 -0
  164. package/gs/io/fs/godoc.txt +35 -0
  165. package/gs/io/fs/index.ts +8 -0
  166. package/gs/io/fs/readdir.ts +126 -0
  167. package/gs/io/fs/readfile.ts +77 -0
  168. package/gs/io/fs/stat.ts +38 -0
  169. package/gs/io/fs/sub.ts +208 -0
  170. package/gs/io/fs/walk.ts +89 -0
  171. package/gs/io/godoc.txt +61 -0
  172. package/gs/io/index.ts +1 -0
  173. package/gs/io/io.go +75 -0
  174. package/gs/io/io.ts +546 -0
  175. package/gs/iter/godoc.txt +203 -0
  176. package/gs/iter/index.ts +1 -0
  177. package/gs/iter/iter.ts +117 -0
  178. package/gs/math/bits/index.ts +356 -0
  179. package/gs/math/godoc.txt +76 -0
  180. package/gs/path/index.ts +2 -0
  181. package/gs/path/match.ts +307 -0
  182. package/gs/path/path.ts +301 -0
  183. package/gs/runtime/godoc.txt +331 -0
  184. package/gs/runtime/index.ts +1 -0
  185. package/gs/runtime/runtime.ts +178 -0
  186. package/gs/slices/godoc.txt +44 -0
  187. package/gs/slices/index.ts +1 -0
  188. package/gs/slices/slices.ts +22 -0
  189. package/gs/strings/builder.test.ts +121 -0
  190. package/gs/strings/builder.ts +223 -0
  191. package/gs/strings/clone.test.ts +43 -0
  192. package/gs/strings/clone.ts +17 -0
  193. package/gs/strings/compare.test.ts +84 -0
  194. package/gs/strings/compare.ts +13 -0
  195. package/gs/strings/godoc.txt +66 -0
  196. package/gs/strings/index.ts +2 -0
  197. package/gs/strings/iter.test.ts +343 -0
  198. package/gs/strings/iter.ts +171 -0
  199. package/gs/strings/reader.test.ts +242 -0
  200. package/gs/strings/reader.ts +451 -0
  201. package/gs/strings/replace.test.ts +181 -0
  202. package/gs/strings/replace.ts +1310 -0
  203. package/gs/strings/search.test.ts +214 -0
  204. package/gs/strings/search.ts +213 -0
  205. package/gs/strings/strings.test.ts +477 -0
  206. package/gs/strings/strings.ts +510 -0
  207. package/gs/stringslite/godoc.txt +17 -0
  208. package/gs/stringslite/index.ts +1 -0
  209. package/gs/stringslite/strings.ts +82 -0
  210. package/gs/sync/godoc.txt +21 -0
  211. package/gs/sync/index.ts +1 -0
  212. package/gs/sync/sync.go +64 -0
  213. package/gs/sync/sync.ts +449 -0
  214. package/gs/time/godoc.txt +116 -0
  215. package/gs/time/index.ts +1 -0
  216. package/gs/time/time.ts +585 -0
  217. package/gs/unicode/godoc.txt +52 -0
  218. package/gs/unicode/index.ts +1 -0
  219. package/gs/unicode/unicode.go +38 -0
  220. package/gs/unicode/unicode.ts +418 -0
  221. package/gs/unicode/utf8/godoc.txt +22 -0
  222. package/gs/unicode/utf8/index.ts +2 -0
  223. package/gs/unicode/utf8/utf8.ts +227 -0
  224. package/gs/unsafe/godoc.txt +19 -0
  225. package/gs/unsafe/index.ts +1 -0
  226. package/gs/unsafe/unsafe.test.ts +68 -0
  227. package/gs/unsafe/unsafe.ts +77 -0
  228. package/package.json +4 -3
@@ -10,6 +10,7 @@ import (
10
10
  "os"
11
11
  "path/filepath"
12
12
  "slices"
13
+ "sort"
13
14
  "strings"
14
15
 
15
16
  gs "github.com/aperturerobotics/goscript"
@@ -145,6 +146,18 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
145
146
 
146
147
  // Visit all imports, including standard library packages
147
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
+
148
161
  visit(imp)
149
162
  }
150
163
  }
@@ -238,50 +251,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
238
251
  continue
239
252
  }
240
253
 
241
- // Check if this is the unsafe package, which is not supported in GoScript
242
- if pkg.PkgPath == "unsafe" {
243
- // Find which packages that would actually be compiled depend on unsafe
244
- var dependentPackages []string
245
- for _, otherPkg := range pkgs {
246
- if otherPkg.PkgPath != "unsafe" {
247
- // Check if this package would actually be compiled (same logic as above)
248
- wouldBeCompiled := true
249
-
250
- // If the package was not explicitly requested, check if it has a handwritten equivalent
251
- if !slices.Contains(patternPkgPaths, otherPkg.PkgPath) {
252
- gsSourcePath := "gs/" + otherPkg.PkgPath
253
- _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
254
- if gsErr == nil {
255
- // Package has handwritten equivalent, so it wouldn't be compiled
256
- wouldBeCompiled = false
257
- }
258
- }
259
-
260
- // Skip packages that failed to load
261
- if len(otherPkg.Errors) > 0 {
262
- wouldBeCompiled = false
263
- }
264
-
265
- // Only include packages that would actually be compiled and import unsafe
266
- if wouldBeCompiled {
267
- for importPath := range otherPkg.Imports {
268
- if importPath == "unsafe" {
269
- dependentPackages = append(dependentPackages, otherPkg.PkgPath)
270
- break
271
- }
272
- }
273
- }
274
- }
275
- }
276
-
277
- dependentList := "unknown package"
278
- if len(dependentPackages) > 0 {
279
- dependentList = strings.Join(dependentPackages, ", ")
280
- }
281
-
282
- 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)
283
- }
284
-
285
254
  pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
286
255
  if err != nil {
287
256
  return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
@@ -346,6 +315,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
346
315
  }
347
316
  }
348
317
 
318
+ // Perform package-level analysis for auto-imports
319
+ packageAnalysis := AnalyzePackage(c.pkg)
320
+
349
321
  // Track all compiled files for later generating the index.ts
350
322
  compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
351
323
 
@@ -357,13 +329,35 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
357
329
  return err
358
330
  }
359
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
+
360
355
  c.le.WithField("file", relWdFileName).Debug("compiling file")
361
- if err := c.CompileFile(ctx, fileName, f); err != nil {
356
+ if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
362
357
  return err
363
358
  }
364
359
 
365
360
  // Add the base filename to our list for the index.ts generation
366
- baseFileName := filepath.Base(fileName)
367
361
  // Strip .go extension and add .gs
368
362
  gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
369
363
  compiledFiles = append(compiledFiles, gsFileName)
@@ -378,8 +372,9 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
378
372
  }
379
373
 
380
374
  // generateIndexFile creates an index.ts file in the package output directory
381
- // that re-exports all symbols from the compiled TypeScript files.
382
- // 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.
383
378
  func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
384
379
  indexFilePath := filepath.Join(c.outputPath, "index.ts")
385
380
 
@@ -390,12 +385,128 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
390
385
  }
391
386
  defer indexFile.Close() //nolint:errcheck
392
387
 
393
- // 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
394
451
  for _, fileName := range compiledFiles {
395
- // Create the re-export line: export * from "./file.gs.js"
396
- exportLine := fmt.Sprintf("export * from \"./%s.js\"\n", fileName)
397
- if _, err := indexFile.WriteString(exportLine); err != nil {
398
- 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
+ }
399
510
  }
400
511
  }
401
512
 
@@ -408,7 +519,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
408
519
  // about varRefing, async functions, defer statements).
409
520
  // Then, it creates a `FileCompiler` instance for the file and invokes its
410
521
  // `Compile` method to generate the TypeScript code.
411
- 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 {
412
523
  // Create a new analysis instance for per-file data
413
524
  analysis := NewAnalysis()
414
525
 
@@ -418,7 +529,7 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
418
529
  // Analyze the file before compiling
419
530
  AnalyzeFile(syntax, p.pkg, analysis, cmap)
420
531
 
421
- fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis)
532
+ fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
422
533
  if err != nil {
423
534
  return err
424
535
  }
@@ -430,12 +541,13 @@ func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *
430
541
  // initializes the `TSCodeWriter` for TypeScript code generation, and uses a
431
542
  // `GoToTSCompiler` to translate Go declarations and statements.
432
543
  type FileCompiler struct {
433
- compilerConfig *Config
434
- codeWriter *TSCodeWriter
435
- pkg *packages.Package
436
- ast *ast.File
437
- fullPath string
438
- 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
439
551
  }
440
552
 
441
553
  // NewFileCompiler creates a new `FileCompiler` for a specific Go file.
@@ -448,13 +560,15 @@ func NewFileCompiler(
448
560
  astFile *ast.File,
449
561
  fullPath string,
450
562
  analysis *Analysis,
563
+ packageAnalysis *PackageAnalysis,
451
564
  ) (*FileCompiler, error) {
452
565
  return &FileCompiler{
453
- compilerConfig: compilerConf,
454
- pkg: pkg,
455
- ast: astFile,
456
- fullPath: fullPath,
457
- Analysis: analysis,
566
+ compilerConfig: compilerConf,
567
+ pkg: pkg,
568
+ ast: astFile,
569
+ fullPath: fullPath,
570
+ Analysis: analysis,
571
+ PackageAnalysis: packageAnalysis,
458
572
  }, nil
459
573
  }
460
574
 
@@ -488,7 +602,26 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
488
602
 
489
603
  // Add import for the goscript runtime using namespace import and alias
490
604
  c.codeWriter.WriteLinef("import * as $ from %q;", "@goscript/builtin/builtin.js")
491
- 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
492
625
 
493
626
  if err := goWriter.WriteDecls(f.Decls); err != nil {
494
627
  return fmt.Errorf("failed to write declarations: %w", err)
@@ -503,7 +636,6 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
503
636
  // decisions about code generation (e.g., varRefing, async behavior).
504
637
  type GoToTSCompiler struct {
505
638
  tsw *TSCodeWriter
506
-
507
639
  pkg *packages.Package
508
640
 
509
641
  analysis *Analysis
@@ -141,7 +141,7 @@ func getParentGoModulePath() (string, error) {
141
141
  return strings.TrimSpace(string(output)), nil
142
142
  }
143
143
 
144
- func TestUnsafePackageErrorMessage(t *testing.T) {
144
+ func TestUnsafePackageCompilation(t *testing.T) {
145
145
  // Create a temporary directory for the test output
146
146
  tempDir, err := os.MkdirTemp("", "goscript-test-unsafe")
147
147
  if err != nil {
@@ -154,11 +154,11 @@ func TestUnsafePackageErrorMessage(t *testing.T) {
154
154
  log.SetLevel(logrus.DebugLevel)
155
155
  le := logrus.NewEntry(log)
156
156
 
157
- // Test with AllDependencies=true to ensure we get all packages including unsafe
157
+ // Test with AllDependencies=true and DisableEmitBuiltin=false to ensure handwritten packages are copied
158
158
  config := &compiler.Config{
159
159
  OutputPath: tempDir,
160
160
  AllDependencies: true,
161
- DisableEmitBuiltin: true, // This ensures handwritten packages are skipped
161
+ DisableEmitBuiltin: false, // This ensures handwritten packages are copied to output
162
162
  }
163
163
 
164
164
  comp, err := compiler.NewCompiler(config, le, nil)
@@ -168,32 +168,25 @@ func TestUnsafePackageErrorMessage(t *testing.T) {
168
168
 
169
169
  // Try to compile a package that has dependencies that import unsafe
170
170
  // We'll use "sync/atomic" which imports unsafe but doesn't have a handwritten equivalent
171
- _, err = comp.CompilePackages(context.Background(), "sync/atomic")
172
-
173
- // We expect this to fail with an unsafe package error
174
- if err == nil {
175
- t.Fatalf("Expected compilation to fail due to unsafe package, but it succeeded")
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)
176
175
  }
177
176
 
178
- errorMsg := err.Error()
179
-
180
- // Verify the error message contains the expected text
181
- if !strings.Contains(errorMsg, "cannot compile package 'unsafe'") {
182
- t.Errorf("Error message should mention unsafe package, got: %s", errorMsg)
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)
183
180
  }
184
181
 
185
- // Verify that packages with handwritten equivalents are NOT mentioned in the error
186
- // These packages have handwritten equivalents in gs/ and should not be in the error message
187
- handwrittenPackages := []string{"runtime", "errors", "time", "context", "slices"}
188
-
189
- for _, pkg := range handwrittenPackages {
190
- if strings.Contains(errorMsg, pkg) {
191
- t.Errorf("Error message should not mention handwritten package '%s', but it does. Error: %s", pkg, errorMsg)
192
- }
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)
193
185
  }
194
186
 
195
- // The error message should mention sync/atomic since it would actually be compiled
196
- if !strings.Contains(errorMsg, "sync/atomic") {
197
- t.Errorf("Error message should mention 'sync/atomic' as it would be compiled, got: %s", errorMsg)
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)
198
191
  }
199
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) {