goscript 0.0.83 → 0.1.0

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 (197) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +27 -7
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +86 -0
  39. package/dist/gs/builtin/hostio.js +266 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -0
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/print.d.ts +8 -0
  45. package/dist/gs/builtin/print.js +111 -0
  46. package/dist/gs/builtin/print.js.map +1 -0
  47. package/dist/gs/builtin/slice.d.ts +1 -1
  48. package/dist/gs/builtin/slice.js.map +1 -1
  49. package/dist/gs/builtin/type.d.ts +11 -0
  50. package/dist/gs/builtin/type.js +55 -1
  51. package/dist/gs/builtin/type.js.map +1 -1
  52. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  53. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  54. package/dist/gs/bytes/reader.gs.js.map +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/crypto/rand/index.d.ts +5 -0
  57. package/dist/gs/crypto/rand/index.js +77 -0
  58. package/dist/gs/crypto/rand/index.js.map +1 -0
  59. package/dist/gs/encoding/json/index.d.ts +3 -0
  60. package/dist/gs/encoding/json/index.js +160 -0
  61. package/dist/gs/encoding/json/index.js.map +1 -0
  62. package/dist/gs/fmt/fmt.js +2 -22
  63. package/dist/gs/fmt/fmt.js.map +1 -1
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  66. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  67. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  68. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  69. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  70. package/dist/gs/go/scanner/index.d.ts +29 -0
  71. package/dist/gs/go/scanner/index.js +120 -0
  72. package/dist/gs/go/scanner/index.js.map +1 -0
  73. package/dist/gs/go/token/index.d.ts +31 -0
  74. package/dist/gs/go/token/index.js +82 -0
  75. package/dist/gs/go/token/index.js.map +1 -0
  76. package/dist/gs/internal/abi/index.js.map +1 -1
  77. package/dist/gs/io/fs/fs.js.map +1 -1
  78. package/dist/gs/io/fs/readdir.js.map +1 -1
  79. package/dist/gs/io/fs/readfile.js.map +1 -1
  80. package/dist/gs/io/fs/stat.js.map +1 -1
  81. package/dist/gs/io/fs/sub.js.map +1 -1
  82. package/dist/gs/io/io.js.map +1 -1
  83. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  84. package/dist/gs/os/error.gs.js +2 -4
  85. package/dist/gs/os/error.gs.js.map +1 -1
  86. package/dist/gs/os/exec.gs.js.map +1 -1
  87. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  88. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  89. package/dist/gs/os/root_js.gs.js.map +1 -1
  90. package/dist/gs/os/tempfile.gs.js +66 -9
  91. package/dist/gs/os/tempfile.gs.js.map +1 -1
  92. package/dist/gs/os/types.gs.js.map +1 -1
  93. package/dist/gs/os/types_js.gs.d.ts +2 -51
  94. package/dist/gs/os/types_js.gs.js +67 -105
  95. package/dist/gs/os/types_js.gs.js.map +1 -1
  96. package/dist/gs/os/types_unix.gs.js.map +1 -1
  97. package/dist/gs/path/filepath/match.js.map +1 -1
  98. package/dist/gs/path/match.js.map +1 -1
  99. package/dist/gs/path/path.js.map +1 -1
  100. package/dist/gs/reflect/index.d.ts +2 -2
  101. package/dist/gs/reflect/index.js +1 -1
  102. package/dist/gs/reflect/index.js.map +1 -1
  103. package/dist/gs/reflect/map.js.map +1 -1
  104. package/dist/gs/reflect/type.d.ts +2 -1
  105. package/dist/gs/reflect/type.js +85 -14
  106. package/dist/gs/reflect/type.js.map +1 -1
  107. package/dist/gs/reflect/types.js.map +1 -1
  108. package/dist/gs/reflect/visiblefields.js.map +1 -1
  109. package/dist/gs/runtime/runtime.js.map +1 -1
  110. package/dist/gs/sort/sort.gs.js.map +1 -1
  111. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  112. package/dist/gs/strconv/quote.gs.js.map +1 -1
  113. package/dist/gs/strings/builder.js.map +1 -1
  114. package/dist/gs/strings/reader.js.map +1 -1
  115. package/dist/gs/strings/replace.js.map +1 -1
  116. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  117. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  118. package/dist/gs/sync/sync.d.ts +1 -0
  119. package/dist/gs/sync/sync.js +12 -0
  120. package/dist/gs/sync/sync.js.map +1 -1
  121. package/dist/gs/time/time.js.map +1 -1
  122. package/dist/gs/unicode/unicode.js.map +1 -1
  123. package/go.mod +2 -2
  124. package/gs/builtin/builtin.ts +31 -6
  125. package/gs/builtin/hostio.test.ts +246 -0
  126. package/gs/builtin/hostio.ts +413 -0
  127. package/gs/builtin/index.ts +1 -0
  128. package/gs/builtin/print.test.ts +48 -0
  129. package/gs/builtin/print.ts +154 -0
  130. package/gs/builtin/runtime-contract.test.ts +230 -0
  131. package/gs/builtin/type.ts +84 -1
  132. package/gs/crypto/rand/index.test.ts +32 -0
  133. package/gs/crypto/rand/index.ts +90 -0
  134. package/gs/crypto/rand/meta.json +5 -0
  135. package/gs/encoding/json/index.test.ts +65 -0
  136. package/gs/encoding/json/index.ts +186 -0
  137. package/gs/fmt/fmt.test.ts +41 -30
  138. package/gs/fmt/fmt.ts +2 -22
  139. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  140. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  141. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  142. package/gs/go/scanner/index.test.ts +50 -0
  143. package/gs/go/scanner/index.ts +157 -0
  144. package/gs/go/token/index.test.ts +21 -0
  145. package/gs/go/token/index.ts +120 -0
  146. package/gs/os/file_unix_js.test.ts +103 -0
  147. package/gs/os/meta.json +1 -2
  148. package/gs/os/tempfile.gs.test.ts +85 -0
  149. package/gs/os/tempfile.gs.ts +71 -11
  150. package/gs/os/types_js.gs.ts +74 -153
  151. package/gs/reflect/index.ts +1 -1
  152. package/gs/reflect/type.ts +106 -17
  153. package/gs/reflect/typefor.test.ts +75 -0
  154. package/gs/sync/sync.test.ts +24 -0
  155. package/gs/sync/sync.ts +12 -0
  156. package/package.json +13 -13
  157. package/compiler/analysis.go +0 -3475
  158. package/compiler/analysis_test.go +0 -338
  159. package/compiler/assignment.go +0 -580
  160. package/compiler/builtin_test.go +0 -92
  161. package/compiler/code-writer.go +0 -115
  162. package/compiler/compiler_test.go +0 -149
  163. package/compiler/composite-lit.go +0 -779
  164. package/compiler/config_test.go +0 -62
  165. package/compiler/constraint.go +0 -86
  166. package/compiler/decl.go +0 -801
  167. package/compiler/expr-call-async.go +0 -188
  168. package/compiler/expr-call-builtins.go +0 -208
  169. package/compiler/expr-call-helpers.go +0 -382
  170. package/compiler/expr-call-make.go +0 -318
  171. package/compiler/expr-call-type-conversion.go +0 -520
  172. package/compiler/expr-call.go +0 -413
  173. package/compiler/expr-selector.go +0 -343
  174. package/compiler/expr-star.go +0 -82
  175. package/compiler/expr-type.go +0 -442
  176. package/compiler/expr-value.go +0 -89
  177. package/compiler/expr.go +0 -773
  178. package/compiler/field.go +0 -183
  179. package/compiler/gs_dependencies_test.go +0 -298
  180. package/compiler/lit.go +0 -322
  181. package/compiler/output.go +0 -72
  182. package/compiler/primitive.go +0 -149
  183. package/compiler/protobuf.go +0 -697
  184. package/compiler/sanitize.go +0 -100
  185. package/compiler/spec-struct.go +0 -995
  186. package/compiler/spec-value.go +0 -540
  187. package/compiler/spec.go +0 -725
  188. package/compiler/stmt-assign.go +0 -664
  189. package/compiler/stmt-for.go +0 -266
  190. package/compiler/stmt-range.go +0 -475
  191. package/compiler/stmt-select.go +0 -262
  192. package/compiler/stmt-type-switch.go +0 -147
  193. package/compiler/stmt.go +0 -1308
  194. package/compiler/type-assert.go +0 -386
  195. package/compiler/type-info.go +0 -156
  196. package/compiler/type-utils.go +0 -207
  197. package/compiler/type.go +0 -892
