goscript 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +2 -2
  2. package/cmd/goscript/cmd_compile.go +18 -2
  3. package/compiler/analysis.go +74 -132
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +37 -43
  6. package/compiler/builtin_test.go +90 -0
  7. package/compiler/compiler.go +307 -22
  8. package/compiler/composite-lit.go +108 -43
  9. package/compiler/config.go +7 -3
  10. package/compiler/config_test.go +6 -33
  11. package/compiler/decl.go +7 -1
  12. package/compiler/expr-call.go +212 -2
  13. package/compiler/expr-selector.go +66 -41
  14. package/compiler/expr-star.go +57 -65
  15. package/compiler/expr-type.go +1 -1
  16. package/compiler/expr-value.go +1 -1
  17. package/compiler/expr.go +125 -20
  18. package/compiler/field.go +4 -4
  19. package/compiler/primitive.go +11 -10
  20. package/compiler/spec-struct.go +3 -3
  21. package/compiler/spec-value.go +75 -29
  22. package/compiler/spec.go +9 -3
  23. package/compiler/stmt-assign.go +36 -2
  24. package/compiler/stmt-for.go +11 -0
  25. package/compiler/stmt-range.go +314 -1
  26. package/compiler/stmt.go +52 -0
  27. package/compiler/type.go +83 -15
  28. package/dist/gs/builtin/builtin.d.ts +9 -0
  29. package/dist/gs/builtin/builtin.js +46 -0
  30. package/dist/gs/builtin/builtin.js.map +1 -0
  31. package/dist/gs/builtin/channel.d.ts +193 -0
  32. package/dist/gs/builtin/channel.js +471 -0
  33. package/dist/gs/builtin/channel.js.map +1 -0
  34. package/dist/gs/builtin/defer.d.ts +38 -0
  35. package/dist/gs/builtin/defer.js +54 -0
  36. package/dist/gs/builtin/defer.js.map +1 -0
  37. package/dist/gs/builtin/index.d.ts +1 -0
  38. package/dist/gs/builtin/index.js +2 -0
  39. package/dist/gs/builtin/index.js.map +1 -0
  40. package/dist/gs/builtin/io.d.ts +16 -0
  41. package/dist/gs/builtin/io.js +15 -0
  42. package/dist/gs/builtin/io.js.map +1 -0
  43. package/dist/gs/builtin/map.d.ts +33 -0
  44. package/dist/gs/builtin/map.js +44 -0
  45. package/dist/gs/builtin/map.js.map +1 -0
  46. package/dist/gs/builtin/slice.d.ts +173 -0
  47. package/dist/gs/builtin/slice.js +799 -0
  48. package/dist/gs/builtin/slice.js.map +1 -0
  49. package/dist/gs/builtin/type.d.ts +203 -0
  50. package/dist/gs/builtin/type.js +744 -0
  51. package/dist/gs/builtin/type.js.map +1 -0
  52. package/dist/gs/builtin/varRef.d.ts +14 -0
  53. package/dist/gs/builtin/varRef.js +14 -0
  54. package/dist/gs/builtin/varRef.js.map +1 -0
  55. package/dist/gs/cmp/index.d.ts +4 -0
  56. package/dist/gs/cmp/index.js +27 -0
  57. package/dist/gs/cmp/index.js.map +1 -0
  58. package/dist/gs/context/context.d.ts +26 -0
  59. package/dist/gs/context/context.js +305 -0
  60. package/dist/gs/context/context.js.map +1 -0
  61. package/dist/gs/context/index.d.ts +1 -0
  62. package/dist/gs/context/index.js +2 -0
  63. package/dist/gs/context/index.js.map +1 -0
  64. package/dist/gs/internal/goarch/index.d.ts +6 -0
  65. package/dist/gs/internal/goarch/index.js +14 -0
  66. package/dist/gs/internal/goarch/index.js.map +1 -0
  67. package/dist/gs/iter/index.d.ts +1 -0
  68. package/dist/gs/iter/index.js +2 -0
  69. package/dist/gs/iter/index.js.map +1 -0
  70. package/dist/gs/iter/iter.d.ts +4 -0
  71. package/dist/gs/iter/iter.js +91 -0
  72. package/dist/gs/iter/iter.js.map +1 -0
  73. package/dist/gs/math/bits/index.d.ts +47 -0
  74. package/dist/gs/math/bits/index.js +298 -0
  75. package/dist/gs/math/bits/index.js.map +1 -0
  76. package/dist/gs/runtime/index.d.ts +1 -0
  77. package/dist/gs/runtime/index.js +2 -0
  78. package/dist/gs/runtime/index.js.map +1 -0
  79. package/dist/gs/runtime/runtime.d.ts +41 -0
  80. package/dist/gs/runtime/runtime.js +158 -0
  81. package/dist/gs/runtime/runtime.js.map +1 -0
  82. package/dist/gs/slices/index.d.ts +1 -0
  83. package/dist/gs/slices/index.js +2 -0
  84. package/dist/gs/slices/index.js.map +1 -0
  85. package/dist/gs/slices/slices.d.ts +8 -0
  86. package/dist/gs/slices/slices.js +20 -0
  87. package/dist/gs/slices/slices.js.map +1 -0
  88. package/dist/gs/time/index.d.ts +1 -0
  89. package/dist/gs/time/index.js +2 -0
  90. package/dist/gs/time/index.js.map +1 -0
  91. package/dist/gs/time/time.d.ts +57 -0
  92. package/dist/gs/time/time.js +208 -0
  93. package/dist/gs/time/time.js.map +1 -0
  94. package/package.json +3 -2
