goscript 0.0.21 → 0.0.23

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.
@@ -5,6 +5,8 @@ import (
5
5
  "os/exec"
6
6
  "path/filepath"
7
7
  "runtime"
8
+ "slices"
9
+ "strings"
8
10
  "sync"
9
11
  "sync/atomic"
10
12
  "testing"
@@ -37,12 +39,15 @@ func TestCompliance(t *testing.T) {
37
39
  testPath := filepath.Join(testsDir, dir.Name())
38
40
  goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
39
41
  if err != nil || len(goFiles) == 0 {
40
- t.Errorf("no .go files found in %s", testPath)
42
+ // t.Errorf("no .go files found in %s", testPath)
41
43
  continue
42
44
  }
43
45
  testPaths = append(testPaths, testPath)
44
46
  }
45
47
 
48
+ // sort testPaths
49
+ slices.Sort(testPaths)
50
+
46
51
  // limit concurrency
47
52
  simulLimit := make(chan struct{}, runtime.GOMAXPROCS(-1)*2)
48
53
  for range cap(simulLimit) {
@@ -93,15 +98,42 @@ func TestCompliance(t *testing.T) {
93
98
  t.SkipNow()
94
99
  }
95
100
 
96
- t.Log("running: npm run typecheck")
97
- cmd := exec.Command("npm", "run", "typecheck")
98
- cmd.Dir = workspaceDir // Run in the workspace directory
99
- cmd.Stdout = os.Stderr
100
- 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
101
114
 
102
- err = cmd.Run()
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)
120
+
121
+ output, err := cmd.CombinedOutput()
103
122
  if err != nil {
104
- 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")
105
126
  }
106
127
  })
