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.
Files changed (188) 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 +23 -0
  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 +15 -1
  39. package/dist/gs/builtin/hostio.js +134 -49
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  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/slice.d.ts +1 -1
  45. package/dist/gs/builtin/slice.js.map +1 -1
  46. package/dist/gs/builtin/type.d.ts +11 -0
  47. package/dist/gs/builtin/type.js +55 -1
  48. package/dist/gs/builtin/type.js.map +1 -1
  49. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  50. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  51. package/dist/gs/bytes/reader.gs.js.map +1 -1
  52. package/dist/gs/context/context.js.map +1 -1
  53. package/dist/gs/crypto/rand/index.d.ts +5 -0
  54. package/dist/gs/crypto/rand/index.js +77 -0
  55. package/dist/gs/crypto/rand/index.js.map +1 -0
  56. package/dist/gs/encoding/json/index.d.ts +3 -0
  57. package/dist/gs/encoding/json/index.js +160 -0
  58. package/dist/gs/encoding/json/index.js.map +1 -0
  59. package/dist/gs/fmt/fmt.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  64. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  65. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  66. package/dist/gs/go/scanner/index.d.ts +29 -0
  67. package/dist/gs/go/scanner/index.js +120 -0
  68. package/dist/gs/go/scanner/index.js.map +1 -0
  69. package/dist/gs/go/token/index.d.ts +31 -0
  70. package/dist/gs/go/token/index.js +82 -0
  71. package/dist/gs/go/token/index.js.map +1 -0
  72. package/dist/gs/internal/abi/index.js.map +1 -1
  73. package/dist/gs/io/fs/fs.js.map +1 -1
  74. package/dist/gs/io/fs/readdir.js.map +1 -1
  75. package/dist/gs/io/fs/readfile.js.map +1 -1
  76. package/dist/gs/io/fs/stat.js.map +1 -1
  77. package/dist/gs/io/fs/sub.js.map +1 -1
  78. package/dist/gs/io/io.js.map +1 -1
  79. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  80. package/dist/gs/os/error.gs.js +2 -4
  81. package/dist/gs/os/error.gs.js.map +1 -1
  82. package/dist/gs/os/exec.gs.js.map +1 -1
  83. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  84. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  85. package/dist/gs/os/root_js.gs.js.map +1 -1
  86. package/dist/gs/os/tempfile.gs.js +66 -9
  87. package/dist/gs/os/tempfile.gs.js.map +1 -1
  88. package/dist/gs/os/types.gs.js.map +1 -1
  89. package/dist/gs/os/types_js.gs.js +9 -9
  90. package/dist/gs/os/types_js.gs.js.map +1 -1
  91. package/dist/gs/os/types_unix.gs.js.map +1 -1
  92. package/dist/gs/path/filepath/match.js.map +1 -1
  93. package/dist/gs/path/match.js.map +1 -1
  94. package/dist/gs/path/path.js.map +1 -1
  95. package/dist/gs/reflect/index.d.ts +2 -2
  96. package/dist/gs/reflect/index.js +1 -1
  97. package/dist/gs/reflect/index.js.map +1 -1
  98. package/dist/gs/reflect/map.js.map +1 -1
  99. package/dist/gs/reflect/type.d.ts +2 -1
  100. package/dist/gs/reflect/type.js +85 -14
  101. package/dist/gs/reflect/type.js.map +1 -1
  102. package/dist/gs/reflect/types.js.map +1 -1
  103. package/dist/gs/reflect/visiblefields.js.map +1 -1
  104. package/dist/gs/runtime/runtime.js.map +1 -1
  105. package/dist/gs/sort/sort.gs.js.map +1 -1
  106. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  107. package/dist/gs/strconv/quote.gs.js.map +1 -1
  108. package/dist/gs/strings/builder.js.map +1 -1
  109. package/dist/gs/strings/reader.js.map +1 -1
  110. package/dist/gs/strings/replace.js.map +1 -1
  111. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  112. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  113. package/dist/gs/sync/sync.d.ts +1 -0
  114. package/dist/gs/sync/sync.js +12 -0
  115. package/dist/gs/sync/sync.js.map +1 -1
  116. package/dist/gs/time/time.js.map +1 -1
  117. package/dist/gs/unicode/unicode.js.map +1 -1
  118. package/go.mod +2 -2
  119. package/gs/builtin/builtin.ts +27 -0
  120. package/gs/builtin/hostio.test.ts +177 -0
  121. package/gs/builtin/hostio.ts +171 -56
  122. package/gs/builtin/index.ts +1 -0
  123. package/gs/builtin/runtime-contract.test.ts +230 -0
  124. package/gs/builtin/type.ts +84 -1
  125. package/gs/crypto/rand/index.test.ts +32 -0
  126. package/gs/crypto/rand/index.ts +90 -0
  127. package/gs/crypto/rand/meta.json +5 -0
  128. package/gs/encoding/json/index.test.ts +65 -0
  129. package/gs/encoding/json/index.ts +186 -0
  130. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  131. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  132. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  133. package/gs/go/scanner/index.test.ts +50 -0
  134. package/gs/go/scanner/index.ts +157 -0
  135. package/gs/go/token/index.test.ts +21 -0
  136. package/gs/go/token/index.ts +120 -0
  137. package/gs/os/file_unix_js.test.ts +50 -0
  138. package/gs/os/meta.json +1 -2
  139. package/gs/os/tempfile.gs.test.ts +85 -0
  140. package/gs/os/tempfile.gs.ts +71 -11
  141. package/gs/os/types_js.gs.ts +9 -9
  142. package/gs/reflect/index.ts +1 -1
  143. package/gs/reflect/type.ts +106 -17
  144. package/gs/reflect/typefor.test.ts +75 -0
  145. package/gs/sync/sync.test.ts +24 -0
  146. package/gs/sync/sync.ts +12 -0
  147. package/package.json +13 -13
  148. package/compiler/analysis.go +0 -3475
  149. package/compiler/analysis_test.go +0 -338
  150. package/compiler/assignment.go +0 -580
  151. package/compiler/builtin_test.go +0 -92
  152. package/compiler/code-writer.go +0 -115
  153. package/compiler/compiler_test.go +0 -149
  154. package/compiler/composite-lit.go +0 -779
  155. package/compiler/config_test.go +0 -62
  156. package/compiler/constraint.go +0 -86
  157. package/compiler/decl.go +0 -801
  158. package/compiler/expr-call-async.go +0 -188
  159. package/compiler/expr-call-builtins.go +0 -208
  160. package/compiler/expr-call-helpers.go +0 -382
  161. package/compiler/expr-call-make.go +0 -318
  162. package/compiler/expr-call-type-conversion.go +0 -520
  163. package/compiler/expr-call.go +0 -413
  164. package/compiler/expr-selector.go +0 -343
  165. package/compiler/expr-star.go +0 -82
  166. package/compiler/expr-type.go +0 -442
  167. package/compiler/expr-value.go +0 -89
  168. package/compiler/expr.go +0 -773
  169. package/compiler/field.go +0 -183
  170. package/compiler/gs_dependencies_test.go +0 -298
  171. package/compiler/lit.go +0 -322
  172. package/compiler/output.go +0 -72
  173. package/compiler/primitive.go +0 -149
  174. package/compiler/protobuf.go +0 -697
  175. package/compiler/sanitize.go +0 -100
  176. package/compiler/spec-struct.go +0 -995
  177. package/compiler/spec-value.go +0 -540
  178. package/compiler/spec.go +0 -725
  179. package/compiler/stmt-assign.go +0 -664
  180. package/compiler/stmt-for.go +0 -266
  181. package/compiler/stmt-range.go +0 -475
  182. package/compiler/stmt-select.go +0 -262
  183. package/compiler/stmt-type-switch.go +0 -147
  184. package/compiler/stmt.go +0 -1308
  185. package/compiler/type-assert.go +0 -386
  186. package/compiler/type-info.go +0 -156
  187. package/compiler/type-utils.go +0 -207
  188. package/compiler/type.go +0 -892