@@ -13,27 +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
18
  // - For `[]byte{...}`, translated to `new Uint8Array([...])`.
19
19
  // - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
20
20
  // It handles both keyed and unkeyed elements, infers length if necessary,
21
21
  // and uses zero values for uninitialized array elements.
22
22
  // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
23
- // Element values are processed by `writeBoxedValue`.
23
+ // Element values are processed by `WriteVarRefedValue`.
24
24
  // - Struct literals:
25
25
  // - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
26
26
  // `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
27
27
  // - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
28
28
  // object literals `{ F: v_ts, ... }`.
29
29
  // It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
30
- // structs or arrays). Field values are processed by `writeBoxedValue`.
30
+ // structs or arrays). Field values are processed by `WriteVarRefedValue`.
31
31
  // Embedded struct fields are initialized, and explicit initializers for embedded
32
32
  // structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
33
- // 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).
34
34
  func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
35
35
  // Get the type of the composite literal
36
36
  litType := c.pkg.TypesInfo.TypeOf(exp)
37
+
37
38
  if exp.Type != nil {
38
39
  // Handle map literals: map[K]V{k1: v1, k2: v2}
39
40
  if _, isMapType := exp.Type.(*ast.MapType); isMapType {
@@ -47,11 +48,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
47
48
 
48
49
  if kv, ok := elm.(*ast.KeyValueExpr); ok {
49
50
  c.tsw.WriteLiterally("[")
50
- if err := c.WriteBoxedValue(kv.Key); err != nil {
51
+ if err := c.WriteVarRefedValue(kv.Key); err != nil {
51
52
  return fmt.Errorf("failed to write map literal key: %w", err)
52
53
  }
53
54
  c.tsw.WriteLiterally(", ")
54
- if err := c.WriteBoxedValue(kv.Value); err != nil {
55
+ if err := c.WriteVarRefedValue(kv.Value); err != nil {
55
56
  return fmt.Errorf("failed to write map literal value: %w", err)
56
57
  }
57
58
  c.tsw.WriteLiterally("]")
@@ -139,7 +140,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
139
140
  hasKeyedElements = true
140
141
  } else {
141
142
  c.tsw.WriteCommentInline("unhandled keyed array literal key type")
142
- if err := c.WriteBoxedValue(elm); err != nil {
143
+ if err := c.WriteVarRefedValue(elm); err != nil {
143
144
  return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
144
145
  }
145
146
  }
@@ -167,7 +168,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
167
168
  c.tsw.WriteLiterally(", ")
168
169
  }
169
170
  if elm, ok := elements[i]; ok && elm != nil {
170
- if err := c.WriteBoxedValue(elm); err != nil {
171
+ if err := c.WriteVarRefedValue(elm); err != nil {
171
172
  return fmt.Errorf("failed to write array literal element: %w", err)
172
173
  }
173
174
  } else {
@@ -340,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
340
341
  }
341
342
  c.tsw.WriteLiterally(keyName)
342
343
  c.tsw.WriteLiterally(": ")
343
- if err := c.WriteBoxedValue(directFields[keyName]); err != nil {
344
+ if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
344
345
  return err
345
346
  }
346
347
  firstFieldWritten = true
@@ -368,14 +369,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
368
369
  if i > 0 {
369
370
  c.tsw.WriteLiterally(", ")
370
371
  }
371
- if err := c.WriteBoxedValue(elem); err != nil {
372
+ if err := c.WriteVarRefedValue(elem); err != nil {
372
373
  return err
373
374
  }
374
375
  }
375
376
  c.tsw.WriteLiterally("}")
376
377
  } else {
377
378
  // Not a composite literal, write it normally
378
- if err := c.WriteBoxedValue(explicitEmbedded[embeddedName]); err != nil {
379
+ if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
379
380
  return err
380
381
  }
381
382
  }
@@ -414,7 +415,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
414
415
  }
415
416
  c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
416
417
  c.tsw.WriteLiterally(": ")
417
- if err := c.WriteBoxedValue(fieldsMap[keyName]); err != nil {
418
+ if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
418
419
  return err
419
420
  }
420
421
  }
@@ -436,7 +437,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
436
437
  if i != 0 {
437
438
  c.tsw.WriteLiterally(", ")
438
439
  }
439
- if err := c.WriteBoxedValue(elm); err != nil {
440
+ if err := c.WriteVarRefedValue(elm); err != nil {
440
441
  return fmt.Errorf("failed to write literal field: %w", err)
441
442
  }
442
443
  }
@@ -448,60 +449,124 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
448
449
 
449
450
  // Untyped composite literal. Let's use type information to determine what it is.
450
451
  // First try to get the type information for the expression
451
- isObject := false
452
452
  if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
453
453
  underlying := tv.Type.Underlying()
454
454
  switch underlying.(type) {
455
455
  case *types.Map, *types.Struct:
456
- 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")
457
462
  case *types.Array, *types.Slice:
458
- 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
+ }
459
479
  default:
460
480
  return fmt.Errorf("unhandled composite literal type: %T", underlying)
461
481
  }
462
482
  } else {
463
483
  return fmt.Errorf("could not determine composite literal type from type information")
464
484
  }