107
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
+ }
@@ -0,0 +1,552 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "go/types"
8
+ "slices"
9
+ )
10
+
11
+ // WriteCompositeLit translates a Go composite literal (ast.CompositeLit) into its
12
+ // TypeScript equivalent.
13
+ //
14
+ // It handles several types of composite literals:
15
+ // - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
16
+ // Values are processed by `writeBoxedValue`.
17
+ // - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
18
+ // - For `[]byte{...}`, translated to `new Uint8Array([...])`.
19
+ // - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
20
+ // It handles both keyed and unkeyed elements, infers length if necessary,
21
+ // and uses zero values for uninitialized array elements.
22
+ // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
23
+ // Element values are processed by `writeBoxedValue`.
24
+ // - Struct literals:
25
+ // - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
26
+ // `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
27
+ // - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
28
+ // object literals `{ F: v_ts, ... }`.
29
+ // It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
30
+ // structs or arrays). Field values are processed by `writeBoxedValue`.
31
+ // Embedded struct fields are initialized, and explicit initializers for embedded
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).
34
+ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
35
+ // Get the type of the composite literal
36
+ litType := c.pkg.TypesInfo.TypeOf(exp)
37
+ if exp.Type != nil {
38
+ // Handle map literals: map[K]V{k1: v1, k2: v2}
39
+ if _, isMapType := exp.Type.(*ast.MapType); isMapType {
40
+ c.tsw.WriteLiterally("new Map([")
41
+
42
+ // Add each key-value pair as an entry
43
+ for i, elm := range exp.Elts {
44
+ if i > 0 {
45
+ c.tsw.WriteLiterally(", ")
46
+ }
47
+
48
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
49
+ c.tsw.WriteLiterally("[")
50
+ if err := c.WriteBoxedValue(kv.Key); err != nil {
51
+ return fmt.Errorf("failed to write map literal key: %w", err)
52
+ }
53
+ c.tsw.WriteLiterally(", ")
54
+ if err := c.WriteBoxedValue(kv.Value); err != nil {
55
+ return fmt.Errorf("failed to write map literal value: %w", err)
56
+ }
57
+ c.tsw.WriteLiterally("]")
58
+ } else {
59
+ return fmt.Errorf("map literal elements must be key-value pairs")
60
+ }
61
+ }
62
+
63
+ c.tsw.WriteLiterally("])")
64
+ return nil
65
+ }
66
+
67
+ // Handle array literals
68
+ if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
69
+ // Check if this is a slice of slices (multi-dimensional array)
70
+ isMultiDimensional := false
71
+ if _, ok := arrType.Elt.(*ast.ArrayType); ok {
72
+ // It's a slice of slices (multi-dimensional array)
73
+ isMultiDimensional = true
74
+ // We'll handle this with depth parameter to arrayToSlice
75
+ }
76
+
77
+ // Check if it's a []byte literal
78
+ isByteSliceLiteral := false
79
+ if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
80
+ if sliceT, ok := typInfo.Underlying().(*types.Slice); ok {
81
+ if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
82
+ isByteSliceLiteral = true
83
+ }
84
+ }
85
+ }
86
+
87
+ if isByteSliceLiteral {
88
+ c.tsw.WriteLiterally("new Uint8Array")
89
+ } else {
90
+ c.tsw.WriteLiterally("$.arrayToSlice")
91
+
92
+ // write the type annotation
93
+ c.tsw.WriteLiterally("<")
94
+ // Write the element type using the existing function
95
+ c.WriteTypeExpr(arrType.Elt)
96
+ c.tsw.WriteLiterally(">")
97
+ }
98
+
99
+ // opening
100
+ c.tsw.WriteLiterally("([")
101
+
102
+ // Use type info to get array length and element type
103
+ var arrayLen int
104
+ var elemType ast.Expr
105
+ var goElemType interface{}
106
+ if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
107
+ if at, ok := typ.Underlying().(*types.Array); ok {
108
+ arrayLen = int(at.Len())
109
+ goElemType = at.Elem()
110
+ }
111
+ }
112
+ if arrType.Len != nil {
113
+ // Try to evaluate the length from the AST if not available from type info
114
+ if bl, ok := arrType.Len.(*ast.BasicLit); ok && bl.Kind == token.INT {
115
+ if _, err := fmt.Sscan(bl.Value, &arrayLen); err != nil {
116
+ return fmt.Errorf("failed to parse array length from basic literal: %w", err)
117
+ }
118
+ }
119
+ }
120
+ elemType = arrType.Elt
121
+
122
+ // Map of index -> value
123
+ elements := make(map[int]ast.Expr)
124
+ orderedCount := 0
125
+ maxIndex := -1
126
+ hasKeyedElements := false
127
+
128
+ for _, elm := range exp.Elts {
129
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
130
+ if lit, ok := kv.Key.(*ast.BasicLit); ok && lit.Kind == token.INT {
131
+ var index int
132
+ if _, err := fmt.Sscan(lit.Value, &index); err != nil {
133
+ return fmt.Errorf("failed to parse array index from basic literal: %w", err)
134
+ }
135
+ elements[index] = kv.Value
136
+ if index > maxIndex {
137
+ maxIndex = index
138
+ }
139
+ hasKeyedElements = true
140
+ } else {
141
+ c.tsw.WriteCommentInline("unhandled keyed array literal key type")
142
+ if err := c.WriteBoxedValue(elm); err != nil {
143
+ return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
144
+ }
145
+ }
146
+ } else {
147
+ elements[orderedCount] = elm
148
+ if orderedCount > maxIndex {
149
+ maxIndex = orderedCount
150
+ }
151
+ orderedCount++
152
+ }
153
+ }
154
+
155
+ // Determine array length
156
+ if arrayLen == 0 {
157
+ // If length is not set, infer from max index or number of elements
158
+ if hasKeyedElements {
159
+ arrayLen = maxIndex + 1
160
+ } else {
161
+ arrayLen = len(exp.Elts)
162
+ }
163
+ }
164
+
165
+ for i := 0; i < arrayLen; i++ {
166
+ if i > 0 {
167
+ c.tsw.WriteLiterally(", ")
168
+ }
169
+ if elm, ok := elements[i]; ok && elm != nil {
170
+ if err := c.WriteBoxedValue(elm); err != nil {
171
+ return fmt.Errorf("failed to write array literal element: %w", err)
172
+ }
173
+ } else {
174
+ // Write zero value for element type
175
+ if goElemType != nil {
176
+ c.WriteZeroValueForType(goElemType)
177
+ } else {
178
+ c.WriteZeroValueForType(elemType)
179
+ }
180
+ }
181
+ }
182
+ c.tsw.WriteLiterally("]")
183
+
184
+ // If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
185
+ if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
186
+ c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
187
+ }
188
+
189
+ c.tsw.WriteLiterally(")")
190
+ return nil
191
+ } else {
192
+ // Check if this is a struct type
193
+ var structType *types.Struct
194
+ isStructLiteral := false
195
+ isAnonymousStruct := false
196
+
197
+ if namedType, ok := litType.(*types.Named); ok {
198
+ if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
199
+ structType = underlyingStruct
200
+ isStructLiteral = true
201
+ // Named struct, use constructor
202
+ c.tsw.WriteLiterally("new ")
203
+ c.WriteTypeExpr(exp.Type)
204
+ }
205
+ } else if ptrType, ok := litType.(*types.Pointer); ok {
206
+ if namedElem, ok := ptrType.Elem().(*types.Named); ok {
207
+ if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
208
+ structType = underlyingStruct
209
+ isStructLiteral = true // Treat pointer-to-struct literal similarly
210
+ // Named struct pointer, use constructor
211
+ c.tsw.WriteLiterally("new ")
212
+ c.WriteTypeExpr(exp.Type)
213
+ }
214
+ }
215
+ } else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
216
+ // Anonymous struct literal
217
+ structType = underlyingStruct
218
+ isStructLiteral = true
219
+ isAnonymousStruct = true
220
+ // For anonymous structs, don't use constructor, just create object literal
221
+ }
222
+
223
+ if isStructLiteral && structType != nil {
224
+ // --- Struct Literal Handling (Nested) ---
225
+ directFields := make(map[string]ast.Expr)
226
+ embeddedFields := make(map[string]map[string]ast.Expr) // map[EmbeddedPropName]map[FieldName]ValueExpr
227
+ explicitEmbedded := make(map[string]ast.Expr) // Tracks explicitly initialized embedded structs
228
+
229
+ // Pre-populate embeddedFields map keys using the correct property name
230
+ for i := 0; i < structType.NumFields(); i++ {
231
+ field := structType.Field(i)
232
+ if field.Anonymous() {
233
+ fieldType := field.Type()
234
+ if ptr, ok := fieldType.(*types.Pointer); ok {
235
+ fieldType = ptr.Elem()
236
+ }
237
+ if named, ok := fieldType.(*types.Named); ok {
238
+ // Use the type name as the property name in TS
239
+ embeddedPropName := named.Obj().Name()
240
+ embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
241
+ }
242
+ }
243
+ }
244
+
245
+ // Group literal elements by direct vs embedded fields
246
+ for _, elt := range exp.Elts {
247
+ kv, ok := elt.(*ast.KeyValueExpr)
248
+ if !ok {
249
+ continue
250
+ } // Skip non-key-value
251
+ keyIdent, ok := kv.Key.(*ast.Ident)
252
+ if !ok {
253
+ continue
254
+ } // Skip non-ident keys
255
+ keyName := keyIdent.Name
256
+
257
+ // Check if this is an explicit embedded struct initialization
258
+ // e.g., Person: Person{...} or Person: personVar
259
+ if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
260
+ // This is an explicit initialization of an embedded struct
261
+ explicitEmbedded[keyName] = kv.Value
262
+ continue
263
+ }
264
+
265
+ isDirectField := false
266
+ for i := range structType.NumFields() {
267
+ field := structType.Field(i)
268
+ if field.Name() == keyName {
269
+ isDirectField = true
270
+ directFields[keyName] = kv.Value
271
+ break
272
+ }
273
+ }
274
+
275
+ // For anonymous structs, all fields are direct fields
276
+ if isAnonymousStruct {
277
+ directFields[keyName] = kv.Value
278
+ isDirectField = true
279
+ }
280
+
281
+ // If not a direct field, return an error
282
+ if !isDirectField {
283
+ // This field was not found as a direct field in the struct
284
+ return fmt.Errorf("field %s not found in type %s for composite literal",
285
+ keyName, litType.String())
286
+ }
287
+ }
288
+
289
+ // Handle the case where an anonymous struct has values without keys
290
+ // This block processes non-key-value elements and associates them with struct fields.
291
+ if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
292
+ // Check if any elements in the composite literal are not key-value pairs.
293
+ hasNonKeyValueElts := false
294
+ for _, elt := range exp.Elts {
295
+ // If an element is not a key-value pair, set the flag to true.
296
+ if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
297
+ hasNonKeyValueElts = true
298
+ break
299
+ }
300
+ }
301
+
302
+ if hasNonKeyValueElts {
303
+ // Get the fields from the struct type
304
+ for i := 0; i < structType.NumFields(); i++ {
305
+ field := structType.Field(i)
306
+ // If we have a value for this field position
307
+ if i < len(exp.Elts) {
308
+ // Check if it's not a key-value pair
309
+ if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
310
+ directFields[field.Name()] = exp.Elts[i]
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ // Write the object literal
318
+ if isAnonymousStruct {
319
+ // For anonymous structs, just write a simple object literal
320
+ c.tsw.WriteLiterally("{")
321
+ } else {
322
+ // For named structs, write the constructor argument
323
+ c.tsw.WriteLiterally("({")
324
+ }
325
+
326
+ firstFieldWritten := false
327
+
328
+ // Write direct fields that aren't embedded struct names
329
+ directKeys := make([]string, 0, len(directFields))
330
+ for k := range directFields {
331
+ // Skip embedded struct names - we'll handle those separately
332
+ if _, isEmbedded := embeddedFields[k]; !isEmbedded {
333
+ directKeys = append(directKeys, k)
334
+ }
335
+ }
336
+ slices.Sort(directKeys)
337
+ for _, keyName := range directKeys {
338
+ if firstFieldWritten {
339
+ c.tsw.WriteLiterally(", ")
340
+ }
341
+ c.tsw.WriteLiterally(keyName)
342
+ c.tsw.WriteLiterally(": ")
343
+ if err := c.WriteBoxedValue(directFields[keyName]); err != nil {
344
+ return err
345
+ }
346
+ firstFieldWritten = true
347
+ }
348
+
349
+ // Write explicitly initialized embedded structs
350
+ explicitKeys := make([]string, 0, len(explicitEmbedded))
351
+ for k := range explicitEmbedded {
352
+ explicitKeys = append(explicitKeys, k)
353
+ }
354
+ slices.Sort(explicitKeys)
355
+ for _, embeddedName := range explicitKeys {
356
+ if firstFieldWritten {
357
+ c.tsw.WriteLiterally(", ")
358
+ }
359
+ c.tsw.WriteLiterally(embeddedName)
360
+ c.tsw.WriteLiterally(": ")
361
+
362
+ // Check if the embedded value is a composite literal for a struct
363
+ // If so, extract the fields and write them directly
364
+ if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
365
+ // Write initialization fields directly without the 'new Constructor'
366
+ c.tsw.WriteLiterally("{")
367
+ for i, elem := range compLit.Elts {
368
+ if i > 0 {
369
+ c.tsw.WriteLiterally(", ")
370
+ }
371
+ if err := c.WriteBoxedValue(elem); err != nil {
372
+ return err
373
+ }
374
+ }
375
+ c.tsw.WriteLiterally("}")
376
+ } else {
377
+ // Not a composite literal, write it normally
378
+ if err := c.WriteBoxedValue(explicitEmbedded[embeddedName]); err != nil {
379
+ return err
380
+ }
381
+ }
382
+ firstFieldWritten = true
383
+ }
384
+
385
+ // Write embedded fields for structs that weren't explicitly initialized
386
+ embeddedKeys := make([]string, 0, len(embeddedFields))
387
+ for k := range embeddedFields {
388
+ // Skip embedded structs that were explicitly initialized
389
+ if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
390
+ embeddedKeys = append(embeddedKeys, k)
391
+ }
392
+ }
393
+ slices.Sort(embeddedKeys)
394
+ for _, embeddedPropName := range embeddedKeys {
395
+ fieldsMap := embeddedFields[embeddedPropName]
396
+ if len(fieldsMap) == 0 {
397
+ continue
398
+ } // Skip empty embedded initializers
399
+
400
+ if firstFieldWritten {
401
+ c.tsw.WriteLiterally(", ")
402
+ }
403
+ c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
404
+ c.tsw.WriteLiterally(": {")
405
+
406
+ innerKeys := make([]string, 0, len(fieldsMap))
407
+ for k := range fieldsMap {
408
+ innerKeys = append(innerKeys, k)
409
+ }
410
+ slices.Sort(innerKeys)
411
+ for i, keyName := range innerKeys {
412
+ if i > 0 {
413
+ c.tsw.WriteLiterally(", ")
414
+ }
415
+ c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
416
+ c.tsw.WriteLiterally(": ")
417
+ if err := c.WriteBoxedValue(fieldsMap[keyName]); err != nil {
418
+ return err
419
+ }
420
+ }
421
+ c.tsw.WriteLiterally("}")
422
+ firstFieldWritten = true
423
+ }
424
+
425
+ // Close the object literal
426
+ if isAnonymousStruct {
427
+ c.tsw.WriteLiterally("}")
428
+ } else {
429
+ c.tsw.WriteLiterally("})")
430
+ }
431
+
432
+ } else {
433
+ // Non-struct type or anonymous struct, handle normally (or potentially error for anonymous struct literals?)
434
+ c.tsw.WriteLiterally("({") // Assuming object literal for constructor
435
+ for i, elm := range exp.Elts {
436
+ if i != 0 {
437
+ c.tsw.WriteLiterally(", ")
438
+ }
439
+ if err := c.WriteBoxedValue(elm); err != nil {
440
+ return fmt.Errorf("failed to write literal field: %w", err)
441
+ }
442
+ }
443
+ c.tsw.WriteLiterally("})")
444
+ }
445
+ return nil
446
+ }
447
+ }
448
+
449
+ // Untyped composite literal. Let's use type information to determine what it is.
450
+ // First try to get the type information for the expression
451
+ isObject := false
452
+ if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
453
+ underlying := tv.Type.Underlying()
454
+ switch underlying.(type) {
455
+ case *types.Map, *types.Struct:
456
+ isObject = true
457
+ case *types.Array, *types.Slice:
458
+ isObject = false
459
+ default:
460
+ return fmt.Errorf("unhandled composite literal type: %T", underlying)
461
+ }
462
+ } else {
463
+ return fmt.Errorf("could not determine composite literal type from type information")
464
+ }
465
+
466
+ if isObject {
467
+ c.tsw.WriteLiterally("{ ")
468
+ } else {
469
+ c.tsw.WriteLiterally("[ ")
470
+ }
471
+
472
+ for i, elm := range exp.Elts {
473
+ if i != 0 {
474
+ c.tsw.WriteLiterally(", ")
475
+ }
476
+ if err := c.WriteBoxedValue(elm); err != nil {
477
+ return fmt.Errorf("failed to write untyped composite literal element: %w", err)
478
+ }
479
+ }
480
+ if isObject {
481
+ c.tsw.WriteLiterally(" }")
482
+ } else {
483
+ c.tsw.WriteLiterally(" ]")
484
+ }
485
+ return nil
486
+ }
487
+
488
+ // WriteBoxedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
489
+ // specifically for use as a value within a composite literal (e.g., struct fields,
490
+ // map keys/values, or array/slice elements). Its primary goal is to ensure that the
491
+ // actual, unboxed value of the expression is used.
492
+ //
493
+ // How it works:
494
+ // - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
495
+ // the `accessValue` flag to `true`. This ensures that if `ident` refers to a
496
+ // GoScript boxed variable, the generated TypeScript accesses its underlying `.value`
497
+ // (e.g., `myVar.value`).
498
+ // - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
499
+ // `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
502
+ // `obj.field.value`).
503
+ // - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
504
+ // This function handles pointer dereferencing, which in GoScript's boxing model
505
+ // often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
506
+ // - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
507
+ // `c.WriteBasicLit(e)` for direct translation.
508
+ // - Other expression types: Falls back to `c.WriteValueExpr(expr)` for general
509
+ // expression handling. This is important for complex expressions like function
510
+ // calls or binary operations that might appear as values within a composite literal.
511
+ //
512
+ // Necessity and Distinction from `WriteValueExpr`:
513
+ // 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`:
516
+ // 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
517
+ // literal, the *unboxed value* is mandatory.
518
+ // 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
519
+ // the correct values for initialization, insulating it from potential changes in
520
+ // the default behavior of `WriteValueExpr` regarding unboxing.
521
+ // 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
522
+ // calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
523
+ // `WriteValueExpr` for its elements, it could lead to unintended recursion or
524
+ // behavior if an element itself was another composite literal. `WriteBoxedValue`
525
+ // acts as a specific intermediary for the *elements*.
526
+ //
527
+ // In summary, `WriteBoxedValue` is a specialized dispatcher used by `WriteCompositeLit`
528
+ // 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 {
531
+ if expr == nil {
532
+ return fmt.Errorf("nil expression passed to writeBoxedValue")
533
+ }
534
+
535
+ // Handle different expression types
536
+ switch e := expr.(type) {
537
+ case *ast.Ident:
538
+ c.WriteIdent(e, true)
539
+ return nil
540
+ case *ast.SelectorExpr:
541
+ return c.WriteSelectorExpr(e)
542
+ case *ast.StarExpr:
543
+ // For star expressions, delegate to WriteStarExpr which handles dereferencing
544
+ return c.WriteStarExpr(e)
545
+ case *ast.BasicLit:
546
+ c.WriteBasicLit(e)
547
+ return nil
548
+ default:
549
+ // For other expression types, use WriteValueExpr
550
+ return c.WriteValueExpr(expr)
551
+ }
552
+ }
@@ -17,6 +17,9 @@ type Config struct {
17
17
  OutputPathRoot string
18
18
  // BuildFlags are the Go build flags (tags) to use during analysis.
19
19
  BuildFlags []string
20
+ // AllDependencies controls whether to compile all dependencies of the requested packages.
21
+ // If true, all dependencies will be compiled; if false, only the requested packages are compiled.
22
+ AllDependencies bool
20
23
  }
21
24
 
22
25
  // Validate checks the config.