goscript 0.0.69 → 0.0.71
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/cmd/goscript-wasm/main.go +46 -0
- package/compiler/wasm/compile.go +12 -0
- package/compiler/wasm_api.go +160 -0
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//go:build js && wasm
|
|
2
|
+
|
|
3
|
+
package main
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"syscall/js"
|
|
7
|
+
|
|
8
|
+
"github.com/aperturerobotics/goscript/compiler/wasm"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func main() {
|
|
12
|
+
// Register the compile function as a global JavaScript function
|
|
13
|
+
js.Global().Set("goscriptCompile", js.FuncOf(compileWrapper))
|
|
14
|
+
|
|
15
|
+
// Keep the program running
|
|
16
|
+
select {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// compileWrapper wraps the compile function for JavaScript interop
|
|
20
|
+
func compileWrapper(this js.Value, args []js.Value) interface{} {
|
|
21
|
+
if len(args) < 1 {
|
|
22
|
+
return map[string]interface{}{
|
|
23
|
+
"error": "missing source code argument",
|
|
24
|
+
"output": "",
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
source := args[0].String()
|
|
29
|
+
packageName := "main"
|
|
30
|
+
if len(args) > 1 {
|
|
31
|
+
packageName = args[1].String()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
output, err := wasm.CompileSource(source, packageName)
|
|
35
|
+
if err != nil {
|
|
36
|
+
return map[string]interface{}{
|
|
37
|
+
"error": err.Error(),
|
|
38
|
+
"output": "",
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return map[string]interface{}{
|
|
43
|
+
"error": "",
|
|
44
|
+
"output": output,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Package wasm provides a WASM-friendly API for compiling Go source code to TypeScript.
|
|
2
|
+
package wasm
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"github.com/aperturerobotics/goscript/compiler"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// CompileSource compiles Go source code to TypeScript.
|
|
9
|
+
// It takes the source code as a string and returns the generated TypeScript.
|
|
10
|
+
func CompileSource(source string, packageName string) (string, error) {
|
|
11
|
+
return compiler.CompileSourceToTypeScript(source, packageName)
|
|
12
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"fmt"
|
|
6
|
+
"go/ast"
|
|
7
|
+
"go/importer"
|
|
8
|
+
"go/parser"
|
|
9
|
+
"go/token"
|
|
10
|
+
"go/types"
|
|
11
|
+
"io"
|
|
12
|
+
|
|
13
|
+
"golang.org/x/tools/go/packages"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// CompileSourceToTypeScript compiles Go source code directly to TypeScript.
|
|
17
|
+
// This is a WASM-friendly API that bypasses filesystem operations.
|
|
18
|
+
// It takes the source code as a string and returns the generated TypeScript.
|
|
19
|
+
func CompileSourceToTypeScript(source string, packageName string) (string, error) {
|
|
20
|
+
if packageName == "" {
|
|
21
|
+
packageName = "main"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Create a new file set for position information
|
|
25
|
+
fset := token.NewFileSet()
|
|
26
|
+
|
|
27
|
+
// Parse the source code
|
|
28
|
+
astFile, err := parser.ParseFile(fset, "main.go", source, parser.ParseComments)
|
|
29
|
+
if err != nil {
|
|
30
|
+
return "", fmt.Errorf("parse error: %w", err)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create type checker configuration with a custom importer
|
|
34
|
+
var typeErrors []error
|
|
35
|
+
conf := types.Config{
|
|
36
|
+
Importer: &wasmImporter{
|
|
37
|
+
defaultImporter: importer.Default(),
|
|
38
|
+
cache: make(map[string]*types.Package),
|
|
39
|
+
},
|
|
40
|
+
Error: func(err error) {
|
|
41
|
+
// Collect errors but don't fail immediately
|
|
42
|
+
typeErrors = append(typeErrors, err)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Type check the package
|
|
47
|
+
info := &types.Info{
|
|
48
|
+
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
49
|
+
Defs: make(map[*ast.Ident]types.Object),
|
|
50
|
+
Uses: make(map[*ast.Ident]types.Object),
|
|
51
|
+
Implicits: make(map[ast.Node]types.Object),
|
|
52
|
+
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
|
53
|
+
Scopes: make(map[ast.Node]*types.Scope),
|
|
54
|
+
Instances: make(map[*ast.Ident]types.Instance),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pkg, _ := conf.Check(packageName, fset, []*ast.File{astFile}, info)
|
|
58
|
+
// We continue even with type check errors for playground flexibility
|
|
59
|
+
|
|
60
|
+
// Create a packages.Package compatible structure
|
|
61
|
+
pkgData := &packages.Package{
|
|
62
|
+
ID: packageName,
|
|
63
|
+
Name: astFile.Name.Name,
|
|
64
|
+
PkgPath: packageName,
|
|
65
|
+
Fset: fset,
|
|
66
|
+
Syntax: []*ast.File{astFile},
|
|
67
|
+
Types: pkg,
|
|
68
|
+
TypesInfo: info,
|
|
69
|
+
CompiledGoFiles: []string{"main.go"},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Create an empty map for all packages (we only have one)
|
|
73
|
+
allPackages := map[string]*packages.Package{
|
|
74
|
+
packageName: pkgData,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Perform package-level analysis
|
|
78
|
+
packageAnalysis := AnalyzePackageImports(pkgData)
|
|
79
|
+
analysis := AnalyzePackageFiles(pkgData, allPackages)
|
|
80
|
+
|
|
81
|
+
// Create a buffer to capture the output
|
|
82
|
+
var buf bytes.Buffer
|
|
83
|
+
|
|
84
|
+
// Create the file compiler
|
|
85
|
+
fileCompiler := &FileCompiler{
|
|
86
|
+
compilerConfig: &Config{},
|
|
87
|
+
pkg: pkgData,
|
|
88
|
+
ast: astFile,
|
|
89
|
+
fullPath: "main.go",
|
|
90
|
+
Analysis: analysis,
|
|
91
|
+
PackageAnalysis: packageAnalysis,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Compile to the buffer
|
|
95
|
+
if err := fileCompiler.CompileToWriter(&buf); err != nil {
|
|
96
|
+
return "", fmt.Errorf("compilation error: %w", err)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return buf.String(), nil
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// CompileToWriter compiles the Go file and writes TypeScript to the given writer.
|
|
103
|
+
// This is used for WASM compilation where we don't want file I/O.
|
|
104
|
+
func (c *FileCompiler) CompileToWriter(w io.Writer) error {
|
|
105
|
+
f := c.ast
|
|
106
|
+
|
|
107
|
+
c.codeWriter = NewTSCodeWriter(w)
|
|
108
|
+
|
|
109
|
+
// Pass analysis to compiler
|
|
110
|
+
goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis, c.fullPath)
|
|
111
|
+
|
|
112
|
+
// Add import for the goscript runtime using namespace import and alias
|
|
113
|
+
c.codeWriter.WriteLinef("import * as $ from %q", "@goscript/builtin/index.js")
|
|
114
|
+
|
|
115
|
+
c.codeWriter.WriteLine("") // Add a newline after imports
|
|
116
|
+
|
|
117
|
+
if err := goWriter.WriteDecls(f.Decls); err != nil {
|
|
118
|
+
return fmt.Errorf("failed to write declarations: %w", err)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return nil
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// wasmImporter provides import resolution for WASM compilation.
|
|
125
|
+
// It wraps the default importer and provides stubs for common packages.
|
|
126
|
+
type wasmImporter struct {
|
|
127
|
+
defaultImporter types.Importer
|
|
128
|
+
cache map[string]*types.Package
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
func (i *wasmImporter) Import(path string) (*types.Package, error) {
|
|
132
|
+
if pkg, ok := i.cache[path]; ok {
|
|
133
|
+
return pkg, nil
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Try the default importer first (works for stdlib packages)
|
|
137
|
+
pkg, err := i.defaultImporter.Import(path)
|
|
138
|
+
if err == nil {
|
|
139
|
+
i.cache[path] = pkg
|
|
140
|
+
return pkg, nil
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// For unknown packages, create an empty stub package
|
|
144
|
+
// This allows compilation to proceed even if dependencies aren't available
|
|
145
|
+
stubPkg := types.NewPackage(path, pathToName(path))
|
|
146
|
+
stubPkg.MarkComplete()
|
|
147
|
+
i.cache[path] = stubPkg
|
|
148
|
+
return stubPkg, nil
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// pathToName extracts a package name from an import path
|
|
152
|
+
func pathToName(path string) string {
|
|
153
|
+
// Find the last component of the path
|
|
154
|
+
for i := len(path) - 1; i >= 0; i-- {
|
|
155
|
+
if path[i] == '/' {
|
|
156
|
+
return path[i+1:]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return path
|
|
160
|
+
}
|