485
+ }
465
486
 
466
- if isObject {
467
- c.tsw.WriteLiterally("{ ")
468
- } else {
469
- c.tsw.WriteLiterally("[ ")
470
- }
471
-
487
+ // writeUntypedArrayLiteral handles untyped composite literals that are arrays/slices
488
+ func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
489
+ c.tsw.WriteLiterally("[ ")
472
490
  for i, elm := range exp.Elts {
473
491
  if i != 0 {
474
492
  c.tsw.WriteLiterally(", ")
475
493
  }
476
- if err := c.WriteBoxedValue(elm); err != nil {
477
- 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)
478
496
  }
479
497
  }
480
- if isObject {
481
- c.tsw.WriteLiterally(" }")
482
- } else {
483
- 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
+ }
484
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
+ }
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("}")
485
550
  return nil
486
551
  }
487
552
 
488
- // WriteBoxedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
553
+ // WriteVarRefedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
489
554
  // specifically for use as a value within a composite literal (e.g., struct fields,
490
555
  // map keys/values, or array/slice elements). Its primary goal is to ensure that the
491
- // actual, unboxed value of the expression is used.
556
+ // actual un-refed value of the expression is used.
492
557
  //
493
558
  // How it works:
494
559
  // - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
495
560
  // the `accessValue` flag to `true`. This ensures that if `ident` refers to a
496
- // GoScript boxed variable, the generated TypeScript accesses its underlying `.value`
561
+ // GoScript var-refed variable, the generated TypeScript accesses its underlying `.value`
497
562
  // (e.g., `myVar.value`).
498
563
  // - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
499
564
  // `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
500
- // accessing fields or methods, including any required unboxing if the field
501
- // 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
502
567
  // `obj.field.value`).
