goscript 0.0.22 → 0.0.24

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 (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
@@ -8,8 +8,10 @@ import (
8
8
  "go/types"
9
9
  "os"
10
10
  "path/filepath"
11
+ "slices"
11
12
  "strings"
12
13
 
14
+ gs "github.com/aperturerobotics/goscript"
13
15
  "github.com/sirupsen/logrus"
14
16
  "golang.org/x/tools/go/packages"
15
17
  )
@@ -88,6 +90,12 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
88
90
  return fmt.Errorf("failed to load packages: %w", err)
89
91
  }
90
92
 
93
+ // build a list of packages that patterns matched
94
+ patternPkgPaths := make([]string, 0, len(pkgs))
95
+ for _, pkg := range pkgs {
96
+ patternPkgPaths = append(patternPkgPaths, pkg.PkgPath)
97
+ }
98
+
91
99
  // If AllDependencies is true, we need to collect all dependencies
92
100
  if c.config.AllDependencies {
93
101
  // Create a set to track processed packages by their ID
@@ -140,6 +148,44 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
140
148
 
141
149
  // Compile all packages
142
150
  for _, pkg := range pkgs {
151
+ // Check if the package has a handwritten equivalent
152
+ if !slices.Contains(patternPkgPaths, pkg.PkgPath) {
153
+ gsSourcePath := "gs/" + pkg.PkgPath
154
+ _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
155
+ if gsErr != nil && !os.IsNotExist(gsErr) {
156
+ return gsErr
157
+ }
158
+ if gsErr == nil {
159
+ if c.config.DisableEmitBuiltin {
160
+ c.le.Infof("Skipping compilation for overridden package %s", pkg.PkgPath)
161
+ continue
162
+ } else {
163
+ // If DisableEmitBuiltin is false, we need to copy the handwritten package to the output directory
164
+ c.le.Infof("Copying handwritten package %s to output directory", pkg.PkgPath)
165
+
166
+ // Compute output path for this package
167
+ outputPath := ComputeModulePath(c.config.OutputPath, pkg.PkgPath)
168
+
169
+ // Remove existing directory if it exists
170
+ if err := os.RemoveAll(outputPath); err != nil {
171
+ return fmt.Errorf("failed to remove existing output directory for %s: %w", pkg.PkgPath, err)
172
+ }
173
+
174
+ // Create the output directory
175
+ if err := os.MkdirAll(outputPath, 0o755); err != nil {
176
+ return fmt.Errorf("failed to create output directory for %s: %w", pkg.PkgPath, err)
177
+ }
178
+
179
+ // Copy files from embedded FS to output directory
180
+ if err := c.copyEmbeddedPackage(gsSourcePath, outputPath); err != nil {
181
+ return fmt.Errorf("failed to copy embedded package %s: %w", pkg.PkgPath, err)
182
+ }
183
+
184
+ continue
185
+ }
186
+ }
187
+ }
188
+
143
189
  // Skip packages that failed to load
144
190
  if len(pkg.Errors) > 0 {
145
191
  c.le.WithError(pkg.Errors[0]).Warnf("Skipping package %s due to errors", pkg.PkgPath)
@@ -182,7 +228,7 @@ func NewPackageCompiler(
182
228
  le: le,
183
229
  pkg: pkg,
184
230
  compilerConf: compilerConf,
185
- outputPath: ComputeModulePath(compilerConf.OutputPathRoot, pkg.PkgPath),
231
+ outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
186
232
  }
187
233
 
188
234
  return res, nil
@@ -267,7 +313,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
267
313
  // CompileFile handles the compilation of a single Go source file to TypeScript.
268
314
  // It first performs a pre-compilation analysis of the file using `AnalyzeFile`
269
315
  // to gather information necessary for accurate TypeScript generation (e.g.,
270
- // about boxing, async functions, defer statements).
316
+ // about varRefing, async functions, defer statements).
271
317
  // Then, it creates a `FileCompiler` instance for the file and invokes its
272
318
  // `Compile` method to generate the TypeScript code.
273
319
  func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File) error {
@@ -328,10 +374,10 @@ func NewFileCompiler(
328
374
  // top-level declarations in the Go file.
329
375
  func (c *FileCompiler) Compile(ctx context.Context) error {
330
376
  f := c.ast
331
-
332
377
  pkgPath := c.pkg.PkgPath
378
+
333
379
  outputFilePath := TranslateGoFilePathToTypescriptFilePath(pkgPath, filepath.Base(c.fullPath))
334
- outputFilePathAbs := filepath.Join(c.compilerConfig.OutputPathRoot, outputFilePath)
380
+ outputFilePathAbs := filepath.Join(c.compilerConfig.OutputPath, outputFilePath)
335
381
 
336
382
  if err := os.MkdirAll(filepath.Dir(outputFilePathAbs), 0o755); err != nil {
337
383
  return err
@@ -349,7 +395,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
349
395
  goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis)
350
396
 
351
397
  // Add import for the goscript runtime using namespace import and alias
352
- c.codeWriter.WriteLine("import * as $ from \"@goscript/builtin\";")
398
+ c.codeWriter.WriteLinef("import * as $ from %q;", "@goscript/builtin/builtin.js")
353
399
  c.codeWriter.WriteLine("") // Add a newline after the import
354
400
 
355
401
  if err := goWriter.WriteDecls(f.Decls); err != nil {
@@ -362,13 +408,13 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
362
408
  // GoToTSCompiler is the core component responsible for translating Go AST nodes
363
409
  // and type information into TypeScript code. It uses a `TSCodeWriter` to output
364
410
  // the generated TypeScript and relies on `Analysis` data to make informed
365
- // decisions about code generation (e.g., boxing, async behavior).
411
+ // decisions about code generation (e.g., varRefing, async behavior).
366
412
  type GoToTSCompiler struct {
367
413
  tsw *TSCodeWriter
368
414
 
369
415
  pkg *packages.Package
370
416
 
371
- analysis *Analysis // Holds analysis information for code generation decisions
417
+ analysis *Analysis
372
418
  }
373
419
 
374
420
  // It initializes the compiler with a `TSCodeWriter` for output,
@@ -388,14 +434,12 @@ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analy
388
434
  // variable, function name) into its TypeScript equivalent.
389
435
  // - If the identifier is `nil`, it writes `null`.
390
436
  // - Otherwise, it writes the identifier's name.
391
- // - If `accessBoxedValue` is true and the analysis (`c.analysis.NeedsBoxedAccess`)
392
- // indicates that this identifier refers to a variable whose value is stored
393
- // in a box (due to its address being taken or other boxing requirements),
394
- // it appends `.value` to access the actual value from the box.
437
+ // - If `accessVarRefedValue` is true and the analysis (`c.analysis.NeedsVarRefAccess`)
438
+ // indicates the variable is variable referenced, `.value` is appended to access the contained value.
395
439
  //
396
440
  // This function relies on `go/types` (`TypesInfo.Uses` or `Defs`) to resolve
397
- // the identifier and the `Analysis` data to determine boxing needs.
398
- func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessBoxedValue bool) {
441
+ // the identifier and the `Analysis` data to determine varRefing needs.
442
+ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
399
443
  if exp.Name == "nil" {
400
444
  c.tsw.WriteLiterally("null")
401
445
  return
@@ -412,7 +456,7 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessBoxedValue bool) {
412
456
  c.tsw.WriteLiterally(exp.Name)
413
457
 
414
458
  // Determine if we need to access .value based on analysis data
415
- if obj != nil && accessBoxedValue && c.analysis.NeedsBoxedAccess(obj) {
459
+ if obj != nil && accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
416
460
  c.tsw.WriteLiterally("!.value")
417
461
  }
418
462
  }
@@ -437,28 +481,31 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
437
481
  c.tsw.WriteLine("")
438
482
  } else {
439
483
  // Case with expressions
440
- c.tsw.WriteLiterally("case ")
441
- for i, expr := range exp.List {
442
- if i > 0 {
443
- c.tsw.WriteLiterally(", ") // Although Go doesn't support multiple expressions per case like this,
444
- } // TypeScript does, so we'll write it this way for now.
445
- if err := c.WriteValueExpr(expr); err != nil {
484
+ // For Go's `case expr1, expr2:`, we translate to:
485
+ // case expr1:
486
+ // case expr2:
487
+ // ... body ...
488
+ // break
489
+ for _, caseExpr := range exp.List {
490
+ c.tsw.WriteLiterally("case ")
491
+ if err := c.WriteValueExpr(caseExpr); err != nil {
446
492
  return fmt.Errorf("failed to write case clause expression: %w", err)
447
493
  }
494
+ c.tsw.WriteLiterally(":")
495
+ c.tsw.WriteLine("")
448
496
  }
449
- c.tsw.WriteLiterally(":")
450
- c.tsw.WriteLine("")
451
497
  }
452
498
 
499
+ // The body is written once, after all case labels for this clause.
500
+ // Indentation for the body starts here.
453
501
  c.tsw.Indent(1)
454
- // Write the body of the case clause
455
502
  for _, stmt := range exp.Body {
456
503
  if err := c.WriteStmt(stmt); err != nil {
457
504
  return fmt.Errorf("failed to write statement in case clause body: %w", err)
458
505
  }
459
506
  }
460
- // Add break statement (Go's switch has implicit breaks)
461
- c.tsw.WriteLine("break") // Remove semicolon
507
+ // Add break statement (Go's switch has implicit breaks, TS needs explicit break)
508
+ c.tsw.WriteLine("break")
462
509
  c.tsw.Indent(-1)
463
510
  return nil
464
511
  }
@@ -527,11 +574,11 @@ func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *as
527
574
  } else {
528
575
  // If both are blank, just await the call and return
529
576
  if okIsBlank {
530
- c.tsw.WriteLiterally("await ")
577
+ c.tsw.WriteLiterally("await $.chanRecvWithOk(")
531
578
  if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
532
579
  return fmt.Errorf("failed to write channel expression in receive: %w", err)
533
580
  }
534
- c.tsw.WriteLiterally(".receiveWithOk()")
581
+ c.tsw.WriteLiterally(")")
535
582
  c.tsw.WriteLine("")
536
583
  return nil // Nothing to assign
537
584
  }
@@ -549,11 +596,11 @@ func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *as
549
596
  // Write the destructuring assignment/declaration
550
597
  c.tsw.WriteLiterally(keyword) // "const " or ""
551
598
  c.tsw.WriteLiterally(destructuringPattern)
552
- c.tsw.WriteLiterally(" = await ")
599
+ c.tsw.WriteLiterally(" = await $.chanRecvWithOk(")
553
600
  if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
554
601
  return fmt.Errorf("failed to write channel expression in receive: %w", err)
555
602
  }
556
- c.tsw.WriteLiterally(".receiveWithOk()")
603
+ c.tsw.WriteLiterally(")")
557
604
  c.tsw.WriteLine("")
558
605
 
559
606
  return nil
@@ -604,3 +651,44 @@ func (c *GoToTSCompiler) WriteDoc(doc *ast.CommentGroup) {
604
651
  }
605
652
  }
606
653
  }
654
+
655
+ // copyEmbeddedPackage recursively copies files from an embedded FS path to a filesystem directory.
656
+ // It handles both regular files and directories.
657
+ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) error {
658
+ // List the entries in the embedded path
659
+ entries, err := gs.GsOverrides.ReadDir(embeddedPath)
660
+ if err != nil {
661
+ return fmt.Errorf("failed to read embedded directory %s: %w", embeddedPath, err)
662
+ }
663
+
664
+ // Process each entry
665
+ for _, entry := range entries {
666
+ entryPath := filepath.Join(embeddedPath, entry.Name())
667
+ outputEntryPath := filepath.Join(outputPath, entry.Name())
668
+
669
+ if entry.IsDir() {
670
+ // Create the output directory
671
+ if err := os.MkdirAll(outputEntryPath, 0o755); err != nil {
672
+ return fmt.Errorf("failed to create output directory %s: %w", outputEntryPath, err)
673
+ }
674
+
675
+ // Recursively copy the directory contents
676
+ if err := c.copyEmbeddedPackage(entryPath, outputEntryPath); err != nil {
677
+ return err
678
+ }
679
+ } else {
680
+ // Read the file content from the embedded FS
681
+ content, err := gs.GsOverrides.ReadFile(entryPath)
682
+ if err != nil {
683
+ return fmt.Errorf("failed to read embedded file %s: %w", entryPath, err)
684
+ }
685
+
686
+ // Write the content to the output file
687
+ if err := os.WriteFile(outputEntryPath, content, 0o644); err != nil {
688
+ return fmt.Errorf("failed to write file %s: %w", outputEntryPath, err)
689
+ }
690
+ }
691
+ }
692
+
693
+ return nil
694
+ }
@@ -6,6 +6,7 @@ import (
6
6
  "path/filepath"
7
7
  "runtime"
8
8
  "slices"
9
+ "strings"
9
10
  "sync"
10
11
  "sync/atomic"
11
12
  "testing"
@@ -38,7 +39,7 @@ func TestCompliance(t *testing.T) {
38
39
  testPath := filepath.Join(testsDir, dir.Name())
39
40
  goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
40
41
  if err != nil || len(goFiles) == 0 {
41
- t.Errorf("no .go files found in %s", testPath)
42
+ // t.Errorf("no .go files found in %s", testPath)
42
43
  continue
43
44
  }
44
45
  testPaths = append(testPaths, testPath)
@@ -97,15 +98,42 @@ func TestCompliance(t *testing.T) {
97
98
  t.SkipNow()
98
99
  }
99
100
 
100
- t.Log("running: npm run typecheck")
101
- cmd := exec.Command("npm", "run", "typecheck")
102
- cmd.Dir = workspaceDir // Run in the workspace directory
103
- cmd.Stdout = os.Stderr
104
- cmd.Stderr = os.Stderr
101
+ // Get parent module path for the global typecheck
102
+ parentModPath, err := getParentGoModulePath()
103
+ if err != nil {
104
+ t.Fatalf("Failed to determine parent Go module path: %v", err)
105
+ }
106
+
107
+ // Create global typecheck tsconfig
108
+ tsconfigPath := compliance.WriteGlobalTypeCheckConfig(t, parentModPath, workspaceDir)
109
+
110
+ // Run TypeScript type checking
111
+ typecheckDir := filepath.Dir(tsconfigPath)
112
+ cmd := exec.Command("tsc", "--project", filepath.Base(tsconfigPath))
113
+ cmd.Dir = typecheckDir
114
+
115
+ // Set up PATH to include node_modules/.bin
116
+ nodeBinDir := filepath.Join(workspaceDir, "node_modules", ".bin")
117
+ currentPath := os.Getenv("PATH")
118
+ newPath := nodeBinDir + string(os.PathListSeparator) + currentPath
119
+ cmd.Env = append(os.Environ(), "PATH="+newPath)
105
120
 
106
- err = cmd.Run()
121
+ output, err := cmd.CombinedOutput()
107
122
  if err != nil {
108
- t.Errorf("npm run typecheck failed: %v", err)
123
+ t.Errorf("Global TypeScript type checking failed: %v\noutput:\n%s", err, string(output))
124
+ } else {
125
+ t.Logf("Global TypeScript type checking passed")
109
126
  }
110
127
  })
111
128
  }
