goscript 0.0.76 → 0.0.78
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/compiler/analysis.go +100 -33
- package/compiler/analysis_test.go +2 -7
- package/compiler/code-writer.go +2 -2
- package/compiler/compiler.go +4 -4
- package/compiler/composite-lit.go +4 -6
- package/compiler/constraint.go +2 -4
- package/compiler/decl.go +34 -0
- package/compiler/expr-call-async.go +4 -0
- package/compiler/expr-call-helpers.go +98 -8
- package/compiler/expr-call-make.go +4 -4
- package/compiler/expr-call.go +3 -0
- package/compiler/expr-type.go +42 -0
- package/compiler/gs_dependencies_test.go +3 -14
- package/compiler/index.ts +20 -5
- package/compiler/protobuf.go +21 -21
- package/compiler/spec-struct.go +195 -30
- package/compiler/spec.go +32 -3
- package/compiler/stmt-assign.go +2 -2
- package/compiler/type-info.go +20 -3
- package/compiler/type-utils.go +2 -4
- package/compiler/type.go +3 -4
- package/dist/compiler/index.js +13 -4
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/slice.js +2 -3
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +1 -0
- package/dist/gs/builtin/type.js +8 -14
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.d.ts +1 -0
- package/dist/gs/bytes/buffer.gs.js +20 -0
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +5 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +10 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.d.ts +50 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js +221 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js +2 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js +2 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.d.ts +56 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js +17 -0
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.js.map +1 -0
- package/dist/gs/io/fs/format.js +2 -6
- package/dist/gs/io/fs/format.js.map +1 -1
- package/dist/gs/io/fs/glob.js +18 -23
- package/dist/gs/io/fs/glob.js.map +1 -1
- package/dist/gs/path/match.js +9 -22
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +1 -1
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +1 -0
- package/dist/gs/reflect/type.js +52 -23
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/strings/iter.js +1 -1
- package/dist/gs/strings/iter.js.map +1 -1
- package/dist/gs/strings/reader.js +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js +9 -20
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/time/time.js +2 -2
- package/dist/gs/time/time.js.map +1 -1
- package/go.mod +1 -1
- package/go.sum +2 -2
- package/gs/builtin/slice.ts +2 -2
- package/gs/builtin/type.ts +14 -14
- package/gs/bytes/buffer.gs.ts +21 -1
- package/gs/fmt/fmt.test.ts +1 -1
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +14 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +238 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/index.ts +1 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +12 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/index.ts +1 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/meta.json +8 -0
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/runtime.ts +94 -0
- package/gs/io/fs/format.ts +2 -5
- package/gs/io/fs/glob.ts +18 -21
- package/gs/path/match.ts +9 -22
- package/gs/reflect/index.ts +1 -0
- package/gs/reflect/type.ts +62 -25
- package/gs/strings/iter.ts +1 -1
- package/gs/strings/reader.ts +1 -1
- package/gs/strings/replace.ts +13 -18
- package/gs/time/time.ts +2 -2
- package/package.json +18 -15
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"slices"
|
|
4
5
|
"testing"
|
|
5
6
|
|
|
6
7
|
"github.com/sirupsen/logrus"
|
|
@@ -36,13 +37,7 @@ func TestReadGsPackageMetadata(t *testing.T) {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// Check for the specific "iter" dependency
|
|
39
|
-
foundIter :=
|
|
40
|
-
for _, dep := range metadata.Dependencies {
|
|
41
|
-
if dep == "iter" {
|
|
42
|
-
foundIter = true
|
|
43
|
-
break
|
|
44
|
-
}
|
|
45
|
-
}
|
|
40
|
+
foundIter := slices.Contains(metadata.Dependencies, "iter")
|
|
46
41
|
|
|
47
42
|
if !foundIter {
|
|
48
43
|
t.Errorf("Expected to find 'iter' dependency, got dependencies: %v", metadata.Dependencies)
|
|
@@ -51,13 +46,7 @@ func TestReadGsPackageMetadata(t *testing.T) {
|
|
|
51
46
|
// Also check for other expected dependencies from the bytes package
|
|
52
47
|
expectedDeps := []string{"errors", "io", "iter", "unicode", "unicode/utf8", "unsafe"}
|
|
53
48
|
for _, expected := range expectedDeps {
|
|
54
|
-
found :=
|
|
55
|
-
for _, dep := range metadata.Dependencies {
|
|
56
|
-
if dep == expected {
|
|
57
|
-
found = true
|
|
58
|
-
break
|
|
59
|
-
}
|
|
60
|
-
}
|
|
49
|
+
found := slices.Contains(metadata.Dependencies, expected)
|
|
61
50
|
if !found {
|
|
62
51
|
t.Errorf("Expected to find dependency '%s', got dependencies: %v", expected, metadata.Dependencies)
|
|
63
52
|
}
|
package/compiler/index.ts
CHANGED
|
@@ -65,15 +65,30 @@ export async function compile(config: CompileConfig): Promise<void> {
|
|
|
65
65
|
// Go compiler often prints status messages to stderr, treat as info unless exit code is non-zero
|
|
66
66
|
console.info(`GoScript stderr:\n${stderr}`);
|
|
67
67
|
}
|
|
68
|
-
} catch (error:
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
} catch (error: unknown) {
|
|
69
|
+
const compileErr =
|
|
70
|
+
error instanceof Error ? error : new Error(String(error));
|
|
71
|
+
|
|
72
|
+
console.error(`GoScript compilation failed: ${compileErr.message}`);
|
|
73
|
+
if (
|
|
74
|
+
typeof error === "object" &&
|
|
75
|
+
error !== null &&
|
|
76
|
+
"stderr" in error &&
|
|
77
|
+
typeof error.stderr === "string"
|
|
78
|
+
) {
|
|
71
79
|
console.error(`GoScript stderr:\n${error.stderr}`);
|
|
72
80
|
}
|
|
73
|
-
if (
|
|
81
|
+
if (
|
|
82
|
+
typeof error === "object" &&
|
|
83
|
+
error !== null &&
|
|
84
|
+
"stdout" in error &&
|
|
85
|
+
typeof error.stdout === "string"
|
|
86
|
+
) {
|
|
74
87
|
console.error(`GoScript stdout:\n${error.stdout}`);
|
|
75
88
|
}
|
|
76
|
-
throw new Error(`GoScript compilation failed: ${
|
|
89
|
+
throw new Error(`GoScript compilation failed: ${compileErr.message}`, {
|
|
90
|
+
cause: error,
|
|
91
|
+
});
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
94
|
|
package/compiler/protobuf.go
CHANGED
|
@@ -146,35 +146,35 @@ func (c *PackageCompiler) writeProtobufExports(indexFile *os.File, fileName stri
|
|
|
146
146
|
// - export function Name
|
|
147
147
|
// We avoid type-only exports for now.
|
|
148
148
|
var exports []string
|
|
149
|
-
lines := strings.
|
|
150
|
-
for
|
|
149
|
+
lines := strings.SplitSeq(string(content), "\n")
|
|
150
|
+
for ln := range lines {
|
|
151
151
|
l := strings.TrimSpace(ln)
|
|
152
|
-
if strings.
|
|
153
|
-
rest :=
|
|
152
|
+
if after, ok := strings.CutPrefix(l, "export const "); ok {
|
|
153
|
+
rest := after
|
|
154
154
|
name := takeIdent(rest)
|
|
155
155
|
if name != "" {
|
|
156
156
|
exports = append(exports, name)
|
|
157
157
|
}
|
|
158
158
|
continue
|
|
159
159
|
}
|
|
160
|
-
if strings.
|
|
161
|
-
rest :=
|
|
160
|
+
if after, ok := strings.CutPrefix(l, "export interface "); ok {
|
|
161
|
+
rest := after
|
|
162
162
|
name := takeIdent(rest)
|
|
163
163
|
if name != "" {
|
|
164
164
|
exports = append(exports, name)
|
|
165
165
|
}
|
|
166
166
|
continue
|
|
167
167
|
}
|
|
168
|
-
if strings.
|
|
169
|
-
rest :=
|
|
168
|
+
if after, ok := strings.CutPrefix(l, "export class "); ok {
|
|
169
|
+
rest := after
|
|
170
170
|
name := takeIdent(rest)
|
|
171
171
|
if name != "" {
|
|
172
172
|
exports = append(exports, name)
|
|
173
173
|
}
|
|
174
174
|
continue
|
|
175
175
|
}
|
|
176
|
-
if strings.
|
|
177
|
-
rest :=
|
|
176
|
+
if after, ok := strings.CutPrefix(l, "export function "); ok {
|
|
177
|
+
rest := after
|
|
178
178
|
name := takeIdent(rest)
|
|
179
179
|
if name != "" {
|
|
180
180
|
exports = append(exports, name)
|
|
@@ -236,9 +236,9 @@ func (c *FileCompiler) addProtobufImports() error {
|
|
|
236
236
|
|
|
237
237
|
for _, fileName := range c.pkg.CompiledGoFiles {
|
|
238
238
|
baseFileName := filepath.Base(fileName)
|
|
239
|
-
if strings.
|
|
239
|
+
if before, ok := strings.CutSuffix(baseFileName, ".pb.go"); ok {
|
|
240
240
|
// Check if there's a corresponding .pb.ts file
|
|
241
|
-
pbTsFileName :=
|
|
241
|
+
pbTsFileName := before + ".pb.ts"
|
|
242
242
|
pbTsPath := filepath.Join(packageDir, pbTsFileName)
|
|
243
243
|
|
|
244
244
|
if _, err := os.Stat(pbTsPath); err == nil {
|
|
@@ -252,28 +252,28 @@ func (c *FileCompiler) addProtobufImports() error {
|
|
|
252
252
|
|
|
253
253
|
// Discover exported identifiers (const/interface/class/function)
|
|
254
254
|
var exports []string
|
|
255
|
-
for
|
|
255
|
+
for ln := range strings.SplitSeq(string(content), "\n") {
|
|
256
256
|
l := strings.TrimSpace(ln)
|
|
257
|
-
if strings.
|
|
258
|
-
if name := takeIdent(
|
|
257
|
+
if after, ok := strings.CutPrefix(l, "export const "); ok {
|
|
258
|
+
if name := takeIdent(after); name != "" {
|
|
259
259
|
exports = append(exports, name)
|
|
260
260
|
}
|
|
261
261
|
continue
|
|
262
262
|
}
|
|
263
|
-
if strings.
|
|
264
|
-
if name := takeIdent(
|
|
263
|
+
if after, ok := strings.CutPrefix(l, "export interface "); ok {
|
|
264
|
+
if name := takeIdent(after); name != "" {
|
|
265
265
|
exports = append(exports, name)
|
|
266
266
|
}
|
|
267
267
|
continue
|
|
268
268
|
}
|
|
269
|
-
if strings.
|
|
270
|
-
if name := takeIdent(
|
|
269
|
+
if after, ok := strings.CutPrefix(l, "export class "); ok {
|
|
270
|
+
if name := takeIdent(after); name != "" {
|
|
271
271
|
exports = append(exports, name)
|
|
272
272
|
}
|
|
273
273
|
continue
|
|
274
274
|
}
|
|
275
|
-
if strings.
|
|
276
|
-
if name := takeIdent(
|
|
275
|
+
if after, ok := strings.CutPrefix(l, "export function "); ok {
|
|
276
|
+
if name := takeIdent(after); name != "" {
|
|
277
277
|
exports = append(exports, name)
|
|
278
278
|
}
|
|
279
279
|
continue
|
package/compiler/spec-struct.go
CHANGED
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/types"
|
|
7
|
+
"maps"
|
|
7
8
|
"slices"
|
|
8
9
|
"strings"
|
|
9
10
|
)
|
|
@@ -72,8 +73,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
// Generate getters and setters for EMBEDDED struct fields themselves
|
|
75
|
-
for
|
|
76
|
-
field := underlyingStruct.Field(i)
|
|
76
|
+
for field := range underlyingStruct.Fields() {
|
|
77
77
|
if field.Anonymous() {
|
|
78
78
|
fieldKeyName := c.getEmbeddedFieldKeyName(field.Type())
|
|
79
79
|
c.writeGetterSetter(fieldKeyName, field.Type(), nil, nil, nil)
|
|
@@ -95,8 +95,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
95
95
|
c.tsw.Indent(1)
|
|
96
96
|
c.tsw.WriteLine("")
|
|
97
97
|
|
|
98
|
-
for
|
|
99
|
-
field := underlyingStruct.Field(i)
|
|
98
|
+
for field := range underlyingStruct.Fields() {
|
|
100
99
|
var fieldKeyName string
|
|
101
100
|
if field.Anonymous() {
|
|
102
101
|
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
@@ -166,25 +165,26 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
166
165
|
c.tsw.WriteLine("")
|
|
167
166
|
|
|
168
167
|
// Generate the clone method
|
|
169
|
-
cloneReturnType
|
|
168
|
+
var cloneReturnType strings.Builder
|
|
169
|
+
cloneReturnType.WriteString(className)
|
|
170
170
|
if a.TypeParams != nil && len(a.TypeParams.List) > 0 {
|
|
171
|
-
cloneReturnType
|
|
171
|
+
cloneReturnType.WriteString("<")
|
|
172
172
|
first := true
|
|
173
173
|
for _, field := range a.TypeParams.List {
|
|
174
174
|
for _, name := range field.Names {
|
|
175
175
|
if !first {
|
|
176
|
-
cloneReturnType
|
|
176
|
+
cloneReturnType.WriteString(", ")
|
|
177
177
|
}
|
|
178
178
|
first = false
|
|
179
|
-
cloneReturnType
|
|
179
|
+
cloneReturnType.WriteString(name.Name)
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
-
cloneReturnType
|
|
182
|
+
cloneReturnType.WriteString(">")
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
c.tsw.WriteLinef("public clone(): %s {", cloneReturnType)
|
|
185
|
+
c.tsw.WriteLinef("public clone(): %s {", cloneReturnType.String())
|
|
186
186
|
c.tsw.Indent(1)
|
|
187
|
-
c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType)
|
|
187
|
+
c.tsw.WriteLinef("const cloned = new %s()", cloneReturnType.String())
|
|
188
188
|
c.tsw.WriteLine("cloned._fields = {")
|
|
189
189
|
c.tsw.Indent(1)
|
|
190
190
|
|
|
@@ -257,8 +257,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
257
257
|
seenPromotedFields := make(map[string]bool)
|
|
258
258
|
directMethods := make(map[string]bool)
|
|
259
259
|
// Populate directMethods (methods defined directly on this struct type)
|
|
260
|
-
for
|
|
261
|
-
method := goStructType.Method(i)
|
|
260
|
+
for method := range goStructType.Methods() {
|
|
262
261
|
sig := method.Type().(*types.Signature)
|
|
263
262
|
if sig.Recv() != nil {
|
|
264
263
|
recvType := sig.Recv().Type()
|
|
@@ -272,8 +271,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
272
271
|
}
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
for
|
|
276
|
-
field := underlyingStruct.Field(i)
|
|
274
|
+
for field := range underlyingStruct.Fields() {
|
|
277
275
|
if !field.Anonymous() {
|
|
278
276
|
continue
|
|
279
277
|
}
|
|
@@ -293,8 +291,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
293
291
|
// Promoted fields
|
|
294
292
|
if namedEmbedded, ok := trueEmbeddedType.(*types.Named); ok {
|
|
295
293
|
if underlyingEmbeddedStruct, ok := namedEmbedded.Underlying().(*types.Struct); ok {
|
|
296
|
-
for
|
|
297
|
-
promotedField := underlyingEmbeddedStruct.Field(j)
|
|
294
|
+
for promotedField := range underlyingEmbeddedStruct.Fields() {
|
|
298
295
|
if !promotedField.Exported() && promotedField.Pkg() != c.pkg.Types {
|
|
299
296
|
continue
|
|
300
297
|
}
|
|
@@ -304,8 +301,8 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
304
301
|
}
|
|
305
302
|
// Check for conflicts with outer struct's own fields or other promoted fields
|
|
306
303
|
conflict := false
|
|
307
|
-
for
|
|
308
|
-
if !
|
|
304
|
+
for field := range underlyingStruct.Fields() {
|
|
305
|
+
if !field.Anonymous() && field.Name() == promotedFieldName {
|
|
309
306
|
conflict = true
|
|
310
307
|
break
|
|
311
308
|
}
|
|
@@ -358,8 +355,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
358
355
|
}
|
|
359
356
|
}
|
|
360
357
|
embeddedMethodSet := types.NewMethodSet(methodSetType)
|
|
361
|
-
for
|
|
362
|
-
methodSelection := embeddedMethodSet.At(k)
|
|
358
|
+
for methodSelection := range embeddedMethodSet.Methods() {
|
|
363
359
|
method := methodSelection.Obj().(*types.Func)
|
|
364
360
|
methodName := method.Name()
|
|
365
361
|
|
|
@@ -367,8 +363,8 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
367
363
|
if len(methodSelection.Index()) == 1 && !directMethods[methodName] && !seenPromotedFields[methodName] {
|
|
368
364
|
// Check for conflict with outer struct's own fields
|
|
369
365
|
conflictWithField := false
|
|
370
|
-
for
|
|
371
|
-
if !
|
|
366
|
+
for field := range underlyingStruct.Fields() {
|
|
367
|
+
if !field.Anonymous() && field.Name() == methodName {
|
|
372
368
|
conflictWithField = true
|
|
373
369
|
break
|
|
374
370
|
}
|
|
@@ -457,8 +453,7 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
457
453
|
c.tsw.WriteLiterally(" [")
|
|
458
454
|
// Collect methods for the struct type
|
|
459
455
|
var structMethods []*types.Func
|
|
460
|
-
for
|
|
461
|
-
method := goStructType.Method(i)
|
|
456
|
+
for method := range goStructType.Methods() {
|
|
462
457
|
// Ensure it's a method directly on this type (not promoted here, promotion handled separately)
|
|
463
458
|
// Check if receiver is *T or T where T is goStructType
|
|
464
459
|
sig := method.Type().(*types.Signature)
|
|
@@ -517,6 +512,179 @@ func (c *GoToTSCompiler) WriteStructTypeSpec(a *ast.TypeSpec, t *ast.StructType)
|
|
|
517
512
|
return nil
|
|
518
513
|
}
|
|
519
514
|
|
|
515
|
+
// WriteNamedStructTypeSpec generates a TypeScript class for a named type whose
|
|
516
|
+
// underlying type is a struct defined elsewhere.
|
|
517
|
+
func (c *GoToTSCompiler) WriteNamedStructTypeSpec(a *ast.TypeSpec, named *types.Named) error {
|
|
518
|
+
isInsideFunction := false
|
|
519
|
+
if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
|
|
520
|
+
isInsideFunction = nodeInfo.IsInsideFunction
|
|
521
|
+
}
|
|
522
|
+
if !isInsideFunction {
|
|
523
|
+
c.tsw.WriteLiterally("export ")
|
|
524
|
+
}
|
|
525
|
+
c.tsw.WriteLiterally("class ")
|
|
526
|
+
if err := c.WriteValueExpr(a.Name); err != nil {
|
|
527
|
+
return err
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if a.TypeParams != nil {
|
|
531
|
+
c.WriteTypeParameters(a.TypeParams)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
c.tsw.WriteLiterally(" extends ")
|
|
535
|
+
c.WriteTypeExpr(a.Type)
|
|
536
|
+
c.tsw.WriteLiterally(" ")
|
|
537
|
+
c.tsw.WriteLine("{")
|
|
538
|
+
c.tsw.Indent(1)
|
|
539
|
+
|
|
540
|
+
className := sanitizeIdentifier(a.Name.Name)
|
|
541
|
+
underlyingStruct, ok := named.Underlying().(*types.Struct)
|
|
542
|
+
if !ok {
|
|
543
|
+
return fmt.Errorf("underlying type of %s is not a struct", a.Name.Name)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
c.tsw.WriteLine("constructor(init?: any) {")
|
|
547
|
+
c.tsw.Indent(1)
|
|
548
|
+
c.tsw.WriteLine("super(init)")
|
|
549
|
+
c.tsw.Indent(-1)
|
|
550
|
+
c.tsw.WriteLine("}")
|
|
551
|
+
c.tsw.WriteLine("")
|
|
552
|
+
|
|
553
|
+
c.tsw.WriteLinef("public clone(): %s {", className)
|
|
554
|
+
c.tsw.Indent(1)
|
|
555
|
+
c.tsw.WriteLinef("const cloned = new %s()", className)
|
|
556
|
+
c.tsw.WriteLine("cloned._fields = {")
|
|
557
|
+
c.tsw.Indent(1)
|
|
558
|
+
|
|
559
|
+
firstFieldWritten := false
|
|
560
|
+
for i := 0; i < underlyingStruct.NumFields(); i++ {
|
|
561
|
+
field := underlyingStruct.Field(i)
|
|
562
|
+
fieldType := field.Type()
|
|
563
|
+
var fieldKeyName string
|
|
564
|
+
if field.Anonymous() {
|
|
565
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
566
|
+
} else {
|
|
567
|
+
fieldKeyName = field.Name()
|
|
568
|
+
}
|
|
569
|
+
if fieldKeyName == "_" {
|
|
570
|
+
continue
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if firstFieldWritten {
|
|
574
|
+
c.tsw.WriteLine(",")
|
|
575
|
+
}
|
|
576
|
+
c.writeClonedFieldInitializer(fieldKeyName, fieldType, field.Anonymous())
|
|
577
|
+
firstFieldWritten = true
|
|
578
|
+
}
|
|
579
|
+
if firstFieldWritten {
|
|
580
|
+
c.tsw.WriteLine("")
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
c.tsw.Indent(-1)
|
|
584
|
+
c.tsw.WriteLine("}")
|
|
585
|
+
c.tsw.WriteLine("return cloned")
|
|
586
|
+
c.tsw.Indent(-1)
|
|
587
|
+
c.tsw.WriteLine("}")
|
|
588
|
+
|
|
589
|
+
for _, fileSyntax := range c.pkg.Syntax {
|
|
590
|
+
for _, decl := range fileSyntax.Decls {
|
|
591
|
+
funcDecl, isFunc := decl.(*ast.FuncDecl)
|
|
592
|
+
if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
|
|
593
|
+
continue
|
|
594
|
+
}
|
|
595
|
+
recvType := funcDecl.Recv.List[0].Type
|
|
596
|
+
if starExpr, ok := recvType.(*ast.StarExpr); ok {
|
|
597
|
+
recvType = starExpr.X
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
var recvTypeName string
|
|
601
|
+
if ident, ok := recvType.(*ast.Ident); ok {
|
|
602
|
+
recvTypeName = sanitizeIdentifier(ident.Name)
|
|
603
|
+
} else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
|
|
604
|
+
if ident, ok := indexExpr.X.(*ast.Ident); ok {
|
|
605
|
+
recvTypeName = sanitizeIdentifier(ident.Name)
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if recvTypeName == className {
|
|
610
|
+
c.tsw.WriteLine("")
|
|
611
|
+
if err := c.WriteFuncDeclAsMethod(funcDecl); err != nil {
|
|
612
|
+
return err
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
c.tsw.WriteLine("")
|
|
619
|
+
c.tsw.WriteLine("// Register this type with the runtime type system")
|
|
620
|
+
|
|
621
|
+
structName := className
|
|
622
|
+
pkgPath := c.pkg.Types.Path()
|
|
623
|
+
pkgName := c.pkg.Types.Name()
|
|
624
|
+
if pkgPath != "" && pkgName != "main" {
|
|
625
|
+
structName = pkgPath + "." + className
|
|
626
|
+
} else if pkgName == "main" {
|
|
627
|
+
structName = "main." + className
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
c.tsw.WriteLine("static __typeInfo = $.registerStructType(")
|
|
631
|
+
c.tsw.WriteLinef(" %q,", structName)
|
|
632
|
+
c.tsw.WriteLinef(" new %s(),", className)
|
|
633
|
+
c.tsw.WriteLiterally(" [")
|
|
634
|
+
|
|
635
|
+
var structMethods []*types.Func
|
|
636
|
+
for method := range named.Methods() {
|
|
637
|
+
sig := method.Type().(*types.Signature)
|
|
638
|
+
recv := sig.Recv().Type()
|
|
639
|
+
if ptr, ok := recv.(*types.Pointer); ok {
|
|
640
|
+
recv = ptr.Elem()
|
|
641
|
+
}
|
|
642
|
+
if namedRecv, ok := recv.(*types.Named); ok && namedRecv.Obj() == named.Obj() {
|
|
643
|
+
structMethods = append(structMethods, method)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
c.writeMethodSignatures(structMethods)
|
|
647
|
+
c.tsw.WriteLiterally("],")
|
|
648
|
+
c.tsw.WriteLine("")
|
|
649
|
+
|
|
650
|
+
c.tsw.WriteLinef(" %s,", className)
|
|
651
|
+
c.tsw.WriteLiterally(" {")
|
|
652
|
+
firstField := true
|
|
653
|
+
for i := 0; i < underlyingStruct.NumFields(); i++ {
|
|
654
|
+
field := underlyingStruct.Field(i)
|
|
655
|
+
var fieldKeyName string
|
|
656
|
+
if field.Anonymous() {
|
|
657
|
+
fieldKeyName = c.getEmbeddedFieldKeyName(field.Type())
|
|
658
|
+
} else {
|
|
659
|
+
fieldKeyName = field.Name()
|
|
660
|
+
}
|
|
661
|
+
if fieldKeyName == "_" {
|
|
662
|
+
continue
|
|
663
|
+
}
|
|
664
|
+
if !firstField {
|
|
665
|
+
c.tsw.WriteLiterally(", ")
|
|
666
|
+
}
|
|
667
|
+
firstField = false
|
|
668
|
+
c.tsw.WriteLiterallyf("%q: ", fieldKeyName)
|
|
669
|
+
|
|
670
|
+
tag := underlyingStruct.Tag(i)
|
|
671
|
+
if tag != "" {
|
|
672
|
+
c.tsw.WriteLiterally("{ type: ")
|
|
673
|
+
c.writeTypeInfoObject(field.Type())
|
|
674
|
+
c.tsw.WriteLiterallyf(", tag: %q }", tag)
|
|
675
|
+
} else {
|
|
676
|
+
c.writeTypeInfoObject(field.Type())
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
c.tsw.WriteLiterally("}")
|
|
680
|
+
c.tsw.WriteLine("")
|
|
681
|
+
c.tsw.WriteLine(");")
|
|
682
|
+
|
|
683
|
+
c.tsw.Indent(-1)
|
|
684
|
+
c.tsw.WriteLine("}")
|
|
685
|
+
return nil
|
|
686
|
+
}
|
|
687
|
+
|
|
520
688
|
// generateFlattenedInitTypeString generates a TypeScript type string for the
|
|
521
689
|
// initialization object passed to a Go struct's constructor (`_init` method in TypeScript).
|
|
522
690
|
// The generated type is a `Partial`-like structure, `"{ Field1?: Type1, Field2?: Type2, ... }"`,
|
|
@@ -545,8 +713,7 @@ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named
|
|
|
545
713
|
}
|
|
546
714
|
|
|
547
715
|
// First add the direct fields and track embedded types
|
|
548
|
-
for
|
|
549
|
-
field := underlying.Field(i)
|
|
716
|
+
for field := range underlying.Fields() {
|
|
550
717
|
fieldName := field.Name()
|
|
551
718
|
|
|
552
719
|
// Skip underscore fields
|
|
@@ -587,9 +754,7 @@ func (c *GoToTSCompiler) generateFlattenedInitTypeString(structType *types.Named
|
|
|
587
754
|
// not typically set directly in `init?` unless the embedded struct itself is named in `init?`.
|
|
588
755
|
|
|
589
756
|
// Add embedded types to the field map (these are the names of the embedded structs themselves)
|
|
590
|
-
|
|
591
|
-
fieldMap[embeddedName] = embeddedTSType
|
|
592
|
-
}
|
|
757
|
+
maps.Copy(fieldMap, embeddedTypeMap)
|
|
593
758
|
|
|
594
759
|
var fieldNames []string
|
|
595
760
|
for name := range fieldMap {
|
package/compiler/spec.go
CHANGED
|
@@ -366,6 +366,20 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
|
|
|
366
366
|
|
|
367
367
|
if recvTypeName == className {
|
|
368
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
|
+
bodyNeedsAsync, err := c.funcBodyNeedsAsync(funcDecl, false)
|
|
375
|
+
if err != nil {
|
|
376
|
+
return err
|
|
377
|
+
}
|
|
378
|
+
isAsync = bodyNeedsAsync
|
|
379
|
+
}
|
|
380
|
+
if isAsync {
|
|
381
|
+
c.tsw.WriteLiterally("async ")
|
|
382
|
+
}
|
|
369
383
|
c.tsw.WriteLiterally(className)
|
|
370
384
|
c.tsw.WriteLiterally("_")
|
|
371
385
|
c.tsw.WriteLiterally(funcDecl.Name.Name)
|
|
@@ -403,6 +417,9 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
|
|
|
403
417
|
// Add return type
|
|
404
418
|
if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) > 0 {
|
|
405
419
|
c.tsw.WriteLiterally(": ")
|
|
420
|
+
if isAsync {
|
|
421
|
+
c.tsw.WriteLiterally("Promise<")
|
|
422
|
+
}
|
|
406
423
|
if len(funcDecl.Type.Results.List) == 1 {
|
|
407
424
|
c.WriteTypeExpr(funcDecl.Type.Results.List[0].Type)
|
|
408
425
|
} else {
|
|
@@ -424,8 +441,15 @@ func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
|
|
|
424
441
|
}
|
|
425
442
|
c.tsw.WriteLiterally("]")
|
|
426
443
|
}
|
|
444
|
+
if isAsync {
|
|
445
|
+
c.tsw.WriteLiterally(">")
|
|
446
|
+
}
|
|
427
447
|
} else {
|
|
428
|
-
|
|
448
|
+
if isAsync {
|
|
449
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
450
|
+
} else {
|
|
451
|
+
c.tsw.WriteLiterally(": void")
|
|
452
|
+
}
|
|
429
453
|
}
|
|
430
454
|
|
|
431
455
|
c.tsw.WriteLine(" {")
|
|
@@ -476,6 +500,11 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
476
500
|
default:
|
|
477
501
|
// Check if this type has receiver methods
|
|
478
502
|
if c.hasReceiverMethods(a.Name.Name) {
|
|
503
|
+
if named, ok := c.pkg.TypesInfo.Defs[a.Name].Type().(*types.Named); ok {
|
|
504
|
+
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
505
|
+
return c.WriteNamedStructTypeSpec(a, named)
|
|
506
|
+
}
|
|
507
|
+
}
|
|
479
508
|
return c.WriteNamedTypeWithMethods(a)
|
|
480
509
|
}
|
|
481
510
|
|
|
@@ -560,8 +589,8 @@ func (c *GoToTSCompiler) WriteInterfaceTypeSpec(a *ast.TypeSpec, t *ast.Interfac
|
|
|
560
589
|
// Collect methods for the interface type
|
|
561
590
|
var interfaceMethods []*types.Func
|
|
562
591
|
if ifaceType != nil { // ifaceType is *types.Interface
|
|
563
|
-
for
|
|
564
|
-
interfaceMethods = append(interfaceMethods,
|
|
592
|
+
for method := range ifaceType.ExplicitMethods() {
|
|
593
|
+
interfaceMethods = append(interfaceMethods, method)
|
|
565
594
|
}
|
|
566
595
|
// TODO: Handle embedded interface methods if necessary for full signature collection.
|
|
567
596
|
// For now, explicit methods are covered.
|
package/compiler/stmt-assign.go
CHANGED
|
@@ -313,8 +313,8 @@ func (c *GoToTSCompiler) writeMultiVarAssignFromCall(lhs []ast.Expr, callExpr *a
|
|
|
313
313
|
if funType := c.pkg.TypesInfo.TypeOf(callExpr.Fun); funType != nil {
|
|
314
314
|
if funcType, ok := funType.Underlying().(*types.Signature); ok {
|
|
315
315
|
if funcType.Results() != nil && funcType.Results().Len() > 0 {
|
|
316
|
-
for
|
|
317
|
-
resultTypes = append(resultTypes,
|
|
316
|
+
for v := range funcType.Results().Variables() {
|
|
317
|
+
resultTypes = append(resultTypes, v)
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
}
|
package/compiler/type-info.go
CHANGED
|
@@ -2,6 +2,23 @@ package compiler
|
|
|
2
2
|
|
|
3
3
|
import "go/types"
|
|
4
4
|
|
|
5
|
+
func qualifiedTypeName(namedType *types.Named) string {
|
|
6
|
+
if namedType == nil || namedType.Obj() == nil {
|
|
7
|
+
return ""
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
typeName := namedType.Obj().Name()
|
|
11
|
+
if pkg := namedType.Obj().Pkg(); pkg != nil {
|
|
12
|
+
switch {
|
|
13
|
+
case pkg.Name() == "main":
|
|
14
|
+
typeName = "main." + typeName
|
|
15
|
+
case pkg.Path() != "":
|
|
16
|
+
typeName = pkg.Path() + "." + typeName
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return typeName
|
|
20
|
+
}
|
|
21
|
+
|
|
5
22
|
// writeTypeInfoObject writes a TypeScript TypeInfo object literal for a given Go type.
|
|
6
23
|
func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
|
|
7
24
|
if typ == nil {
|
|
@@ -17,7 +34,7 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
|
|
|
17
34
|
// For all other named types, output their name as a string literal.
|
|
18
35
|
// This relies on the type being registered elsewhere (e.g., via registerStructType or registerInterfaceType)
|
|
19
36
|
// so the TypeScript runtime can resolve the reference.
|
|
20
|
-
c.tsw.WriteLiterallyf("%q", namedType
|
|
37
|
+
c.tsw.WriteLiterallyf("%q", qualifiedTypeName(namedType))
|
|
21
38
|
}
|
|
22
39
|
return // Return after handling the named type by reference.
|
|
23
40
|
}
|
|
@@ -64,8 +81,8 @@ func (c *GoToTSCompiler) writeTypeInfoObject(typ types.Type) {
|
|
|
64
81
|
case *types.Interface: // Anonymous interface or underlying of a non-named type alias
|
|
65
82
|
c.tsw.WriteLiterally("{ kind: $.TypeKind.Interface, methods: [")
|
|
66
83
|
var methods []*types.Func
|
|
67
|
-
for
|
|
68
|
-
methods = append(methods,
|
|
84
|
+
for method := range t.ExplicitMethods() {
|
|
85
|
+
methods = append(methods, method)
|
|
69
86
|
}
|
|
70
87
|
// TODO: Handle embedded methods for anonymous interfaces if needed.
|
|
71
88
|
c.writeMethodSignatures(methods) // Calls writeMethodSignatures -> writeTypeInfoObject
|
package/compiler/type-utils.go
CHANGED
|
@@ -47,12 +47,10 @@ func (c *GoToTSCompiler) constraintIncludesString(constraint *types.Interface) b
|
|
|
47
47
|
return false // Pure method interface, no type terms
|
|
48
48
|
}
|
|
49
49
|
// For union constraints like []byte | string, check each term
|
|
50
|
-
for
|
|
51
|
-
embedded := constraint.EmbeddedType(i)
|
|
50
|
+
for embedded := range constraint.EmbeddedTypes() {
|
|
52
51
|
// Check if embedded is a union
|
|
53
52
|
if union, isUnion := embedded.(*types.Union); isUnion {
|
|
54
|
-
for
|
|
55
|
-
term := union.Term(j)
|
|
53
|
+
for term := range union.Terms() {
|
|
56
54
|
termType := term.Type()
|
|
57
55
|
// Check if term is string or ~string
|
|
58
56
|
if basic, isBasic := termType.Underlying().(*types.Basic); isBasic {
|
package/compiler/type.go
CHANGED
|
@@ -605,8 +605,7 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
|
|
|
605
605
|
c.tsw.Indent(1)
|
|
606
606
|
c.tsw.WriteLine("") // Newline after opening brace, before the first method
|
|
607
607
|
|
|
608
|
-
for
|
|
609
|
-
method := iface.ExplicitMethod(i)
|
|
608
|
+
for method := range iface.ExplicitMethods() {
|
|
610
609
|
sig := method.Type().(*types.Signature)
|
|
611
610
|
|
|
612
611
|
// Find corresponding ast.Field for comments if astNode is available
|
|
@@ -745,14 +744,14 @@ func (c *GoToTSCompiler) writeInterfaceStructure(iface *types.Interface, astNode
|
|
|
745
744
|
|
|
746
745
|
// Handle embedded types
|
|
747
746
|
if iface.NumEmbeddeds() > 0 {
|
|
748
|
-
for
|
|
747
|
+
for etyp := range iface.EmbeddedTypes() {
|
|
749
748
|
if firstPartWritten {
|
|
750
749
|
c.tsw.WriteLiterally(" & ")
|
|
751
750
|
} else {
|
|
752
751
|
// This is the first part being written (no explicit methods, only embedded)
|
|
753
752
|
firstPartWritten = true
|
|
754
753
|
}
|
|
755
|
-
embeddedType :=
|
|
754
|
+
embeddedType := etyp
|
|
756
755
|
// When WriteGoType encounters an interface, it will call WriteInterfaceType
|
|
757
756
|
// which will pass nil for astNode, so comments for deeply embedded interface literals
|
|
758
757
|
// might not be available unless they are named types.
|