goscript 0.2.0 → 0.2.2
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 +38 -6
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/runner.go +99 -17
- package/compiler/gotest/runner_test.go +65 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +9 -7
- package/compiler/lowering.go +361 -72
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/protobuf-ts-binding.go +65 -12
- package/compiler/protobuf-ts-binding_test.go +339 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/service.go +1 -0
- package/compiler/skeleton_test.go +60 -3
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/wasm-api.go +57 -7
- package/dist/gs/builtin/hostio.js +6 -1
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +11 -1
- package/dist/gs/builtin/slice.js +158 -2
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/crypto/aes/index.d.ts +15 -0
- package/dist/gs/crypto/aes/index.js +57 -0
- package/dist/gs/crypto/aes/index.js.map +1 -0
- package/dist/gs/crypto/cipher/index.d.ts +41 -0
- package/dist/gs/crypto/cipher/index.js +255 -0
- package/dist/gs/crypto/cipher/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/io/io.js +18 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/reflect/type.js +57 -0
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/runtime/debug/index.js +2 -1
- package/dist/gs/runtime/debug/index.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/go.mod +2 -2
- package/go.sum +2 -0
- package/gs/builtin/hostio.test.ts +22 -1
- package/gs/builtin/hostio.ts +6 -1
- package/gs/builtin/runtime-contract.test.ts +28 -0
- package/gs/builtin/slice.ts +225 -20
- package/gs/crypto/aes/index.test.ts +120 -0
- package/gs/crypto/aes/index.ts +76 -0
- package/gs/crypto/cipher/index.ts +345 -0
- package/gs/crypto/cipher/meta.json +6 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
- package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/io/io.test.ts +56 -1
- package/gs/io/io.ts +19 -2
- package/gs/reflect/type.ts +64 -0
- package/gs/reflect/typefor.test.ts +21 -1
- package/gs/runtime/debug/index.test.ts +32 -4
- package/gs/runtime/debug/index.ts +5 -2
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/package.json +10 -3
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
package main
|
|
4
4
|
|
|
5
5
|
import (
|
|
6
|
+
"errors"
|
|
6
7
|
"syscall/js"
|
|
7
8
|
|
|
9
|
+
"github.com/aperturerobotics/goscript/compiler"
|
|
8
10
|
"github.com/aperturerobotics/goscript/compiler/wasm"
|
|
9
11
|
)
|
|
10
12
|
|
|
@@ -20,8 +22,9 @@ func main() {
|
|
|
20
22
|
func compileWrapper(this js.Value, args []js.Value) interface{} {
|
|
21
23
|
if len(args) < 1 {
|
|
22
24
|
return map[string]interface{}{
|
|
23
|
-
"error":
|
|
24
|
-
"output":
|
|
25
|
+
"error": "missing source code argument",
|
|
26
|
+
"output": "",
|
|
27
|
+
"diagnostics": []interface{}{},
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -33,14 +36,43 @@ func compileWrapper(this js.Value, args []js.Value) interface{} {
|
|
|
33
36
|
|
|
34
37
|
output, err := wasm.CompileSource(source, packageName)
|
|
35
38
|
if err != nil {
|
|
39
|
+
diagnostics := []interface{}{}
|
|
40
|
+
var compileErr *compiler.CompileError
|
|
41
|
+
if errors.As(err, &compileErr) {
|
|
42
|
+
diagnostics = encodeDiagnostics(compileErr.Diagnostics)
|
|
43
|
+
}
|
|
36
44
|
return map[string]interface{}{
|
|
37
|
-
"error":
|
|
38
|
-
"output":
|
|
45
|
+
"error": err.Error(),
|
|
46
|
+
"output": "",
|
|
47
|
+
"diagnostics": diagnostics,
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
return map[string]interface{}{
|
|
43
|
-
"error":
|
|
44
|
-
"output":
|
|
52
|
+
"error": "",
|
|
53
|
+
"output": output,
|
|
54
|
+
"diagnostics": []interface{}{},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func encodeDiagnostics(diagnostics []compiler.Diagnostic) []interface{} {
|
|
59
|
+
encoded := make([]interface{}, 0, len(diagnostics))
|
|
60
|
+
for _, diagnostic := range diagnostics {
|
|
61
|
+
item := map[string]interface{}{
|
|
62
|
+
"severity": string(diagnostic.Severity),
|
|
63
|
+
"code": diagnostic.Code,
|
|
64
|
+
"message": diagnostic.Message,
|
|
65
|
+
"detail": diagnostic.Detail,
|
|
66
|
+
}
|
|
67
|
+
if diagnostic.Position != nil {
|
|
68
|
+
item["position"] = map[string]interface{}{
|
|
69
|
+
"file": diagnostic.Position.File,
|
|
70
|
+
"displayFile": diagnostic.Position.DisplayFile,
|
|
71
|
+
"line": diagnostic.Position.Line,
|
|
72
|
+
"column": diagnostic.Position.Column,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
encoded = append(encoded, item)
|
|
45
76
|
}
|
|
77
|
+
return encoded
|
|
46
78
|
}
|
package/compiler/diagnostic.go
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"strconv"
|
|
6
|
+
"strings"
|
|
7
|
+
)
|
|
4
8
|
|
|
5
9
|
// DiagnosticSeverity is the severity of a compiler diagnostic.
|
|
6
10
|
type DiagnosticSeverity string
|
|
@@ -22,6 +26,20 @@ type Diagnostic struct {
|
|
|
22
26
|
Message string
|
|
23
27
|
// Detail carries optional longer guidance.
|
|
24
28
|
Detail string
|
|
29
|
+
// Position is the optional source point that caused the diagnostic.
|
|
30
|
+
Position *DiagnosticPosition
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// DiagnosticPosition identifies the source point that caused a diagnostic.
|
|
34
|
+
type DiagnosticPosition struct {
|
|
35
|
+
// File is the raw file identity from the compiler source owner.
|
|
36
|
+
File string
|
|
37
|
+
// DisplayFile is the request-relative file identity for human output.
|
|
38
|
+
DisplayFile string
|
|
39
|
+
// Line is the 1-based source line.
|
|
40
|
+
Line int
|
|
41
|
+
// Column is the 1-based source column.
|
|
42
|
+
Column int
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
// CompileError wraps structured diagnostics for ordinary Go error paths.
|
|
@@ -41,25 +59,99 @@ func (e *CompileError) Error() string {
|
|
|
41
59
|
return "goscript: compile failed"
|
|
42
60
|
}
|
|
43
61
|
|
|
62
|
+
return FormatDiagnostics(e.Diagnostics)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// FormatDiagnostics returns the canonical human-readable diagnostic summary.
|
|
66
|
+
func FormatDiagnostics(diagnostics []Diagnostic) string {
|
|
44
67
|
var b strings.Builder
|
|
45
|
-
for i, diag := range
|
|
68
|
+
for i, diag := range diagnostics {
|
|
46
69
|
if i != 0 {
|
|
47
70
|
b.WriteString("; ")
|
|
48
71
|
}
|
|
49
|
-
|
|
50
|
-
b.WriteString(diag.Code)
|
|
51
|
-
b.WriteString(": ")
|
|
52
|
-
}
|
|
53
|
-
b.WriteString(diag.Message)
|
|
54
|
-
if diag.Detail != "" {
|
|
55
|
-
b.WriteString(" (")
|
|
56
|
-
b.WriteString(diag.Detail)
|
|
57
|
-
b.WriteString(")")
|
|
58
|
-
}
|
|
72
|
+
b.WriteString(FormatDiagnostic(diag))
|
|
59
73
|
}
|
|
60
74
|
return b.String()
|
|
61
75
|
}
|
|
62
76
|
|
|
77
|
+
// FormatDiagnostic returns the canonical human-readable form of one diagnostic.
|
|
78
|
+
func FormatDiagnostic(diag Diagnostic) string {
|
|
79
|
+
var b strings.Builder
|
|
80
|
+
if pos := formatDiagnosticPosition(diag.Position); pos != "" {
|
|
81
|
+
b.WriteString(pos)
|
|
82
|
+
b.WriteString(": ")
|
|
83
|
+
}
|
|
84
|
+
if diag.Code != "" {
|
|
85
|
+
b.WriteString(diag.Code)
|
|
86
|
+
b.WriteString(": ")
|
|
87
|
+
}
|
|
88
|
+
b.WriteString(diag.Message)
|
|
89
|
+
if diag.Detail != "" {
|
|
90
|
+
b.WriteString(" (")
|
|
91
|
+
b.WriteString(diag.Detail)
|
|
92
|
+
b.WriteString(")")
|
|
93
|
+
}
|
|
94
|
+
return b.String()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func formatDiagnosticPosition(pos *DiagnosticPosition) string {
|
|
98
|
+
if pos == nil || pos.Line <= 0 {
|
|
99
|
+
return ""
|
|
100
|
+
}
|
|
101
|
+
file := strings.TrimSpace(pos.DisplayFile)
|
|
102
|
+
if file == "" {
|
|
103
|
+
file = strings.TrimSpace(pos.File)
|
|
104
|
+
}
|
|
105
|
+
if file == "" {
|
|
106
|
+
return ""
|
|
107
|
+
}
|
|
108
|
+
var b strings.Builder
|
|
109
|
+
b.WriteString(filepath.ToSlash(file))
|
|
110
|
+
b.WriteString(":")
|
|
111
|
+
b.WriteString(strconv.Itoa(pos.Line))
|
|
112
|
+
if pos.Column > 0 {
|
|
113
|
+
b.WriteString(":")
|
|
114
|
+
b.WriteString(strconv.Itoa(pos.Column))
|
|
115
|
+
}
|
|
116
|
+
return b.String()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func diagnosticPositionFromSource(pos sourcePosition, displayRoot string) *DiagnosticPosition {
|
|
120
|
+
if pos.line <= 0 {
|
|
121
|
+
return nil
|
|
122
|
+
}
|
|
123
|
+
file := strings.TrimSpace(pos.file)
|
|
124
|
+
return &DiagnosticPosition{
|
|
125
|
+
File: file,
|
|
126
|
+
DisplayFile: diagnosticDisplayFile(file, displayRoot),
|
|
127
|
+
Line: pos.line,
|
|
128
|
+
Column: pos.column,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func diagnosticDisplayFile(file string, displayRoot string) string {
|
|
133
|
+
file = strings.TrimSpace(file)
|
|
134
|
+
if file == "" {
|
|
135
|
+
return ""
|
|
136
|
+
}
|
|
137
|
+
displayRoot = strings.TrimSpace(displayRoot)
|
|
138
|
+
if displayRoot == "" {
|
|
139
|
+
return filepath.ToSlash(file)
|
|
140
|
+
}
|
|
141
|
+
root := displayRoot
|
|
142
|
+
if absRoot, err := filepath.Abs(root); err == nil {
|
|
143
|
+
root = absRoot
|
|
144
|
+
}
|
|
145
|
+
candidate := file
|
|
146
|
+
if !filepath.IsAbs(candidate) {
|
|
147
|
+
candidate = filepath.Join(root, candidate)
|
|
148
|
+
}
|
|
149
|
+
if rel, err := filepath.Rel(root, candidate); err == nil && rel != "." && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
150
|
+
return filepath.ToSlash(rel)
|
|
151
|
+
}
|
|
152
|
+
return filepath.ToSlash(file)
|
|
153
|
+
}
|
|
154
|
+
|
|
63
155
|
func diagnosticsHaveErrors(diagnostics []Diagnostic) bool {
|
|
64
156
|
for _, diag := range diagnostics {
|
|
65
157
|
if diag.Severity == DiagnosticSeverityError {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"errors"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func TestFormatDiagnosticIncludesDisplayPosition(t *testing.T) {
|
|
12
|
+
diag := Diagnostic{
|
|
13
|
+
Severity: DiagnosticSeverityError,
|
|
14
|
+
Code: "goscript/test",
|
|
15
|
+
Message: "failed",
|
|
16
|
+
Detail: "bad input",
|
|
17
|
+
Position: &DiagnosticPosition{
|
|
18
|
+
File: filepath.Join("internal", "raw.go"),
|
|
19
|
+
DisplayFile: filepath.Join("pkg", "main.go"),
|
|
20
|
+
Line: 12,
|
|
21
|
+
Column: 3,
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
got := FormatDiagnostic(diag)
|
|
26
|
+
want := "pkg/main.go:12:3: goscript/test: failed (bad input)"
|
|
27
|
+
if got != want {
|
|
28
|
+
t.Fatalf("FormatDiagnostic() = %q, want %q", got, want)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func TestFormatDiagnosticWithoutPositionPreservesLegacyShape(t *testing.T) {
|
|
33
|
+
diag := Diagnostic{
|
|
34
|
+
Severity: DiagnosticSeverityError,
|
|
35
|
+
Code: "goscript/test",
|
|
36
|
+
Message: "failed",
|
|
37
|
+
Detail: "bad input",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
got := FormatDiagnostic(diag)
|
|
41
|
+
want := "goscript/test: failed (bad input)"
|
|
42
|
+
if got != want {
|
|
43
|
+
t.Fatalf("FormatDiagnostic() = %q, want %q", got, want)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func TestDiagnosticPositionFromSourceUsesDisplayRoot(t *testing.T) {
|
|
48
|
+
root := t.TempDir()
|
|
49
|
+
file := filepath.Join(root, "pkg", "main.go")
|
|
50
|
+
pos := diagnosticPositionFromSource(sourcePosition{
|
|
51
|
+
file: file,
|
|
52
|
+
line: 7,
|
|
53
|
+
column: 9,
|
|
54
|
+
}, root)
|
|
55
|
+
|
|
56
|
+
if pos == nil {
|
|
57
|
+
t.Fatal("expected diagnostic position")
|
|
58
|
+
}
|
|
59
|
+
if pos.File != file {
|
|
60
|
+
t.Fatalf("File = %q, want %q", pos.File, file)
|
|
61
|
+
}
|
|
62
|
+
if pos.DisplayFile != "pkg/main.go" {
|
|
63
|
+
t.Fatalf("DisplayFile = %q, want %q", pos.DisplayFile, "pkg/main.go")
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func TestLoweringUnsupportedDiagnosticIncludesSourcePosition(t *testing.T) {
|
|
68
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
69
|
+
"go.mod": "module example.test/loweringdiag\n\ngo 1.25.3\n",
|
|
70
|
+
"main.go": "package loweringdiag\n\nfunc Make[T ~[]int]() T {\n\treturn make(T, 1)\n}\n",
|
|
71
|
+
})
|
|
72
|
+
service := NewCompileService()
|
|
73
|
+
_, err := service.Compile(context.Background(), &CompileRequest{
|
|
74
|
+
Patterns: []string{"."},
|
|
75
|
+
Dir: moduleDir,
|
|
76
|
+
OutputPath: filepath.Join(t.TempDir(), "output"),
|
|
77
|
+
DependencyMode: DependencyModeRequested,
|
|
78
|
+
RuntimeEmissionMode: RuntimeEmissionModeReference,
|
|
79
|
+
})
|
|
80
|
+
if err == nil {
|
|
81
|
+
t.Fatal("expected lowering diagnostic")
|
|
82
|
+
}
|
|
83
|
+
var compileErr *CompileError
|
|
84
|
+
if !errors.As(err, &compileErr) {
|
|
85
|
+
t.Fatalf("expected CompileError, got %T: %v", err, err)
|
|
86
|
+
}
|
|
87
|
+
if len(compileErr.Diagnostics) != 1 {
|
|
88
|
+
t.Fatalf("Diagnostics = %#v, want exactly one", compileErr.Diagnostics)
|
|
89
|
+
}
|
|
90
|
+
diag := compileErr.Diagnostics[0]
|
|
91
|
+
if diag.Code != "goscript/lowering:unsupported" {
|
|
92
|
+
t.Fatalf("Code = %q, want goscript/lowering:unsupported", diag.Code)
|
|
93
|
+
}
|
|
94
|
+
if diag.Position == nil {
|
|
95
|
+
t.Fatalf("missing position in %#v", diag)
|
|
96
|
+
}
|
|
97
|
+
if diag.Position.DisplayFile != "main.go" {
|
|
98
|
+
t.Fatalf("DisplayFile = %q, want main.go", diag.Position.DisplayFile)
|
|
99
|
+
}
|
|
100
|
+
if diag.Position.Line != 4 {
|
|
101
|
+
t.Fatalf("Line = %d, want 4", diag.Position.Line)
|
|
102
|
+
}
|
|
103
|
+
if got := FormatDiagnostics(compileErr.Diagnostics); !strings.HasPrefix(got, "main.go:4:") {
|
|
104
|
+
t.Fatalf("formatted diagnostic = %q, want main.go:4 prefix", got)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -141,6 +141,10 @@ func (r *Runner) runPackageTools(
|
|
|
141
141
|
if len(indexes) == 0 {
|
|
142
142
|
return
|
|
143
143
|
}
|
|
144
|
+
if phase := materializeRuntimeModuleShims(req, outputRoots); phase.Failed() {
|
|
145
|
+
markRuntimeFailures(result, indexes, OwnerTestRunner, phase.Error)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
144
148
|
if len(indexes) == 1 {
|
|
145
149
|
r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, outputRoots, indexes[0])
|
|
146
150
|
return
|
|
@@ -286,6 +290,100 @@ func (r *Runner) runPackageRuntimes(
|
|
|
286
290
|
r.runPackageRuntimesIndividually(ctx, req, workspace, result, outputRoots, indexes)
|
|
287
291
|
}
|
|
288
292
|
|
|
293
|
+
func materializeRuntimeModuleShims(req *normalizedRequest, outputRoots []string) tsworkspace.Result {
|
|
294
|
+
if req.RuntimeBackend != RuntimeBackendBun {
|
|
295
|
+
return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace}
|
|
296
|
+
}
|
|
297
|
+
seen := make(map[string]bool)
|
|
298
|
+
shimRoots := runtimeModuleShimRoots(req, outputRoots)
|
|
299
|
+
for _, outputRoot := range outputRoots {
|
|
300
|
+
if outputRoot == "" {
|
|
301
|
+
continue
|
|
302
|
+
}
|
|
303
|
+
root := filepath.Join(outputRoot, "@goscript")
|
|
304
|
+
if _, err := os.Stat(root); err != nil {
|
|
305
|
+
if os.IsNotExist(err) {
|
|
306
|
+
continue
|
|
307
|
+
}
|
|
308
|
+
return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace, Error: errors.Wrap(err, "stat GoScript runtime output root").Error()}
|
|
309
|
+
}
|
|
310
|
+
err := filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
|
|
311
|
+
if err != nil {
|
|
312
|
+
return err
|
|
313
|
+
}
|
|
314
|
+
if entry.IsDir() || filepath.Ext(path) != ".ts" {
|
|
315
|
+
return nil
|
|
316
|
+
}
|
|
317
|
+
rel, err := filepath.Rel(root, path)
|
|
318
|
+
if err != nil {
|
|
319
|
+
return err
|
|
320
|
+
}
|
|
321
|
+
shimRel := filepath.Join("node_modules", "@goscript", strings.TrimSuffix(rel, ".ts")+".js")
|
|
322
|
+
for _, shimRoot := range shimRoots {
|
|
323
|
+
shimPath := filepath.Join(shimRoot, shimRel)
|
|
324
|
+
seenKey := shimPath
|
|
325
|
+
if seen[seenKey] {
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
seen[seenKey] = true
|
|
329
|
+
shimDir := filepath.Dir(shimPath)
|
|
330
|
+
importPath, err := filepath.Rel(shimDir, path)
|
|
331
|
+
if err != nil {
|
|
332
|
+
return err
|
|
333
|
+
}
|
|
334
|
+
importPath = filepath.ToSlash(importPath)
|
|
335
|
+
if !strings.HasPrefix(importPath, ".") {
|
|
336
|
+
importPath = "./" + importPath
|
|
337
|
+
}
|
|
338
|
+
if err := writeRuntimeModuleShim(shimPath, "export * from "+strconv.Quote(importPath)+"\n"); err != nil {
|
|
339
|
+
return err
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return nil
|
|
343
|
+
})
|
|
344
|
+
if err != nil {
|
|
345
|
+
return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace, Error: errors.Wrap(err, "materialize GoScript runtime module shims").Error()}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return tsworkspace.Result{Phase: tsworkspace.PhaseWorkspace}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
func runtimeModuleShimRoots(req *normalizedRequest, outputRoots []string) []string {
|
|
352
|
+
seen := make(map[string]bool)
|
|
353
|
+
var roots []string
|
|
354
|
+
for _, root := range append([]string{req.WorkDir}, outputRoots...) {
|
|
355
|
+
if root == "" {
|
|
356
|
+
continue
|
|
357
|
+
}
|
|
358
|
+
clean := filepath.Clean(root)
|
|
359
|
+
if seen[clean] {
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
seen[clean] = true
|
|
363
|
+
roots = append(roots, clean)
|
|
364
|
+
}
|
|
365
|
+
return roots
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
func writeRuntimeModuleShim(path string, data string) error {
|
|
369
|
+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
370
|
+
return err
|
|
371
|
+
}
|
|
372
|
+
return os.WriteFile(path, []byte(data), 0o644)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
func markRuntimeFailures(result *Result, indexes []int, owner Owner, message string) {
|
|
376
|
+
for _, idx := range indexes {
|
|
377
|
+
if idx < 0 || idx >= len(result.Packages) {
|
|
378
|
+
continue
|
|
379
|
+
}
|
|
380
|
+
result.Packages[idx].Action = ActionFail
|
|
381
|
+
result.Packages[idx].Owner = owner
|
|
382
|
+
result.Packages[idx].Phases.Runtime = PhaseStatusFail
|
|
383
|
+
result.Packages[idx].Error = message
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
289
387
|
func (r *Runner) runPackageRuntimesIndividually(
|
|
290
388
|
ctx context.Context,
|
|
291
389
|
req *normalizedRequest,
|
|
@@ -1531,21 +1629,5 @@ func diagnosticsHaveErrors(diagnostics []compiler.Diagnostic) bool {
|
|
|
1531
1629
|
}
|
|
1532
1630
|
|
|
1533
1631
|
func diagnosticsSummary(diagnostics []compiler.Diagnostic) string {
|
|
1534
|
-
|
|
1535
|
-
for idx, diagnostic := range diagnostics {
|
|
1536
|
-
if idx != 0 {
|
|
1537
|
-
b.WriteString("; ")
|
|
1538
|
-
}
|
|
1539
|
-
if diagnostic.Code != "" {
|
|
1540
|
-
b.WriteString(diagnostic.Code)
|
|
1541
|
-
b.WriteString(": ")
|
|
1542
|
-
}
|
|
1543
|
-
b.WriteString(diagnostic.Message)
|
|
1544
|
-
if diagnostic.Detail != "" {
|
|
1545
|
-
b.WriteString(" (")
|
|
1546
|
-
b.WriteString(diagnostic.Detail)
|
|
1547
|
-
b.WriteString(")")
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
return b.String()
|
|
1632
|
+
return compiler.FormatDiagnostics(diagnostics)
|
|
1551
1633
|
}
|
|
@@ -10,8 +10,28 @@ import (
|
|
|
10
10
|
"strings"
|
|
11
11
|
"testing"
|
|
12
12
|
"time"
|
|
13
|
+
|
|
14
|
+
"github.com/aperturerobotics/goscript/compiler"
|
|
13
15
|
)
|
|
14
16
|
|
|
17
|
+
func TestDiagnosticsSummaryUsesCompilerFormatter(t *testing.T) {
|
|
18
|
+
got := diagnosticsSummary([]compiler.Diagnostic{{
|
|
19
|
+
Severity: compiler.DiagnosticSeverityError,
|
|
20
|
+
Code: "goscript/test",
|
|
21
|
+
Message: "failed",
|
|
22
|
+
Detail: "bad input",
|
|
23
|
+
Position: &compiler.DiagnosticPosition{
|
|
24
|
+
DisplayFile: "pkg/main.go",
|
|
25
|
+
Line: 4,
|
|
26
|
+
Column: 2,
|
|
27
|
+
},
|
|
28
|
+
}})
|
|
29
|
+
want := "pkg/main.go:4:2: goscript/test: failed (bad input)"
|
|
30
|
+
if got != want {
|
|
31
|
+
t.Fatalf("diagnosticsSummary() = %q, want %q", got, want)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
func TestRunnerRunsOrdinaryPackageTest(t *testing.T) {
|
|
16
36
|
moduleDir := writeFixture(t, map[string]string{
|
|
17
37
|
"go.mod": "module example.test/gotest\n\ngo 1.25.3\n",
|
|
@@ -1609,6 +1629,51 @@ func TestRenderRuntimeTypeScriptProjectDisablesEmit(t *testing.T) {
|
|
|
1609
1629
|
}
|
|
1610
1630
|
}
|
|
1611
1631
|
|
|
1632
|
+
func TestMaterializeRuntimeModuleShimsReexportsGeneratedTypeScript(t *testing.T) {
|
|
1633
|
+
workDir := t.TempDir()
|
|
1634
|
+
outputRoot := filepath.Join(workDir, "output")
|
|
1635
|
+
for _, name := range []string{
|
|
1636
|
+
"errors/index.ts",
|
|
1637
|
+
"github.com/s4wave/spacewave/core/plugin/space/config.gs.ts",
|
|
1638
|
+
} {
|
|
1639
|
+
path := filepath.Join(outputRoot, "@goscript", filepath.FromSlash(name))
|
|
1640
|
+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
1641
|
+
t.Fatalf("create output parent: %v", err)
|
|
1642
|
+
}
|
|
1643
|
+
if err := os.WriteFile(path, []byte("export const value = 1\n"), 0o644); err != nil {
|
|
1644
|
+
t.Fatalf("write output file: %v", err)
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
req := &normalizedRequest{
|
|
1649
|
+
WorkDir: workDir,
|
|
1650
|
+
RuntimeBackend: RuntimeBackendBun,
|
|
1651
|
+
}
|
|
1652
|
+
phase := materializeRuntimeModuleShims(req, []string{outputRoot})
|
|
1653
|
+
if phase.Failed() {
|
|
1654
|
+
t.Fatalf("materialize shims: %s", phase.Error)
|
|
1655
|
+
}
|
|
1656
|
+
for _, root := range []string{workDir, outputRoot} {
|
|
1657
|
+
for _, name := range []string{
|
|
1658
|
+
"errors/index.js",
|
|
1659
|
+
"github.com/s4wave/spacewave/core/plugin/space/config.gs.js",
|
|
1660
|
+
} {
|
|
1661
|
+
path := filepath.Join(root, "node_modules", "@goscript", filepath.FromSlash(name))
|
|
1662
|
+
data, err := os.ReadFile(path)
|
|
1663
|
+
if err != nil {
|
|
1664
|
+
t.Fatalf("read shim %s: %v", name, err)
|
|
1665
|
+
}
|
|
1666
|
+
text := string(data)
|
|
1667
|
+
if !strings.Contains(text, ".ts\"") {
|
|
1668
|
+
t.Fatalf("shim should re-export generated TypeScript: %s", text)
|
|
1669
|
+
}
|
|
1670
|
+
if strings.Contains(text, workDir) {
|
|
1671
|
+
t.Fatalf("shim should use a portable relative import, got: %s", text)
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1612
1677
|
func TestRenderTypeScriptProjectsCanUseIncrementalBuildInfo(t *testing.T) {
|
|
1613
1678
|
req := &normalizedRequest{
|
|
1614
1679
|
WorkDir: "/work",
|
package/compiler/index.test.ts
CHANGED
|
@@ -28,4 +28,27 @@ describe('GoScript Compiler API', () => {
|
|
|
28
28
|
expect(generated).toContain('export async function main(): globalThis.Promise<void>')
|
|
29
29
|
expect(generated).toContain('$.println("api")')
|
|
30
30
|
}, 30000)
|
|
31
|
+
|
|
32
|
+
it('inherits positioned compiler diagnostics on stderr', async () => {
|
|
33
|
+
const dir = await mkdtemp(join(tmpdir(), 'goscript-api-diagnostic-'))
|
|
34
|
+
const output = join(dir, 'output')
|
|
35
|
+
await mkdir(dir, { recursive: true })
|
|
36
|
+
await writeFile(join(dir, 'go.mod'), 'module example.test/apierr\n\ngo 1.25.3\n')
|
|
37
|
+
await writeFile(join(dir, 'main.go'), [
|
|
38
|
+
'package apierr',
|
|
39
|
+
'',
|
|
40
|
+
'func Make[T ~[]int]() T {',
|
|
41
|
+
' return make(T, 1)',
|
|
42
|
+
'}',
|
|
43
|
+
'',
|
|
44
|
+
].join('\n'))
|
|
45
|
+
|
|
46
|
+
await expect(compile({
|
|
47
|
+
pkg: '.',
|
|
48
|
+
output,
|
|
49
|
+
dir,
|
|
50
|
+
})).rejects.toMatchObject({
|
|
51
|
+
stderr: expect.stringContaining('main.go:4:'),
|
|
52
|
+
})
|
|
53
|
+
}, 30000)
|
|
31
54
|
})
|
|
@@ -59,13 +59,14 @@ type loweredDecl struct {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
type loweredStruct struct {
|
|
62
|
-
exported
|
|
63
|
-
indexExported
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
exported bool
|
|
63
|
+
indexExported bool
|
|
64
|
+
protobufPreserveJSON bool
|
|
65
|
+
name string
|
|
66
|
+
typeName string
|
|
67
|
+
cloneMethod string
|
|
68
|
+
fields []loweredStructField
|
|
69
|
+
methods []loweredFunction
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
type loweredStructField struct {
|
|
@@ -90,6 +91,7 @@ type loweredFunction struct {
|
|
|
90
91
|
indexExported bool
|
|
91
92
|
init bool
|
|
92
93
|
async bool
|
|
94
|
+
sourcePath string
|
|
93
95
|
name string
|
|
94
96
|
typeParams []string
|
|
95
97
|
runtimeName string
|