129
+
130
+ // getParentGoModulePath is a helper function to get the parent Go module path
131
+ // This is similar to the one in compliance.go but simplified for use in tests
132
+ func getParentGoModulePath() (string, error) {
133
+ cmd := exec.Command("go", "list", "-m")
134
+ output, err := cmd.Output()
135
+ if err != nil {
136
+ return "", err
137
+ }
138
+ return strings.TrimSpace(string(output)), nil
139
+ }
@@ -13,26 +13,28 @@ import (
13
13
  //
14
14
  // It handles several types of composite literals:
15
15
  // - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
16
- // Values are processed by `writeBoxedValue`.
16
+ // Values are processed by `WriteVarRefedValue`.
17
17
  // - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
18
- // Translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
18
+ // - For `[]byte{...}`, translated to `new Uint8Array([...])`.
19
+ // - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
19
20
  // It handles both keyed and unkeyed elements, infers length if necessary,
20
21
  // and uses zero values for uninitialized array elements.
21
22
  // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
22
- // Element values are processed by `writeBoxedValue`.
23
+ // Element values are processed by `WriteVarRefedValue`.
23
24
  // - Struct literals:
24
25
  // - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
25
26
  // `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
26
27
  // - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
27
28
  // object literals `{ F: v_ts, ... }`.
28
29
  // It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
29
- // structs or arrays). Field values are processed by `writeBoxedValue`.
30
+ // structs or arrays). Field values are processed by `WriteVarRefedValue`.
30
31
  // Embedded struct fields are initialized, and explicit initializers for embedded
31
32
  // structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
32
- // The function uses `c.analysis` to determine correct value access (e.g., `.value` for boxed fields).
33
+ // The function uses `c.analysis` to determine correct value access (e.g., `.value` for var-refed fields).
33
34
  func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
34
35
  // Get the type of the composite literal
35
36
  litType := c.pkg.TypesInfo.TypeOf(exp)
37
+
36
38
  if exp.Type != nil {
37
39
  // Handle map literals: map[K]V{k1: v1, k2: v2}
38
40
  if _, isMapType := exp.Type.(*ast.MapType); isMapType {
@@ -46,11 +48,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
46
48
 
47
49
  if kv, ok := elm.(*ast.KeyValueExpr); ok {
48
50
  c.tsw.WriteLiterally("[")
49
- if err := c.WriteBoxedValue(kv.Key); err != nil {
51
+ if err := c.WriteVarRefedValue(kv.Key); err != nil {
50
52
  return fmt.Errorf("failed to write map literal key: %w", err)
51
53
  }
52
54
  c.tsw.WriteLiterally(", ")
53
- if err := c.WriteBoxedValue(kv.Value); err != nil {
55
+ if err := c.WriteVarRefedValue(kv.Value); err != nil {
54
56
  return fmt.Errorf("failed to write map literal value: %w", err)
55
57
  }
56
58
  c.tsw.WriteLiterally("]")
@@ -73,14 +75,29 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
73
75
  // We'll handle this with depth parameter to arrayToSlice
74
76
  }
75
77
 
76
- c.tsw.WriteLiterally("$.arrayToSlice")
78
+ // Check if it's a []byte literal
79
+ isByteSliceLiteral := false
80
+ if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
81
+ if sliceT, ok := typInfo.Underlying().(*types.Slice); ok {
82
+ if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
83
+ isByteSliceLiteral = true
84
+ }
85
+ }
86
+ }
87
+
88
+ if isByteSliceLiteral {
89
+ c.tsw.WriteLiterally("new Uint8Array")
90
+ } else {
91
+ c.tsw.WriteLiterally("$.arrayToSlice")
77
92
 
78
- // write the type annotation
79
- c.tsw.WriteLiterally("<")
80
- // Write the element type using the existing function
81
- c.WriteTypeExpr(arrType.Elt)
82
- c.tsw.WriteLiterally(">")
93
+ // write the type annotation
94
+ c.tsw.WriteLiterally("<")
95
+ // Write the element type using the existing function
96
+ c.WriteTypeExpr(arrType.Elt)
97
+ c.tsw.WriteLiterally(">")
98
+ }
83
99
 
100
+ // opening
84
101
  c.tsw.WriteLiterally("([")
85
102
 
86
103
  // Use type info to get array length and element type
@@ -123,7 +140,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
123
140
  hasKeyedElements = true
124
141
  } else {
125
142
  c.tsw.WriteCommentInline("unhandled keyed array literal key type")
126
- if err := c.WriteBoxedValue(elm); err != nil {
143
+ if err := c.WriteVarRefedValue(elm); err != nil {
127
144
  return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
128
145
  }
129
146
  }
@@ -151,7 +168,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
151
168
  c.tsw.WriteLiterally(", ")
152
169
  }
153
170
  if elm, ok := elements[i]; ok && elm != nil {
154
- if err := c.WriteBoxedValue(elm); err != nil {
171
+ if err := c.WriteVarRefedValue(elm); err != nil {
155
172
  return fmt.Errorf("failed to write array literal element: %w", err)
156
173
  }
157
174
  } else {
@@ -166,7 +183,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
166
183
  c.tsw.WriteLiterally("]")
167
184
 
168
185
  // If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
169
- if isMultiDimensional {
186
+ if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
170
187
  c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
171
188
  }
172
189
 
@@ -271,9 +288,8 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
271
288
  }
272
289
 
273
290
  // Handle the case where an anonymous struct has values without keys
274
- // Handle the case where an anonymous struct has values without keys.
275
291
  // This block processes non-key-value elements and associates them with struct fields.
276
- if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 && structType != nil {
292
+ if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
277
293
  // Check if any elements in the composite literal are not key-value pairs.
278
294
  hasNonKeyValueElts := false
279
295
  for _, elt := range exp.Elts {
@@ -325,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
325
341
  }
326
342
  c.tsw.WriteLiterally(keyName)
327
343
  c.tsw.WriteLiterally(": ")
328
- if err := c.WriteBoxedValue(directFields[keyName]); err != nil {
344
+ if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
329
345
  return err
330
346
  }
331
347
  firstFieldWritten = true
@@ -353,14 +369,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
353
369
  if i > 0 {
354
370
  c.tsw.WriteLiterally(", ")
355
371
  }
356
- if err := c.WriteBoxedValue(elem); err != nil {
372
+ if err := c.WriteVarRefedValue(elem); err != nil {
357
373
  return err
358
374
  }
359
375
  }
360
376
  c.tsw.WriteLiterally("}")
361
377
  } else {
362
378
  // Not a composite literal, write it normally
363
- if err := c.WriteBoxedValue(explicitEmbedded[embeddedName]); err != nil {
379
+ if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
364
380
  return err
365
381
  }
366
382
  }
@@ -399,7 +415,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
399
415
  }
400
416
  c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
401
417
  c.tsw.WriteLiterally(": ")
402
- if err := c.WriteBoxedValue(fieldsMap[keyName]); err != nil {
418
+ if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
403
419
  return err
404
420
  }
405
421
  }
@@ -421,7 +437,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
421
437
  if i != 0 {
422
438
  c.tsw.WriteLiterally(", ")
423
439
  }
424
- if err := c.WriteBoxedValue(elm); err != nil {
440
+ if err := c.WriteVarRefedValue(elm); err != nil {
425
441
  return fmt.Errorf("failed to write literal field: %w", err)
426
442
  }
427
443
  }
@@ -433,60 +449,124 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
433
449
 
434
450
  // Untyped composite literal. Let's use type information to determine what it is.
435
451
  // First try to get the type information for the expression
436
- isObject := false
437
452
  if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
438
453
  underlying := tv.Type.Underlying()
439
454
  switch underlying.(type) {
440
455
  case *types.Map, *types.Struct:
441
- isObject = true
456
+ // Handle struct directly with the struct literal logic
457
+ if structType, ok := underlying.(*types.Struct); ok {
458
+ return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
459
+ }
460
+ // Map case would be handled here
461
+ return fmt.Errorf("untyped map composite literals not yet supported")
442
462
  case *types.Array, *types.Slice:
443
- isObject = false
463
+ // Handle array/slice
464
+ return c.writeUntypedArrayLiteral(exp)
465
+ case *types.Pointer:
466
+ // Handle pointer to composite literal
467
+ ptrType := underlying.(*types.Pointer)
468
+ elemType := ptrType.Elem().Underlying()
469
+ switch elemType.(type) {
470
+ case *types.Struct:
471
+ // This is an anonymous struct literal with inferred pointer type
472
+ // Just create the struct object directly - no var-refing needed
473
+ // Anonymous literals are not variables, so they don't get var-refed
474
+ structType := elemType.(*types.Struct)
475
+ return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
476
+ default:
477
+ return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
478
+ }
444
479
  default:
445
480
  return fmt.Errorf("unhandled composite literal type: %T", underlying)
446
481
  }
447
482
  } else {
448
483
  return fmt.Errorf("could not determine composite literal type from type information")
449
484
  }
