goscript 0.0.22 → 0.0.24
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 +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
package/compiler/compiler.go
CHANGED
|
@@ -8,8 +8,10 @@ import (
|
|
|
8
8
|
"go/types"
|
|
9
9
|
"os"
|
|
10
10
|
"path/filepath"
|
|
11
|
+
"slices"
|
|
11
12
|
"strings"
|
|
12
13
|
|
|
14
|
+
gs "github.com/aperturerobotics/goscript"
|
|
13
15
|
"github.com/sirupsen/logrus"
|
|
14
16
|
"golang.org/x/tools/go/packages"
|
|
15
17
|
)
|
|
@@ -88,6 +90,12 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
|
|
|
88
90
|
return fmt.Errorf("failed to load packages: %w", err)
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
// build a list of packages that patterns matched
|
|
94
|
+
patternPkgPaths := make([]string, 0, len(pkgs))
|
|
95
|
+
for _, pkg := range pkgs {
|
|
96
|
+
patternPkgPaths = append(patternPkgPaths, pkg.PkgPath)
|
|
97
|
+
}
|
|
98
|
+
|
|
91
99
|
// If AllDependencies is true, we need to collect all dependencies
|
|
92
100
|
if c.config.AllDependencies {
|
|
93
101
|
// Create a set to track processed packages by their ID
|
|
@@ -140,6 +148,44 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) erro
|
|
|
140
148
|
|
|
141
149
|
// Compile all packages
|
|
142
150
|
for _, pkg := range pkgs {
|
|
151
|
+
// Check if the package has a handwritten equivalent
|
|
152
|
+
if !slices.Contains(patternPkgPaths, pkg.PkgPath) {
|
|
153
|
+
gsSourcePath := "gs/" + pkg.PkgPath
|
|
154
|
+
_, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
|
|
155
|
+
if gsErr != nil && !os.IsNotExist(gsErr) {
|
|
156
|
+
return gsErr
|
|
157
|
+
}
|
|
158
|
+
if gsErr == nil {
|
|
159
|
+
if c.config.DisableEmitBuiltin {
|
|
160
|
+
c.le.Infof("Skipping compilation for overridden package %s", pkg.PkgPath)
|
|
161
|
+
continue
|
|
162
|
+
} else {
|
|
163
|
+
// If DisableEmitBuiltin is false, we need to copy the handwritten package to the output directory
|
|
164
|
+
c.le.Infof("Copying handwritten package %s to output directory", pkg.PkgPath)
|
|
165
|
+
|
|
166
|
+
// Compute output path for this package
|
|
167
|
+
outputPath := ComputeModulePath(c.config.OutputPath, pkg.PkgPath)
|
|
168
|
+
|
|
169
|
+
// Remove existing directory if it exists
|
|
170
|
+
if err := os.RemoveAll(outputPath); err != nil {
|
|
171
|
+
return fmt.Errorf("failed to remove existing output directory for %s: %w", pkg.PkgPath, err)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Create the output directory
|
|
175
|
+
if err := os.MkdirAll(outputPath, 0o755); err != nil {
|
|
176
|
+
return fmt.Errorf("failed to create output directory for %s: %w", pkg.PkgPath, err)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Copy files from embedded FS to output directory
|
|
180
|
+
if err := c.copyEmbeddedPackage(gsSourcePath, outputPath); err != nil {
|
|
181
|
+
return fmt.Errorf("failed to copy embedded package %s: %w", pkg.PkgPath, err)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
143
189
|
// Skip packages that failed to load
|
|
144
190
|
if len(pkg.Errors) > 0 {
|
|
145
191
|
c.le.WithError(pkg.Errors[0]).Warnf("Skipping package %s due to errors", pkg.PkgPath)
|
|
@@ -182,7 +228,7 @@ func NewPackageCompiler(
|
|
|
182
228
|
le: le,
|
|
183
229
|
pkg: pkg,
|
|
184
230
|
compilerConf: compilerConf,
|
|
185
|
-
outputPath: ComputeModulePath(compilerConf.
|
|
231
|
+
outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
|
|
186
232
|
}
|
|
187
233
|
|
|
188
234
|
return res, nil
|
|
@@ -267,7 +313,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
267
313
|
// CompileFile handles the compilation of a single Go source file to TypeScript.
|
|
268
314
|
// It first performs a pre-compilation analysis of the file using `AnalyzeFile`
|
|
269
315
|
// to gather information necessary for accurate TypeScript generation (e.g.,
|
|
270
|
-
// about
|
|
316
|
+
// about varRefing, async functions, defer statements).
|
|
271
317
|
// Then, it creates a `FileCompiler` instance for the file and invokes its
|
|
272
318
|
// `Compile` method to generate the TypeScript code.
|
|
273
319
|
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File) error {
|
|
@@ -328,10 +374,10 @@ func NewFileCompiler(
|
|
|
328
374
|
// top-level declarations in the Go file.
|
|
329
375
|
func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
330
376
|
f := c.ast
|
|
331
|
-
|
|
332
377
|
pkgPath := c.pkg.PkgPath
|
|
378
|
+
|
|
333
379
|
outputFilePath := TranslateGoFilePathToTypescriptFilePath(pkgPath, filepath.Base(c.fullPath))
|
|
334
|
-
outputFilePathAbs := filepath.Join(c.compilerConfig.
|
|
380
|
+
outputFilePathAbs := filepath.Join(c.compilerConfig.OutputPath, outputFilePath)
|
|
335
381
|
|
|
336
382
|
if err := os.MkdirAll(filepath.Dir(outputFilePathAbs), 0o755); err != nil {
|
|
337
383
|
return err
|
|
@@ -349,7 +395,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
349
395
|
goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis)
|
|
350
396
|
|
|
351
397
|
// Add import for the goscript runtime using namespace import and alias
|
|
352
|
-
c.codeWriter.
|
|
398
|
+
c.codeWriter.WriteLinef("import * as $ from %q;", "@goscript/builtin/builtin.js")
|
|
353
399
|
c.codeWriter.WriteLine("") // Add a newline after the import
|
|
354
400
|
|
|
355
401
|
if err := goWriter.WriteDecls(f.Decls); err != nil {
|
|
@@ -362,13 +408,13 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
362
408
|
// GoToTSCompiler is the core component responsible for translating Go AST nodes
|
|
363
409
|
// and type information into TypeScript code. It uses a `TSCodeWriter` to output
|
|
364
410
|
// the generated TypeScript and relies on `Analysis` data to make informed
|
|
365
|
-
// decisions about code generation (e.g.,
|
|
411
|
+
// decisions about code generation (e.g., varRefing, async behavior).
|
|
366
412
|
type GoToTSCompiler struct {
|
|
367
413
|
tsw *TSCodeWriter
|
|
368
414
|
|
|
369
415
|
pkg *packages.Package
|
|
370
416
|
|
|
371
|
-
analysis *Analysis
|
|
417
|
+
analysis *Analysis
|
|
372
418
|
}
|
|
373
419
|
|
|
374
420
|
// It initializes the compiler with a `TSCodeWriter` for output,
|
|
@@ -388,14 +434,12 @@ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analy
|
|
|
388
434
|
// variable, function name) into its TypeScript equivalent.
|
|
389
435
|
// - If the identifier is `nil`, it writes `null`.
|
|
390
436
|
// - Otherwise, it writes the identifier's name.
|
|
391
|
-
// - If `
|
|
392
|
-
// indicates
|
|
393
|
-
// in a box (due to its address being taken or other boxing requirements),
|
|
394
|
-
// it appends `.value` to access the actual value from the box.
|
|
437
|
+
// - If `accessVarRefedValue` is true and the analysis (`c.analysis.NeedsVarRefAccess`)
|
|
438
|
+
// indicates the variable is variable referenced, `.value` is appended to access the contained value.
|
|
395
439
|
//
|
|
396
440
|
// This function relies on `go/types` (`TypesInfo.Uses` or `Defs`) to resolve
|
|
397
|
-
// the identifier and the `Analysis` data to determine
|
|
398
|
-
func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident,
|
|
441
|
+
// the identifier and the `Analysis` data to determine varRefing needs.
|
|
442
|
+
func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
|
|
399
443
|
if exp.Name == "nil" {
|
|
400
444
|
c.tsw.WriteLiterally("null")
|
|
401
445
|
return
|
|
@@ -412,7 +456,7 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessBoxedValue bool) {
|
|
|
412
456
|
c.tsw.WriteLiterally(exp.Name)
|
|
413
457
|
|
|
414
458
|
// Determine if we need to access .value based on analysis data
|
|
415
|
-
if obj != nil &&
|
|
459
|
+
if obj != nil && accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
|
|
416
460
|
c.tsw.WriteLiterally("!.value")
|
|
417
461
|
}
|
|
418
462
|
}
|
|
@@ -437,28 +481,31 @@ func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
|
|
|
437
481
|
c.tsw.WriteLine("")
|
|
438
482
|
} else {
|
|
439
483
|
// Case with expressions
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
484
|
+
// For Go's `case expr1, expr2:`, we translate to:
|
|
485
|
+
// case expr1:
|
|
486
|
+
// case expr2:
|
|
487
|
+
// ... body ...
|
|
488
|
+
// break
|
|
489
|
+
for _, caseExpr := range exp.List {
|
|
490
|
+
c.tsw.WriteLiterally("case ")
|
|
491
|
+
if err := c.WriteValueExpr(caseExpr); err != nil {
|
|
446
492
|
return fmt.Errorf("failed to write case clause expression: %w", err)
|
|
447
493
|
}
|
|
494
|
+
c.tsw.WriteLiterally(":")
|
|
495
|
+
c.tsw.WriteLine("")
|
|
448
496
|
}
|
|
449
|
-
c.tsw.WriteLiterally(":")
|
|
450
|
-
c.tsw.WriteLine("")
|
|
451
497
|
}
|
|
452
498
|
|
|
499
|
+
// The body is written once, after all case labels for this clause.
|
|
500
|
+
// Indentation for the body starts here.
|
|
453
501
|
c.tsw.Indent(1)
|
|
454
|
-
// Write the body of the case clause
|
|
455
502
|
for _, stmt := range exp.Body {
|
|
456
503
|
if err := c.WriteStmt(stmt); err != nil {
|
|
457
504
|
return fmt.Errorf("failed to write statement in case clause body: %w", err)
|
|
458
505
|
}
|
|
459
506
|
}
|
|
460
|
-
// Add break statement (Go's switch has implicit breaks)
|
|
461
|
-
c.tsw.WriteLine("break")
|
|
507
|
+
// Add break statement (Go's switch has implicit breaks, TS needs explicit break)
|
|
508
|
+
c.tsw.WriteLine("break")
|
|
462
509
|
c.tsw.Indent(-1)
|
|
463
510
|
return nil
|
|
464
511
|
}
|
|
@@ -527,11 +574,11 @@ func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *as
|
|
|
527
574
|
} else {
|
|
528
575
|
// If both are blank, just await the call and return
|
|
529
576
|
if okIsBlank {
|
|
530
|
-
c.tsw.WriteLiterally("await ")
|
|
577
|
+
c.tsw.WriteLiterally("await $.chanRecvWithOk(")
|
|
531
578
|
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
532
579
|
return fmt.Errorf("failed to write channel expression in receive: %w", err)
|
|
533
580
|
}
|
|
534
|
-
c.tsw.WriteLiterally("
|
|
581
|
+
c.tsw.WriteLiterally(")")
|
|
535
582
|
c.tsw.WriteLine("")
|
|
536
583
|
return nil // Nothing to assign
|
|
537
584
|
}
|
|
@@ -549,11 +596,11 @@ func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *as
|
|
|
549
596
|
// Write the destructuring assignment/declaration
|
|
550
597
|
c.tsw.WriteLiterally(keyword) // "const " or ""
|
|
551
598
|
c.tsw.WriteLiterally(destructuringPattern)
|
|
552
|
-
c.tsw.WriteLiterally(" = await ")
|
|
599
|
+
c.tsw.WriteLiterally(" = await $.chanRecvWithOk(")
|
|
553
600
|
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
554
601
|
return fmt.Errorf("failed to write channel expression in receive: %w", err)
|
|
555
602
|
}
|
|
556
|
-
c.tsw.WriteLiterally("
|
|
603
|
+
c.tsw.WriteLiterally(")")
|
|
557
604
|
c.tsw.WriteLine("")
|
|
558
605
|
|
|
559
606
|
return nil
|
|
@@ -604,3 +651,44 @@ func (c *GoToTSCompiler) WriteDoc(doc *ast.CommentGroup) {
|
|
|
604
651
|
}
|
|
605
652
|
}
|
|
606
653
|
}
|
|
654
|
+
|
|
655
|
+
// copyEmbeddedPackage recursively copies files from an embedded FS path to a filesystem directory.
|
|
656
|
+
// It handles both regular files and directories.
|
|
657
|
+
func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) error {
|
|
658
|
+
// List the entries in the embedded path
|
|
659
|
+
entries, err := gs.GsOverrides.ReadDir(embeddedPath)
|
|
660
|
+
if err != nil {
|
|
661
|
+
return fmt.Errorf("failed to read embedded directory %s: %w", embeddedPath, err)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Process each entry
|
|
665
|
+
for _, entry := range entries {
|
|
666
|
+
entryPath := filepath.Join(embeddedPath, entry.Name())
|
|
667
|
+
outputEntryPath := filepath.Join(outputPath, entry.Name())
|
|
668
|
+
|
|
669
|
+
if entry.IsDir() {
|
|
670
|
+
// Create the output directory
|
|
671
|
+
if err := os.MkdirAll(outputEntryPath, 0o755); err != nil {
|
|
672
|
+
return fmt.Errorf("failed to create output directory %s: %w", outputEntryPath, err)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Recursively copy the directory contents
|
|
676
|
+
if err := c.copyEmbeddedPackage(entryPath, outputEntryPath); err != nil {
|
|
677
|
+
return err
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
// Read the file content from the embedded FS
|
|
681
|
+
content, err := gs.GsOverrides.ReadFile(entryPath)
|
|
682
|
+
if err != nil {
|
|
683
|
+
return fmt.Errorf("failed to read embedded file %s: %w", entryPath, err)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Write the content to the output file
|
|
687
|
+
if err := os.WriteFile(outputEntryPath, content, 0o644); err != nil {
|
|
688
|
+
return fmt.Errorf("failed to write file %s: %w", outputEntryPath, err)
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return nil
|
|
694
|
+
}
|
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"path/filepath"
|
|
7
7
|
"runtime"
|
|
8
8
|
"slices"
|
|
9
|
+
"strings"
|
|
9
10
|
"sync"
|
|
10
11
|
"sync/atomic"
|
|
11
12
|
"testing"
|
|
@@ -38,7 +39,7 @@ func TestCompliance(t *testing.T) {
|
|
|
38
39
|
testPath := filepath.Join(testsDir, dir.Name())
|
|
39
40
|
goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
|
|
40
41
|
if err != nil || len(goFiles) == 0 {
|
|
41
|
-
t.Errorf("no .go files found in %s", testPath)
|
|
42
|
+
// t.Errorf("no .go files found in %s", testPath)
|
|
42
43
|
continue
|
|
43
44
|
}
|
|
44
45
|
testPaths = append(testPaths, testPath)
|
|
@@ -97,15 +98,42 @@ func TestCompliance(t *testing.T) {
|
|
|
97
98
|
t.SkipNow()
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
// Get parent module path for the global typecheck
|
|
102
|
+
parentModPath, err := getParentGoModulePath()
|
|
103
|
+
if err != nil {
|
|
104
|
+
t.Fatalf("Failed to determine parent Go module path: %v", err)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Create global typecheck tsconfig
|
|
108
|
+
tsconfigPath := compliance.WriteGlobalTypeCheckConfig(t, parentModPath, workspaceDir)
|
|
109
|
+
|
|
110
|
+
// Run TypeScript type checking
|
|
111
|
+
typecheckDir := filepath.Dir(tsconfigPath)
|
|
112
|
+
cmd := exec.Command("tsc", "--project", filepath.Base(tsconfigPath))
|
|
113
|
+
cmd.Dir = typecheckDir
|
|
114
|
+
|
|
115
|
+
// Set up PATH to include node_modules/.bin
|
|
116
|
+
nodeBinDir := filepath.Join(workspaceDir, "node_modules", ".bin")
|
|
117
|
+
currentPath := os.Getenv("PATH")
|
|
118
|
+
newPath := nodeBinDir + string(os.PathListSeparator) + currentPath
|
|
119
|
+
cmd.Env = append(os.Environ(), "PATH="+newPath)
|
|
105
120
|
|
|
106
|
-
err
|
|
121
|
+
output, err := cmd.CombinedOutput()
|
|
107
122
|
if err != nil {
|
|
108
|
-
t.Errorf("
|
|
123
|
+
t.Errorf("Global TypeScript type checking failed: %v\noutput:\n%s", err, string(output))
|
|
124
|
+
} else {
|
|
125
|
+
t.Logf("Global TypeScript type checking passed")
|
|
109
126
|
}
|
|
110
127
|
})
|
|
111
128
|
}
|
|
129
|
+
|
|
130
|
+
// getParentGoModulePath is a helper function to get the parent Go module path
|
|
131
|
+
// This is similar to the one in compliance.go but simplified for use in tests
|
|
132
|
+
func getParentGoModulePath() (string, error) {
|
|
133
|
+
cmd := exec.Command("go", "list", "-m")
|
|
134
|
+
output, err := cmd.Output()
|
|
135
|
+
if err != nil {
|
|
136
|
+
return "", err
|
|
137
|
+
}
|
|
138
|
+
return strings.TrimSpace(string(output)), nil
|
|
139
|
+
}
|
|
@@ -13,26 +13,28 @@ import (
|
|
|
13
13
|
//
|
|
14
14
|
// It handles several types of composite literals:
|
|
15
15
|
// - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
|
|
16
|
-
// Values are processed by `
|
|
16
|
+
// Values are processed by `WriteVarRefedValue`.
|
|
17
17
|
// - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
|
|
18
|
-
//
|
|
18
|
+
// - For `[]byte{...}`, translated to `new Uint8Array([...])`.
|
|
19
|
+
// - For other `[]T` or `[N]T`, translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
|
|
19
20
|
// It handles both keyed and unkeyed elements, infers length if necessary,
|
|
20
21
|
// and uses zero values for uninitialized array elements.
|
|
21
22
|
// Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
|
|
22
|
-
// Element values are processed by `
|
|
23
|
+
// Element values are processed by `WriteVarRefedValue`.
|
|
23
24
|
// - Struct literals:
|
|
24
25
|
// - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
|
|
25
26
|
// `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
|
|
26
27
|
// - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
|
|
27
28
|
// object literals `{ F: v_ts, ... }`.
|
|
28
29
|
// It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
|
|
29
|
-
// structs or arrays). Field values are processed by `
|
|
30
|
+
// structs or arrays). Field values are processed by `WriteVarRefedValue`.
|
|
30
31
|
// Embedded struct fields are initialized, and explicit initializers for embedded
|
|
31
32
|
// structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
|
|
32
|
-
// The function uses `c.analysis` to determine correct value access (e.g., `.value` for
|
|
33
|
+
// The function uses `c.analysis` to determine correct value access (e.g., `.value` for var-refed fields).
|
|
33
34
|
func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
34
35
|
// Get the type of the composite literal
|
|
35
36
|
litType := c.pkg.TypesInfo.TypeOf(exp)
|
|
37
|
+
|
|
36
38
|
if exp.Type != nil {
|
|
37
39
|
// Handle map literals: map[K]V{k1: v1, k2: v2}
|
|
38
40
|
if _, isMapType := exp.Type.(*ast.MapType); isMapType {
|
|
@@ -46,11 +48,11 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
46
48
|
|
|
47
49
|
if kv, ok := elm.(*ast.KeyValueExpr); ok {
|
|
48
50
|
c.tsw.WriteLiterally("[")
|
|
49
|
-
if err := c.
|
|
51
|
+
if err := c.WriteVarRefedValue(kv.Key); err != nil {
|
|
50
52
|
return fmt.Errorf("failed to write map literal key: %w", err)
|
|
51
53
|
}
|
|
52
54
|
c.tsw.WriteLiterally(", ")
|
|
53
|
-
if err := c.
|
|
55
|
+
if err := c.WriteVarRefedValue(kv.Value); err != nil {
|
|
54
56
|
return fmt.Errorf("failed to write map literal value: %w", err)
|
|
55
57
|
}
|
|
56
58
|
c.tsw.WriteLiterally("]")
|
|
@@ -73,14 +75,29 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
73
75
|
// We'll handle this with depth parameter to arrayToSlice
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
// Check if it's a []byte literal
|
|
79
|
+
isByteSliceLiteral := false
|
|
80
|
+
if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
|
|
81
|
+
if sliceT, ok := typInfo.Underlying().(*types.Slice); ok {
|
|
82
|
+
if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
|
|
83
|
+
isByteSliceLiteral = true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if isByteSliceLiteral {
|
|
89
|
+
c.tsw.WriteLiterally("new Uint8Array")
|
|
90
|
+
} else {
|
|
91
|
+
c.tsw.WriteLiterally("$.arrayToSlice")
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
// write the type annotation
|
|
94
|
+
c.tsw.WriteLiterally("<")
|
|
95
|
+
// Write the element type using the existing function
|
|
96
|
+
c.WriteTypeExpr(arrType.Elt)
|
|
97
|
+
c.tsw.WriteLiterally(">")
|
|
98
|
+
}
|
|
83
99
|
|
|
100
|
+
// opening
|
|
84
101
|
c.tsw.WriteLiterally("([")
|
|
85
102
|
|
|
86
103
|
// Use type info to get array length and element type
|
|
@@ -123,7 +140,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
123
140
|
hasKeyedElements = true
|
|
124
141
|
} else {
|
|
125
142
|
c.tsw.WriteCommentInline("unhandled keyed array literal key type")
|
|
126
|
-
if err := c.
|
|
143
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
127
144
|
return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
|
|
128
145
|
}
|
|
129
146
|
}
|
|
@@ -151,7 +168,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
151
168
|
c.tsw.WriteLiterally(", ")
|
|
152
169
|
}
|
|
153
170
|
if elm, ok := elements[i]; ok && elm != nil {
|
|
154
|
-
if err := c.
|
|
171
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
155
172
|
return fmt.Errorf("failed to write array literal element: %w", err)
|
|
156
173
|
}
|
|
157
174
|
} else {
|
|
@@ -166,7 +183,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
166
183
|
c.tsw.WriteLiterally("]")
|
|
167
184
|
|
|
168
185
|
// If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
|
|
169
|
-
if isMultiDimensional {
|
|
186
|
+
if isMultiDimensional && !isByteSliceLiteral { // Depth parameter not applicable to Uint8Array constructor
|
|
170
187
|
c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
|
|
171
188
|
}
|
|
172
189
|
|
|
@@ -271,9 +288,8 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
271
288
|
}
|
|
272
289
|
|
|
273
290
|
// Handle the case where an anonymous struct has values without keys
|
|
274
|
-
// Handle the case where an anonymous struct has values without keys.
|
|
275
291
|
// This block processes non-key-value elements and associates them with struct fields.
|
|
276
|
-
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0
|
|
292
|
+
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
|
|
277
293
|
// Check if any elements in the composite literal are not key-value pairs.
|
|
278
294
|
hasNonKeyValueElts := false
|
|
279
295
|
for _, elt := range exp.Elts {
|
|
@@ -325,7 +341,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
325
341
|
}
|
|
326
342
|
c.tsw.WriteLiterally(keyName)
|
|
327
343
|
c.tsw.WriteLiterally(": ")
|
|
328
|
-
if err := c.
|
|
344
|
+
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
329
345
|
return err
|
|
330
346
|
}
|
|
331
347
|
firstFieldWritten = true
|
|
@@ -353,14 +369,14 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
353
369
|
if i > 0 {
|
|
354
370
|
c.tsw.WriteLiterally(", ")
|
|
355
371
|
}
|
|
356
|
-
if err := c.
|
|
372
|
+
if err := c.WriteVarRefedValue(elem); err != nil {
|
|
357
373
|
return err
|
|
358
374
|
}
|
|
359
375
|
}
|
|
360
376
|
c.tsw.WriteLiterally("}")
|
|
361
377
|
} else {
|
|
362
378
|
// Not a composite literal, write it normally
|
|
363
|
-
if err := c.
|
|
379
|
+
if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
|
|
364
380
|
return err
|
|
365
381
|
}
|
|
366
382
|
}
|
|
@@ -399,7 +415,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
399
415
|
}
|
|
400
416
|
c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
|
|
401
417
|
c.tsw.WriteLiterally(": ")
|
|
402
|
-
if err := c.
|
|
418
|
+
if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
|
|
403
419
|
return err
|
|
404
420
|
}
|
|
405
421
|
}
|
|
@@ -421,7 +437,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
421
437
|
if i != 0 {
|
|
422
438
|
c.tsw.WriteLiterally(", ")
|
|
423
439
|
}
|
|
424
|
-
if err := c.
|
|
440
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
425
441
|
return fmt.Errorf("failed to write literal field: %w", err)
|
|
426
442
|
}
|
|
427
443
|
}
|
|
@@ -433,60 +449,124 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
433
449
|
|
|
434
450
|
// Untyped composite literal. Let's use type information to determine what it is.
|
|
435
451
|
// First try to get the type information for the expression
|
|
436
|
-
isObject := false
|
|
437
452
|
if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
|
|
438
453
|
underlying := tv.Type.Underlying()
|
|
439
454
|
switch underlying.(type) {
|
|
440
455
|
case *types.Map, *types.Struct:
|
|
441
|
-
|
|
456
|
+
// Handle struct directly with the struct literal logic
|
|
457
|
+
if structType, ok := underlying.(*types.Struct); ok {
|
|
458
|
+
return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
|
|
459
|
+
}
|
|
460
|
+
// Map case would be handled here
|
|
461
|
+
return fmt.Errorf("untyped map composite literals not yet supported")
|
|
442
462
|
case *types.Array, *types.Slice:
|
|
443
|
-
|
|
463
|
+
// Handle array/slice
|
|
464
|
+
return c.writeUntypedArrayLiteral(exp)
|
|
465
|
+
case *types.Pointer:
|
|
466
|
+
// Handle pointer to composite literal
|
|
467
|
+
ptrType := underlying.(*types.Pointer)
|
|
468
|
+
elemType := ptrType.Elem().Underlying()
|
|
469
|
+
switch elemType.(type) {
|
|
470
|
+
case *types.Struct:
|
|
471
|
+
// This is an anonymous struct literal with inferred pointer type
|
|
472
|
+
// Just create the struct object directly - no var-refing needed
|
|
473
|
+
// Anonymous literals are not variables, so they don't get var-refed
|
|
474
|
+
structType := elemType.(*types.Struct)
|
|
475
|
+
return c.writeUntypedStructLiteral(exp, structType) // true = anonymous
|
|
476
|
+
default:
|
|
477
|
+
return fmt.Errorf("unhandled pointer composite literal element type: %T", elemType)
|
|
478
|
+
}
|
|
444
479
|
default:
|
|
445
480
|
return fmt.Errorf("unhandled composite literal type: %T", underlying)
|
|
446
481
|
}
|
|
447
482
|
} else {
|
|
448
483
|
return fmt.Errorf("could not determine composite literal type from type information")
|
|
449
484
|
}
|
|
485
|
+
}
|
|
450
486
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
c.tsw.WriteLiterally("[ ")
|
|
455
|
-
}
|
|
456
|
-
|
|
487
|
+
// writeUntypedArrayLiteral handles untyped composite literals that are arrays/slices
|
|
488
|
+
func (c *GoToTSCompiler) writeUntypedArrayLiteral(exp *ast.CompositeLit) error {
|
|
489
|
+
c.tsw.WriteLiterally("[ ")
|
|
457
490
|
for i, elm := range exp.Elts {
|
|
458
491
|
if i != 0 {
|
|
459
492
|
c.tsw.WriteLiterally(", ")
|
|
460
493
|
}
|
|
461
|
-
if err := c.
|
|
462
|
-
return fmt.Errorf("failed to write untyped
|
|
494
|
+
if err := c.WriteVarRefedValue(elm); err != nil {
|
|
495
|
+
return fmt.Errorf("failed to write untyped array literal element: %w", err)
|
|
463
496
|
}
|
|
464
497
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
498
|
+
c.tsw.WriteLiterally(" ]")
|
|
499
|
+
return nil
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// writeUntypedStructLiteral handles untyped composite literals that are structs or pointers to structs
|
|
503
|
+
func (c *GoToTSCompiler) writeUntypedStructLiteral(exp *ast.CompositeLit, structType *types.Struct) error {
|
|
504
|
+
// Create field mapping like the typed struct case
|
|
505
|
+
directFields := make(map[string]ast.Expr)
|
|
506
|
+
|
|
507
|
+
// Handle elements that are key-value pairs
|
|
508
|
+
for _, elt := range exp.Elts {
|
|
509
|
+
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
|
510
|
+
if keyIdent, ok := kv.Key.(*ast.Ident); ok {
|
|
511
|
+
directFields[keyIdent.Name] = kv.Value
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Handle elements that are positional (no key specified)
|
|
517
|
+
if len(directFields) == 0 {
|
|
518
|
+
// If no key-value pairs, try to match positional values to struct fields
|
|
519
|
+
for i, elt := range exp.Elts {
|
|
520
|
+
if _, isKV := elt.(*ast.KeyValueExpr); !isKV && i < structType.NumFields() {
|
|
521
|
+
field := structType.Field(i)
|
|
522
|
+
directFields[field.Name()] = elt
|
|
523
|
+
}
|
|
524
|
+
}
|
|
469
525
|
}
|
|
526
|
+
|
|
527
|
+
// Write the object literal (always anonymous for untyped)
|
|
528
|
+
c.tsw.WriteLiterally("{")
|
|
529
|
+
|
|
530
|
+
firstFieldWritten := false
|
|
531
|
+
// Write fields in order
|
|
532
|
+
directKeys := make([]string, 0, len(directFields))
|
|
533
|
+
for k := range directFields {
|
|
534
|
+
directKeys = append(directKeys, k)
|
|
535
|
+
}
|
|
536
|
+
slices.Sort(directKeys)
|
|
537
|
+
for _, keyName := range directKeys {
|
|
538
|
+
if firstFieldWritten {
|
|
539
|
+
c.tsw.WriteLiterally(", ")
|
|
540
|
+
}
|
|
541
|
+
c.tsw.WriteLiterally(keyName)
|
|
542
|
+
c.tsw.WriteLiterally(": ")
|
|
543
|
+
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
544
|
+
return err
|
|
545
|
+
}
|
|
546
|
+
firstFieldWritten = true
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
c.tsw.WriteLiterally("}")
|
|
470
550
|
return nil
|
|
471
551
|
}
|
|
472
552
|
|
|
473
|
-
//
|
|
553
|
+
// WriteVarRefedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
|
|
474
554
|
// specifically for use as a value within a composite literal (e.g., struct fields,
|
|
475
555
|
// map keys/values, or array/slice elements). Its primary goal is to ensure that the
|
|
476
|
-
// actual
|
|
556
|
+
// actual un-refed value of the expression is used.
|
|
477
557
|
//
|
|
478
558
|
// How it works:
|
|
479
559
|
// - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
|
|
480
560
|
// the `accessValue` flag to `true`. This ensures that if `ident` refers to a
|
|
481
|
-
// GoScript
|
|
561
|
+
// GoScript var-refed variable, the generated TypeScript accesses its underlying `.value`
|
|
482
562
|
// (e.g., `myVar.value`).
|
|
483
563
|
// - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
|
|
484
564
|
// `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
|
|
485
|
-
// accessing fields or methods, including any required
|
|
486
|
-
// itself or the object it's accessed on is
|
|
565
|
+
// accessing fields or methods, including any required un-var-refing if the field
|
|
566
|
+
// itself or the object it's accessed on is var-refed (e.g., `obj.value.field` or
|
|
487
567
|
// `obj.field.value`).
|
|
488
568
|
// - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
|
|
489
|
-
// This function handles pointer dereferencing, which in GoScript's
|
|
569
|
+
// This function handles pointer dereferencing, which in GoScript's var-refing model
|
|
490
570
|
// often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
|
|
491
571
|
// - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
|
|
492
572
|
// `c.WriteBasicLit(e)` for direct translation.
|
|
@@ -496,25 +576,25 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
496
576
|
//
|
|
497
577
|
// Necessity and Distinction from `WriteValueExpr`:
|
|
498
578
|
// While `WriteValueExpr` is a general-purpose function for translating Go expressions
|
|
499
|
-
// and also
|
|
500
|
-
// `
|
|
579
|
+
// and also un-var-refes identifiers (by calling `WriteIdent` with `accessValue: true`),
|
|
580
|
+
// `WriteVarRefedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
|
|
501
581
|
// 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
|
|
502
|
-
// literal, the *
|
|
582
|
+
// literal, the *un-var-refed value* is mandatory.
|
|
503
583
|
// 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
|
|
504
584
|
// the correct values for initialization, insulating it from potential changes in
|
|
505
|
-
// the default behavior of `WriteValueExpr` regarding
|
|
585
|
+
// the default behavior of `WriteValueExpr` regarding un-var-refing.
|
|
506
586
|
// 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
|
|
507
587
|
// calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
|
|
508
588
|
// `WriteValueExpr` for its elements, it could lead to unintended recursion or
|
|
509
|
-
// behavior if an element itself was another composite literal. `
|
|
589
|
+
// behavior if an element itself was another composite literal. `WriteVarRefedValue`
|
|
510
590
|
// acts as a specific intermediary for the *elements*.
|
|
511
591
|
//
|
|
512
|
-
// In summary, `
|
|
592
|
+
// In summary, `WriteVarRefedValue` is a specialized dispatcher used by `WriteCompositeLit`
|
|
513
593
|
// to guarantee that all parts of a Go composite literal are initialized with their
|
|
514
|
-
// proper,
|
|
515
|
-
func (c *GoToTSCompiler)
|
|
594
|
+
// proper, unrefed TypeScript values.
|
|
595
|
+
func (c *GoToTSCompiler) WriteVarRefedValue(expr ast.Expr) error {
|
|
516
596
|
if expr == nil {
|
|
517
|
-
return fmt.Errorf("nil expression passed to
|
|
597
|
+
return fmt.Errorf("nil expression passed to write var refed value")
|
|
518
598
|
}
|
|
519
599
|
|
|
520
600
|
// Handle different expression types
|