503
568
  // - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
504
- // This function handles pointer dereferencing, which in GoScript's boxing model
569
+ // This function handles pointer dereferencing, which in GoScript's var-refing model
505
570
  // often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
506
571
  // - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
507
572
  // `c.WriteBasicLit(e)` for direct translation.
@@ -511,25 +576,25 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
511
576
  //
512
577
  // Necessity and Distinction from `WriteValueExpr`:
513
578
  // While `WriteValueExpr` is a general-purpose function for translating Go expressions
514
- // and also unboxes identifiers (by calling `WriteIdent` with `accessValue: true`),
515
- // `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`:
516
581
  // 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
517
- // literal, the *unboxed value* is mandatory.
582
+ // literal, the *un-var-refed value* is mandatory.
518
583
  // 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
519
584
  // the correct values for initialization, insulating it from potential changes in
520
- // the default behavior of `WriteValueExpr` regarding unboxing.
585
+ // the default behavior of `WriteValueExpr` regarding un-var-refing.
521
586
  // 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
522
587
  // calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
523
588
  // `WriteValueExpr` for its elements, it could lead to unintended recursion or
524
- // behavior if an element itself was another composite literal. `WriteBoxedValue`
589
+ // behavior if an element itself was another composite literal. `WriteVarRefedValue`
525
590
  // acts as a specific intermediary for the *elements*.
526
591
  //
527
- // In summary, `WriteBoxedValue` is a specialized dispatcher used by `WriteCompositeLit`
592
+ // In summary, `WriteVarRefedValue` is a specialized dispatcher used by `WriteCompositeLit`
528
593
  // to guarantee that all parts of a Go composite literal are initialized with their
529
- // proper, unboxed TypeScript values.
530
- func (c *GoToTSCompiler) WriteBoxedValue(expr ast.Expr) error {
594
+ // proper, unrefed TypeScript values.
595
+ func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
531
596
  if expr == nil {
532
- return fmt.Errorf("nil expression passed to writeBoxedValue")
597
+ return fmt.Errorf("nil expression passed to write var refed value")
533
598
  }
534
599
 
535
600
  // Handle different expression types
@@ -13,13 +13,17 @@ type Config struct {
13
13
 
14
14
  // Dir is the working directory for the compiler. If empty, uses the current working directory.
15
15
  Dir string
16
- // OutputPathRoot is the output path root.
17
- OutputPathRoot string
16
+ // OutputPath is the output path root.
17
+ OutputPath string
18
18
  // BuildFlags are the Go build flags (tags) to use during analysis.
19
19
  BuildFlags []string
20
20
  // AllDependencies controls whether to compile all dependencies of the requested packages.
21
21
  // If true, all dependencies will be compiled; if false, only the requested packages are compiled.
22
22
  AllDependencies bool
23
+ // DisableEmitBuiltin controls whether to emit builtin packages when they are referenced.
24
+ // If true, builtin packages will not be emitted; if false, they will be emitted if referenced.
25
+ // Default is false (emit builtin packages).
26
+ DisableEmitBuiltin bool
23
27
  }
24
28
 
25
29
  // Validate checks the config.
@@ -30,7 +34,7 @@ func (c *Config) Validate() error {
30
34
  if c.fset == nil {
31
35
  c.fset = token.NewFileSet()
32
36
  }
33
- if c.OutputPathRoot == "" {
37
+ if c.OutputPath == "" {
34
38
  return errors.New("output path root must be specified")
35
39
  }
36
40
  return nil
@@ -1,7 +1,6 @@
1
1
  package compiler
2
2
 
3
3
  import (
4
- "reflect"
5
4
  "testing"
6
5
  )
7
6
 
@@ -15,9 +14,9 @@ func TestConfigValidate(t *testing.T) {
15
14
  {
16
15
  name: "valid config",
17
16
  config: &Config{
18
- Dir: "/some/dir",
19
- OutputPathRoot: "/output/path",
20
- BuildFlags: []string{"-tags", "sometag"},
17
+ Dir: "/some/dir",
18
+ OutputPath: "/output/path",
19
+ BuildFlags: []string{"-tags", "sometag"},
21
20
  },
22
21
  wantErr: false,
23
22
  },
@@ -33,9 +32,9 @@ func TestConfigValidate(t *testing.T) {
33
32
  {
34
33
  name: "nil fset gets initialized",
35
34
  config: &Config{
36
- fset: nil,
37
- Dir: "/some/dir",
38
- OutputPathRoot: "/output/path",
35
+ fset: nil,
36
+ Dir: "/some/dir",
37
+ OutputPath: "/output/path",
39
38
  },
40
39
  wantErr: false,
41
40
  },
@@ -61,29 +60,3 @@ func TestConfigValidate(t *testing.T) {
61
60
  })
62
61
  }
63
62
  }
64
-
65
- func TestConfigFields(t *testing.T) {
66
- // Verify that Config has the expected fields
67
- config := Config{}
68
- configType := reflect.TypeOf(config)
69
-
70
- expectedFields := map[string]string{
71
- "fset": "*token.FileSet",
72
- "Dir": "string",
73
- "OutputPathRoot": "string",
74
- "BuildFlags": "[]string",
75
- }
76
-
77
- for fieldName, expectedType := range expectedFields {
78
- field, exists := configType.FieldByName(fieldName)
79
- if !exists {
80
- t.Errorf("Expected Config to have field %s", fieldName)
81
- continue
82
- }
83
-
84
- actualType := field.Type.String()
85
- if actualType != expectedType {
86
- t.Errorf("Field %s has type %s, expected %s", fieldName, actualType, expectedType)
87
- }
88
- }
89
- }
package/compiler/decl.go CHANGED
@@ -37,7 +37,7 @@ func (c *GoToTSCompiler) WriteDecls(decls []ast.Decl) error {
37
37
  c.tsw.WriteLine("") // Add space after spec
38
38
  }
39
39
  default:
40
- fmt.Printf("unknown decl: %#v\n", decl)
40
+ return fmt.Errorf("unknown decl: %#v", decl)
41
41
  }
42
42
  }