485
+ }
450
486
 
451
- if isObject {
452
- c.tsw.WriteLiterally("{ ")
453
- } else {
454
- c.tsw.WriteLiterally("[ ")
455
- }
456
-
487
+ // writeUntypedArrayLiteral handles untyped composite literals that are arrays/slices
488
+ func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
489
+ c.tsw.WriteLiterally("[ ")
457
490
  for i, elm := range exp.Elts {
458
491
  if i != 0 {
459
492
  c.tsw.WriteLiterally(", ")
460
493
  }
461
- if err := c.WriteBoxedValue(elm); err != nil {
462
- return fmt.Errorf("failed to write untyped composite literal element: %w", err)
494
+ if err := c.WriteVarRefedValue(elm); err != nil {
495
+ return fmt.Errorf("failed to write untyped array literal element: %w", err)
463
496
  }
464
497
  }
465
- if isObject {
466
- c.tsw.WriteLiterally(" }")
467
- } else {
468
- c.tsw.WriteLiterally(" ]")
498
+ c.tsw.WriteLiterally(" ]")
499
+ return nil
500
+ }
501
+
502
+ // writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
503
+ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, structType *types.Struct) error {
504
+ // Create field mapping like the typed struct case
505
+ directFields := make(map[string]ast.Expr)
506
+
507
+ // Handle elements that are key-value pairs
508
+ for _, elt := range exp.Elts {
509
+ if kv, ok := elt.(*ast.KeyValueExpr); ok {
510
+ if keyIdent, ok := kv.Key.(*ast.Ident); ok {
511
+ directFields[keyIdent.Name] = kv.Value
512
+ }
513
+ }
514
+ }
515
+
516
+ // Handle elements that are positional (no key specified)
517
+ if len(directFields) == 0 {
518
+ // If no key-value pairs, try to match positional values to struct fields
519
+ for i, elt := range exp.Elts {
520
+ if _, isKV := elt.(*ast.KeyValueExpr); !isKV && i < structType.NumFields() {
521
+ field := structType.Field(i)
522
+ directFields[field.Name()] = elt
523
+ }
524
+ }
469
525
  }
526
+
527
+ // Write the object literal (always anonymous for untyped)
528
+ c.tsw.WriteLiterally("{")
529
+
530
+ firstFieldWritten := false
531
+ // Write fields in order
532
+ directKeys := make([]string, 0, len(directFields))
533
+ for k := range directFields {
534
+ directKeys = append(directKeys, k)
535
+ }
536
+ slices.Sort(directKeys)
537
+ for _, keyName := range directKeys {
538
+ if firstFieldWritten {
539
+ c.tsw.WriteLiterally(", ")
540
+ }
541
+ c.tsw.WriteLiterally(keyName)
542
+ c.tsw.WriteLiterally(": ")
543
+ if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
544
+ return err
545
+ }
546
+ firstFieldWritten = true
547
+ }
548
+
549
+ c.tsw.WriteLiterally("}")
470
550
  return nil
