goscript 0.0.84 → 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.
- package/README.md +13 -1
- package/cmd/goscript/cmd_compile.go +70 -69
- package/cmd/goscript/cmd_compile_test.go +79 -0
- package/cmd/goscript/main.go +10 -5
- package/compiler/compile-request.go +218 -0
- package/compiler/compiler.go +16 -1336
- package/compiler/compliance_test.go +196 -0
- package/compiler/config.go +6 -13
- package/compiler/diagnostic.go +70 -0
- package/compiler/index.test.ts +28 -28
- package/compiler/index.ts +40 -72
- package/compiler/lowered-program.go +132 -0
- package/compiler/lowering.go +3576 -0
- package/compiler/override-registry.go +422 -0
- package/compiler/override-registry_test.go +207 -0
- package/compiler/package-graph.go +231 -0
- package/compiler/package-graph_test.go +281 -0
- package/compiler/result.go +13 -0
- package/compiler/runtime-contract.go +279 -0
- package/compiler/runtime-contract_test.go +90 -0
- package/compiler/semantic-model-types.go +110 -0
- package/compiler/semantic-model.go +922 -0
- package/compiler/semantic-model_test.go +416 -0
- package/compiler/service.go +133 -0
- package/compiler/skeleton_test.go +1145 -0
- package/compiler/typescript-emitter.go +663 -0
- package/compiler/wasm/compile.go +2 -3
- package/compiler/wasm/compile_test.go +29 -0
- package/compiler/wasm_api.go +10 -159
- package/dist/compiler/index.d.ts +1 -3
- package/dist/compiler/index.js +31 -55
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +13 -0
- package/dist/gs/builtin/builtin.js +23 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +3 -3
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +15 -1
- package/dist/gs/builtin/hostio.js +134 -49
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +11 -0
- package/dist/gs/builtin/type.js +55 -1
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/crypto/rand/index.d.ts +5 -0
- package/dist/gs/crypto/rand/index.js +77 -0
- package/dist/gs/crypto/rand/index.js.map +1 -0
- package/dist/gs/encoding/json/index.d.ts +3 -0
- package/dist/gs/encoding/json/index.js +160 -0
- package/dist/gs/encoding/json/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +29 -0
- package/dist/gs/go/scanner/index.js +120 -0
- package/dist/gs/go/scanner/index.js.map +1 -0
- package/dist/gs/go/token/index.d.ts +31 -0
- package/dist/gs/go/token/index.js +82 -0
- package/dist/gs/go/token/index.js.map +1 -0
- package/dist/gs/internal/abi/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/readfile.js.map +1 -1
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +2 -4
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/rawconn_js.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/tempfile.gs.js +66 -9
- package/dist/gs/os/tempfile.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +9 -9
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +2 -2
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -1
- package/dist/gs/reflect/type.js +85 -14
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/sort/sort.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/builder.js.map +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -0
- package/dist/gs/sync/sync.js +12 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.mod +2 -2
- package/gs/builtin/builtin.ts +27 -0
- package/gs/builtin/hostio.test.ts +177 -0
- package/gs/builtin/hostio.ts +171 -56
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/runtime-contract.test.ts +230 -0
- package/gs/builtin/type.ts +84 -1
- package/gs/crypto/rand/index.test.ts +32 -0
- package/gs/crypto/rand/index.ts +90 -0
- package/gs/crypto/rand/meta.json +5 -0
- package/gs/encoding/json/index.test.ts +65 -0
- package/gs/encoding/json/index.ts +186 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
- package/gs/go/scanner/index.test.ts +50 -0
- package/gs/go/scanner/index.ts +157 -0
- package/gs/go/token/index.test.ts +21 -0
- package/gs/go/token/index.ts +120 -0
- package/gs/os/file_unix_js.test.ts +50 -0
- package/gs/os/meta.json +1 -2
- package/gs/os/tempfile.gs.test.ts +85 -0
- package/gs/os/tempfile.gs.ts +71 -11
- package/gs/os/types_js.gs.ts +9 -9
- package/gs/reflect/index.ts +1 -1
- package/gs/reflect/type.ts +106 -17
- package/gs/reflect/typefor.test.ts +75 -0
- package/gs/sync/sync.test.ts +24 -0
- package/gs/sync/sync.ts +12 -0
- package/package.json +13 -13
- package/compiler/analysis.go +0 -3475
- package/compiler/analysis_test.go +0 -338
- package/compiler/assignment.go +0 -580
- package/compiler/builtin_test.go +0 -92
- package/compiler/code-writer.go +0 -115
- package/compiler/compiler_test.go +0 -149
- package/compiler/composite-lit.go +0 -779
- package/compiler/config_test.go +0 -62
- package/compiler/constraint.go +0 -86
- package/compiler/decl.go +0 -801
- package/compiler/expr-call-async.go +0 -188
- package/compiler/expr-call-builtins.go +0 -208
- package/compiler/expr-call-helpers.go +0 -382
- package/compiler/expr-call-make.go +0 -318
- package/compiler/expr-call-type-conversion.go +0 -520
- package/compiler/expr-call.go +0 -413
- package/compiler/expr-selector.go +0 -343
- package/compiler/expr-star.go +0 -82
- package/compiler/expr-type.go +0 -442
- package/compiler/expr-value.go +0 -89
- package/compiler/expr.go +0 -773
- package/compiler/field.go +0 -183
- package/compiler/gs_dependencies_test.go +0 -298
- package/compiler/lit.go +0 -322
- package/compiler/output.go +0 -72
- package/compiler/primitive.go +0 -149
- package/compiler/protobuf.go +0 -697
- package/compiler/sanitize.go +0 -100
- package/compiler/spec-struct.go +0 -995
- package/compiler/spec-value.go +0 -540
- package/compiler/spec.go +0 -725
- package/compiler/stmt-assign.go +0 -664
- package/compiler/stmt-for.go +0 -266
- package/compiler/stmt-range.go +0 -475
- package/compiler/stmt-select.go +0 -262
- package/compiler/stmt-type-switch.go +0 -147
- package/compiler/stmt.go +0 -1308
- package/compiler/type-assert.go +0 -386
- package/compiler/type-info.go +0 -156
- package/compiler/type-utils.go +0 -207
- 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
|
-
}
|