43
43
  return nil
@@ -79,6 +79,12 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
79
79
  if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
80
80
  isAsync = c.analysis.IsAsyncFunc(obj)
81
81
  }
82
+
83
+ // Always make main function async (only in main package)
84
+ if decl.Name.Name == "main" && c.pkg.Name == "main" {
85
+ isAsync = true
86
+ }
87
+
82
88
  if isAsync {
83
89
  c.tsw.WriteLiterally("async ")
84
90
  }
@@ -111,6 +111,15 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
111
111
  return errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
112
112
  }
113
113
  c.tsw.WriteLiterally("$.cap")
114
+ case "new":
115
+ // Translate new(T) to new T_ts()
116
+ if len(exp.Args) != 1 {
117
+ return errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
118
+ }
119
+ c.tsw.WriteLiterally("new ")
120
+ c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
121
+ c.tsw.WriteLiterally("()")
122
+ return nil // Prevent falling through to generic argument handling
114
123
  case "delete":
115
124
  // Translate delete(map, key) to $.deleteMapEntry(map, key)
116
125
  if len(exp.Args) != 2 {
@@ -205,6 +214,33 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
205
214
  return nil // Handled make for []byte
206
215
  }
207
216
 
217
+ // Check if the element type is a generic type parameter
218
+ if _, isTypeParam := goElemType.(*types.TypeParam); isTypeParam {
219
+ // This is make([]E, n) where E is a type parameter
220
+ c.tsw.WriteLiterally("$.makeSlice<")
221
+ c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type parameter
222
+ c.tsw.WriteLiterally(">(")
223
+
224
+ if len(exp.Args) >= 2 {
225
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
226
+ return err
227
+ }
228
+ if len(exp.Args) == 3 {
229
+ c.tsw.WriteLiterally(", ")
230
+ if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
231
+ return err
232
+ }
233
+ } else if len(exp.Args) > 3 {
234
+ return errors.New("makeSlice expects 2 or 3 arguments")
235
+ }
236
+ } else {
237
+ // If no length is provided, default to 0
238
+ c.tsw.WriteLiterally("0")
239
+ }
240
+ c.tsw.WriteLiterally(")")
241
+ return nil // Handled make for []E where E is type parameter
242
+ }
243
+
208
244
  c.tsw.WriteLiterally("$.makeSlice<")