471
551
  }
472
552
 
473
- // WriteBoxedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
553
+ // WriteVarRefedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
474
554
  // specifically for use as a value within a composite literal (e.g., struct fields,
475
555
  // map keys/values, or array/slice elements). Its primary goal is to ensure that the
476
- // actual, unboxed value of the expression is used.
556
+ // actual un-refed value of the expression is used.
477
557
  //
478
558
  // How it works:
479
559
  // - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
480
560
  // the `accessValue` flag to `true`. This ensures that if `ident` refers to a
481
- // GoScript boxed variable, the generated TypeScript accesses its underlying `.value`
561
+ // GoScript var-refed variable, the generated TypeScript accesses its underlying `.value`
482
562
  // (e.g., `myVar.value`).
483
563
  // - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
484
564
  // `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
485
- // accessing fields or methods, including any required unboxing if the field
486
- // itself or the object it's accessed on is boxed (e.g., `obj.value.field` or
565
+ // accessing fields or methods, including any required un-var-refing if the field
566
+ // itself or the object it's accessed on is var-refed (e.g., `obj.value.field` or
487
567
  // `obj.field.value`).
488
568
  // - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
489
- // This function handles pointer dereferencing, which in GoScript's boxing model
569
+ // This function handles pointer dereferencing, which in GoScript's var-refing model
490
570
  // often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