@@ -1,779 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "fmt"
5
- "go/ast"
6
- "go/constant"
7
- "go/token"
8
- "go/types"
9
- "slices"
10
- )
11
-
12
- // WriteCompositeLit translates a Go composite literal (ast.CompositeLit) into its
13
- // TypeScript equivalent.
14
- //
15
- // It handles several types of composite literals:
16
- // - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
17
- // Values are processed by `WriteVarRefedValue`.
18
- // - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
19
- // - For `[]byte{...}`, translated to `new Uint8Array([...])`.
20
- // - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
21
- // It handles both keyed and unkeyed elements, infers length if necessary,
22
- // and uses zero values for uninitialized array elements.
23
- // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
24
- // Element values are processed by `WriteVarRefedValue`.
25
- // - Struct literals:
26
- // - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
27
- // `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
28
- // - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
29
- // object literals `{ F: v_ts, ... }`.
30
- // It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
31
- // structs or arrays). Field values are processed by `WriteVarRefedValue`.
32
- // Embedded struct fields are initialized, and explicit initializers for embedded
33
- // structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
34
- // The function uses `c.analysis` to determine correct value access (e.g., `.value` for var-refed fields).
35
- func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
36
- // Get the type of the composite literal
37
- litType := c.pkg.TypesInfo.TypeOf(exp)
38
-
39
- if exp.Type != nil {
40
- // Handle map literals: map[K]V{k1: v1, k2: v2}
41
- // Also handle type alias for map: type OpNames map[K]V; OpNames{k1: v1}
42
- isMapType := false
43
- if _, astMapType := exp.Type.(*ast.MapType); astMapType {
44
- isMapType = true
45
- } else if litType != nil {
46
- _, isMapType = litType.Underlying().(*types.Map)
47
- }
48
- if isMapType {
49
- c.tsw.WriteLiterally("new Map([")
50
-
51
- // Add each key-value pair as an entry
52
- for i, elm := range exp.Elts {
53
- if i > 0 {
54
- c.tsw.WriteLiterally(", ")
55
- }
56
-
57
- if kv, ok := elm.(*ast.KeyValueExpr); ok {
58
- c.tsw.WriteLiterally("[")
59
- if err := c.WriteVarRefedValue(kv.Key); err != nil {
60
- return fmt.Errorf("failed to write map literal key: %w", err)
61
- }
62
- c.tsw.WriteLiterally(", ")
63
- if err := c.WriteVarRefedValue(kv.Value); err != nil {
64
- return fmt.Errorf("failed to write map literal value: %w", err)
65
- }
66
- c.tsw.WriteLiterally("]")
67
- } else {
68
- return fmt.Errorf("map literal elements must be key-value pairs")
69
- }
70
- }
71
-
72
- c.tsw.WriteLiterally("])")
73
- return nil
74
- }
75
-
76
- // Handle array literals
77
- if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
78
- // Check if this is a slice of slices (multi-dimensional array)
79
- isMultiDimensional := false
80
- if _, ok := arrType.Elt.(*ast.ArrayType); ok {
81
- // It's a slice of slices (multi-dimensional array)
82
- isMultiDimensional = true
83
- // We'll handle this with depth parameter to arrayToSlice
84
- }
85
-
86
- // Check if it's a []byte literal
87
- isByteSliceLiteral := false
88
- if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
89
- isByteSliceLiteral = c.isByteSliceType(typInfo)
90
- }
91
-
92
- if isByteSliceLiteral {
93
- c.tsw.WriteLiterally("new Uint8Array")
94
- } else {
95
- c.tsw.WriteLiterally("$.arrayToSlice")
96
-
97
- // write the type annotation
98
- c.tsw.WriteLiterally("<")
99
- // Write the element type using the existing function
100
- c.WriteTypeExpr(arrType.Elt)
101
- c.tsw.WriteLiterally(">")
102
- }
103
-
104
- // opening
105
- c.tsw.WriteLiterally("([")
106
-
107
- // Use type info to get array length and element type
108
- var arrayLen int
109
- var elemType ast.Expr
110
- var goElemType any
111
- if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
112
- if at, ok := typ.Underlying().(*types.Array); ok {
113
- arrayLen = int(at.Len())
114
- goElemType = at.Elem()
115
- } else if st, ok := typ.Underlying().(*types.Slice); ok {
116
- // For slices, get the element type
117
- goElemType = st.Elem()
118
- }
119
- }
120
- if arrType.Len != nil {
121
- // Try to evaluate the length from the AST if not available from type info
122
- if bl, ok := arrType.Len.(*ast.BasicLit); ok && bl.Kind == token.INT {
123
- if _, err := fmt.Sscan(bl.Value, &arrayLen); err != nil {
124
- return fmt.Errorf("failed to parse array length from basic literal: %w", err)
125
- }
126
- } else {
127
- // Try to evaluate as a constant expression (e.g., const N = 5; [N]int{})
128
- if lenValue := c.evaluateConstantExpr(arrType.Len); lenValue != nil {
129
- if length, ok := lenValue.(int); ok {
130
- arrayLen = length
131
- }
132
- }
133
- }
134
- }
135
- elemType = arrType.Elt
136
-
137
- // Map of index -> value
138
- elements := make(map[int]ast.Expr)
139
- orderedCount := 0
140
- maxIndex := -1
141
- hasKeyedElements := false
142
-
143
- for _, elm := range exp.Elts {
144
- if kv, ok := elm.(*ast.KeyValueExpr); ok {
145
- // Try to evaluate the key expression as a constant (handles both literals and expressions)
146
- if keyValue := c.evaluateConstantExpr(kv.Key); keyValue != nil {
147
- if index, ok := keyValue.(int); ok {
148
- elements[index] = kv.Value
149
- if index > maxIndex {
150
- maxIndex = index
151
- }
152
- hasKeyedElements = true
153
- } else {
154
- return fmt.Errorf("keyed array literal key must evaluate to an integer, got %T", keyValue)
155
- }
156
- } else {
157
- return fmt.Errorf("keyed array literal key must be a constant expression")
158
- }
159
- } else {
160
- // For unkeyed elements, place them at the next available index
161
- // If we have keyed elements, start after the highest keyed index
162
- currentIndex := orderedCount
163
- if hasKeyedElements && orderedCount <= maxIndex {
164
- currentIndex = maxIndex + 1
165
- for elements[currentIndex] != nil {
166
- currentIndex++
167
- }
168
- }
169
- elements[currentIndex] = elm
170
- if currentIndex > maxIndex {
171
- maxIndex = currentIndex
172
- }
173
- orderedCount = currentIndex + 1
174
- }
175
- }
176
-
177
- // Determine array length
178
- if arrayLen == 0 {
179
- // If length is not set, infer from max index or number of elements
180
- if hasKeyedElements {
181
- arrayLen = maxIndex + 1
182
- } else {
183
- arrayLen = len(exp.Elts)
184
- }
185
- }
186
-
187
- for i := 0; i < arrayLen; i++ {
188
- if i > 0 {
189
- c.tsw.WriteLiterally(", ")
190
- }
191
- if elm, ok := elements[i]; ok && elm != nil {
192
- if err := c.WriteVarRefedValue(elm); err != nil {
193
- return fmt.Errorf("failed to write array literal element: %w", err)
194
- }
195
- } else {
196
- // Write zero value for element type
197
- if goElemType != nil {
198
- c.WriteZeroValueForType(goElemType)
199
- } else {
200
- c.WriteZeroValueForType(elemType)
201
- }
202
- }
203
- }
204
- c.tsw.WriteLiterally("]")
205
-
206
- // If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
207
- if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
208
- c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
209
- }
210
-
211
- c.tsw.WriteLiterally(")")
212
- return nil
213
- } else {
214
- // Check if this is a struct type
215
- var structType *types.Struct
216
- isStructLiteral := false
217
- isAnonymousStruct := false
218
- needsValueMarkerClose := false // Track if we need to close $.markAsStructValue()
219
-
220
- if namedType, ok := litType.(*types.Named); ok {
221
- if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
222
- structType = underlyingStruct
223
- isStructLiteral = true
224
-
225
- // Check if this is a protobuf type
226
- if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
227
- if err != nil {
228
- return err
229
- }
230
- } else {
231
- // Named struct value, use constructor
232
- if !c.insideAddressOf {
233
- // Only mark as struct value if not inside address-of operator
234
- c.tsw.WriteLiterally("$.markAsStructValue(new ")
235
- needsValueMarkerClose = true
236
- } else {
237
- c.tsw.WriteLiterally("new ")
238
- }
239
- c.WriteTypeExpr(exp.Type)
240
- }
241
- }
242
- } else if aliasType, ok := litType.(*types.Alias); ok {
243
- // Handle type aliases (like os.PathError)
244
- if underlyingStruct, ok := aliasType.Underlying().(*types.Struct); ok {
245
- structType = underlyingStruct
246
- isStructLiteral = true
247
-
248
- // Check if this is a protobuf type
249
- if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
250
- if err != nil {
251
- return err
252
- }
253
- } else {
254
- // Type alias for struct value, use constructor
255
- if !c.insideAddressOf {
256
- // Only mark as struct value if not inside address-of operator
257
- c.tsw.WriteLiterally("$.markAsStructValue(new ")
258
- needsValueMarkerClose = true
259
- } else {
260
- c.tsw.WriteLiterally("new ")
261
- }
262
- c.WriteTypeExpr(exp.Type)
263
- }
264
- }
265
- } else if ptrType, ok := litType.(*types.Pointer); ok {
266
- if namedElem, ok := ptrType.Elem().(*types.Named); ok {
267
- if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
268
- structType = underlyingStruct
269
- isStructLiteral = true // Treat pointer-to-struct literal similarly
270
-
271
- // Check if this is a protobuf type
272
- if handled, err := c.writeProtobufCompositeLit(exp, litType); handled {
273
- if err != nil {
274
- return err
275
- }
276
- } else {
277
- // Named struct pointer, use constructor
278
- c.tsw.WriteLiterally("new ")
279
- c.WriteTypeExpr(exp.Type)
280
- }
281
- }
282
- }
283
- } else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
284
- // Anonymous struct literal
285
- structType = underlyingStruct
286
- isStructLiteral = true
287
- isAnonymousStruct = true
288
- // For anonymous structs, don't use constructor, just create object literal
289
- }
290
-
291
- if isStructLiteral && structType != nil {
292
- // --- Struct Literal Handling (Nested) ---
293
- // Categorize fields into direct, embedded, and explicit embedded
294
- directFields, embeddedFields, explicitEmbedded, err := c.categorizeStructFields(
295
- exp, structType, litType, isAnonymousStruct,
296
- )
297
- if err != nil {
298
- return err
299
- }
300
-
301
- // Write the object literal
302
- if isAnonymousStruct {
303
- // For anonymous structs, just write a simple object literal
304
- c.tsw.WriteLiterally("{")
305
- } else {
306
- // For named structs, write the constructor argument
307
- c.tsw.WriteLiterally("({")
308
- }
309
-
310
- // Write all fields
311
- if err := c.writeStructLiteralFields(directFields, embeddedFields, explicitEmbedded, litType); err != nil {
312
- return err
313
- }
314
-
315
- // Close the object literal
316
- if isAnonymousStruct {
317
- c.tsw.WriteLiterally("}")
318
- } else {
319
- c.tsw.WriteLiterally("})")
320
- // Close markAsStructValue wrapper if we opened one
321
- if needsValueMarkerClose {
322
- c.tsw.WriteLiterally(")")
323
- }
324
- }
325
-
326
- } else {
327
- // Non-struct type or anonymous struct, handle normally (or potentially error for anonymous struct literals?)
328
- c.tsw.WriteLiterally("({") // Assuming object literal for constructor
329
- for i, elm := range exp.Elts {
330
- if i != 0 {
331
- c.tsw.WriteLiterally(", ")
332
- }
333
- if err := c.WriteVarRefedValue(elm); err != nil {
334
- return fmt.Errorf("failed to write literal field: %w", err)
335
- }
336
- }
337
- c.tsw.WriteLiterally("})")
338
- }
339
- return nil
340
- }
341
- }
342
-
343
- // Untyped composite literal. Let's use type information to determine what it is.
344
- // First try to get the type information for the expression
345
- if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
346
- underlying := tv.Type.Underlying()
347
- switch underlying.(type) {
348
- case *types.Map, *types.Struct:
349
- // Handle struct directly with the struct literal logic
350
- if structType, ok := underlying.(*types.Struct); ok {
351
- return c.writeUntypedStructLiteral(exp, tv.Type, structType)
352
- }
353
- // Map case would be handled here
354
- return fmt.Errorf("untyped map composite literals not yet supported")
355
- case *types.Array, *types.Slice:
356
- // Handle array/slice
357
- return c.writeUntypedArrayLiteral(exp)
358
- case *types.Pointer:
359
- // Handle pointer to composite literal
360
- ptrType := underlying.(*types.Pointer)
361
- switch elemType := ptrType.Elem().Underlying().(type) {
362
- case *types.Struct:
363
- // This is an anonymous struct literal with inferred pointer type
364
- // Just create the struct object directly - no var-refing needed
365
- // Anonymous literals are not variables, so they don't get var-refed
366
- return c.writeUntypedStructLiteral(exp, ptrType.Elem(), elemType)
367
- default:
368
- return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
369
- }
370
- default:
371
- return fmt.Errorf("unhandled composite literal type: %T", underlying)
372
- }
373
- } else {
374
- return fmt.Errorf("could not determine composite literal type from type information")
375
- }
376
- }
377
-
378
- // writeUntypedArrayLiteral handles untyped composite literals that are arrays/slices
379
- func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
380
- c.tsw.WriteLiterally("[ ")
381
- for i, elm := range exp.Elts {
382
- if i != 0 {
383
- c.tsw.WriteLiterally(", ")
384
- }
385
- if err := c.WriteVarRefedValue(elm); err != nil {
386
- return fmt.Errorf("failed to write untyped array literal element: %w", err)
387
- }
388
- }
389
- c.tsw.WriteLiterally(" ]")
390
- return nil
391
- }
392
-
393
- // writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
394
- func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, actualType types.Type, structType *types.Struct) error {
395
- // Create field mapping like the typed struct case
396
- directFields := make(map[string]ast.Expr)
397
-
398
- // Handle elements that are key-value pairs
399
- for _, elt := range exp.Elts {
400
- if kv, ok := elt.(*ast.KeyValueExpr); ok {
401
- if keyIdent, ok := kv.Key.(*ast.Ident); ok {
402
- directFields[keyIdent.Name] = kv.Value
403
- }
404
- }
405
- }
406
-
407
- // Handle elements that are positional (no key specified)
408
- if len(directFields) == 0 {
409
- // If no key-value pairs, try to match positional values to struct fields
410
- for i, elt := range exp.Elts {
411
- if _, isKV := elt.(*ast.KeyValueExpr); !isKV && i < structType.NumFields() {
412
- field := structType.Field(i)
413
- directFields[field.Name()] = elt
414
- }
415
- }
416
- }
417
-
418
- // Check if this is a named type
419
- isNamed := false
420
- if _, ok := actualType.(*types.Named); ok {
421
- isNamed = true
422
- }
423
-
424
- // Write the object literal
425
- if isNamed {
426
- // For named structs, use constructor
427
- c.tsw.WriteLiterally("$.markAsStructValue(new ")
428
- // Write the type name
429
- c.WriteGoType(actualType, GoTypeContextGeneral)
430
- c.tsw.WriteLiterally("({")
431
- } else {
432
- // For truly anonymous structs, just write a simple object literal
433
- c.tsw.WriteLiterally("{")
434
- }
435
-
436
- firstFieldWritten := false
437
- // Write fields in order
438
- directKeys := make([]string, 0, len(directFields))
439
- for k := range directFields {
440
- directKeys = append(directKeys, k)
441
- }
442
- slices.Sort(directKeys)
443
- for _, keyName := range directKeys {
444
- if firstFieldWritten {
445
- c.tsw.WriteLiterally(", ")
446
- }
447
-
448
- // Convert field name for protobuf types
449
- fieldName := c.convertProtobufFieldNameInLiteral(keyName, structType.Underlying())
450
-
451
- c.tsw.WriteLiterally(fieldName)
452
- c.tsw.WriteLiterally(": ")
453
- if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
454
- return err
455
- }
456
- firstFieldWritten = true
457
- }
458
-
459
- // Close the object literal
460
- if isNamed {
461
- c.tsw.WriteLiterally("}))")
462
- } else {
463
- c.tsw.WriteLiterally("}")
464
- }
465
- return nil
466
- }
467
-
468
- // WriteVarRefedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
469
- // specifically for use as a value within a composite literal (e.g., struct fields,
470
- // map keys/values, or array/slice elements). Its primary goal is to ensure that the
471
- // actual un-refed value of the expression is used.
472
- //
473
- // How it works:
474
- // - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
475
- // the `accessValue` flag to `true`. This ensures that if `ident` refers to a
476
- // GoScript var-refed variable, the generated TypeScript accesses its underlying `.value`
477
- // (e.g., `myVar.value`).
478
- // - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
479
- // `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
480
- // accessing fields or methods, including any required un-var-refing if the field
481
- // itself or the object it's accessed on is var-refed (e.g., `obj.value.field` or
482
- // `obj.field.value`).
483
- // - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
484
- // This function handles pointer dereferencing, which in GoScript's var-refing model
485
- // often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
486
- // - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
487
- // `c.WriteBasicLit(e)` for direct translation.
488
- // - Other expression types: Falls back to `c.WriteValueExpr(expr)` for general
489
- // expression handling. This is important for complex expressions like function
490
- // calls or binary operations that might appear as values within a composite literal.
491
- //
492
- // Necessity and Distinction from `WriteValueExpr`:
493
- // While `WriteValueExpr` is a general-purpose function for translating Go expressions
494
- // and also un-var-refes identifiers (by calling `WriteIdent` with `accessValue: true`),
495
- // `WriteVarRefedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
496
- // 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
497
- // literal, the *un-var-refed value* is mandatory.
498
- // 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
499
- // the correct values for initialization, insulating it from potential changes in
500
- // the default behavior of `WriteValueExpr` regarding un-var-refing.
501
- // 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
502
- // calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
503
- // `WriteValueExpr` for its elements, it could lead to unintended recursion or
504
- // behavior if an element itself was another composite literal. `WriteVarRefedValue`
505
- // acts as a specific intermediary for the *elements*.
506
- //
507
- // In summary, `WriteVarRefedValue` is a specialized dispatcher used by `WriteCompositeLit`
508
- // to guarantee that all parts of a Go composite literal are initialized with their
509
- // proper, unrefed TypeScript values.
510
- func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
511
- if expr == nil {
512
- return fmt.Errorf("nil expression passed to write var refed value")
513
- }
514
-
515
- // Handle different expression types
516
- switch e := expr.(type) {
517
- case *ast.Ident:
518
- c.WriteIdent(e, true)
519
- return nil
520
- case *ast.SelectorExpr:
521
- return c.WriteSelectorExpr(e)
522
- case *ast.StarExpr:
523
- // For star expressions, delegate to WriteStarExpr which handles dereferencing
524
- return c.WriteStarExpr(e)
525
- case *ast.BasicLit:
526
- c.WriteBasicLit(e)
527
- return nil
528
- default:
529
- // For other expression types, use WriteValueExpr
530
- return c.WriteValueExpr(expr)
531
- }
532
- }
533
-
534
- // evaluateConstantExpr attempts to evaluate a Go expression as a compile-time constant.
535
- // It returns the constant value if successful, or nil if the expression is not a constant.
536
- // This is used for evaluating array literal keys that are constant expressions.
537
- func (c *GoToTSCompiler) evaluateConstantExpr(expr ast.Expr) any {
538
- // Use the type checker's constant evaluation
539
- if tv, ok := c.pkg.TypesInfo.Types[expr]; ok && tv.Value != nil {
540
- // The expression has a constant value
541
- switch tv.Value.Kind() {
542
- case constant.Int:
543
- if val, exact := constant.Int64Val(tv.Value); exact {
544
- return int(val)
545
- }
546
- case constant.Float:
547
- if val, exact := constant.Float64Val(tv.Value); exact {
548
- return val
549
- }
550
- case constant.String:
551
- return constant.StringVal(tv.Value)
552
- case constant.Bool:
553
- return constant.BoolVal(tv.Value)
554
- }
555
- }
556
- return nil
557
- }
558
-
559
- // categorizeStructFields organizes the elements of a struct composite literal into
560
- // three categories: direct fields, embedded fields, and explicitly initialized embedded structs.
561
- // Returns maps for direct fields, embedded fields (nested map), and explicit embedded initializations.
562
- func (c *GoToTSCompiler) categorizeStructFields(
563
- exp *ast.CompositeLit,
564
- structType *types.Struct,
565
- litType types.Type,
566
- isAnonymousStruct bool,
567
- ) (
568
- directFields map[string]ast.Expr,
569
- embeddedFields map[string]map[string]ast.Expr,
570
- explicitEmbedded map[string]ast.Expr,
571
- err error,
572
- ) {
573
- directFields = make(map[string]ast.Expr)
574
- embeddedFields = make(map[string]map[string]ast.Expr)
575
- explicitEmbedded = make(map[string]ast.Expr)
576
-
577
- // Pre-populate embeddedFields map keys using the correct property name
578
- for field := range structType.Fields() {
579
- if field.Anonymous() {
580
- fieldType := field.Type()
581
- if ptr, ok := fieldType.(*types.Pointer); ok {
582
- fieldType = ptr.Elem()
583
- }
584
- if named, ok := fieldType.(*types.Named); ok {
585
- // Use the type name as the property name in TS
586
- embeddedPropName := named.Obj().Name()
587
- embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
588
- }
589
- }
590
- }
591
-
592
- // Group literal elements by direct vs embedded fields
593
- for _, elt := range exp.Elts {
594
- kv, ok := elt.(*ast.KeyValueExpr)
595
- if !ok {
596
- continue
597
- } // Skip non-key-value
598
- keyIdent, ok := kv.Key.(*ast.Ident)
599
- if !ok {
600
- continue
601
- } // Skip non-ident keys
602
- keyName := keyIdent.Name
603
-
604
- // Check if this is an explicit embedded struct initialization
605
- // e.g., Person: Person{...} or Person: personVar
606
- if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
607
- // This is an explicit initialization of an embedded struct
608
- explicitEmbedded[keyName] = kv.Value
609
- continue
610
- }
611
-
612
- isDirectField := false
613
- for field := range structType.Fields() {
614
- if field.Name() == keyName {
615
- isDirectField = true
616
- directFields[keyName] = kv.Value
617
- break
618
- }
619
- }
620
-
621
- // For anonymous structs, all fields are direct fields
622
- if isAnonymousStruct {
623
- directFields[keyName] = kv.Value
624
- isDirectField = true
625
- }
626
-
627
- // If not a direct field, return an error
628
- if !isDirectField {
629
- return nil, nil, nil, fmt.Errorf("field %s not found in type %s for composite literal",
630
- keyName, litType.String())
631
- }
632
- }
633
-
634
- // Handle the case where a struct has values without keys (positional initialization)
635
- // This block processes non-key-value elements and associates them with struct fields.
636
- // This applies to both named and anonymous structs.
637
- if len(exp.Elts) > 0 && len(directFields) == 0 {
638
- // Check if any elements in the composite literal are not key-value pairs.
639
- hasNonKeyValueElts := false
640
- for _, elt := range exp.Elts {
641
- // If an element is not a key-value pair, set the flag to true.
642
- if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
643
- hasNonKeyValueElts = true
644
- break
645
- }
646
- }
647
-
648
- if hasNonKeyValueElts {
649
- // Get the fields from the struct type
650
- for i := 0; i < structType.NumFields(); i++ {
651
- field := structType.Field(i)
652
- // If we have a value for this field position
653
- if i < len(exp.Elts) {
654
- // Check if it's not a key-value pair
655
- if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
656
- directFields[field.Name()] = exp.Elts[i]
657
- }
658
- }
659
- }
660
- }
661
- }
662
-
663
- return directFields, embeddedFields, explicitEmbedded, nil
664
- }
665
-
666
- // writeStructLiteralFields writes the field initializations for a struct composite literal.
667
- // It handles direct fields, explicitly initialized embedded structs, and implicitly initialized
668
- // embedded fields, writing them in sorted order.
669
- func (c *GoToTSCompiler) writeStructLiteralFields(
670
- directFields map[string]ast.Expr,
671
- embeddedFields map[string]map[string]ast.Expr,
672
- explicitEmbedded map[string]ast.Expr,
673
- litType types.Type,
674
- ) error {
675
- firstFieldWritten := false
676
-
677
- // Write direct fields that aren't embedded struct names
678
- directKeys := make([]string, 0, len(directFields))
679
- for k := range directFields {
680
- // Skip embedded struct names - we'll handle those separately
681
- if _, isEmbedded := embeddedFields[k]; !isEmbedded {
682
- directKeys = append(directKeys, k)
683
- }
684
- }
685
- slices.Sort(directKeys)
686
- for _, keyName := range directKeys {
687
- if firstFieldWritten {
688
- c.tsw.WriteLiterally(", ")
689
- }
690
-
691
- // Convert field name for protobuf types
692
- fieldName := c.convertProtobufFieldNameInLiteral(keyName, litType)
693
-
694
- c.tsw.WriteLiterally(fieldName)
695
- c.tsw.WriteLiterally(": ")
696
- if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
697
- return err
698
- }
699
- firstFieldWritten = true
700
- }
701
-
702
- // Write explicitly initialized embedded structs
703
- explicitKeys := make([]string, 0, len(explicitEmbedded))
704
- for k := range explicitEmbedded {
705
- explicitKeys = append(explicitKeys, k)
706
- }
707
- slices.Sort(explicitKeys)
708
- for _, embeddedName := range explicitKeys {
709
- if firstFieldWritten {
710
- c.tsw.WriteLiterally(", ")
711
- }
712
- c.tsw.WriteLiterally(embeddedName)
713
- c.tsw.WriteLiterally(": ")
714
-
715
- // Check if the embedded value is a composite literal for a struct
716
- // If so, extract the fields and write them directly
717
- if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
718
- // Write initialization fields directly without the 'new Constructor'
719
- c.tsw.WriteLiterally("{")
720
- for i, elem := range compLit.Elts {
721
- if i > 0 {
722
- c.tsw.WriteLiterally(", ")
723
- }
724
- if err := c.WriteVarRefedValue(elem); err != nil {
725
- return err
726
- }
727
- }
728
- c.tsw.WriteLiterally("}")
729
- } else {
730
- // Not a composite literal, write it normally
731
- if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
732
- return err
733
- }
734
- }
735
- firstFieldWritten = true
736
- }
737
-
738
- // Write embedded fields for structs that weren't explicitly initialized
739
- embeddedKeys := make([]string, 0, len(embeddedFields))
740
- for k := range embeddedFields {
741
- // Skip embedded structs that were explicitly initialized
742
- if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
743
- embeddedKeys = append(embeddedKeys, k)
744
- }
745
- }
746
- slices.Sort(embeddedKeys)
747
- for _, embeddedPropName := range embeddedKeys {
748
- fieldsMap := embeddedFields[embeddedPropName]
749
- if len(fieldsMap) == 0 {
750
- continue
751
- } // Skip empty embedded initializers
752
-
753
- if firstFieldWritten {
754
- c.tsw.WriteLiterally(", ")
755
- }
756
- c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
757
- c.tsw.WriteLiterally(": {")
758
-
759
- innerKeys := make([]string, 0, len(fieldsMap))
760
- for k := range fieldsMap {
761
- innerKeys = append(innerKeys, k)
762
- }
763
- slices.Sort(innerKeys)
764
- for i, keyName := range innerKeys {
765
- if i > 0 {
766
- c.tsw.WriteLiterally(", ")
767
- }
768
- c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
769
- c.tsw.WriteLiterally(": ")
770
- if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
771
- return err
772
- }
773
- }
774
- c.tsw.WriteLiterally("}")
775
- firstFieldWritten = true
776
- }
777
-
778
- return nil
779
- }