goscript 0.0.28 → 0.0.30
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 +41 -2
- package/compiler/compiler.go +8 -1
- package/compiler/expr-call.go +87 -15
- package/compiler/expr-selector.go +100 -0
- package/compiler/spec-value.go +79 -8
- package/compiler/spec.go +236 -0
- package/dist/gs/builtin/builtin.d.ts +1 -0
- package/dist/gs/builtin/builtin.js +11 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/internal/oserror/errors.d.ts +6 -0
- package/dist/gs/internal/oserror/errors.js +7 -0
- package/dist/gs/internal/oserror/errors.js.map +1 -0
- package/dist/gs/internal/oserror/index.d.ts +1 -0
- package/dist/gs/internal/oserror/index.js +2 -0
- package/dist/gs/internal/oserror/index.js.map +1 -0
- package/dist/gs/io/fs/format.d.ts +3 -0
- package/dist/gs/io/fs/format.js +56 -0
- package/dist/gs/io/fs/format.js.map +1 -0
- package/dist/gs/io/fs/fs.d.ts +79 -0
- package/dist/gs/io/fs/fs.js +200 -0
- package/dist/gs/io/fs/fs.js.map +1 -0
- package/dist/gs/io/fs/glob.d.ts +10 -0
- package/dist/gs/io/fs/glob.js +141 -0
- package/dist/gs/io/fs/glob.js.map +1 -0
- package/dist/gs/io/fs/index.d.ts +8 -0
- package/dist/gs/io/fs/index.js +9 -0
- package/dist/gs/io/fs/index.js.map +1 -0
- package/dist/gs/io/fs/readdir.d.ts +7 -0
- package/dist/gs/io/fs/readdir.js +152 -0
- package/dist/gs/io/fs/readdir.js.map +1 -0
- package/dist/gs/io/fs/readfile.d.ts +6 -0
- package/dist/gs/io/fs/readfile.js +118 -0
- package/dist/gs/io/fs/readfile.js.map +1 -0
- package/dist/gs/io/fs/stat.d.ts +6 -0
- package/dist/gs/io/fs/stat.js +87 -0
- package/dist/gs/io/fs/stat.js.map +1 -0
- package/dist/gs/io/fs/sub.d.ts +6 -0
- package/dist/gs/io/fs/sub.js +172 -0
- package/dist/gs/io/fs/sub.js.map +1 -0
- package/dist/gs/io/fs/walk.d.ts +7 -0
- package/dist/gs/io/fs/walk.js +76 -0
- package/dist/gs/io/fs/walk.js.map +1 -0
- package/dist/gs/path/index.d.ts +2 -0
- package/dist/gs/path/index.js +3 -0
- package/dist/gs/path/index.js.map +1 -0
- package/dist/gs/path/match.d.ts +6 -0
- package/dist/gs/path/match.js +281 -0
- package/dist/gs/path/match.js.map +1 -0
- package/dist/gs/path/path.d.ts +7 -0
- package/dist/gs/path/path.js +256 -0
- package/dist/gs/path/path.js.map +1 -0
- package/dist/gs/time/time.d.ts +11 -2
- package/dist/gs/time/time.js +337 -12
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +13 -0
- package/gs/internal/oserror/errors.ts +14 -0
- package/gs/internal/oserror/index.ts +1 -0
- package/gs/io/fs/format.ts +65 -0
- package/gs/io/fs/fs.ts +359 -0
- package/gs/io/fs/glob.ts +167 -0
- package/gs/io/fs/godoc.txt +35 -0
- package/gs/io/fs/index.ts +8 -0
- package/gs/io/fs/readdir.ts +126 -0
- package/gs/io/fs/readfile.ts +77 -0
- package/gs/io/fs/stat.ts +38 -0
- package/gs/io/fs/sub.ts +208 -0
- package/gs/io/fs/walk.ts +89 -0
- package/gs/path/index.ts +2 -0
- package/gs/path/match.ts +307 -0
- package/gs/path/path.ts +301 -0
- package/gs/strings/reader.test.ts +0 -1
- package/gs/time/time.ts +325 -12
- package/package.json +1 -1
- package/gs/time/godoc.md +0 -116
package/compiler/analysis.go
CHANGED
|
@@ -57,6 +57,7 @@ type NodeInfo struct {
|
|
|
57
57
|
EnclosingFuncDecl *ast.FuncDecl
|
|
58
58
|
EnclosingFuncLit *ast.FuncLit
|
|
59
59
|
IsInsideFunction bool // true if this declaration is inside a function body
|
|
60
|
+
IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
@@ -236,6 +237,18 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
|
236
237
|
return false
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
// IsMethodValue returns whether the given SelectorExpr node is a method value that needs binding.
|
|
241
|
+
func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
|
|
242
|
+
if node == nil {
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
nodeInfo := a.NodeData[node]
|
|
246
|
+
if nodeInfo == nil {
|
|
247
|
+
return false
|
|
248
|
+
}
|
|
249
|
+
return nodeInfo.IsMethodValue
|
|
250
|
+
}
|
|
251
|
+
|
|
239
252
|
// analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
|
|
240
253
|
type analysisVisitor struct {
|
|
241
254
|
// analysis stores information gathered during the traversal
|
|
@@ -566,8 +579,20 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
566
579
|
return v
|
|
567
580
|
|
|
568
581
|
case *ast.SelectorExpr:
|
|
569
|
-
//
|
|
570
|
-
|
|
582
|
+
// Initialize NodeData for this selector expression
|
|
583
|
+
if v.analysis.NodeData[n] == nil {
|
|
584
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
585
|
+
}
|
|
586
|
+
v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
|
|
587
|
+
|
|
588
|
+
// Check if this is a method value (method being used as a value, not called immediately)
|
|
589
|
+
if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
|
|
590
|
+
if selection.Kind() == types.MethodVal {
|
|
591
|
+
// This is a method value - mark it for binding during code generation
|
|
592
|
+
v.analysis.NodeData[n].IsMethodValue = true
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return v // Continue traversal
|
|
571
596
|
|
|
572
597
|
case *ast.AssignStmt:
|
|
573
598
|
for i, currentLHSExpr := range n.Lhs {
|
|
@@ -859,6 +884,20 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
|
|
|
859
884
|
|
|
860
885
|
// Walk the AST with our visitor
|
|
861
886
|
ast.Walk(visitor, file)
|
|
887
|
+
|
|
888
|
+
// Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
|
|
889
|
+
// This distinguishes between method calls (obj.Method()) and method values (obj.Method)
|
|
890
|
+
ast.Inspect(file, func(n ast.Node) bool {
|
|
891
|
+
if callExpr, ok := n.(*ast.CallExpr); ok {
|
|
892
|
+
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
893
|
+
// This SelectorExpr is the function being called, so it's NOT a method value
|
|
894
|
+
if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
|
|
895
|
+
nodeInfo.IsMethodValue = false
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return true
|
|
900
|
+
})
|
|
862
901
|
}
|
|
863
902
|
|
|
864
903
|
// getNamedReturns retrieves the named returns for a function
|
package/compiler/compiler.go
CHANGED
|
@@ -1015,7 +1015,7 @@ func (c *GoToTSCompiler) writeConstantValue(constObj *types.Const) {
|
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
1017
|
// copyEmbeddedPackage recursively copies files from an embedded FS path to a filesystem directory.
|
|
1018
|
-
// It handles both regular files and directories.
|
|
1018
|
+
// It handles both regular files and directories, but only copies .gs.ts and .ts files.
|
|
1019
1019
|
func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) error {
|
|
1020
1020
|
// Remove the output path if it exists
|
|
1021
1021
|
if err := os.RemoveAll(outputPath); err != nil {
|
|
@@ -1049,6 +1049,13 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
|
|
|
1049
1049
|
return err
|
|
1050
1050
|
}
|
|
1051
1051
|
} else {
|
|
1052
|
+
// Only copy .gs.ts and .ts files, skip .go files and others
|
|
1053
|
+
fileName := entry.Name()
|
|
1054
|
+
if !strings.HasSuffix(fileName, ".gs.ts") && !strings.HasSuffix(fileName, ".ts") {
|
|
1055
|
+
c.le.Debugf("Skipping non-TypeScript file: %s", fileName)
|
|
1056
|
+
continue
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1052
1059
|
// Read the file content from the embedded FS
|
|
1053
1060
|
content, err := gs.GsOverrides.ReadFile(entryPath)
|
|
1054
1061
|
if err != nil {
|
package/compiler/expr-call.go
CHANGED
|
@@ -513,16 +513,51 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
513
513
|
}
|
|
514
514
|
return errors.New("unhandled append call with incorrect number of arguments")
|
|
515
515
|
case "byte":
|
|
516
|
-
// Translate byte(
|
|
516
|
+
// Translate byte(arg) to $.byte(arg)
|
|
517
|
+
if len(exp.Args) != 1 {
|
|
518
|
+
return errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
519
|
+
}
|
|
520
|
+
c.tsw.WriteLiterally("$.byte(")
|
|
521
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
522
|
+
return fmt.Errorf("failed to write argument for byte() conversion: %w", err)
|
|
523
|
+
}
|
|
524
|
+
c.tsw.WriteLiterally(")")
|
|
525
|
+
return nil // Handled byte() conversion
|
|
526
|
+
case "int":
|
|
527
|
+
// Handle int() conversion
|
|
517
528
|
if len(exp.Args) == 1 {
|
|
518
|
-
|
|
529
|
+
arg := exp.Args[0]
|
|
530
|
+
|
|
531
|
+
// Check if we're converting FROM a type with receiver methods TO int
|
|
532
|
+
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
533
|
+
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
534
|
+
argTypeName := namedArgType.Obj().Name()
|
|
535
|
+
// Check if the argument type has receiver methods
|
|
536
|
+
if c.hasReceiverMethods(argTypeName) {
|
|
537
|
+
// Check if we're converting to int (the underlying type)
|
|
538
|
+
if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
|
|
539
|
+
// This is a conversion from a type with methods to int
|
|
540
|
+
// Use valueOf() instead of $.int()
|
|
541
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
542
|
+
return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
|
|
543
|
+
}
|
|
544
|
+
c.tsw.WriteLiterally(".valueOf()")
|
|
545
|
+
return nil // Handled conversion from type with methods to int
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Default case: Translate int(value) to $.int(value)
|
|
552
|
+
c.tsw.WriteLiterally("$.int(")
|
|
519
553
|
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
520
|
-
return err
|
|
554
|
+
return fmt.Errorf("failed to write argument for int() conversion: %w", err)
|
|
521
555
|
}
|
|
522
556
|
c.tsw.WriteLiterally(")")
|
|
523
|
-
return nil // Handled
|
|
557
|
+
return nil // Handled int() conversion
|
|
524
558
|
}
|
|
525
|
-
|
|
559
|
+
// Return error for incorrect number of arguments
|
|
560
|
+
return fmt.Errorf("unhandled int conversion with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
526
561
|
default:
|
|
527
562
|
// Check if this is a type conversion to a function type
|
|
528
563
|
if funIdent != nil {
|
|
@@ -531,6 +566,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
531
566
|
if typeName, isType := obj.(*types.TypeName); isType {
|
|
532
567
|
// Make sure we have exactly one argument
|
|
533
568
|
if len(exp.Args) == 1 {
|
|
569
|
+
arg := exp.Args[0]
|
|
570
|
+
|
|
571
|
+
// Check if we're converting FROM a type with receiver methods TO its underlying type
|
|
572
|
+
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
573
|
+
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
574
|
+
argTypeName := namedArgType.Obj().Name()
|
|
575
|
+
// Check if the argument type has receiver methods
|
|
576
|
+
if c.hasReceiverMethods(argTypeName) {
|
|
577
|
+
// Check if we're converting to the underlying type
|
|
578
|
+
targetType := typeName.Type()
|
|
579
|
+
underlyingType := namedArgType.Underlying()
|
|
580
|
+
if types.Identical(targetType, underlyingType) {
|
|
581
|
+
// This is a conversion from a type with methods to its underlying type
|
|
582
|
+
// Use valueOf() instead of TypeScript cast
|
|
583
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
584
|
+
return fmt.Errorf("failed to write argument for valueOf conversion: %w", err)
|
|
585
|
+
}
|
|
586
|
+
c.tsw.WriteLiterally(".valueOf()")
|
|
587
|
+
return nil // Handled conversion from type with methods to underlying type
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
534
593
|
// Check if this is a function type
|
|
535
594
|
if _, isFuncType := typeName.Type().Underlying().(*types.Signature); isFuncType {
|
|
536
595
|
// For function types, we need to add a __goTypeName property
|
|
@@ -545,17 +604,30 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
545
604
|
c.tsw.WriteLiterallyf(", { __goTypeName: '%s' })", funIdent.String())
|
|
546
605
|
return nil // Handled function type cast
|
|
547
606
|
} else {
|
|
548
|
-
//
|
|
549
|
-
c.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
607
|
+
// Check if this type has receiver methods
|
|
608
|
+
if c.hasReceiverMethods(funIdent.String()) {
|
|
609
|
+
// For types with methods, use class constructor
|
|
610
|
+
c.tsw.WriteLiterally("new ")
|
|
611
|
+
c.tsw.WriteLiterally(funIdent.String())
|
|
612
|
+
c.tsw.WriteLiterally("(")
|
|
613
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
614
|
+
return fmt.Errorf("failed to write argument for type constructor: %w", err)
|
|
615
|
+
}
|
|
616
|
+
c.tsw.WriteLiterally(")")
|
|
617
|
+
return nil // Handled type with methods conversion
|
|
618
|
+
} else {
|
|
619
|
+
// For non-function types without methods, use the TypeScript "as" operator
|
|
620
|
+
c.tsw.WriteLiterally("(")
|
|
621
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
622
|
+
return fmt.Errorf("failed to write argument for type cast: %w", err)
|
|
623
|
+
}
|
|
553
624
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
625
|
+
// Then use the TypeScript "as" operator with the mapped type name
|
|
626
|
+
c.tsw.WriteLiterally(" as ")
|
|
627
|
+
c.WriteGoType(typeName.Type(), GoTypeContextGeneral)
|
|
628
|
+
c.tsw.WriteLiterally(")")
|
|
629
|
+
return nil // Handled non-function type cast
|
|
630
|
+
}
|
|
559
631
|
}
|
|
560
632
|
}
|
|
561
633
|
}
|
|
@@ -38,6 +38,14 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Check if this is a method value (method being used as a value, not called immediately)
|
|
42
|
+
if c.analysis.IsMethodValue(exp) {
|
|
43
|
+
// This is a method value - we need to bind it properly
|
|
44
|
+
if selection := c.pkg.TypesInfo.Selections[exp]; selection != nil {
|
|
45
|
+
return c.writeMethodValue(exp, selection)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
// --- Special case for dereferenced pointer to struct with field access: (*p).field or (**p).field etc ---
|
|
42
50
|
var baseExpr ast.Expr = exp.X
|
|
43
51
|
// Look inside parentheses if present
|
|
@@ -148,3 +156,95 @@ func (c *GoToTSCompiler) WriteSelectorExpr(exp *ast.SelectorExpr) error {
|
|
|
148
156
|
c.WriteIdent(exp.Sel, false)
|
|
149
157
|
return nil
|
|
150
158
|
}
|
|
159
|
+
|
|
160
|
+
// writeMethodValue handles method values (methods used as values, not called immediately)
|
|
161
|
+
// and generates proper binding code to maintain the 'this' context.
|
|
162
|
+
func (c *GoToTSCompiler) writeMethodValue(exp *ast.SelectorExpr, selection *types.Selection) error {
|
|
163
|
+
// Get the method signature to understand the receiver type
|
|
164
|
+
methodObj := selection.Obj().(*types.Func)
|
|
165
|
+
sig := methodObj.Type().(*types.Signature)
|
|
166
|
+
recv := sig.Recv()
|
|
167
|
+
|
|
168
|
+
if recv == nil {
|
|
169
|
+
// This shouldn't happen for method values, but handle gracefully
|
|
170
|
+
return fmt.Errorf("method value has no receiver: %s", methodObj.Name())
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Determine if this is a pointer receiver or value receiver
|
|
174
|
+
recvType := recv.Type()
|
|
175
|
+
isPointerReceiver := false
|
|
176
|
+
if _, ok := recvType.(*types.Pointer); ok {
|
|
177
|
+
isPointerReceiver = true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Get the base expression type to understand what we're working with
|
|
181
|
+
baseType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
182
|
+
baseIsPointer := false
|
|
183
|
+
if _, ok := baseType.(*types.Pointer); ok {
|
|
184
|
+
baseIsPointer = true
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Write the receiver expression
|
|
188
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
189
|
+
return fmt.Errorf("failed to write method value receiver: %w", err)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Add null assertion if needed
|
|
193
|
+
if baseIsPointer {
|
|
194
|
+
c.tsw.WriteLiterally("!")
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Handle different receiver type combinations according to Go semantics
|
|
198
|
+
if isPointerReceiver && !baseIsPointer {
|
|
199
|
+
// Pointer receiver method on value type: t.Mp equivalent to (&t).Mp
|
|
200
|
+
// The receiver should be the address of the value
|
|
201
|
+
c.tsw.WriteLiterally(".")
|
|
202
|
+
c.WriteIdent(exp.Sel, false)
|
|
203
|
+
c.tsw.WriteLiterally(".bind(")
|
|
204
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
205
|
+
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
206
|
+
}
|
|
207
|
+
if baseIsPointer {
|
|
208
|
+
c.tsw.WriteLiterally("!")
|
|
209
|
+
}
|
|
210
|
+
c.tsw.WriteLiterally(")")
|
|
211
|
+
} else if !isPointerReceiver && baseIsPointer {
|
|
212
|
+
// Value receiver method on pointer type: pt.Mv equivalent to (*pt).Mv
|
|
213
|
+
// The receiver should be a copy of the dereferenced value
|
|
214
|
+
c.tsw.WriteLiterally(".value.")
|
|
215
|
+
c.WriteIdent(exp.Sel, false)
|
|
216
|
+
c.tsw.WriteLiterally(".bind(")
|
|
217
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
218
|
+
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
219
|
+
}
|
|
220
|
+
c.tsw.WriteLiterally("!.value.clone())")
|
|
221
|
+
} else if !isPointerReceiver && !baseIsPointer {
|
|
222
|
+
// Value receiver method on value type: t.Mv
|
|
223
|
+
// The receiver should be a copy of the value
|
|
224
|
+
c.tsw.WriteLiterally(".")
|
|
225
|
+
c.WriteIdent(exp.Sel, false)
|
|
226
|
+
c.tsw.WriteLiterally(".bind(")
|
|
227
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
228
|
+
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
229
|
+
}
|
|
230
|
+
if baseIsPointer {
|
|
231
|
+
c.tsw.WriteLiterally("!")
|
|
232
|
+
}
|
|
233
|
+
c.tsw.WriteLiterally(".clone())")
|
|
234
|
+
} else {
|
|
235
|
+
// Pointer receiver method on pointer type: pt.Mp
|
|
236
|
+
// The receiver should be the pointer itself
|
|
237
|
+
c.tsw.WriteLiterally(".")
|
|
238
|
+
c.WriteIdent(exp.Sel, false)
|
|
239
|
+
c.tsw.WriteLiterally(".bind(")
|
|
240
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
241
|
+
return fmt.Errorf("failed to write method value receiver for binding: %w", err)
|
|
242
|
+
}
|
|
243
|
+
if baseIsPointer {
|
|
244
|
+
c.tsw.WriteLiterally("!")
|
|
245
|
+
}
|
|
246
|
+
c.tsw.WriteLiterally(")")
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return nil
|
|
250
|
+
}
|
package/compiler/spec-value.go
CHANGED
|
@@ -216,21 +216,92 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
216
216
|
c.tsw.WriteLiterally(")")
|
|
217
217
|
}
|
|
218
218
|
} else {
|
|
219
|
-
//
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
// Check if this is a named type with methods and the initializer is a basic value
|
|
220
|
+
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
221
|
+
typeName := namedType.Obj().Name()
|
|
222
|
+
if c.hasReceiverMethods(typeName) {
|
|
223
|
+
// Check if the initializer is a basic literal or simple value that needs wrapping
|
|
224
|
+
needsConstructor := false
|
|
225
|
+
switch initializerExpr.(type) {
|
|
226
|
+
case *ast.BasicLit:
|
|
227
|
+
needsConstructor = true
|
|
228
|
+
case *ast.Ident:
|
|
229
|
+
// Check if it's a simple identifier (not a function call or complex expression)
|
|
230
|
+
if ident := initializerExpr.(*ast.Ident); ident.Name != "nil" {
|
|
231
|
+
// Check if this identifier refers to a value of the underlying type
|
|
232
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
233
|
+
if objType := obj.Type(); objType != nil {
|
|
234
|
+
// If the identifier's type matches the underlying type, wrap it
|
|
235
|
+
if types.Identical(objType, namedType.Underlying()) {
|
|
236
|
+
needsConstructor = true
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if needsConstructor {
|
|
244
|
+
c.tsw.WriteLiterallyf("new %s(", typeName)
|
|
245
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
246
|
+
return err
|
|
247
|
+
}
|
|
248
|
+
c.tsw.WriteLiterally(")")
|
|
249
|
+
} else {
|
|
250
|
+
// Regular initializer for named type (e.g., function call that returns the type)
|
|
251
|
+
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
252
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
253
|
+
return err
|
|
254
|
+
}
|
|
255
|
+
c.tsw.WriteLiterally(".clone()")
|
|
256
|
+
} else {
|
|
257
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
258
|
+
return err
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
// Named type without methods, handle normally
|
|
264
|
+
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
265
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
266
|
+
return err
|
|
267
|
+
}
|
|
268
|
+
c.tsw.WriteLiterally(".clone()")
|
|
269
|
+
} else {
|
|
270
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
271
|
+
return err
|
|
272
|
+
}
|
|
273
|
+
}
|
|
223
274
|
}
|
|
224
|
-
c.tsw.WriteLiterally(".clone()")
|
|
225
275
|
} else {
|
|
226
|
-
|
|
227
|
-
|
|
276
|
+
// Regular initializer, clone if needed
|
|
277
|
+
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
278
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
279
|
+
return err
|
|
280
|
+
}
|
|
281
|
+
c.tsw.WriteLiterally(".clone()")
|
|
282
|
+
} else {
|
|
283
|
+
if err := c.WriteValueExpr(initializerExpr); err != nil {
|
|
284
|
+
return err
|
|
285
|
+
}
|
|
228
286
|
}
|
|
229
287
|
}
|
|
230
288
|
}
|
|
231
289
|
} else {
|
|
232
290
|
// No initializer, use the zero value directly
|
|
233
|
-
|
|
291
|
+
// Check if this is a named type with methods
|
|
292
|
+
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
293
|
+
typeName := namedType.Obj().Name()
|
|
294
|
+
if c.hasReceiverMethods(typeName) {
|
|
295
|
+
// For named types with methods, create a new instance with zero value
|
|
296
|
+
c.tsw.WriteLiterallyf("new %s(", typeName)
|
|
297
|
+
c.WriteZeroValueForType(namedType.Underlying())
|
|
298
|
+
c.tsw.WriteLiterally(")")
|
|
299
|
+
} else {
|
|
300
|
+
c.WriteZeroValueForType(goType)
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
c.WriteZeroValueForType(goType)
|
|
304
|
+
}
|
|
234
305
|
}
|
|
235
306
|
}
|
|
236
307
|
c.tsw.WriteLine("") // Finish the declaration line
|
package/compiler/spec.go
CHANGED
|
@@ -150,6 +150,237 @@ func (c *GoToTSCompiler) writeClonedFieldInitializer(fieldName string, fieldType
|
|
|
150
150
|
c.tsw.WriteLiterally(")")
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// hasReceiverMethods checks if a type declaration has any receiver methods defined
|
|
154
|
+
func (c *GoToTSCompiler) hasReceiverMethods(typeName string) bool {
|
|
155
|
+
for _, fileSyntax := range c.pkg.Syntax {
|
|
156
|
+
for _, decl := range fileSyntax.Decls {
|
|
157
|
+
funcDecl, isFunc := decl.(*ast.FuncDecl)
|
|
158
|
+
if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
recvField := funcDecl.Recv.List[0]
|
|
162
|
+
recvType := recvField.Type
|
|
163
|
+
if starExpr, ok := recvType.(*ast.StarExpr); ok {
|
|
164
|
+
recvType = starExpr.X
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check for both simple identifiers (FileMode) and generic types (FileMode[T])
|
|
168
|
+
var recvTypeName string
|
|
169
|
+
if ident, ok := recvType.(*ast.Ident); ok {
|
|
170
|
+
recvTypeName = ident.Name
|
|
171
|
+
} else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
|
|
172
|
+
if ident, ok := indexExpr.X.(*ast.Ident); ok {
|
|
173
|
+
recvTypeName = ident.Name
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if recvTypeName == typeName {
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// WriteNamedTypeWithMethods generates a TypeScript class for a Go named type that has receiver methods
|
|
186
|
+
func (c *GoToTSCompiler) WriteNamedTypeWithMethods(a *ast.TypeSpec) error {
|
|
187
|
+
className := a.Name.Name
|
|
188
|
+
underlyingType := c.pkg.TypesInfo.TypeOf(a.Type)
|
|
189
|
+
|
|
190
|
+
// Add export for Go-exported types (but not if inside a function)
|
|
191
|
+
isInsideFunction := false
|
|
192
|
+
if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
|
|
193
|
+
isInsideFunction = nodeInfo.IsInsideFunction
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if a.Name.IsExported() && !isInsideFunction {
|
|
197
|
+
c.tsw.WriteLiterally("export ")
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
c.tsw.WriteLiterallyf("class %s {", className)
|
|
201
|
+
c.tsw.WriteLine("")
|
|
202
|
+
c.tsw.Indent(1)
|
|
203
|
+
|
|
204
|
+
// Constructor that takes the underlying type value
|
|
205
|
+
c.tsw.WriteLiterally("constructor(private _value: ")
|
|
206
|
+
c.WriteGoType(underlyingType, GoTypeContextGeneral)
|
|
207
|
+
c.tsw.WriteLine(") {}")
|
|
208
|
+
c.tsw.WriteLine("")
|
|
209
|
+
|
|
210
|
+
// valueOf method to get the underlying value (for type conversions and operations)
|
|
211
|
+
c.tsw.WriteLiterally("valueOf(): ")
|
|
212
|
+
c.WriteGoType(underlyingType, GoTypeContextGeneral)
|
|
213
|
+
c.tsw.WriteLine(" {")
|
|
214
|
+
c.tsw.Indent(1)
|
|
215
|
+
c.tsw.WriteLine("return this._value")
|
|
216
|
+
c.tsw.Indent(-1)
|
|
217
|
+
c.tsw.WriteLine("}")
|
|
218
|
+
c.tsw.WriteLine("")
|
|
219
|
+
|
|
220
|
+
// toString method for string conversion
|
|
221
|
+
c.tsw.WriteLine("toString(): string {")
|
|
222
|
+
c.tsw.Indent(1)
|
|
223
|
+
c.tsw.WriteLine("return String(this._value)")
|
|
224
|
+
c.tsw.Indent(-1)
|
|
225
|
+
c.tsw.WriteLine("}")
|
|
226
|
+
c.tsw.WriteLine("")
|
|
227
|
+
|
|
228
|
+
// Static from method for type conversion
|
|
229
|
+
c.tsw.WriteLiterallyf("static from(value: ")
|
|
230
|
+
c.WriteGoType(underlyingType, GoTypeContextGeneral)
|
|
231
|
+
c.tsw.WriteLiterallyf("): %s {", className)
|
|
232
|
+
c.tsw.WriteLine("")
|
|
233
|
+
c.tsw.Indent(1)
|
|
234
|
+
c.tsw.WriteLiterallyf("return new %s(value)", className)
|
|
235
|
+
c.tsw.WriteLine("")
|
|
236
|
+
c.tsw.Indent(-1)
|
|
237
|
+
c.tsw.WriteLine("}")
|
|
238
|
+
|
|
239
|
+
// Add receiver methods for this type
|
|
240
|
+
for _, fileSyntax := range c.pkg.Syntax {
|
|
241
|
+
for _, decl := range fileSyntax.Decls {
|
|
242
|
+
funcDecl, isFunc := decl.(*ast.FuncDecl)
|
|
243
|
+
if !isFunc || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 {
|
|
244
|
+
continue
|
|
245
|
+
}
|
|
246
|
+
recvField := funcDecl.Recv.List[0]
|
|
247
|
+
recvType := recvField.Type
|
|
248
|
+
if starExpr, ok := recvType.(*ast.StarExpr); ok {
|
|
249
|
+
recvType = starExpr.X
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check for both simple identifiers (FileMode) and generic types (FileMode[T])
|
|
253
|
+
var recvTypeName string
|
|
254
|
+
if ident, ok := recvType.(*ast.Ident); ok {
|
|
255
|
+
recvTypeName = ident.Name
|
|
256
|
+
} else if indexExpr, ok := recvType.(*ast.IndexExpr); ok {
|
|
257
|
+
if ident, ok := indexExpr.X.(*ast.Ident); ok {
|
|
258
|
+
recvTypeName = ident.Name
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if recvTypeName == className {
|
|
263
|
+
c.tsw.WriteLine("")
|
|
264
|
+
if err := c.writeNamedTypeMethod(funcDecl, className); err != nil {
|
|
265
|
+
return err
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
c.tsw.Indent(-1)
|
|
272
|
+
c.tsw.WriteLine("}")
|
|
273
|
+
|
|
274
|
+
return nil
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// writeNamedTypeMethod writes a method for a named type, handling receiver binding properly
|
|
278
|
+
func (c *GoToTSCompiler) writeNamedTypeMethod(decl *ast.FuncDecl, className string) error {
|
|
279
|
+
if decl.Doc != nil {
|
|
280
|
+
c.WriteDoc(decl.Doc)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Determine if method is async
|
|
284
|
+
var isAsync bool
|
|
285
|
+
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
286
|
+
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Methods are typically public in the TS output
|
|
290
|
+
c.tsw.WriteLiterally("public ")
|
|
291
|
+
|
|
292
|
+
// Add async modifier if needed
|
|
293
|
+
if isAsync {
|
|
294
|
+
c.tsw.WriteLiterally("async ")
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Keep original Go casing for method names
|
|
298
|
+
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
299
|
+
return err
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Write signature (parameters and return type)
|
|
303
|
+
funcType := decl.Type
|
|
304
|
+
c.tsw.WriteLiterally("(")
|
|
305
|
+
if funcType.Params != nil {
|
|
306
|
+
c.WriteFieldList(funcType.Params, true) // true = arguments
|
|
307
|
+
}
|
|
308
|
+
c.tsw.WriteLiterally(")")
|
|
309
|
+
|
|
310
|
+
// Handle return type
|
|
311
|
+
if funcType.Results != nil && len(funcType.Results.List) > 0 {
|
|
312
|
+
c.tsw.WriteLiterally(": ")
|
|
313
|
+
if isAsync {
|
|
314
|
+
c.tsw.WriteLiterally("Promise<")
|
|
315
|
+
}
|
|
316
|
+
if len(funcType.Results.List) == 1 {
|
|
317
|
+
// Single return value
|
|
318
|
+
resultType := funcType.Results.List[0].Type
|
|
319
|
+
c.WriteTypeExpr(resultType)
|
|
320
|
+
} else {
|
|
321
|
+
// Multiple return values -> tuple type
|
|
322
|
+
c.tsw.WriteLiterally("[")
|
|
323
|
+
for i, field := range funcType.Results.List {
|
|
324
|
+
if i > 0 {
|
|
325
|
+
c.tsw.WriteLiterally(", ")
|
|
326
|
+
}
|
|
327
|
+
c.WriteTypeExpr(field.Type)
|
|
328
|
+
}
|
|
329
|
+
c.tsw.WriteLiterally("]")
|
|
330
|
+
}
|
|
331
|
+
if isAsync {
|
|
332
|
+
c.tsw.WriteLiterally(">")
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
// No return value -> void
|
|
336
|
+
if isAsync {
|
|
337
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
338
|
+
} else {
|
|
339
|
+
c.tsw.WriteLiterally(": void")
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
c.tsw.WriteLiterally(" ")
|
|
344
|
+
|
|
345
|
+
// For named types with methods, bind receiver name to this._value
|
|
346
|
+
if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
|
|
347
|
+
recvName := recvField.Names[0].Name
|
|
348
|
+
if recvName != "_" {
|
|
349
|
+
c.tsw.WriteLine("{")
|
|
350
|
+
c.tsw.Indent(1)
|
|
351
|
+
// Bind the receiver name to this._value for value types
|
|
352
|
+
sanitizedRecvName := c.sanitizeIdentifier(recvName)
|
|
353
|
+
c.tsw.WriteLinef("const %s = this._value", sanitizedRecvName)
|
|
354
|
+
|
|
355
|
+
// Add using statement if needed
|
|
356
|
+
if c.analysis.NeedsDefer(decl.Body) {
|
|
357
|
+
if c.analysis.IsInAsyncFunction(decl) {
|
|
358
|
+
c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
|
|
359
|
+
} else {
|
|
360
|
+
c.tsw.WriteLine("using cleanup = new $.DisposableStack();")
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// write method body without outer braces
|
|
365
|
+
for _, stmt := range decl.Body.List {
|
|
366
|
+
if err := c.WriteStmt(stmt); err != nil {
|
|
367
|
+
return fmt.Errorf("failed to write statement in function body: %w", err)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
c.tsw.Indent(-1)
|
|
371
|
+
c.tsw.WriteLine("}")
|
|
372
|
+
|
|
373
|
+
return nil
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// no named receiver, write whole body
|
|
377
|
+
if err := c.WriteStmt(decl.Body); err != nil {
|
|
378
|
+
return fmt.Errorf("failed to write function body: %w", err)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return nil
|
|
382
|
+
}
|
|
383
|
+
|
|
153
384
|
// WriteTypeSpec writes the type specification to the output.
|
|
154
385
|
func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
155
386
|
if a.Doc != nil {
|
|
@@ -165,6 +396,11 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
165
396
|
case *ast.InterfaceType:
|
|
166
397
|
return c.WriteInterfaceTypeSpec(a, t)
|
|
167
398
|
default:
|
|
399
|
+
// Check if this type has receiver methods
|
|
400
|
+
if c.hasReceiverMethods(a.Name.Name) {
|
|
401
|
+
return c.WriteNamedTypeWithMethods(a)
|
|
402
|
+
}
|
|
403
|
+
|
|
168
404
|
// type alias - add export for Go-exported types (but not if inside a function)
|
|
169
405
|
isInsideFunction := false
|
|
170
406
|
if nodeInfo := c.analysis.NodeData[a]; nodeInfo != nil {
|
|
@@ -5,6 +5,7 @@ export * from './io.js';
|
|
|
5
5
|
export * from './map.js';
|
|
6
6
|
export * from './slice.js';
|
|
7
7
|
export * from './type.js';
|
|
8
|
+
export declare function int(value: number): number;
|
|
8
9
|
export declare function copy<T>(dst: T[] | Uint8Array, src: T[] | Uint8Array | string): number;
|
|
9
10
|
export declare function multiplyDuration(duration: any, multiplier: number): any;
|
|
10
11
|
/**
|