package/compiler/spec.go DELETED
@@ -1,725 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "fmt"
5
- "go/ast"
6
- "go/types"
7
- "strings"
8
-
9
- "github.com/pkg/errors"
10
- )
11
-
12
- // WriteSpec is a dispatcher function that translates a Go specification node
13
- // (`ast.Spec`) into its TypeScript equivalent. It handles different types of
14
- // specifications found within `GenDecl` (general declarations):
15
- // - `ast.ImportSpec` (import declarations): Delegates to `WriteImportSpec`.
16
- // - `ast.ValueSpec` (variable or constant declarations): Delegates to `WriteValueSpec`.
17
- // - `ast.TypeSpec` (type definitions like structs, interfaces): Delegates to `WriteTypeSpec`.
18
- // If an unknown specification type is encountered, it returns an error.
19
- func (c *GoToTSCompiler) WriteSpec(a ast.Spec) error {
20
- switch d := a.(type) {
21
- case *ast.ImportSpec:
22
- c.WriteImportSpec(d)
23
- case *ast.ValueSpec:
24
- if err := c.WriteValueSpec(d); err != nil {
25
- return err
26
- }
27
- case *ast.TypeSpec:
28
- if err := c.WriteTypeSpec(d); err != nil {
29
- return err
30
- }
31
- default:
32
- return fmt.Errorf("unknown spec type: %T", a)
33
- }
34
- return nil
35
- }
36
-
37
- func (c *GoToTSCompiler) getEmbeddedFieldKeyName(fieldType types.Type) string {
38
- trueType := fieldType
39
- if ptr, isPtr := trueType.(*types.Pointer); isPtr {
40
- trueType = ptr.Elem()
41
- }
42
-
43
- if named, isNamed := trueType.(*types.Named); isNamed {
44
- return named.Obj().Name()
45
- } else {
46
- // Fallback for unnamed embedded types, though less common for structs
47
- fieldKeyName := trueType.String()
48
- if len(fieldKeyName) > 0 {
49
- fieldKeyName = strings.ToUpper(fieldKeyName[:1]) + fieldKeyName[1:]
50
- }
51
- if dotIndex := strings.LastIndex(fieldKeyName, "."); dotIndex != -1 {
52
- fieldKeyName = fieldKeyName[dotIndex+1:]
53
- }
54
- return fieldKeyName
55
- }
56
- }
57
-
58
- func (c *GoToTSCompiler) writeGetterSetter(fieldName string, fieldType types.Type, doc, comment *ast.CommentGroup, astType ast.Expr) {
59
- // Use AST type information if available to preserve qualified names
60
- // Note: getASTTypeString/getTypeString already includes "null |" for interface types
61
- var fieldTypeStr string
62
- if astType != nil {
63
- fieldTypeStr = c.getASTTypeString(astType, fieldType)
64
- } else {
65
- fieldTypeStr = c.getTypeString(fieldType)
66
- }
67
-
68
- // Generate getter
69
- if doc != nil {
70
- c.WriteDoc(doc)
71
- }
72
- if comment != nil {
73
- c.WriteDoc(comment)
74
- }
75
- c.tsw.WriteLinef("public get %s(): %s {", fieldName, fieldTypeStr)
76
- c.tsw.Indent(1)
77
- c.tsw.WriteLinef("return this._fields.%s.value", fieldName)
78
- c.tsw.Indent(-1)
79
- c.tsw.WriteLine("}")
80
-
81
- // Generate setter (no comments)
82
- c.tsw.WriteLinef("public set %s(value: %s) {", fieldName, fieldTypeStr)
83
- c.tsw.Indent(1)
84
- c.tsw.WriteLinef("this._fields.%s.value = value", fieldName)
85
- c.tsw.Indent(-1)
86
- c.tsw.WriteLine("}")
87
- c.tsw.WriteLine("")
88
- }
89
-
90
- func (c *GoToTSCompiler) writeVarRefedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool, astType ast.Expr) {
91
- c.tsw.WriteLiterally(fieldName)
92
- c.tsw.WriteLiterally(": $.varRef(")
93
-
94
- if isEmbedded {
95
- c.writeEmbeddedFieldInitializer(fieldName, fieldType)
96
- } else {
97
- c.writeRegularFieldInitializer(fieldName, fieldType, astType)
98
- }
99
-
100
- c.tsw.WriteLiterally(")")
101
- }
102
-
103
- func (c *GoToTSCompiler) writeEmbeddedFieldInitializer(fieldName string, fieldType types.Type) {
104
- _, isPtr := fieldType.(*types.Pointer)
105
- _, isInterface := fieldType.Underlying().(*types.Interface)
106
-
107
- if isPtr || isInterface {
108
- c.tsw.WriteLiterallyf("init?.%s ?? null", fieldName)
109
- return
110
- }
111
-
112
- // Check if the embedded type is an interface
113
- embeddedTypeUnderlying := fieldType
114
- if named, isNamed := embeddedTypeUnderlying.(*types.Named); isNamed {
115
- embeddedTypeUnderlying = named.Underlying()
116
- }
117
-
118
- if _, isInterface := embeddedTypeUnderlying.(*types.Interface); isInterface {
119
- // For interfaces, use the provided value or null instead of trying to instantiate
120
- c.tsw.WriteLiterallyf("init?.%s ?? null", fieldName)
121
- return
122
- }
123
-
124
- // For structs, instantiate with provided fields
125
- typeForNew := c.getTypeString(fieldType)
126
- c.tsw.WriteLiterallyf("new %s(init?.%s)", typeForNew, fieldName)
127
- }
128
-
129
- func (c *GoToTSCompiler) writeRegularFieldInitializer(fieldName string, fieldType types.Type, astType ast.Expr) {
130
- // Check if this is a struct value type that needs cloning
131
- if c.isStructValueType(fieldType) {
132
- structTypeNameForClone := c.getTypeString(fieldType)
133
- c.tsw.WriteLiterallyf("init?.%s ? $.markAsStructValue(init.%s.clone()) : new %s()", fieldName, fieldName, structTypeNameForClone)
134
- return
135
- }
136
-
137
- c.tsw.WriteLiterallyf("init?.%s ?? ", fieldName)
138
-
139
- // Priority 1: Check if this is a wrapper type
140
- if c.isWrapperType(fieldType) {
141
- // For wrapper types, use the zero value of the underlying type with type casting
142
- if named, ok := fieldType.(*types.Named); ok {
143
- c.WriteZeroValueForType(named.Underlying())
144
- c.tsw.WriteLiterally(" as ")
145
- c.WriteGoType(fieldType, GoTypeContextGeneral)
146
- } else if alias, ok := fieldType.(*types.Alias); ok {
147
- c.WriteZeroValueForType(alias.Underlying())
148
- c.tsw.WriteLiterally(" as ")
149
- c.WriteGoType(fieldType, GoTypeContextGeneral)
150
- } else {
151
- // Fallback to original behavior
152
- c.WriteZeroValueForType(fieldType)
153
- }
154
- return
155
- }
156
-
157
- // Priority 2: Handle imported types with basic underlying types (like os.FileMode)
158
- if c.isImportedBasicType(fieldType) {
159
- if err := c.writeImportedBasicTypeZeroValue(fieldType); err != nil {
160
- // Emit diagnostic and conservative zero value to avoid silent fallback
161
- c.tsw.WriteCommentInlinef(" writeImportedBasicTypeZeroValue error: %v ", err)
162
- c.WriteZeroValueForType(fieldType)
163
- }
164
- return
165
- }
166
-
167
- // Priority 3: Handle named types
168
- if named, isNamed := fieldType.(*types.Named); isNamed {
169
- c.writeNamedTypeZeroValue(named)
170
- return
171
- }
172
-
173
- // Priority 4: Handle type aliases
174
- if alias, isAlias := fieldType.(*types.Alias); isAlias {
175
- c.writeTypeAliasZeroValue(alias, astType)
176
- return
177
- }
178
-
179
- c.WriteZeroValueForType(fieldType)
180
- }
181
-
182
- func (c *GoToTSCompiler) writeImportedBasicTypeZeroValue(fieldType types.Type) error {
183
- if named, ok := fieldType.(*types.Named); ok {
184
- underlying := named.Underlying()
185
- // Write zero value of underlying type with type casting
186
- c.WriteZeroValueForType(underlying)
187
- c.tsw.WriteLiterally(" as ")
188
- c.WriteGoType(fieldType, GoTypeContextGeneral)
189
- return nil
190
- }
191
-
192
- if alias, ok := fieldType.(*types.Alias); ok {
193
- underlying := alias.Underlying()
194
- // Write zero value of underlying type with type casting
195
- c.WriteZeroValueForType(underlying)
196
- c.tsw.WriteLiterally(" as ")
197
- c.WriteGoType(fieldType, GoTypeContextGeneral)
198
- return nil
199
- }
200
-
201
- // Reaching here indicates an internal inconsistency between isImportedBasicType and field kind
202
- // Return an error to surface the issue to callers
203
- return fmt.Errorf("writeImportedBasicTypeZeroValue: unexpected non-named/alias type %T", fieldType)
204
- }
205
-
206
- func (c *GoToTSCompiler) writeNamedTypeZeroValue(named *types.Named) {
207
- // Check if this is a wrapper type first
208
- if c.isWrapperType(named) {
209
- // For wrapper types, use the zero value of the underlying type with type casting
210
- c.WriteZeroValueForType(named.Underlying())
211
- c.tsw.WriteLiterally(" as ")
212
- c.WriteGoType(named, GoTypeContextGeneral)
213
- return
214
- }
215
-
216
- // Check if underlying type is an interface
217
- if _, isInterface := named.Underlying().(*types.Interface); isInterface {
218
- c.tsw.WriteLiterally("null")
219
- return
220
- }
221
-
222
- // Check if underlying type is a struct
223
- if _, isStruct := named.Underlying().(*types.Struct); isStruct {
224
- c.WriteZeroValueForType(named)
225
- return
226
- }
227
-
228
- // Check if underlying type is a function
229
- if _, isFunc := named.Underlying().(*types.Signature); isFunc {
230
- c.tsw.WriteLiterally("null")
231
- return
232
- }
233
-
234
- // For non-struct, non-interface named types, use constructor
235
- c.tsw.WriteLiterally("new ")
236
- c.WriteNamedType(named)
237
- c.tsw.WriteLiterally("(")
238
- c.WriteZeroValueForType(named.Underlying())
239
- c.tsw.WriteLiterally(")")
240
- }
241
-
242
- func (c *GoToTSCompiler) writeTypeAliasZeroValue(alias *types.Alias, astType ast.Expr) {
243
- // Check if this is a wrapper type first
244
- if c.isWrapperType(alias) {
245
- // For wrapper types, use the zero value of the underlying type with type casting
246
- c.WriteZeroValueForType(alias.Underlying())
247
- c.tsw.WriteLiterally(" as ")
248
- c.WriteGoType(alias, GoTypeContextGeneral)
249
- return
250
- }
251
-
252
- // Check if underlying type is an interface
253
- if _, isInterface := alias.Underlying().(*types.Interface); isInterface {
254
- c.tsw.WriteLiterally("null")
255
- return
256
- }
257
-
258
- // Check if underlying type is a struct
259
- if _, isStruct := alias.Underlying().(*types.Struct); isStruct {
260
- c.WriteZeroValueForType(alias)
261
- return
262
- }
263
-
264
- // Check if underlying type is a function
265
- if _, isFunc := alias.Underlying().(*types.Signature); isFunc {
266
- c.tsw.WriteLiterally("null")
267
- return
268
- }
269
-
270
- // For non-struct, non-interface type aliases, use constructor
271
- c.tsw.WriteLiterally("new ")
272
- // Use AST type information if available to preserve qualified names
273
- if astType != nil {
274
- c.WriteTypeExpr(astType)
275
- } else {
276
- c.WriteGoType(alias, GoTypeContextGeneral)
277
- }
278
- c.tsw.WriteLiterally("(")
279
- c.WriteZeroValueForType(alias.Underlying())
280
- c.tsw.WriteLiterally(")")
281
- }
282
-
283
- // hasReceiverMethods checks if a type declaration has any receiver methods defined
284
- func (c *GoToTSCompiler) hasReceiverMethods(typeName string) bool {
285
- for _, fileSyntax := range c.pkg.Syntax {
286
- for _, decl := range fileSyntax.Decls {
287
- funcDecl, isFunc := decl.(*ast.FuncDecl)
288
- if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
289
- continue
290
- }
291
- recvField := funcDecl.Recv.List[0]
292
- recvType := recvField.Type
293
- if starExpr, ok := recvType.(*ast.StarExpr); ok {
294
- recvType = starExpr.X
295
- }
296
-
297
- // Check for both simple identifiers (FileMode) and generic types (FileMode[T])
298
- var recvTypeName string
299
- if ident, ok := recvType.(*ast.Ident); ok {
300
- recvTypeName = ident.Name
301
- } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
302
- if ident, ok := indexExpr.X.(*ast.Ident); ok {
303
- recvTypeName = ident.Name
304
- }
305
- }
306
-
307
- if recvTypeName == typeName {
308
- return true
309
- }
310
- }
311
- }
312
- return false
313
- }
314
-
315
- // WriteNamedTypeWithMethods generates TypeScript code for Go named types that have methods.
316
- // Instead of generating a class, it now generates:
317
- // 1. A type alias for the underlying type
318
- // 2. Function declarations for each method (TypeName_MethodName)
319
- // 3. Function implementations for each method
320
- func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
321
- className := a.Name.Name
322
-
323
- // Add export for Go-exported types (but not if inside a function)
324
- isInsideFunction := false
325
- if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
326
- isInsideFunction = nodeInfo.IsInsideFunction
327
- }
328
-
329
- if !isInsideFunction {
330
- c.tsw.WriteLiterally("export ")
331
- }
332
-
333
- // Generate type alias instead of class
334
- c.tsw.WriteLiterally("type ")
335
- c.tsw.WriteLiterally(className)
336
- c.tsw.WriteLiterally(" = ")
337
- // Use AST-based type writing to preserve qualified names like os.FileInfo
338
- c.WriteTypeExpr(a.Type)
339
- c.tsw.WriteLine(";")
340
- c.tsw.WriteLine("")
341
-
342
- // Generate function declarations and implementations for each method
343
- for _, fileSyntax := range c.pkg.Syntax {
344
- for _, decl := range fileSyntax.Decls {
345
- funcDecl, isFunc := decl.(*ast.FuncDecl)
346
- if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
347
- continue
348
- }
349
- recvField := funcDecl.Recv.List[0]
350
- recvType := recvField.Type
351
- isPointerReceiver := false
352
- if starExpr, ok := recvType.(*ast.StarExpr); ok {
353
- recvType = starExpr.X
354
- isPointerReceiver = true
355
- }
356
-
357
- // Check for both simple identifiers (FileMode) and generic types (FileMode[T])
358
- var recvTypeName string
359
- if ident, ok := recvType.(*ast.Ident); ok {
360
- recvTypeName = ident.Name
361
- } else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
362
- if ident, ok := indexExpr.X.(*ast.Ident); ok {
363
- recvTypeName = ident.Name
364
- }
365
- }
366
-
367
- if recvTypeName == className {
368
- c.tsw.WriteLiterally("export function ")
369
- var isAsync bool
370
- if obj := c.pkg.TypesInfo.Defs[funcDecl.Name]; obj != nil {
371
- isAsync = c.analysis.IsAsyncFunc(obj)
372
- }
373
- if isAsync {
374
- c.tsw.WriteLiterally("async ")
375
- }
376
- c.tsw.WriteLiterally(className)
377
- c.tsw.WriteLiterally("_")
378
- c.tsw.WriteLiterally(funcDecl.Name.Name)
379
- c.tsw.WriteLiterally("(")
380
-
381
- // First parameter is the receiver - use the original receiver parameter name
382
- var receiverParamName string = "receiver" // default fallback
383
- if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
384
- if len(funcDecl.Recv.List[0].Names) > 0 {
385
- if name := funcDecl.Recv.List[0].Names[0]; name != nil && name.Name != "_" {
386
- receiverParamName = name.Name
387
- }
388
- }
389
- }
390
- c.tsw.WriteLiterally(receiverParamName)
391
- c.tsw.WriteLiterally(": ")
392
-
393
- // For pointer receivers, use VarRef<T>
394
- if isPointerReceiver {
395
- c.tsw.WriteLiterally("$.VarRef<")
396
- c.tsw.WriteLiterally(className)
397
- c.tsw.WriteLiterally(">")
398
- } else {
399
- c.tsw.WriteLiterally(className)
400
- }
401
-
402
- // Add other parameters
403
- if funcDecl.Type.Params != nil && len(funcDecl.Type.Params.List) > 0 {
404
- c.tsw.WriteLiterally(", ")
405
- c.WriteFieldList(funcDecl.Type.Params, true) // true = arguments
406
- }
407
-
408
- c.tsw.WriteLiterally(")")
409
-
410
- // Add return type
411
- if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
412
- c.tsw.WriteLiterally(": ")
413
- if isAsync {
414
- c.tsw.WriteLiterally("Promise<")
415
- }
416
- if len(funcDecl.Type.Results.List) == 1 {
417
- c.WriteTypeExpr(funcDecl.Type.Results.List[0].Type)
418
- } else {
419
- c.tsw.WriteLiterally("[")
420
- first := true
421
- for _, field := range funcDecl.Type.Results.List {
422
- // Each field may represent multiple return values (e.g., "a, b int")
423
- count := len(field.Names)
424
- if count == 0 {
425
- count = 1 // Unnamed return value
426
- }
427
- for j := 0; j < count; j++ {
428
- if !first {
429
- c.tsw.WriteLiterally(", ")
430
- }
431
- first = false
432
- c.WriteTypeExpr(field.Type)
433
- }
434
- }
435
- c.tsw.WriteLiterally("]")
436
- }
437
- if isAsync {
438
- c.tsw.WriteLiterally(">")
439
- }
440
- } else {
441
- if isAsync {
442
- c.tsw.WriteLiterally(": Promise<void>")
443
- } else {
444
- c.tsw.WriteLiterally(": void")
445
- }
446
- }
447
-
448
- c.tsw.WriteLine(" {")
449
- c.tsw.Indent(1)
450
-
451
- // Write method body with receiver as first parameter
452
- if err := c.writeWrapperFunctionBody(funcDecl, className); err != nil {
453
- return err
454
- }
455
-
456
- c.tsw.Indent(-1)
457
- c.tsw.WriteLine("}")
458
- c.tsw.WriteLine("")
459
- }
460
- }
461
- }
462
-
463
- return nil
464
- }
465
-
466
- // writeWrapperFunctionBody writes the body of a wrapper function, treating the receiver as the first parameter
467
- func (c *GoToTSCompiler) writeWrapperFunctionBody(decl *ast.FuncDecl, typeName string) error {
468
- // Write function body statements directly - identifier mapping is handled by pre-computed analysis
469
- if decl.Body != nil {
470
- for _, stmt := range decl.Body.List {
471
- if err := c.WriteStmt(stmt); err != nil {
472
- return err
473
- }
474
- }
475
- }
476
- return nil
477
- }
478
-
479
- // WriteTypeSpec writes the type specification to the output.
480
- func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
481
- if a.Doc != nil {
482
- c.WriteDoc(a.Doc)
483
- }
484
- if a.Comment != nil {
485
- c.WriteDoc(a.Comment)
486
- }
487
-
488
- switch t := a.Type.(type) {
489
- case *ast.StructType:
490
- return c.WriteStructTypeSpec(a, t)
491
- case *ast.InterfaceType:
492
- return c.WriteInterfaceTypeSpec(a, t)
493
- default:
494
- // Check if this type has receiver methods
495
- if c.hasReceiverMethods(a.Name.Name) {
496
- if named, ok := c.pkg.TypesInfo.Defs[a.Name].Type().(*types.Named); ok {
497
- if _, isStruct := named.Underlying().(*types.Struct); isStruct {
498
- return c.WriteNamedStructTypeSpec(a, named)
499
- }
500
- }
501
- return c.WriteNamedTypeWithMethods(a)
502
- }
503
-
504
- // Always export types for cross-file imports within the same package (but not if inside a function)
505
- isInsideFunction := false
506
- if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
507
- isInsideFunction = nodeInfo.IsInsideFunction
508
- }
509
-
510
- if !isInsideFunction {
511
- c.tsw.WriteLiterally("export ")
512
- }
513
- c.tsw.WriteLiterally("type ")
514
- if err := c.WriteValueExpr(a.Name); err != nil {
515
- return err
516
- }
517
-
518
- // Write type parameters if present (for generics)
519
- if a.TypeParams != nil {
520
- c.WriteTypeParameters(a.TypeParams)
521
- }
522
-
523
- c.tsw.WriteLiterally(" = ")
524
- c.WriteTypeExpr(a.Type) // The aliased type
525
- c.tsw.WriteLine(";")
526
- }
527
- return nil
528
- }
529
-
530
- // WriteInterfaceTypeSpec writes the TypeScript type for a Go interface type.
531
- func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.InterfaceType) error {
532
- // Add export for Go-exported interfaces (but not if inside a function)
533
- isInsideFunction := false
534
- if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
535
- isInsideFunction = nodeInfo.IsInsideFunction
536
- }
537
-
538
- if !isInsideFunction {
539
- c.tsw.WriteLiterally("export ")
540
- }
541
- c.tsw.WriteLiterally("type ")
542
- if err := c.WriteValueExpr(a.Name); err != nil {
543
- return err
544
- }
545
-
546
- // Write type parameters if present (for generics)
547
- if a.TypeParams != nil {
548
- c.WriteTypeParameters(a.TypeParams)
549
- }
550
-
551
- c.tsw.WriteLiterally(" = ")
552
- // Get the types.Interface from the ast.InterfaceType.
553
- // For an interface definition like `type MyInterface interface { M() }`,
554
- // 't' is the *ast.InterfaceType representing `interface { M() }`.
555
- // TypesInfo.TypeOf(t) will give the *types.Interface.
556
- goType := c.pkg.TypesInfo.TypeOf(t)
557
- if goType == nil {
558
- return errors.Errorf("could not get type for interface AST node for %s", a.Name.Name)
559
- }
560
- ifaceType, ok := goType.(*types.Interface)
561
- if !ok {
562
- return errors.Errorf("expected *types.Interface, got %T for %s when processing interface literal", goType, a.Name.Name)
563
- }
564
- c.WriteInterfaceType(ifaceType, t) // Pass the *ast.InterfaceType for comment fetching
565
- c.tsw.WriteLine("")
566
-
567
- // Add code to register the interface with the runtime system
568
- // Build full package path name for registration
569
- interfaceName := a.Name.Name
570
- pkgPath := c.pkg.Types.Path()
571
- pkgName := c.pkg.Types.Name()
572
- if pkgPath != "" && pkgName != "main" {
573
- interfaceName = pkgPath + "." + interfaceName
574
- } else if pkgName == "main" {
575
- interfaceName = "main." + interfaceName
576
- }
577
- c.tsw.WriteLine("")
578
- c.tsw.WriteLinef("$.registerInterfaceType(")
579
- c.tsw.WriteLinef(" '%s',", interfaceName)
580
- c.tsw.WriteLinef(" null, // Zero value for interface is null")
581
-
582
- // Collect methods for the interface type
583
- var interfaceMethods []*types.Func
584
- if ifaceType != nil { // ifaceType is *types.Interface
585
- for method := range ifaceType.ExplicitMethods() {
586
- interfaceMethods = append(interfaceMethods, method)
587
- }
588
- // TODO: Handle embedded interface methods if necessary for full signature collection.
589
- // For now, explicit methods are covered.
590
- }
591
- c.tsw.WriteLiterally(" [")
592
- c.writeMethodSignatures(interfaceMethods)
593
- c.tsw.WriteLiterally("]")
594
- c.tsw.WriteLine("")
595
-
596
- c.tsw.WriteLinef(");")
597
-
598
- return nil
599
- }
600
-
601
- // WriteImportSpec translates a Go import specification (`ast.ImportSpec`)
602
- // into a TypeScript import statement.
603
- //
604
- // It extracts the Go import path (e.g., `"path/to/pkg"`) and determines the
605
- // import alias/name for TypeScript. If the Go import has an explicit name
606
- // (e.g., `alias "path/to/pkg"`), that alias is used. Otherwise, the package
607
- // name is derived from the actual Go package name, not the import path.
608
- //
609
- // The Go path is then translated to a TypeScript module path using
610
- // `translateGoPathToTypescriptPath`.
611
- //
612
- // Finally, it writes a TypeScript import statement like `import * as alias from "typescript/path/to/pkg";`
613
- // and records the import details in `c.analysis.Imports` for later use (e.g.,
614
- // resolving qualified identifiers).
615
- func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
616
- if a.Doc != nil {
617
- c.WriteDoc(a.Doc)
618
- }
619
- if a.Comment != nil {
620
- c.WriteDoc(a.Comment)
621
- }
622
-
623
- goPath := a.Path.Value[1 : len(a.Path.Value)-1]
624
-
625
- // Determine the import name to use in TypeScript
626
- var impName string
627
- if a.Name != nil && a.Name.Name != "" && a.Name.Name != "." && a.Name.Name != "_" {
628
- // Explicit alias provided: import alias "path/to/pkg"
629
- // Skip dot imports (. "pkg") and blank imports (_ "pkg")
630
- impName = a.Name.Name
631
- } else if a.Name != nil && a.Name.Name == "_" {
632
- // Blank import (_ "pkg") - skip entirely, these are for side effects only
633
- return
634
- } else {
635
- // No explicit alias, or dot import - use the actual package name from type information
636
- // This handles cases where package name differs from the last path segment
637
- // For dot imports, we still need a valid identifier for the import
638
- if actualName, err := getActualPackageName(goPath, c.pkg.Imports); err == nil {
639
- impName = actualName
640
- } else {
641
- // Fallback to last segment of path if package not found in type information
642
- pts := strings.Split(goPath, "/")
643
- impName = pts[len(pts)-1]
644
- }
645
- }
646
-
647
- // Apply sanitization to handle known names like "Promise" -> "PromiseType"
648
- impName = c.sanitizeIdentifier(impName)
649
-
650
- // All Go package imports are mapped to the @goscript/ scope.
651
- // The TypeScript compiler will resolve these using tsconfig paths to either
652
- // handwritten versions (in .goscript-assets) or transpiled versions (in goscript).
653
- tsImportPath := translateGoImportPathToTypescriptModulePath(goPath)
654
-
655
- c.analysis.Imports[impName] = &fileImport{
656
- importPath: tsImportPath,
657
- importVars: make(map[string]struct{}),
658
- }
659
-
660
- // Skip writing the import if it was already written as a synthetic import
661
- // This prevents duplicate imports when a file needs an import both from
662
- // its AST and for promoted methods from embedded structs
663
- if syntheticImports := c.analysis.SyntheticImportsPerFile[c.currentFilePath]; syntheticImports != nil {
664
- if _, isSynthetic := syntheticImports[impName]; isSynthetic {
665
- return
666
- }
667
- }
668
-
669
- c.tsw.WriteImport(impName, translateTypescriptModulePathToIndexImportPath(tsImportPath))
670
- }
671
-
672
- func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType types.Type, isEmbedded bool) {
673
- c.tsw.WriteLiterally(fieldName)
674
- c.tsw.WriteLiterally(": $.varRef(")
675
-
676
- if isEmbedded {
677
- isPointerToStruct := false
678
- trueType := fieldType
679
- if ptr, isPtr := trueType.(*types.Pointer); isPtr {
680
- trueType = ptr.Elem()
681
- isPointerToStruct = true
682
- }
683
-
684
- if named, isNamed := trueType.(*types.Named); isNamed {
685
- _, isUnderlyingStruct := named.Underlying().(*types.Struct)
686
- if isUnderlyingStruct && !isPointerToStruct { // Is a value struct
687
- c.tsw.WriteLiterallyf("$.markAsStructValue(this._fields.%s.value.clone())", fieldName)
688
- } else { // Is a pointer to a struct, or not a struct
689
- c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
690
- }
691
- } else {
692
- c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
693
- }
694
- } else {
695
- // Check if this is a pointer type (nullable) or value type (non-nullable)
696
- isPointerType := false
697
- actualType := fieldType
698
- if ptr, ok := fieldType.(*types.Pointer); ok {
699
- isPointerType = true
700
- actualType = ptr.Elem()
701
- }
702
-
703
- // Check if the actual type (after dereferencing pointer) is a struct
704
- isValueTypeStruct := false
705
- if named, ok := actualType.(*types.Named); ok {
706
- if _, isStruct := named.Underlying().(*types.Struct); isStruct {
707
- isValueTypeStruct = true
708
- }
709
- }
710
-
711
- if isValueTypeStruct {
712
- if isPointerType {
713
- // Nullable struct pointer: field could be nil, use conditional
714
- c.tsw.WriteLiterallyf("this._fields.%s.value ? $.markAsStructValue(this._fields.%s.value.clone()) : null", fieldName, fieldName)
715
- } else {
716
- // Non-nullable struct value: field is always present, no conditional needed
717
- c.tsw.WriteLiterallyf("$.markAsStructValue(this._fields.%s.value.clone())", fieldName)
718
- }
719
- } else {
720
- c.tsw.WriteLiterallyf("this._fields.%s.value", fieldName)
721
- }
722
- }
723
-
724
- c.tsw.WriteLiterally(")")
725
- }