491
571
  // - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
492
572
  // `c.WriteBasicLit(e)` for direct translation.
@@ -496,25 +576,25 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
496
576
  //
497
577
  // Necessity and Distinction from `WriteValueExpr`:
498
578
  // While `WriteValueExpr` is a general-purpose function for translating Go expressions
499
- // and also unboxes identifiers (by calling `WriteIdent` with `accessValue: true`),
500
- // `WriteBoxedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
579
+ // and also un-var-refes identifiers (by calling `WriteIdent` with `accessValue: true`),
580
+ // `WriteVarRefedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
501
581
  // 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
502
- // literal, the *unboxed value* is mandatory.
582
+ // literal, the *un-var-refed value* is mandatory.
503
583
  // 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
504
584
  // the correct values for initialization, insulating it from potential changes in
505
- // the default behavior of `WriteValueExpr` regarding unboxing.
585
+ // the default behavior of `WriteValueExpr` regarding un-var-refing.
506
586
  // 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
507
587
  // calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
508
588
  // `WriteValueExpr` for its elements, it could lead to unintended recursion or
509
- // behavior if an element itself was another composite literal. `WriteBoxedValue`
589
+ // behavior if an element itself was another composite literal. `WriteVarRefedValue`
510
590
  // acts as a specific intermediary for the *elements*.
511
591
  //
512
- // In summary, `WriteBoxedValue` is a specialized dispatcher used by `WriteCompositeLit`
592
+ // In summary, `WriteVarRefedValue` is a specialized dispatcher used by `WriteCompositeLit`
513
593
  // to guarantee that all parts of a Go composite literal are initialized with their
514
- // proper, unboxed TypeScript values.
515
- func (c *GoToTSCompiler) WriteBoxedValue(expr ast.Expr) error {
594
+ // proper, unrefed TypeScript values.
595
+ func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
516
596
  if expr == nil {
517
- return fmt.Errorf("nil expression passed to writeBoxedValue")
597
+ return fmt.Errorf("nil expression passed to write var refed value")
518
598
  }
519
599
 
520
600
  // Handle different expression types