goscript 0.0.84 → 0.1.0
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 +13 -1
- package/cmd/goscript/cmd_compile.go +70 -69
- package/cmd/goscript/cmd_compile_test.go +79 -0
- package/cmd/goscript/main.go +10 -5
- package/compiler/compile-request.go +218 -0
- package/compiler/compiler.go +16 -1336
- package/compiler/compliance_test.go +196 -0
- package/compiler/config.go +6 -13
- package/compiler/diagnostic.go +70 -0
- package/compiler/index.test.ts +28 -28
- package/compiler/index.ts +40 -72
- package/compiler/lowered-program.go +132 -0
- package/compiler/lowering.go +3576 -0
- package/compiler/override-registry.go +422 -0
- package/compiler/override-registry_test.go +207 -0
- package/compiler/package-graph.go +231 -0
- package/compiler/package-graph_test.go +281 -0
- package/compiler/result.go +13 -0
- package/compiler/runtime-contract.go +279 -0
- package/compiler/runtime-contract_test.go +90 -0
- package/compiler/semantic-model-types.go +110 -0
- package/compiler/semantic-model.go +922 -0
- package/compiler/semantic-model_test.go +416 -0
- package/compiler/service.go +133 -0
- package/compiler/skeleton_test.go +1145 -0
- package/compiler/typescript-emitter.go +663 -0
- package/compiler/wasm/compile.go +2 -3
- package/compiler/wasm/compile_test.go +29 -0
- package/compiler/wasm_api.go +10 -159
- package/dist/compiler/index.d.ts +1 -3
- package/dist/compiler/index.js +31 -55
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +13 -0
- package/dist/gs/builtin/builtin.js +23 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +3 -3
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +15 -1
- package/dist/gs/builtin/hostio.js +134 -49
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +11 -0
- package/dist/gs/builtin/type.js +55 -1
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/crypto/rand/index.d.ts +5 -0
- package/dist/gs/crypto/rand/index.js +77 -0
- package/dist/gs/crypto/rand/index.js.map +1 -0
- package/dist/gs/encoding/json/index.d.ts +3 -0
- package/dist/gs/encoding/json/index.js +160 -0
- package/dist/gs/encoding/json/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +29 -0
- package/dist/gs/go/scanner/index.js +120 -0
- package/dist/gs/go/scanner/index.js.map +1 -0
- package/dist/gs/go/token/index.d.ts +31 -0
- package/dist/gs/go/token/index.js +82 -0
- package/dist/gs/go/token/index.js.map +1 -0
- package/dist/gs/internal/abi/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/readfile.js.map +1 -1
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +2 -4
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/rawconn_js.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/tempfile.gs.js +66 -9
- package/dist/gs/os/tempfile.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +9 -9
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +2 -2
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -1
- package/dist/gs/reflect/type.js +85 -14
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/sort/sort.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/builder.js.map +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -0
- package/dist/gs/sync/sync.js +12 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.mod +2 -2
- package/gs/builtin/builtin.ts +27 -0
- package/gs/builtin/hostio.test.ts +177 -0
- package/gs/builtin/hostio.ts +171 -56
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/runtime-contract.test.ts +230 -0
- package/gs/builtin/type.ts +84 -1
- package/gs/crypto/rand/index.test.ts +32 -0
- package/gs/crypto/rand/index.ts +90 -0
- package/gs/crypto/rand/meta.json +5 -0
- package/gs/encoding/json/index.test.ts +65 -0
- package/gs/encoding/json/index.ts +186 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
- package/gs/go/scanner/index.test.ts +50 -0
- package/gs/go/scanner/index.ts +157 -0
- package/gs/go/token/index.test.ts +21 -0
- package/gs/go/token/index.ts +120 -0
- package/gs/os/file_unix_js.test.ts +50 -0
- package/gs/os/meta.json +1 -2
- package/gs/os/tempfile.gs.test.ts +85 -0
- package/gs/os/tempfile.gs.ts +71 -11
- package/gs/os/types_js.gs.ts +9 -9
- package/gs/reflect/index.ts +1 -1
- package/gs/reflect/type.ts +106 -17
- package/gs/reflect/typefor.test.ts +75 -0
- package/gs/sync/sync.test.ts +24 -0
- package/gs/sync/sync.ts +12 -0
- package/package.json +13 -13
- package/compiler/analysis.go +0 -3475
- package/compiler/analysis_test.go +0 -338
- package/compiler/assignment.go +0 -580
- package/compiler/builtin_test.go +0 -92
- package/compiler/code-writer.go +0 -115
- package/compiler/compiler_test.go +0 -149
- package/compiler/composite-lit.go +0 -779
- package/compiler/config_test.go +0 -62
- package/compiler/constraint.go +0 -86
- package/compiler/decl.go +0 -801
- package/compiler/expr-call-async.go +0 -188
- package/compiler/expr-call-builtins.go +0 -208
- package/compiler/expr-call-helpers.go +0 -382
- package/compiler/expr-call-make.go +0 -318
- package/compiler/expr-call-type-conversion.go +0 -520
- package/compiler/expr-call.go +0 -413
- package/compiler/expr-selector.go +0 -343
- package/compiler/expr-star.go +0 -82
- package/compiler/expr-type.go +0 -442
- package/compiler/expr-value.go +0 -89
- package/compiler/expr.go +0 -773
- package/compiler/field.go +0 -183
- package/compiler/gs_dependencies_test.go +0 -298
- package/compiler/lit.go +0 -322
- package/compiler/output.go +0 -72
- package/compiler/primitive.go +0 -149
- package/compiler/protobuf.go +0 -697
- package/compiler/sanitize.go +0 -100
- package/compiler/spec-struct.go +0 -995
- package/compiler/spec-value.go +0 -540
- package/compiler/spec.go +0 -725
- package/compiler/stmt-assign.go +0 -664
- package/compiler/stmt-for.go +0 -266
- package/compiler/stmt-range.go +0 -475
- package/compiler/stmt-select.go +0 -262
- package/compiler/stmt-type-switch.go +0 -147
- package/compiler/stmt.go +0 -1308
- package/compiler/type-assert.go +0 -386
- package/compiler/type-info.go +0 -156
- package/compiler/type-utils.go +0 -207
- package/compiler/type.go +0 -892
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"errors"
|
|
6
|
+
"io"
|
|
7
|
+
"io/fs"
|
|
8
|
+
"os"
|
|
9
|
+
"path"
|
|
10
|
+
"path/filepath"
|
|
11
|
+
"slices"
|
|
12
|
+
"strings"
|
|
13
|
+
|
|
14
|
+
gs "github.com/aperturerobotics/goscript"
|
|
15
|
+
jsoniter "github.com/aperturerobotics/json-iterator-lite"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// OverrideMetadata describes compiler-visible facts from a package override.
|
|
19
|
+
type OverrideMetadata struct {
|
|
20
|
+
// Dependencies are override package dependencies.
|
|
21
|
+
Dependencies []string
|
|
22
|
+
// AsyncMethods maps Type.Method keys to async status.
|
|
23
|
+
AsyncMethods map[string]bool
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// OverrideRegistryOwner owns GoScript override package metadata and copy plans.
|
|
27
|
+
type OverrideRegistryOwner struct {
|
|
28
|
+
metadata map[string]*OverrideMetadata
|
|
29
|
+
packageRootCache map[string]bool
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// NewOverrideRegistryOwner creates the override registry owner.
|
|
33
|
+
func NewOverrideRegistryOwner() *OverrideRegistryOwner {
|
|
34
|
+
return &OverrideRegistryOwner{
|
|
35
|
+
metadata: make(map[string]*OverrideMetadata),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Metadata returns compiler-visible override metadata for a package path.
|
|
40
|
+
func (o *OverrideRegistryOwner) Metadata(pkgPath string) (*OverrideMetadata, error) {
|
|
41
|
+
if o == nil {
|
|
42
|
+
o = NewOverrideRegistryOwner()
|
|
43
|
+
}
|
|
44
|
+
if metadata := o.metadata[pkgPath]; metadata != nil {
|
|
45
|
+
return metadata, nil
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
metadata := &OverrideMetadata{
|
|
49
|
+
AsyncMethods: make(map[string]bool),
|
|
50
|
+
}
|
|
51
|
+
data, err := gs.GsOverrides.ReadFile("gs/" + pkgPath + "/meta.json")
|
|
52
|
+
if err != nil {
|
|
53
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
54
|
+
o.metadata[pkgPath] = metadata
|
|
55
|
+
return metadata, nil
|
|
56
|
+
}
|
|
57
|
+
return nil, err
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
iter := jsoniter.ParseBytes(data)
|
|
61
|
+
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
|
|
62
|
+
switch field {
|
|
63
|
+
case "dependencies":
|
|
64
|
+
for iter.ReadArray() {
|
|
65
|
+
metadata.Dependencies = append(metadata.Dependencies, iter.ReadString())
|
|
66
|
+
}
|
|
67
|
+
case "asyncMethods":
|
|
68
|
+
for method := iter.ReadObject(); method != ""; method = iter.ReadObject() {
|
|
69
|
+
metadata.AsyncMethods[method] = iter.ReadBool()
|
|
70
|
+
}
|
|
71
|
+
default:
|
|
72
|
+
iter.Skip()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if iter.Error != nil && !errors.Is(iter.Error, io.EOF) {
|
|
76
|
+
return nil, iter.Error
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
o.metadata[pkgPath] = metadata
|
|
80
|
+
return metadata, nil
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// CopyPlan builds the ordered override package copy plan for a request.
|
|
84
|
+
func (o *OverrideRegistryOwner) CopyPlan(
|
|
85
|
+
ctx context.Context,
|
|
86
|
+
req *CompileRequest,
|
|
87
|
+
graph *PackageGraph,
|
|
88
|
+
) (*overrideCopyPlan, []Diagnostic) {
|
|
89
|
+
if o == nil {
|
|
90
|
+
o = NewOverrideRegistryOwner()
|
|
91
|
+
}
|
|
92
|
+
if err := ctx.Err(); err != nil {
|
|
93
|
+
return nil, []Diagnostic{{
|
|
94
|
+
Severity: DiagnosticSeverityError,
|
|
95
|
+
Code: "goscript/context:canceled",
|
|
96
|
+
Message: err.Error(),
|
|
97
|
+
}}
|
|
98
|
+
}
|
|
99
|
+
plan := &overrideCopyPlan{}
|
|
100
|
+
if req == nil || req.RuntimeEmissionMode == RuntimeEmissionModeReference {
|
|
101
|
+
return plan, nil
|
|
102
|
+
}
|
|
103
|
+
if graph == nil {
|
|
104
|
+
return nil, []Diagnostic{{
|
|
105
|
+
Severity: DiagnosticSeverityError,
|
|
106
|
+
Code: "goscript/overrides:no-graph",
|
|
107
|
+
Message: "override copy planning requires a package graph",
|
|
108
|
+
}}
|
|
109
|
+
}
|
|
110
|
+
if _, err := o.packageRoots(); err != nil {
|
|
111
|
+
return nil, []Diagnostic{overrideError("discover override packages", "", err)}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var diagnostics []Diagnostic
|
|
115
|
+
visiting := make(map[string]bool)
|
|
116
|
+
visited := make(map[string]bool)
|
|
117
|
+
diagnostics = append(diagnostics, o.addPackageToPlan(ctx, plan, "builtin", visiting, visited)...)
|
|
118
|
+
for _, node := range graph.Nodes {
|
|
119
|
+
if node.OverrideCandidate {
|
|
120
|
+
diagnostics = append(diagnostics, o.addPackageToPlan(ctx, plan, node.PkgPath, visiting, visited)...)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
124
|
+
return nil, diagnostics
|
|
125
|
+
}
|
|
126
|
+
return plan, diagnostics
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// CopyPackages writes override packages from a copy plan to the request output tree.
|
|
130
|
+
func (o *OverrideRegistryOwner) CopyPackages(
|
|
131
|
+
ctx context.Context,
|
|
132
|
+
req *CompileRequest,
|
|
133
|
+
plan *overrideCopyPlan,
|
|
134
|
+
) ([]string, []Diagnostic) {
|
|
135
|
+
if err := ctx.Err(); err != nil {
|
|
136
|
+
return nil, []Diagnostic{{
|
|
137
|
+
Severity: DiagnosticSeverityError,
|
|
138
|
+
Code: "goscript/context:canceled",
|
|
139
|
+
Message: err.Error(),
|
|
140
|
+
}}
|
|
141
|
+
}
|
|
142
|
+
if req == nil {
|
|
143
|
+
return nil, []Diagnostic{{
|
|
144
|
+
Severity: DiagnosticSeverityError,
|
|
145
|
+
Code: "goscript/overrides:no-request",
|
|
146
|
+
Message: "override package copying requires a compile request",
|
|
147
|
+
}}
|
|
148
|
+
}
|
|
149
|
+
if plan == nil || len(plan.packages) == 0 {
|
|
150
|
+
return nil, nil
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var copied []string
|
|
154
|
+
var diagnostics []Diagnostic
|
|
155
|
+
for _, pkg := range plan.packages {
|
|
156
|
+
if err := ctx.Err(); err != nil {
|
|
157
|
+
diagnostics = append(diagnostics, Diagnostic{
|
|
158
|
+
Severity: DiagnosticSeverityError,
|
|
159
|
+
Code: "goscript/context:canceled",
|
|
160
|
+
Message: err.Error(),
|
|
161
|
+
})
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
for _, file := range pkg.files {
|
|
165
|
+
dest := filepath.Join(req.OutputPath, "@goscript", filepath.FromSlash(file.path))
|
|
166
|
+
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
|
167
|
+
diagnostics = append(diagnostics, overrideError("create override output", file.path, err))
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
if err := os.WriteFile(dest, file.data, 0o644); err != nil {
|
|
171
|
+
diagnostics = append(diagnostics, overrideError("write override file", file.path, err))
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
copied = append(copied, pkg.path)
|
|
175
|
+
}
|
|
176
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
177
|
+
return copied, diagnostics
|
|
178
|
+
}
|
|
179
|
+
return copied, diagnostics
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// IsMethodAsync returns true when override metadata marks a method async.
|
|
183
|
+
func (o *OverrideRegistryOwner) IsMethodAsync(pkgPath, method string) (bool, error) {
|
|
184
|
+
metadata, err := o.Metadata(pkgPath)
|
|
185
|
+
if err != nil {
|
|
186
|
+
return false, err
|
|
187
|
+
}
|
|
188
|
+
return metadata.AsyncMethods[method], nil
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
type overrideCopyPlan struct {
|
|
192
|
+
packages []overrideCopyPackage
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
type overrideCopyPackage struct {
|
|
196
|
+
path string
|
|
197
|
+
files []overrideCopyFile
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
type overrideCopyFile struct {
|
|
201
|
+
path string
|
|
202
|
+
data []byte
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func (o *OverrideRegistryOwner) addPackageToPlan(
|
|
206
|
+
ctx context.Context,
|
|
207
|
+
plan *overrideCopyPlan,
|
|
208
|
+
pkgPath string,
|
|
209
|
+
visiting map[string]bool,
|
|
210
|
+
visited map[string]bool,
|
|
211
|
+
) []Diagnostic {
|
|
212
|
+
if err := ctx.Err(); err != nil {
|
|
213
|
+
return []Diagnostic{{
|
|
214
|
+
Severity: DiagnosticSeverityError,
|
|
215
|
+
Code: "goscript/context:canceled",
|
|
216
|
+
Message: err.Error(),
|
|
217
|
+
}}
|
|
218
|
+
}
|
|
219
|
+
if visited[pkgPath] {
|
|
220
|
+
return nil
|
|
221
|
+
}
|
|
222
|
+
if visiting[pkgPath] {
|
|
223
|
+
return []Diagnostic{{
|
|
224
|
+
Severity: DiagnosticSeverityError,
|
|
225
|
+
Code: "goscript/overrides:dependency-cycle",
|
|
226
|
+
Message: "override package dependencies contain a cycle",
|
|
227
|
+
Detail: pkgPath,
|
|
228
|
+
}}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pkg, dependencies, diagnostics := o.loadCopyPackage(pkgPath)
|
|
232
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
233
|
+
return diagnostics
|
|
234
|
+
}
|
|
235
|
+
visiting[pkgPath] = true
|
|
236
|
+
for _, dependency := range dependencies {
|
|
237
|
+
diagnostics = append(diagnostics, o.addPackageToPlan(ctx, plan, dependency, visiting, visited)...)
|
|
238
|
+
}
|
|
239
|
+
delete(visiting, pkgPath)
|
|
240
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
241
|
+
return diagnostics
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
visited[pkgPath] = true
|
|
245
|
+
plan.packages = append(plan.packages, pkg)
|
|
246
|
+
return diagnostics
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func (o *OverrideRegistryOwner) loadCopyPackage(pkgPath string) (overrideCopyPackage, []string, []Diagnostic) {
|
|
250
|
+
roots, err := o.packageRoots()
|
|
251
|
+
if err != nil {
|
|
252
|
+
return overrideCopyPackage{}, nil, []Diagnostic{overrideError("discover override packages", "", err)}
|
|
253
|
+
}
|
|
254
|
+
if !roots[pkgPath] {
|
|
255
|
+
return overrideCopyPackage{}, nil, []Diagnostic{{
|
|
256
|
+
Severity: DiagnosticSeverityError,
|
|
257
|
+
Code: "goscript/overrides:missing-package",
|
|
258
|
+
Message: "override package does not exist",
|
|
259
|
+
Detail: pkgPath,
|
|
260
|
+
}}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
metadata, err := o.Metadata(pkgPath)
|
|
264
|
+
if err != nil {
|
|
265
|
+
return overrideCopyPackage{}, nil, []Diagnostic{overrideError("read override metadata", pkgPath, err)}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
copyPackage := overrideCopyPackage{path: pkgPath}
|
|
269
|
+
dependencySet := make(map[string]bool)
|
|
270
|
+
for _, dependency := range metadata.Dependencies {
|
|
271
|
+
dependency = strings.TrimSpace(dependency)
|
|
272
|
+
if dependency != "" && dependency != pkgPath {
|
|
273
|
+
dependencySet[dependency] = true
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
root := "gs/" + pkgPath
|
|
278
|
+
err = fs.WalkDir(gs.GsOverrides, root, func(filePath string, entry fs.DirEntry, walkErr error) error {
|
|
279
|
+
if walkErr != nil {
|
|
280
|
+
return walkErr
|
|
281
|
+
}
|
|
282
|
+
if entry.IsDir() {
|
|
283
|
+
nestedPkg := strings.TrimPrefix(filePath, "gs/")
|
|
284
|
+
if nestedPkg != pkgPath && roots[nestedPkg] {
|
|
285
|
+
return fs.SkipDir
|
|
286
|
+
}
|
|
287
|
+
return nil
|
|
288
|
+
}
|
|
289
|
+
if !isOverrideSourceFile(filePath) {
|
|
290
|
+
return nil
|
|
291
|
+
}
|
|
292
|
+
data, readErr := gs.GsOverrides.ReadFile(filePath)
|
|
293
|
+
if readErr != nil {
|
|
294
|
+
return readErr
|
|
295
|
+
}
|
|
296
|
+
rel := strings.TrimPrefix(filePath, "gs/")
|
|
297
|
+
copyPackage.files = append(copyPackage.files, overrideCopyFile{
|
|
298
|
+
path: rel,
|
|
299
|
+
data: data,
|
|
300
|
+
})
|
|
301
|
+
for _, imported := range scanOverrideImports(string(data)) {
|
|
302
|
+
dependency, ok := o.importPackageRoot(imported)
|
|
303
|
+
if ok && dependency != "builtin" && dependency != pkgPath {
|
|
304
|
+
dependencySet[dependency] = true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return nil
|
|
308
|
+
})
|
|
309
|
+
if err != nil {
|
|
310
|
+
return overrideCopyPackage{}, nil, []Diagnostic{overrideError("read override package", pkgPath, err)}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if len(copyPackage.files) == 0 {
|
|
314
|
+
return overrideCopyPackage{}, nil, []Diagnostic{{
|
|
315
|
+
Severity: DiagnosticSeverityError,
|
|
316
|
+
Code: "goscript/overrides:empty-package",
|
|
317
|
+
Message: "override package does not contain TypeScript source files",
|
|
318
|
+
Detail: pkgPath,
|
|
319
|
+
}}
|
|
320
|
+
}
|
|
321
|
+
slices.SortFunc(copyPackage.files, func(a, b overrideCopyFile) int {
|
|
322
|
+
return strings.Compare(a.path, b.path)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
dependencies := make([]string, 0, len(dependencySet))
|
|
326
|
+
for dependency := range dependencySet {
|
|
327
|
+
dependencies = append(dependencies, dependency)
|
|
328
|
+
}
|
|
329
|
+
slices.Sort(dependencies)
|
|
330
|
+
return copyPackage, dependencies, nil
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
func (o *OverrideRegistryOwner) packageRoots() (map[string]bool, error) {
|
|
334
|
+
if o.packageRootCache != nil {
|
|
335
|
+
return o.packageRootCache, nil
|
|
336
|
+
}
|
|
337
|
+
roots := make(map[string]bool)
|
|
338
|
+
if err := fs.WalkDir(gs.GsOverrides, "gs", func(filePath string, entry fs.DirEntry, err error) error {
|
|
339
|
+
if err != nil {
|
|
340
|
+
return err
|
|
341
|
+
}
|
|
342
|
+
if entry.IsDir() || path.Base(filePath) != "index.ts" {
|
|
343
|
+
return nil
|
|
344
|
+
}
|
|
345
|
+
pkgPath := strings.TrimPrefix(path.Dir(filePath), "gs/")
|
|
346
|
+
if pkgPath != "." && pkgPath != "" {
|
|
347
|
+
roots[pkgPath] = true
|
|
348
|
+
}
|
|
349
|
+
return nil
|
|
350
|
+
}); err != nil {
|
|
351
|
+
return nil, err
|
|
352
|
+
}
|
|
353
|
+
o.packageRootCache = roots
|
|
354
|
+
return roots, nil
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
func (o *OverrideRegistryOwner) importPackageRoot(importPath string) (string, bool) {
|
|
358
|
+
roots, err := o.packageRoots()
|
|
359
|
+
if err != nil {
|
|
360
|
+
return "", false
|
|
361
|
+
}
|
|
362
|
+
importPath = strings.TrimPrefix(importPath, "@goscript/")
|
|
363
|
+
importPath = strings.TrimSuffix(importPath, ".js")
|
|
364
|
+
importPath = strings.TrimSuffix(importPath, ".ts")
|
|
365
|
+
if before, ok := strings.CutSuffix(importPath, "/index"); ok {
|
|
366
|
+
importPath = before
|
|
367
|
+
}
|
|
368
|
+
parts := strings.Split(importPath, "/")
|
|
369
|
+
for idx := len(parts); idx > 0; idx-- {
|
|
370
|
+
candidate := strings.Join(parts[:idx], "/")
|
|
371
|
+
if roots[candidate] {
|
|
372
|
+
return candidate, true
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return "", false
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
func isOverrideSourceFile(filePath string) bool {
|
|
379
|
+
return strings.HasSuffix(filePath, ".ts") && !strings.HasSuffix(filePath, ".test.ts")
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
func scanOverrideImports(data string) []string {
|
|
383
|
+
imports := make(map[string]bool)
|
|
384
|
+
for line := range strings.SplitSeq(data, "\n") {
|
|
385
|
+
line = strings.TrimSpace(line)
|
|
386
|
+
if strings.HasPrefix(line, "//") {
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
for _, quote := range []string{"\"", "'"} {
|
|
390
|
+
prefix := quote + "@goscript/"
|
|
391
|
+
remaining := line
|
|
392
|
+
for {
|
|
393
|
+
idx := strings.Index(remaining, prefix)
|
|
394
|
+
if idx < 0 {
|
|
395
|
+
break
|
|
396
|
+
}
|
|
397
|
+
remaining = remaining[idx+len(quote):]
|
|
398
|
+
end := strings.Index(remaining, quote)
|
|
399
|
+
if end < 0 {
|
|
400
|
+
break
|
|
401
|
+
}
|
|
402
|
+
imports[remaining[:end]] = true
|
|
403
|
+
remaining = remaining[end+len(quote):]
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
result := make([]string, 0, len(imports))
|
|
408
|
+
for imported := range imports {
|
|
409
|
+
result = append(result, imported)
|
|
410
|
+
}
|
|
411
|
+
slices.Sort(result)
|
|
412
|
+
return result
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
func overrideError(action, detail string, err error) Diagnostic {
|
|
416
|
+
return Diagnostic{
|
|
417
|
+
Severity: DiagnosticSeverityError,
|
|
418
|
+
Code: "goscript/overrides:io",
|
|
419
|
+
Message: "failed to " + action,
|
|
420
|
+
Detail: strings.TrimSpace(detail + " " + err.Error()),
|
|
421
|
+
}
|
|
422
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"slices"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestOverrideRegistryPlansRuntimeAndOverrideDependencies(t *testing.T) {
|
|
13
|
+
owner := NewOverrideRegistryOwner()
|
|
14
|
+
plan, diagnostics := owner.CopyPlan(context.Background(), &CompileRequest{
|
|
15
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
16
|
+
}, &PackageGraph{Nodes: []*PackageGraphNode{{
|
|
17
|
+
PkgPath: "fmt",
|
|
18
|
+
OverrideCandidate: true,
|
|
19
|
+
}}})
|
|
20
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
21
|
+
t.Fatalf("copy plan failed: %#v", diagnostics)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var packages []string
|
|
25
|
+
for _, pkg := range plan.packages {
|
|
26
|
+
packages = append(packages, pkg.path)
|
|
27
|
+
}
|
|
28
|
+
for _, pkg := range []string{"builtin", "errors", "fmt"} {
|
|
29
|
+
if !slices.Contains(packages, pkg) {
|
|
30
|
+
t.Fatalf("missing %s in copy plan: %v", pkg, packages)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if slices.Index(packages, "errors") > slices.Index(packages, "fmt") {
|
|
34
|
+
t.Fatalf("dependency order is wrong: %v", packages)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func TestOverrideRegistryCopiesRuntimeAndOverrides(t *testing.T) {
|
|
39
|
+
owner := NewOverrideRegistryOwner()
|
|
40
|
+
req := &CompileRequest{
|
|
41
|
+
OutputPath: filepath.Join(t.TempDir(), "out"),
|
|
42
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
43
|
+
}
|
|
44
|
+
plan, diagnostics := owner.CopyPlan(context.Background(), req, &PackageGraph{Nodes: []*PackageGraphNode{{
|
|
45
|
+
PkgPath: "fmt",
|
|
46
|
+
OverrideCandidate: true,
|
|
47
|
+
}}})
|
|
48
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
49
|
+
t.Fatalf("copy plan failed: %#v", diagnostics)
|
|
50
|
+
}
|
|
51
|
+
copied, diagnostics := owner.CopyPackages(context.Background(), req, plan)
|
|
52
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
53
|
+
t.Fatalf("copy failed: %#v", diagnostics)
|
|
54
|
+
}
|
|
55
|
+
for _, pkg := range []string{"builtin", "errors", "fmt"} {
|
|
56
|
+
if !slices.Contains(copied, pkg) {
|
|
57
|
+
t.Fatalf("missing copied package %s in %v", pkg, copied)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for _, path := range []string{
|
|
61
|
+
"@goscript/builtin/index.ts",
|
|
62
|
+
"@goscript/errors/index.ts",
|
|
63
|
+
"@goscript/fmt/index.ts",
|
|
64
|
+
} {
|
|
65
|
+
if _, err := os.Stat(filepath.Join(req.OutputPath, filepath.FromSlash(path))); err != nil {
|
|
66
|
+
t.Fatalf("expected copied file %s: %v", path, err)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if _, err := os.Stat(filepath.Join(req.OutputPath, "@goscript", "fmt", "fmt.test.ts")); !os.IsNotExist(err) {
|
|
70
|
+
t.Fatalf("override copy should not include test files")
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func TestOverrideRegistryReportsMissingOverridePackage(t *testing.T) {
|
|
75
|
+
_, diagnostics := NewOverrideRegistryOwner().CopyPlan(context.Background(), &CompileRequest{
|
|
76
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
77
|
+
}, &PackageGraph{Nodes: []*PackageGraphNode{{
|
|
78
|
+
PkgPath: "does/not/exist",
|
|
79
|
+
OverrideCandidate: true,
|
|
80
|
+
}}})
|
|
81
|
+
requireDiagnosticCode(t, diagnostics, "goscript/overrides:missing-package")
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
func TestOverrideRegistryPlansOsOverrideDependencies(t *testing.T) {
|
|
85
|
+
owner := NewOverrideRegistryOwner()
|
|
86
|
+
plan, diagnostics := owner.CopyPlan(context.Background(), &CompileRequest{
|
|
87
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
88
|
+
}, &PackageGraph{Nodes: []*PackageGraphNode{{
|
|
89
|
+
PkgPath: "os",
|
|
90
|
+
OverrideCandidate: true,
|
|
91
|
+
}}})
|
|
92
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
93
|
+
t.Fatalf("copy plan failed: %#v", diagnostics)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var packages []string
|
|
97
|
+
for _, pkg := range plan.packages {
|
|
98
|
+
packages = append(packages, pkg.path)
|
|
99
|
+
}
|
|
100
|
+
if !slices.Contains(packages, "os") {
|
|
101
|
+
t.Fatalf("missing os in copy plan: %v", packages)
|
|
102
|
+
}
|
|
103
|
+
if slices.Contains(packages, "internal/poll") {
|
|
104
|
+
t.Fatalf("os copy plan includes stale internal/poll dependency: %v", packages)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
func TestOverrideRegistryPlansNestedOverrideMetadataDependencies(t *testing.T) {
|
|
109
|
+
owner := NewOverrideRegistryOwner()
|
|
110
|
+
plan, diagnostics := owner.CopyPlan(context.Background(), &CompileRequest{
|
|
111
|
+
RuntimeEmissionMode: RuntimeEmissionModeEmit,
|
|
112
|
+
}, &PackageGraph{Nodes: []*PackageGraphNode{{
|
|
113
|
+
PkgPath: "github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser",
|
|
114
|
+
OverrideCandidate: true,
|
|
115
|
+
}}})
|
|
116
|
+
if diagnosticsHaveErrors(diagnostics) {
|
|
117
|
+
t.Fatalf("copy plan failed: %#v", diagnostics)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var packages []string
|
|
121
|
+
for _, pkg := range plan.packages {
|
|
122
|
+
packages = append(packages, pkg.path)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
runtimePkg := "github.com/aperturerobotics/wasivm/wazero/kernel/runtime"
|
|
126
|
+
browserPkg := "github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser"
|
|
127
|
+
for _, pkg := range []string{"builtin", runtimePkg, browserPkg} {
|
|
128
|
+
if !slices.Contains(packages, pkg) {
|
|
129
|
+
t.Fatalf("missing %s in copy plan: %v", pkg, packages)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if slices.Index(packages, runtimePkg) > slices.Index(packages, browserPkg) {
|
|
133
|
+
t.Fatalf("nested override dependency order is wrong: %v", packages)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
func TestCompilePackagesCopiesRuntimeOverrides(t *testing.T) {
|
|
138
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
139
|
+
"go.mod": "module example.test/overridecopy\n\ngo 1.25.3\n",
|
|
140
|
+
"main.go": "package main\nimport \"fmt\"\nfunc main() { fmt.Println(\"ok\") }\n",
|
|
141
|
+
})
|
|
142
|
+
out := filepath.Join(t.TempDir(), "out")
|
|
143
|
+
comp, err := NewCompiler(&Config{
|
|
144
|
+
Dir: moduleDir,
|
|
145
|
+
OutputPath: out,
|
|
146
|
+
AllDependencies: true,
|
|
147
|
+
}, nil, nil)
|
|
148
|
+
if err != nil {
|
|
149
|
+
t.Fatal(err.Error())
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
result, err := comp.CompilePackages(context.Background(), ".")
|
|
153
|
+
if err != nil {
|
|
154
|
+
t.Fatalf("compile failed: %v\n%#v", err, result.Diagnostics)
|
|
155
|
+
}
|
|
156
|
+
for _, pkg := range []string{"builtin", "errors", "fmt"} {
|
|
157
|
+
if !slices.Contains(result.CopiedPackages, pkg) {
|
|
158
|
+
t.Fatalf("missing copied package %s in %#v", pkg, result.CopiedPackages)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for _, path := range []string{
|
|
162
|
+
"@goscript/example.test/overridecopy/main.gs.ts",
|
|
163
|
+
"@goscript/builtin/index.ts",
|
|
164
|
+
"@goscript/fmt/index.ts",
|
|
165
|
+
} {
|
|
166
|
+
if _, err := os.Stat(filepath.Join(out, filepath.FromSlash(path))); err != nil {
|
|
167
|
+
t.Fatalf("expected output file %s: %v", path, err)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
func TestCompilePackagesAwaitsOverrideAsyncMethods(t *testing.T) {
|
|
173
|
+
moduleDir := writePackageGraphFixture(t, map[string]string{
|
|
174
|
+
"go.mod": "module example.test/overrideasync\n\ngo 1.25.3\n",
|
|
175
|
+
"main.go": strings.Join([]string{
|
|
176
|
+
"package main",
|
|
177
|
+
"import \"sync\"",
|
|
178
|
+
"func main() {",
|
|
179
|
+
" var m sync.Map",
|
|
180
|
+
" if value, ok := m.Load(\"key\"); ok {",
|
|
181
|
+
" println(value)",
|
|
182
|
+
" }",
|
|
183
|
+
"}",
|
|
184
|
+
"",
|
|
185
|
+
}, "\n"),
|
|
186
|
+
})
|
|
187
|
+
out := filepath.Join(t.TempDir(), "out")
|
|
188
|
+
comp, err := NewCompiler(&Config{
|
|
189
|
+
Dir: moduleDir,
|
|
190
|
+
OutputPath: out,
|
|
191
|
+
AllDependencies: true,
|
|
192
|
+
}, nil, nil)
|
|
193
|
+
if err != nil {
|
|
194
|
+
t.Fatal(err.Error())
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
|
|
198
|
+
t.Fatal(err.Error())
|
|
199
|
+
}
|
|
200
|
+
content, err := os.ReadFile(filepath.Join(out, "@goscript", "example.test", "overrideasync", "main.gs.ts"))
|
|
201
|
+
if err != nil {
|
|
202
|
+
t.Fatal(err.Error())
|
|
203
|
+
}
|
|
204
|
+
if !strings.Contains(string(content), "let [value, ok] = await m.value.Load(\"key\")") {
|
|
205
|
+
t.Fatalf("override async method call was not awaited:\n%s", string(content))
|
|
206
|
+
}
|
|
207
|
+
}
|