209
245
  c.WriteGoType(goElemType, GoTypeContextGeneral) // Write the element type
210
246
  c.tsw.WriteLiterally(">(")
@@ -228,6 +264,71 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
228
264
  c.tsw.WriteLiterally(")")
229
265
  return nil // Handled make for slice
230
266
  }
267
+
268
+ // Handle generic type parameter make calls: make(S, len, cap) where S ~[]E
269
+ if ident, ok := exp.Args[0].(*ast.Ident); ok {
270
+ // Check if this identifier refers to a type parameter
271
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
272
+ if typeName, isTypeName := obj.(*types.TypeName); isTypeName {
273
+ if typeParam, isTypeParam := typeName.Type().(*types.TypeParam); isTypeParam {
274
+ // Check if the type parameter is constrained to slice types
275
+ constraint := typeParam.Constraint()
276
+ if constraint != nil {
277
+ underlying := constraint.Underlying()
278
+ if iface, isInterface := underlying.(*types.Interface); isInterface {
279
+ // Check if the constraint includes slice types
280
+ // For constraints like ~[]E, we need to look at the type terms
281
+ if hasSliceConstraint(iface) {
282
+ // This is a generic slice type parameter
283
+ // We need to determine the element type from the constraint
284
+ elemType := getSliceElementTypeFromConstraint(iface)
285
+ if elemType != nil {
286
+ // Check if it's make(S, ...) where S constrains to []byte
287
+ if basicElem, isBasic := elemType.(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
288
+ c.tsw.WriteLiterally("new Uint8Array(")
289
+ if len(exp.Args) >= 2 {
290
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
291
+ return err
292
+ }
293
+ // Capacity argument for make([]byte, len, cap) is ignored for new Uint8Array(len)
294
+ } else {
295
+ // If no length is provided, default to 0
296
+ c.tsw.WriteLiterally("0")
297
+ }
298
+ c.tsw.WriteLiterally(")")
299
+ return nil // Handled make for generic []byte
300
+ }
301
+
302
+ c.tsw.WriteLiterally("$.makeSlice<")
303
+ c.WriteGoType(elemType, GoTypeContextGeneral) // Write the element type
304
+ c.tsw.WriteLiterally(">(")
305
+
306
+ if len(exp.Args) >= 2 {
307
+ if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
308
+ return err
309
+ }
310
+ if len(exp.Args) == 3 {
311
+ c.tsw.WriteLiterally(", ")
312
+ if err := c.WriteValueExpr(exp.Args[2]); err != nil { // Capacity
313
+ return err
314
+ }
315
+ } else if len(exp.Args) > 3 {
316
+ return errors.New("makeSlice expects 2 or 3 arguments")
317
+ }
318
+ } else {
319
+ // If no length is provided, default to 0
320
+ c.tsw.WriteLiterally("0")
321
+ }
322
+ c.tsw.WriteLiterally(")")
323
+ return nil // Handled make for generic slice
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ }
231
332
  }
232
333
  // Fallthrough for unhandled make calls (e.g., channels)
233
334
  return errors.New("unhandled make call")
@@ -424,7 +525,17 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
424
525
 
425
526
  if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
426
527
  if _, ok := funType.Underlying().(*types.Signature); ok {
427
- if _, isNamed := funType.(*types.Named); isNamed {
528
+ // Check if this is a function parameter identifier that needs not-null assertion
529
+ if ident, isIdent := expFun.(*ast.Ident); isIdent {
530
+ // Check if this identifier is a function parameter
531
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
532
+ if _, isVar := obj.(*types.Var); isVar {
533
+ // This is a variable (including function parameters)
534
+ // Function parameters that are function types need ! assertion
535
+ c.tsw.WriteLiterally("!")
536
+ }
537
+ }
538
+ } else if _, isNamed := funType.(*types.Named); isNamed {
428
539
  c.tsw.WriteLiterally("!")
429
540
  }
430
541
  }
@@ -458,7 +569,17 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
458
569
 
459
570
  if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
460
571
  if _, ok := funType.Underlying().(*types.Signature); ok {
461
- if _, isNamed := funType.(*types.Named); isNamed {
572
+ // Check if this is a function parameter identifier that needs not-null assertion
573
+ if ident, isIdent := expFun.(*ast.Ident); isIdent {
574
+ // Check if this identifier is a function parameter
575
+ if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
576
+ if _, isVar := obj.(*types.Var); isVar {
577
+ // This is a variable (including function parameters)
578
+ // Function parameters that are function types need ! assertion
579
+ c.tsw.WriteLiterally("!")
580
+ }
581
+ }
582
+ } else if _, isNamed := funType.(*types.Named); isNamed {
462
583
  c.tsw.WriteLiterally("!")
463
584
  }
464
585
  }
@@ -477,3 +598,92 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
477
598
  c.tsw.WriteLiterally(")")
478
599
  return nil
479
600
  }
601
+
602
+ // hasSliceConstraint checks if an interface constraint includes slice types
603
+ // For constraints like ~[]E, this returns true
604
+ func hasSliceConstraint(iface *types.Interface) bool {
605
+ // Check if the interface has type terms that include slice types
606
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
607
+ embedded := iface.EmbeddedType(i)
608
+ if union, ok := embedded.(*types.Union); ok {
609
+ for j := 0; j < union.Len(); j++ {
610
+ term := union.Term(j)
611
+ if _, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
612
+ return true
613
+ }
614
+ }
615
+ } else if _, isSlice := embedded.Underlying().(*types.Slice); isSlice {
616
+ return true
617
+ }
618
+ }
619
+ return false
620
+ }
621
+
622
+ // getSliceElementTypeFromConstraint extracts the element type from a slice constraint
623
+ // For constraints like ~[]E, this returns E
624
+ func getSliceElementTypeFromConstraint(iface *types.Interface) types.Type {
625
+ // Check if the interface has type terms that include slice types
626
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
627
+ embedded := iface.EmbeddedType(i)
628
+ if union, ok := embedded.(*types.Union); ok {
629
+ for j := 0; j < union.Len(); j++ {
630
+ term := union.Term(j)
631
+ if sliceType, isSlice := term.Type().Underlying().(*types.Slice); isSlice {
632
+ return sliceType.Elem()
633
+ }
634
+ }
635
+ } else if sliceType, isSlice := embedded.Underlying().(*types.Slice); isSlice {
636
+ return sliceType.Elem()
637
+ }
638
+ }
639
+ return nil
640
+ }
641
+
642
+ // hasMixedStringByteConstraint checks if an interface constraint includes both string and []byte types
643
+ // For constraints like string | []byte, this returns true
644
+ // For pure slice constraints like ~[]E, this returns false
645
+ func hasMixedStringByteConstraint(iface *types.Interface) bool {
646
+ hasString := false
647
+ hasByteSlice := false
648
+
649
+ // Check if the interface has type terms that include both string and []byte
650
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
651
+ embedded := iface.EmbeddedType(i)
652
+ if union, ok := embedded.(*types.Union); ok {
653
+ for j := 0; j < union.Len(); j++ {
654
+ term := union.Term(j)
655
+ termType := term.Type().Underlying()
656
+
657
+ // Check for string type
658
+ if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
659
+ hasString = true
660
+ }
661
+
662
+ // Check for []byte type
663
+ if sliceType, isSlice := termType.(*types.Slice); isSlice {
664
+ if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
665
+ hasByteSlice = true
666
+ }
667
+ }
668
+ }
669
+ } else {
670
+ // Handle non-union embedded types
671
+ termType := embedded.Underlying()
672
+
673
+ // Check for string type
674
+ if basicType, isBasic := termType.(*types.Basic); isBasic && (basicType.Info()&types.IsString) != 0 {
675
+ hasString = true
676
+ }
677
+
678
+ // Check for []byte type
679
+ if sliceType, isSlice := termType.(*types.Slice); isSlice {
680
+ if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
681
+ hasByteSlice = true
682
+ }
683
+ }
684
+ }
685
+ }
686
+
687
+ // Return true only if we have both string and []byte in the constraint
688
+ return hasString && hasByteSlice
689
+ }