goscript 0.0.60 → 0.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/compiler/analysis.go +974 -369
- package/compiler/assignment.go +72 -0
- package/compiler/compiler.go +74 -15
- package/compiler/composite-lit.go +29 -8
- package/compiler/decl.go +67 -98
- package/compiler/expr-call-async.go +26 -1
- package/compiler/expr-call-builtins.go +60 -4
- package/compiler/expr-call-helpers.go +182 -0
- package/compiler/expr-call-type-conversion.go +37 -5
- package/compiler/expr-call.go +25 -33
- package/compiler/expr-selector.go +71 -1
- package/compiler/expr-type.go +49 -3
- package/compiler/expr.go +37 -28
- package/compiler/index.test.ts +3 -1
- package/compiler/lit.go +13 -4
- package/compiler/spec-struct.go +42 -9
- package/compiler/spec-value.go +2 -2
- package/compiler/spec.go +42 -5
- package/compiler/stmt-assign.go +71 -0
- package/compiler/stmt-range.go +2 -2
- package/compiler/stmt.go +130 -10
- package/compiler/type-utils.go +40 -16
- package/compiler/type.go +50 -12
- package/dist/gs/builtin/builtin.d.ts +8 -1
- package/dist/gs/builtin/builtin.js +26 -1
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/errors.d.ts +1 -0
- package/dist/gs/builtin/errors.js +8 -0
- package/dist/gs/builtin/errors.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +5 -4
- package/dist/gs/builtin/slice.js +51 -21
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +28 -2
- package/dist/gs/builtin/type.js +132 -0
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/reader.gs.d.ts +1 -1
- package/dist/gs/bytes/reader.gs.js +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/internal/byteorder/index.d.ts +6 -0
- package/dist/gs/internal/byteorder/index.js +34 -0
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.d.ts +3 -2
- package/dist/gs/reflect/map.js +37 -3
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +55 -8
- package/dist/gs/reflect/type.js +889 -23
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +11 -12
- package/dist/gs/reflect/types.js +26 -15
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.d.ts +4 -4
- package/dist/gs/reflect/value.js +8 -2
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +32 -0
- package/dist/gs/slices/slices.js +81 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.d.ts +2 -2
- package/dist/gs/sync/atomic/type.gs.js +12 -2
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
- package/dist/gs/unicode/utf8/utf8.js +10 -6
- package/dist/gs/unicode/utf8/utf8.js.map +1 -1
- package/go.mod +4 -4
- package/go.sum +8 -16
- package/gs/builtin/builtin.ts +27 -2
- package/gs/builtin/errors.ts +12 -0
- package/gs/builtin/slice.ts +77 -14
- package/gs/builtin/type.ts +167 -2
- package/gs/bytes/reader.gs.ts +2 -2
- package/gs/internal/byteorder/index.ts +40 -0
- package/gs/math/hypot.gs.test.ts +3 -1
- package/gs/math/pow10.gs.test.ts +5 -4
- package/gs/reflect/index.ts +6 -3
- package/gs/reflect/map.test.ts +7 -6
- package/gs/reflect/map.ts +49 -7
- package/gs/reflect/type.ts +1139 -43
- package/gs/reflect/types.ts +34 -21
- package/gs/reflect/value.ts +12 -6
- package/gs/slices/slices.ts +92 -0
- package/gs/sync/atomic/type.gs.ts +14 -5
- package/gs/sync/meta.json +1 -1
- package/gs/unicode/utf8/utf8.ts +12 -8
- package/package.json +13 -13
package/compiler/assignment.go
CHANGED
|
@@ -219,6 +219,15 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
219
219
|
if shouldApplyClone(c.pkg, r) {
|
|
220
220
|
// When cloning for value assignment, mark the result as struct value
|
|
221
221
|
c.tsw.WriteLiterally("$.markAsStructValue(")
|
|
222
|
+
|
|
223
|
+
// Check if RHS is an async call - if so, wrap in parentheses so .clone() binds correctly
|
|
224
|
+
// Example: (await asyncFunc()).clone() instead of await asyncFunc().clone()
|
|
225
|
+
needsParensForAsync := false
|
|
226
|
+
if callExpr, isCall := r.(*ast.CallExpr); isCall && c.isCallExprAsync(callExpr) {
|
|
227
|
+
needsParensForAsync = true
|
|
228
|
+
c.tsw.WriteLiterally("(")
|
|
229
|
+
}
|
|
230
|
+
|
|
222
231
|
// For other expressions, we need to handle variable referenced access differently
|
|
223
232
|
if _, isIdent := r.(*ast.Ident); isIdent {
|
|
224
233
|
// For identifiers, WriteValueExpr already adds .value if needed
|
|
@@ -236,6 +245,9 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
236
245
|
}
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
if needsParensForAsync {
|
|
249
|
+
c.tsw.WriteLiterally(")")
|
|
250
|
+
}
|
|
239
251
|
c.tsw.WriteLiterally(".clone())") // Always add clone for struct values
|
|
240
252
|
} else {
|
|
241
253
|
// Check if this is a pointer variable assignment to an interface type
|
|
@@ -270,6 +282,11 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
270
282
|
}
|
|
271
283
|
|
|
272
284
|
// Non-struct case: write RHS normally
|
|
285
|
+
// Check if this is a primitive error type being assigned to an error interface
|
|
286
|
+
if c.writePrimitiveErrorWrapperForAssign(lhs, r, i) {
|
|
287
|
+
continue
|
|
288
|
+
}
|
|
289
|
+
|
|
273
290
|
if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
|
|
274
291
|
return err
|
|
275
292
|
}
|
|
@@ -288,6 +305,61 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
288
305
|
return nil
|
|
289
306
|
}
|
|
290
307
|
|
|
308
|
+
// writePrimitiveErrorWrapperForAssign checks if an RHS value is a primitive type
|
|
309
|
+
// that implements the error interface being assigned to an error-typed LHS,
|
|
310
|
+
// and if so, wraps it with $.wrapPrimitiveError.
|
|
311
|
+
// Returns true if the wrapper was written, false otherwise.
|
|
312
|
+
func (c *GoToTSCompiler) writePrimitiveErrorWrapperForAssign(lhs []ast.Expr, rhs ast.Expr, rhsIndex int) bool {
|
|
313
|
+
// Only handle single assignments for now
|
|
314
|
+
if len(lhs) != 1 || rhsIndex != 0 {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Get the LHS type
|
|
319
|
+
lhsType := c.pkg.TypesInfo.TypeOf(lhs[0])
|
|
320
|
+
if lhsType == nil {
|
|
321
|
+
return false
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Check if the LHS type is the error interface
|
|
325
|
+
if iface, ok := lhsType.Underlying().(*types.Interface); !ok || iface.String() != "interface{Error() string}" {
|
|
326
|
+
return false
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Get the actual type of the RHS expression
|
|
330
|
+
rhsType := c.pkg.TypesInfo.TypeOf(rhs)
|
|
331
|
+
if rhsType == nil {
|
|
332
|
+
return false
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Check if the RHS type is a wrapper type (named type with basic underlying type)
|
|
336
|
+
if !c.isWrapperType(rhsType) {
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Check if the RHS type has an Error() method
|
|
341
|
+
if !c.typeHasMethods(rhsType, "Error") {
|
|
342
|
+
return false
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Get the qualified type name for the Error function
|
|
346
|
+
typeName := c.getQualifiedTypeName(rhsType)
|
|
347
|
+
if typeName == "" {
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Write: $.wrapPrimitiveError(value, TypeName_Error)
|
|
352
|
+
c.tsw.WriteLiterally("$.wrapPrimitiveError(")
|
|
353
|
+
if err := c.WriteValueExpr(rhs); err != nil {
|
|
354
|
+
return false
|
|
355
|
+
}
|
|
356
|
+
c.tsw.WriteLiterally(", ")
|
|
357
|
+
c.tsw.WriteLiterally(typeName)
|
|
358
|
+
c.tsw.WriteLiterally("_Error)")
|
|
359
|
+
|
|
360
|
+
return true
|
|
361
|
+
}
|
|
362
|
+
|
|
291
363
|
// writeBlankIdentifierAssign handles assignment to blank identifier (_)
|
|
292
364
|
func (c *GoToTSCompiler) writeBlankIdentifierAssign(rhs ast.Expr) error {
|
|
293
365
|
c.tsw.WriteLiterally("/* _ = */ ")
|
package/compiler/compiler.go
CHANGED
|
@@ -13,7 +13,6 @@ import (
|
|
|
13
13
|
"os"
|
|
14
14
|
"path/filepath"
|
|
15
15
|
"slices"
|
|
16
|
-
"sort"
|
|
17
16
|
"strings"
|
|
18
17
|
|
|
19
18
|
gs "github.com/aperturerobotics/goscript"
|
|
@@ -487,7 +486,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
487
486
|
|
|
488
487
|
// Write exports if this file has exported symbols
|
|
489
488
|
if len(valueSymbols) > 0 {
|
|
490
|
-
|
|
489
|
+
slices.Sort(valueSymbols)
|
|
491
490
|
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
492
491
|
strings.Join(valueSymbols, ", "), fileName)
|
|
493
492
|
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
@@ -497,7 +496,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
497
496
|
|
|
498
497
|
// Write struct exports (both as types and values)
|
|
499
498
|
if len(structSymbols) > 0 {
|
|
500
|
-
|
|
499
|
+
slices.Sort(structSymbols)
|
|
501
500
|
// Export classes as values (which makes them available as both types and values in TypeScript)
|
|
502
501
|
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
503
502
|
strings.Join(structSymbols, ", "), fileName)
|
|
@@ -507,7 +506,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
507
506
|
}
|
|
508
507
|
|
|
509
508
|
if len(typeSymbols) > 0 {
|
|
510
|
-
|
|
509
|
+
slices.Sort(typeSymbols)
|
|
511
510
|
exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
|
|
512
511
|
strings.Join(typeSymbols, ", "), fileName)
|
|
513
512
|
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
@@ -612,7 +611,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
612
611
|
for sourceFile := range imports {
|
|
613
612
|
sourceFiles = append(sourceFiles, sourceFile)
|
|
614
613
|
}
|
|
615
|
-
|
|
614
|
+
slices.Sort(sourceFiles)
|
|
616
615
|
|
|
617
616
|
for _, sourceFile := range sourceFiles {
|
|
618
617
|
functions := imports[sourceFile]
|
|
@@ -623,7 +622,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
623
622
|
sanitizedFunctions = append(sanitizedFunctions, sanitizeIdentifier(fn))
|
|
624
623
|
}
|
|
625
624
|
// Sort functions for consistent output
|
|
626
|
-
|
|
625
|
+
slices.Sort(sanitizedFunctions)
|
|
627
626
|
c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
|
|
628
627
|
strings.Join(sanitizedFunctions, ", "), sourceFile)
|
|
629
628
|
}
|
|
@@ -637,7 +636,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
637
636
|
for sourceFile := range typeImports {
|
|
638
637
|
sourceFiles = append(sourceFiles, sourceFile)
|
|
639
638
|
}
|
|
640
|
-
|
|
639
|
+
slices.Sort(sourceFiles)
|
|
641
640
|
|
|
642
641
|
for _, sourceFile := range sourceFiles {
|
|
643
642
|
typeImports := typeImports[sourceFile]
|
|
@@ -668,7 +667,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
668
667
|
sanitizedTypes = append(sanitizedTypes, sanitizeIdentifier(typeName))
|
|
669
668
|
}
|
|
670
669
|
// Sort types for consistent output
|
|
671
|
-
|
|
670
|
+
slices.Sort(sanitizedTypes)
|
|
672
671
|
c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
|
|
673
672
|
strings.Join(sanitizedTypes, ", "), sourceFile)
|
|
674
673
|
}
|
|
@@ -676,6 +675,43 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
676
675
|
}
|
|
677
676
|
}
|
|
678
677
|
|
|
678
|
+
// Generate auto-imports for variables from other files in the same package
|
|
679
|
+
if varImports := c.PackageAnalysis.VariableCalls[currentFileName]; varImports != nil {
|
|
680
|
+
// Sort source files for consistent import order
|
|
681
|
+
var sourceFiles []string
|
|
682
|
+
for sourceFile := range varImports {
|
|
683
|
+
sourceFiles = append(sourceFiles, sourceFile)
|
|
684
|
+
}
|
|
685
|
+
slices.Sort(sourceFiles)
|
|
686
|
+
|
|
687
|
+
for _, sourceFile := range sourceFiles {
|
|
688
|
+
variables := varImports[sourceFile]
|
|
689
|
+
if len(variables) > 0 {
|
|
690
|
+
// Apply sanitization to variable names
|
|
691
|
+
var sanitizedVariables []string
|
|
692
|
+
for _, varName := range variables {
|
|
693
|
+
sanitizedVariables = append(sanitizedVariables, sanitizeIdentifier(varName))
|
|
694
|
+
}
|
|
695
|
+
// Sort variables for consistent output
|
|
696
|
+
slices.Sort(sanitizedVariables)
|
|
697
|
+
c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
|
|
698
|
+
strings.Join(sanitizedVariables, ", "), sourceFile)
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Write synthetic imports (for promoted methods from embedded structs)
|
|
704
|
+
// Sort by package name for consistent output
|
|
705
|
+
var syntheticPkgNames []string
|
|
706
|
+
for pkgName := range c.Analysis.SyntheticImports {
|
|
707
|
+
syntheticPkgNames = append(syntheticPkgNames, pkgName)
|
|
708
|
+
}
|
|
709
|
+
slices.Sort(syntheticPkgNames)
|
|
710
|
+
for _, pkgName := range syntheticPkgNames {
|
|
711
|
+
imp := c.Analysis.SyntheticImports[pkgName]
|
|
712
|
+
c.codeWriter.WriteImport(pkgName, imp.importPath+"/index.js")
|
|
713
|
+
}
|
|
714
|
+
|
|
679
715
|
c.codeWriter.WriteLine("") // Add a newline after imports
|
|
680
716
|
|
|
681
717
|
if err := goWriter.WriteDecls(f.Decls); err != nil {
|
|
@@ -697,6 +733,10 @@ type GoToTSCompiler struct {
|
|
|
697
733
|
|
|
698
734
|
// Context flags
|
|
699
735
|
insideAddressOf bool // true when processing operand of & operator
|
|
736
|
+
|
|
737
|
+
// renamedVars tracks variables that have been renamed to avoid type shadowing
|
|
738
|
+
// Key: types.Object of the original variable, Value: new name to use
|
|
739
|
+
renamedVars map[types.Object]string
|
|
700
740
|
}
|
|
701
741
|
|
|
702
742
|
// It initializes the compiler with a `TSCodeWriter` for output,
|
|
@@ -704,9 +744,10 @@ type GoToTSCompiler struct {
|
|
|
704
744
|
// analysis results (`Analysis`) to guide the translation process.
|
|
705
745
|
func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
|
|
706
746
|
return &GoToTSCompiler{
|
|
707
|
-
tsw:
|
|
708
|
-
pkg:
|
|
709
|
-
analysis:
|
|
747
|
+
tsw: tsw,
|
|
748
|
+
pkg: pkg,
|
|
749
|
+
analysis: analysis,
|
|
750
|
+
renamedVars: make(map[types.Object]string),
|
|
710
751
|
}
|
|
711
752
|
}
|
|
712
753
|
|
|
@@ -785,6 +826,18 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
|
|
|
785
826
|
// Use TypesInfo to find the object associated with the identifier
|
|
786
827
|
obj := c.objectOfIdent(exp)
|
|
787
828
|
|
|
829
|
+
// Check if this variable has been renamed to avoid type shadowing
|
|
830
|
+
if obj != nil {
|
|
831
|
+
if renamedName, ok := c.renamedVars[obj]; ok {
|
|
832
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(renamedName))
|
|
833
|
+
// Determine if we need to access .value based on analysis data
|
|
834
|
+
if accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
|
|
835
|
+
c.tsw.WriteLiterally("!.value")
|
|
836
|
+
}
|
|
837
|
+
return
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
788
841
|
// Check if this identifier refers to a constant
|
|
789
842
|
if obj != nil {
|
|
790
843
|
if constObj, isConst := obj.(*types.Const); isConst {
|
|
@@ -844,7 +897,7 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
|
|
|
844
897
|
if exp.List == nil {
|
|
845
898
|
// Default case
|
|
846
899
|
c.tsw.WriteLiterally("default:")
|
|
847
|
-
c.tsw.WriteLine("")
|
|
900
|
+
c.tsw.WriteLine(" {")
|
|
848
901
|
} else {
|
|
849
902
|
// Case with expressions
|
|
850
903
|
// For Go's `case expr1, expr2:`, we translate to:
|
|
@@ -852,18 +905,23 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
|
|
|
852
905
|
// case expr2:
|
|
853
906
|
// ... body ...
|
|
854
907
|
// break
|
|
855
|
-
for
|
|
908
|
+
for i, caseExpr := range exp.List {
|
|
856
909
|
c.tsw.WriteLiterally("case ")
|
|
857
910
|
if err := c.WriteValueExpr(caseExpr); err != nil {
|
|
858
911
|
return fmt.Errorf("failed to write case clause expression: %w", err)
|
|
859
912
|
}
|
|
860
913
|
c.tsw.WriteLiterally(":")
|
|
861
|
-
|
|
914
|
+
// Only add opening brace after the last case label
|
|
915
|
+
if i == len(exp.List)-1 {
|
|
916
|
+
c.tsw.WriteLine(" {")
|
|
917
|
+
} else {
|
|
918
|
+
c.tsw.WriteLine("")
|
|
919
|
+
}
|
|
862
920
|
}
|
|
863
921
|
}
|
|
864
922
|
|
|
865
923
|
// The body is written once, after all case labels for this clause.
|
|
866
|
-
//
|
|
924
|
+
// Wrap in block to provide Go-like case scope semantics.
|
|
867
925
|
c.tsw.Indent(1)
|
|
868
926
|
for _, stmt := range exp.Body {
|
|
869
927
|
if err := c.WriteStmt(stmt); err != nil {
|
|
@@ -873,6 +931,7 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
|
|
|
873
931
|
// Add break statement (Go's switch has implicit breaks, TS needs explicit break)
|
|
874
932
|
c.tsw.WriteLine("break")
|
|
875
933
|
c.tsw.Indent(-1)
|
|
934
|
+
c.tsw.WriteLine("}")
|
|
876
935
|
return nil
|
|
877
936
|
}
|
|
878
937
|
|
|
@@ -341,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
341
341
|
case *types.Map, *types.Struct:
|
|
342
342
|
// Handle struct directly with the struct literal logic
|
|
343
343
|
if structType, ok := underlying.(*types.Struct); ok {
|
|
344
|
-
return c.writeUntypedStructLiteral(exp, structType)
|
|
344
|
+
return c.writeUntypedStructLiteral(exp, tv.Type, structType)
|
|
345
345
|
}
|
|
346
346
|
// Map case would be handled here
|
|
347
347
|
return fmt.Errorf("untyped map composite literals not yet supported")
|
|
@@ -356,7 +356,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
356
356
|
// This is an anonymous struct literal with inferred pointer type
|
|
357
357
|
// Just create the struct object directly - no var-refing needed
|
|
358
358
|
// Anonymous literals are not variables, so they don't get var-refed
|
|
359
|
-
return c.writeUntypedStructLiteral(exp, elemType)
|
|
359
|
+
return c.writeUntypedStructLiteral(exp, ptrType.Elem(), elemType)
|
|
360
360
|
default:
|
|
361
361
|
return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
|
|
362
362
|
}
|
|
@@ -384,7 +384,7 @@ func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
|
|
387
|
-
func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, structType *types.Struct) error {
|
|
387
|
+
func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, actualType types.Type, structType *types.Struct) error {
|
|
388
388
|
// Create field mapping like the typed struct case
|
|
389
389
|
directFields := make(map[string]ast.Expr)
|
|
390
390
|
|
|
@@ -408,8 +408,23 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
|
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
//
|
|
412
|
-
|
|
411
|
+
// Check if this is a named type
|
|
412
|
+
isNamed := false
|
|
413
|
+
if _, ok := actualType.(*types.Named); ok {
|
|
414
|
+
isNamed = true
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Write the object literal
|
|
418
|
+
if isNamed {
|
|
419
|
+
// For named structs, use constructor
|
|
420
|
+
c.tsw.WriteLiterally("$.markAsStructValue(new ")
|
|
421
|
+
// Write the type name
|
|
422
|
+
c.WriteGoType(actualType, GoTypeContextGeneral)
|
|
423
|
+
c.tsw.WriteLiterally("({")
|
|
424
|
+
} else {
|
|
425
|
+
// For truly anonymous structs, just write a simple object literal
|
|
426
|
+
c.tsw.WriteLiterally("{")
|
|
427
|
+
}
|
|
413
428
|
|
|
414
429
|
firstFieldWritten := false
|
|
415
430
|
// Write fields in order
|
|
@@ -434,7 +449,12 @@ func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, struct
|
|
|
434
449
|
firstFieldWritten = true
|
|
435
450
|
}
|
|
436
451
|
|
|
437
|
-
|
|
452
|
+
// Close the object literal
|
|
453
|
+
if isNamed {
|
|
454
|
+
c.tsw.WriteLiterally("}))")
|
|
455
|
+
} else {
|
|
456
|
+
c.tsw.WriteLiterally("}")
|
|
457
|
+
}
|
|
438
458
|
return nil
|
|
439
459
|
}
|
|
440
460
|
|
|
@@ -606,9 +626,10 @@ func (c *GoToTSCompiler) categorizeStructFields(
|
|
|
606
626
|
}
|
|
607
627
|
}
|
|
608
628
|
|
|
609
|
-
// Handle the case where
|
|
629
|
+
// Handle the case where a struct has values without keys (positional initialization)
|
|
610
630
|
// This block processes non-key-value elements and associates them with struct fields.
|
|
611
|
-
|
|
631
|
+
// This applies to both named and anonymous structs.
|
|
632
|
+
if len(exp.Elts) > 0 && len(directFields) == 0 {
|
|
612
633
|
// Check if any elements in the composite literal are not key-value pairs.
|
|
613
634
|
hasNonKeyValueElts := false
|
|
614
635
|
for _, elt := range exp.Elts {
|
package/compiler/decl.go
CHANGED
|
@@ -5,7 +5,7 @@ import (
|
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
7
|
"go/types"
|
|
8
|
-
"
|
|
8
|
+
"slices"
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
@@ -210,7 +210,7 @@ func (c *GoToTSCompiler) extractTypeDependencies(typeExpr ast.Expr, typeSpecMap
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
// Sort dependencies for deterministic output
|
|
213
|
-
|
|
213
|
+
slices.Sort(deps)
|
|
214
214
|
return deps
|
|
215
215
|
}
|
|
216
216
|
|
|
@@ -252,122 +252,82 @@ func (c *GoToTSCompiler) extractStructFieldDependencies(fieldType ast.Expr, type
|
|
|
252
252
|
return deps
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
// sortVarSpecsByTypeDependencies sorts variable declarations based on their
|
|
255
|
+
// sortVarSpecsByTypeDependencies sorts variable declarations based on their value dependencies
|
|
256
|
+
// to ensure that variables are initialized in the correct order (respecting JavaScript's TDZ).
|
|
257
|
+
// For example: var StdEncoding = NewEncoding(...) must come before var RawStdEncoding = StdEncoding.WithPadding(...)
|
|
256
258
|
func (c *GoToTSCompiler) sortVarSpecsByTypeDependencies(varSpecs []*ast.ValueSpec, typeSpecs []*ast.TypeSpec) ([]*ast.ValueSpec, error) {
|
|
257
259
|
if len(varSpecs) <= 1 {
|
|
258
260
|
return varSpecs, nil
|
|
259
261
|
}
|
|
260
262
|
|
|
261
|
-
// Build
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
typeSpecMap[typeSpec.Name.Name] = typeSpec
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Group variables by dependency status with names for sorting
|
|
268
|
-
type namedVarSpec struct {
|
|
269
|
-
spec *ast.ValueSpec
|
|
270
|
-
name string
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
var independentVars []namedVarSpec
|
|
274
|
-
var dependentVars []namedVarSpec
|
|
275
|
-
|
|
263
|
+
// Build a map of variable names to their specs
|
|
264
|
+
varSpecMap := make(map[string]*ast.ValueSpec)
|
|
265
|
+
varNames := []string{}
|
|
276
266
|
for _, varSpec := range varSpecs {
|
|
277
|
-
// Get variable name for sorting
|
|
278
|
-
varName := ""
|
|
279
267
|
if len(varSpec.Names) > 0 {
|
|
280
|
-
|
|
268
|
+
name := varSpec.Names[0].Name
|
|
269
|
+
varSpecMap[name] = varSpec
|
|
270
|
+
varNames = append(varNames, name)
|
|
281
271
|
}
|
|
272
|
+
}
|
|
282
273
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if len(deps) > 0 {
|
|
289
|
-
hasDependency = true
|
|
290
|
-
}
|
|
291
|
-
}
|
|
274
|
+
// Build dependency graph: varName -> list of variables it depends on
|
|
275
|
+
dependencies := make(map[string][]string)
|
|
276
|
+
for _, name := range varNames {
|
|
277
|
+
dependencies[name] = []string{}
|
|
278
|
+
}
|
|
292
279
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
hasDependency = true
|
|
298
|
-
break
|
|
299
|
-
}
|
|
300
|
-
}
|
|
280
|
+
// Extract value dependencies from initializer expressions
|
|
281
|
+
for _, varSpec := range varSpecs {
|
|
282
|
+
if len(varSpec.Names) == 0 {
|
|
283
|
+
continue
|
|
301
284
|
}
|
|
285
|
+
varName := varSpec.Names[0].Name
|
|
302
286
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
independentVars = append(independentVars, namedVar)
|
|
287
|
+
// Check initializer expressions for variable references
|
|
288
|
+
for _, value := range varSpec.Values {
|
|
289
|
+
deps := c.extractVarDependencies(value, varSpecMap)
|
|
290
|
+
dependencies[varName] = append(dependencies[varName], deps...)
|
|
308
291
|
}
|
|
309
292
|
}
|
|
310
293
|
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return dependentVars[i].name < dependentVars[j].name
|
|
317
|
-
})
|
|
294
|
+
// Perform topological sort
|
|
295
|
+
sorted, err := c.topologicalSort(dependencies)
|
|
296
|
+
if err != nil {
|
|
297
|
+
return nil, err
|
|
298
|
+
}
|
|
318
299
|
|
|
319
|
-
//
|
|
300
|
+
// Build result in sorted order
|
|
320
301
|
result := make([]*ast.ValueSpec, 0, len(varSpecs))
|
|
321
|
-
for _,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
result = append(result, namedVar.spec)
|
|
302
|
+
for _, varName := range sorted {
|
|
303
|
+
if spec, exists := varSpecMap[varName]; exists {
|
|
304
|
+
result = append(result, spec)
|
|
305
|
+
}
|
|
326
306
|
}
|
|
327
307
|
|
|
328
308
|
return result, nil
|
|
329
309
|
}
|
|
330
310
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
311
|
+
// extractVarDependencies extracts variable dependencies from an initializer expression.
|
|
312
|
+
// It returns a list of variable names that the expression depends on.
|
|
313
|
+
func (c *GoToTSCompiler) extractVarDependencies(expr ast.Expr, varSpecMap map[string]*ast.ValueSpec) []string {
|
|
314
|
+
var deps []string
|
|
315
|
+
seen := make(map[string]bool)
|
|
334
316
|
|
|
335
317
|
ast.Inspect(expr, func(n ast.Node) bool {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return false
|
|
318
|
+
if ident, ok := n.(*ast.Ident); ok {
|
|
319
|
+
// Check if this identifier refers to a package-level variable
|
|
320
|
+
if _, isVar := varSpecMap[ident.Name]; isVar {
|
|
321
|
+
if !seen[ident.Name] {
|
|
322
|
+
deps = append(deps, ident.Name)
|
|
323
|
+
seen[ident.Name] = true
|
|
343
324
|
}
|
|
344
325
|
}
|
|
345
|
-
// Check type arguments in generic calls
|
|
346
|
-
if funcType, ok := t.Fun.(*ast.IndexExpr); ok {
|
|
347
|
-
if c.hasTypeReferences(funcType.Index, typeSpecMap) {
|
|
348
|
-
hasRef = true
|
|
349
|
-
return false
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
case *ast.CompositeLit:
|
|
353
|
-
// Check composite literals like Message{...}
|
|
354
|
-
if ident, ok := t.Type.(*ast.Ident); ok {
|
|
355
|
-
if _, isLocalType := typeSpecMap[ident.Name]; isLocalType {
|
|
356
|
-
hasRef = true
|
|
357
|
-
return false
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
case *ast.Ident:
|
|
361
|
-
// Check direct type references
|
|
362
|
-
if _, isLocalType := typeSpecMap[t.Name]; isLocalType {
|
|
363
|
-
hasRef = true
|
|
364
|
-
return false
|
|
365
|
-
}
|
|
366
326
|
}
|
|
367
|
-
return
|
|
327
|
+
return true
|
|
368
328
|
})
|
|
369
329
|
|
|
370
|
-
return
|
|
330
|
+
return deps
|
|
371
331
|
}
|
|
372
332
|
|
|
373
333
|
// topologicalSort performs a topological sort of the dependency graph
|
|
@@ -387,7 +347,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
|
|
|
387
347
|
// Sort dependencies for consistent output
|
|
388
348
|
sortedDeps := make([]string, len(deps))
|
|
389
349
|
copy(sortedDeps, deps)
|
|
390
|
-
|
|
350
|
+
slices.Sort(sortedDeps)
|
|
391
351
|
|
|
392
352
|
for _, dep := range sortedDeps {
|
|
393
353
|
if _, exists := inDegree[dep]; exists {
|
|
@@ -399,7 +359,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
|
|
|
399
359
|
|
|
400
360
|
// Sort neighbors in graph for consistency
|
|
401
361
|
for node := range graph {
|
|
402
|
-
|
|
362
|
+
slices.Sort(graph[node])
|
|
403
363
|
}
|
|
404
364
|
|
|
405
365
|
// Find nodes with no incoming edges and sort them
|
|
@@ -409,7 +369,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
|
|
|
409
369
|
queue = append(queue, node)
|
|
410
370
|
}
|
|
411
371
|
}
|
|
412
|
-
|
|
372
|
+
slices.Sort(queue) // Sort initial queue for deterministic output
|
|
413
373
|
|
|
414
374
|
var result []string
|
|
415
375
|
|
|
@@ -429,7 +389,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
|
|
|
429
389
|
}
|
|
430
390
|
|
|
431
391
|
// Sort new zero-degree nodes and add to queue
|
|
432
|
-
|
|
392
|
+
slices.Sort(newZeroNodes)
|
|
433
393
|
queue = append(queue, newZeroNodes...)
|
|
434
394
|
}
|
|
435
395
|
|
|
@@ -447,7 +407,7 @@ func (c *GoToTSCompiler) topologicalSort(dependencies map[string][]string) ([]st
|
|
|
447
407
|
remaining = append(remaining, name)
|
|
448
408
|
}
|
|
449
409
|
}
|
|
450
|
-
|
|
410
|
+
slices.Sort(remaining)
|
|
451
411
|
|
|
452
412
|
return nil, fmt.Errorf("circular dependency detected in type declarations. Remaining types: %v", remaining)
|
|
453
413
|
}
|
|
@@ -662,11 +622,20 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
|
|
|
662
622
|
} else {
|
|
663
623
|
// Multiple return values -> tuple type
|
|
664
624
|
c.tsw.WriteLiterally("[")
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
625
|
+
first := true
|
|
626
|
+
for _, field := range funcType.Results.List {
|
|
627
|
+
// Each field may represent multiple return values (e.g., "a, b int")
|
|
628
|
+
count := len(field.Names)
|
|
629
|
+
if count == 0 {
|
|
630
|
+
count = 1 // Unnamed return value
|
|
631
|
+
}
|
|
632
|
+
for j := 0; j < count; j++ {
|
|
633
|
+
if !first {
|
|
634
|
+
c.tsw.WriteLiterally(", ")
|
|
635
|
+
}
|
|
636
|
+
first = false
|
|
637
|
+
c.WriteTypeExpr(field.Type)
|
|
668
638
|
}
|
|
669
|
-
c.WriteTypeExpr(field.Type)
|
|
670
639
|
}
|
|
671
640
|
c.tsw.WriteLiterally("]")
|
|
672
641
|
}
|
|
@@ -12,7 +12,15 @@ func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
|
|
|
12
12
|
case *ast.Ident:
|
|
13
13
|
// Function call (e.g., func())
|
|
14
14
|
if obj := c.objectOfIdent(fun); obj != nil {
|
|
15
|
-
|
|
15
|
+
// Check if this is a known async function
|
|
16
|
+
if c.analysis.IsAsyncFunc(obj) {
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
// Check if this is a variable that returns async values
|
|
20
|
+
// (e.g., indirect := sync.OnceValue(asyncFunc))
|
|
21
|
+
if c.analysis.IsAsyncReturningVar(obj) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
16
24
|
}
|
|
17
25
|
return false
|
|
18
26
|
|
|
@@ -42,6 +50,13 @@ func (c *GoToTSCompiler) isCallExprAsync(exp *ast.CallExpr) bool {
|
|
|
42
50
|
objOk = true
|
|
43
51
|
}
|
|
44
52
|
|
|
53
|
+
case *ast.CallExpr:
|
|
54
|
+
// Method call on function result: funcCall().method()
|
|
55
|
+
// Get the type of the function call result
|
|
56
|
+
if callType := c.pkg.TypesInfo.TypeOf(x); callType != nil {
|
|
57
|
+
objOk = true
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
default:
|
|
46
61
|
objOk = false
|
|
47
62
|
}
|
|
@@ -144,6 +159,16 @@ func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
|
|
|
144
159
|
c.tsw.WriteLiterally("!")
|
|
145
160
|
}
|
|
146
161
|
}
|
|
162
|
+
} else if selectorExpr, isSelectorExpr := expFun.(*ast.SelectorExpr); isSelectorExpr {
|
|
163
|
+
// Check if this is a field access that returns a function type
|
|
164
|
+
// e.g., s.step where step is a function-typed field
|
|
165
|
+
if selection := c.pkg.TypesInfo.Selections[selectorExpr]; selection != nil {
|
|
166
|
+
// This is a field or method selection
|
|
167
|
+
if selection.Kind() == types.FieldVal {
|
|
168
|
+
// It's a field - function-typed fields may be nil
|
|
169
|
+
c.tsw.WriteLiterally("!")
|
|
170
|
+
}
|
|
171
|
+
}
|
|
147
172
|
} else if _, isNamed := funType.(*types.Named); isNamed {
|
|
148
173
|
c.tsw.WriteLiterally("!")
|
|
149
174
|
}
|