goscript 0.0.83 → 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.
Files changed (197) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +27 -7
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +86 -0
  39. package/dist/gs/builtin/hostio.js +266 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -0
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/print.d.ts +8 -0
  45. package/dist/gs/builtin/print.js +111 -0
  46. package/dist/gs/builtin/print.js.map +1 -0
  47. package/dist/gs/builtin/slice.d.ts +1 -1
  48. package/dist/gs/builtin/slice.js.map +1 -1
  49. package/dist/gs/builtin/type.d.ts +11 -0
  50. package/dist/gs/builtin/type.js +55 -1
  51. package/dist/gs/builtin/type.js.map +1 -1
  52. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  53. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  54. package/dist/gs/bytes/reader.gs.js.map +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/crypto/rand/index.d.ts +5 -0
  57. package/dist/gs/crypto/rand/index.js +77 -0
  58. package/dist/gs/crypto/rand/index.js.map +1 -0
  59. package/dist/gs/encoding/json/index.d.ts +3 -0
  60. package/dist/gs/encoding/json/index.js +160 -0
  61. package/dist/gs/encoding/json/index.js.map +1 -0
  62. package/dist/gs/fmt/fmt.js +2 -22
  63. package/dist/gs/fmt/fmt.js.map +1 -1
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  66. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  67. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  68. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  69. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  70. package/dist/gs/go/scanner/index.d.ts +29 -0
  71. package/dist/gs/go/scanner/index.js +120 -0
  72. package/dist/gs/go/scanner/index.js.map +1 -0
  73. package/dist/gs/go/token/index.d.ts +31 -0
  74. package/dist/gs/go/token/index.js +82 -0
  75. package/dist/gs/go/token/index.js.map +1 -0
  76. package/dist/gs/internal/abi/index.js.map +1 -1
  77. package/dist/gs/io/fs/fs.js.map +1 -1
  78. package/dist/gs/io/fs/readdir.js.map +1 -1
  79. package/dist/gs/io/fs/readfile.js.map +1 -1
  80. package/dist/gs/io/fs/stat.js.map +1 -1
  81. package/dist/gs/io/fs/sub.js.map +1 -1
  82. package/dist/gs/io/io.js.map +1 -1
  83. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  84. package/dist/gs/os/error.gs.js +2 -4
  85. package/dist/gs/os/error.gs.js.map +1 -1
  86. package/dist/gs/os/exec.gs.js.map +1 -1
  87. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  88. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  89. package/dist/gs/os/root_js.gs.js.map +1 -1
  90. package/dist/gs/os/tempfile.gs.js +66 -9
  91. package/dist/gs/os/tempfile.gs.js.map +1 -1
  92. package/dist/gs/os/types.gs.js.map +1 -1
  93. package/dist/gs/os/types_js.gs.d.ts +2 -51
  94. package/dist/gs/os/types_js.gs.js +67 -105
  95. package/dist/gs/os/types_js.gs.js.map +1 -1
  96. package/dist/gs/os/types_unix.gs.js.map +1 -1
  97. package/dist/gs/path/filepath/match.js.map +1 -1
  98. package/dist/gs/path/match.js.map +1 -1
  99. package/dist/gs/path/path.js.map +1 -1
  100. package/dist/gs/reflect/index.d.ts +2 -2
  101. package/dist/gs/reflect/index.js +1 -1
  102. package/dist/gs/reflect/index.js.map +1 -1
  103. package/dist/gs/reflect/map.js.map +1 -1
  104. package/dist/gs/reflect/type.d.ts +2 -1
  105. package/dist/gs/reflect/type.js +85 -14
  106. package/dist/gs/reflect/type.js.map +1 -1
  107. package/dist/gs/reflect/types.js.map +1 -1
  108. package/dist/gs/reflect/visiblefields.js.map +1 -1
  109. package/dist/gs/runtime/runtime.js.map +1 -1
  110. package/dist/gs/sort/sort.gs.js.map +1 -1
  111. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  112. package/dist/gs/strconv/quote.gs.js.map +1 -1
  113. package/dist/gs/strings/builder.js.map +1 -1
  114. package/dist/gs/strings/reader.js.map +1 -1
  115. package/dist/gs/strings/replace.js.map +1 -1
  116. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  117. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  118. package/dist/gs/sync/sync.d.ts +1 -0
  119. package/dist/gs/sync/sync.js +12 -0
  120. package/dist/gs/sync/sync.js.map +1 -1
  121. package/dist/gs/time/time.js.map +1 -1
  122. package/dist/gs/unicode/unicode.js.map +1 -1
  123. package/go.mod +2 -2
  124. package/gs/builtin/builtin.ts +31 -6
  125. package/gs/builtin/hostio.test.ts +246 -0
  126. package/gs/builtin/hostio.ts +413 -0
  127. package/gs/builtin/index.ts +1 -0
  128. package/gs/builtin/print.test.ts +48 -0
  129. package/gs/builtin/print.ts +154 -0
  130. package/gs/builtin/runtime-contract.test.ts +230 -0
  131. package/gs/builtin/type.ts +84 -1
  132. package/gs/crypto/rand/index.test.ts +32 -0
  133. package/gs/crypto/rand/index.ts +90 -0
  134. package/gs/crypto/rand/meta.json +5 -0
  135. package/gs/encoding/json/index.test.ts +65 -0
  136. package/gs/encoding/json/index.ts +186 -0
  137. package/gs/fmt/fmt.test.ts +41 -30
  138. package/gs/fmt/fmt.ts +2 -22
  139. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  140. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  141. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  142. package/gs/go/scanner/index.test.ts +50 -0
  143. package/gs/go/scanner/index.ts +157 -0
  144. package/gs/go/token/index.test.ts +21 -0
  145. package/gs/go/token/index.ts +120 -0
  146. package/gs/os/file_unix_js.test.ts +103 -0
  147. package/gs/os/meta.json +1 -2
  148. package/gs/os/tempfile.gs.test.ts +85 -0
  149. package/gs/os/tempfile.gs.ts +71 -11
  150. package/gs/os/types_js.gs.ts +74 -153
  151. package/gs/reflect/index.ts +1 -1
  152. package/gs/reflect/type.ts +106 -17
  153. package/gs/reflect/typefor.test.ts +75 -0
  154. package/gs/sync/sync.test.ts +24 -0
  155. package/gs/sync/sync.ts +12 -0
  156. package/package.json +13 -13
  157. package/compiler/analysis.go +0 -3475
  158. package/compiler/analysis_test.go +0 -338
  159. package/compiler/assignment.go +0 -580
  160. package/compiler/builtin_test.go +0 -92
  161. package/compiler/code-writer.go +0 -115
  162. package/compiler/compiler_test.go +0 -149
  163. package/compiler/composite-lit.go +0 -779
  164. package/compiler/config_test.go +0 -62
  165. package/compiler/constraint.go +0 -86
  166. package/compiler/decl.go +0 -801
  167. package/compiler/expr-call-async.go +0 -188
  168. package/compiler/expr-call-builtins.go +0 -208
  169. package/compiler/expr-call-helpers.go +0 -382
  170. package/compiler/expr-call-make.go +0 -318
  171. package/compiler/expr-call-type-conversion.go +0 -520
  172. package/compiler/expr-call.go +0 -413
  173. package/compiler/expr-selector.go +0 -343
  174. package/compiler/expr-star.go +0 -82
  175. package/compiler/expr-type.go +0 -442
  176. package/compiler/expr-value.go +0 -89
  177. package/compiler/expr.go +0 -773
  178. package/compiler/field.go +0 -183
  179. package/compiler/gs_dependencies_test.go +0 -298
  180. package/compiler/lit.go +0 -322
  181. package/compiler/output.go +0 -72
  182. package/compiler/primitive.go +0 -149
  183. package/compiler/protobuf.go +0 -697
  184. package/compiler/sanitize.go +0 -100
  185. package/compiler/spec-struct.go +0 -995
  186. package/compiler/spec-value.go +0 -540
  187. package/compiler/spec.go +0 -725
  188. package/compiler/stmt-assign.go +0 -664
  189. package/compiler/stmt-for.go +0 -266
  190. package/compiler/stmt-range.go +0 -475
  191. package/compiler/stmt-select.go +0 -262
  192. package/compiler/stmt-type-switch.go +0 -147
  193. package/compiler/stmt.go +0 -1308
  194. package/compiler/type-assert.go +0 -386
  195. package/compiler/type-info.go +0 -156
  196. package/compiler/type-utils.go +0 -207
  197. package/compiler/type.go +0 -892
@@ -0,0 +1,231 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "os"
6
+ "sort"
7
+
8
+ gs "github.com/aperturerobotics/goscript"
9
+ "golang.org/x/tools/go/packages"
10
+ )
11
+
12
+ // PackageGraph is the immutable package graph produced for a compile request.
13
+ type PackageGraph struct {
14
+ // RequestedPatterns are the package patterns from the compile request.
15
+ RequestedPatterns []string
16
+ // RequestedPackagePaths are the loaded package paths for requested patterns.
17
+ RequestedPackagePaths []string
18
+ // Nodes are the deterministic package graph nodes.
19
+ Nodes []*PackageGraphNode
20
+ // NodesByPackagePath maps package path to graph node.
21
+ NodesByPackagePath map[string]*PackageGraphNode
22
+
23
+ packagesByPath map[string]*packages.Package
24
+ }
25
+
26
+ // PackageGraphNode is one package in the loaded graph.
27
+ type PackageGraphNode struct {
28
+ // ID is the go/packages package identity.
29
+ ID string
30
+ // PkgPath is the stable Go package path.
31
+ PkgPath string
32
+ // Name is the Go package name.
33
+ Name string
34
+ // ModulePath is the owning module path when known.
35
+ ModulePath string
36
+ // ModuleDir is the owning module directory when known.
37
+ ModuleDir string
38
+ // GoFiles are the package source files.
39
+ GoFiles []string
40
+ // CompiledGoFiles are files selected by build constraints.
41
+ CompiledGoFiles []string
42
+ // Imports are imported package paths.
43
+ Imports []string
44
+ // Requested marks packages matched by request patterns.
45
+ Requested bool
46
+ // OverrideCandidate marks packages with a matching GoScript override package.
47
+ OverrideCandidate bool
48
+ }
49
+
50
+ // PackageGraphOwner owns Go package loading and graph identity.
51
+ type PackageGraphOwner struct{}
52
+
53
+ // NewPackageGraphOwner creates the package graph owner.
54
+ func NewPackageGraphOwner() *PackageGraphOwner {
55
+ return &PackageGraphOwner{}
56
+ }
57
+
58
+ // Load builds the package graph for a validated request.
59
+ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*PackageGraph, []Diagnostic) {
60
+ if err := ctx.Err(); err != nil {
61
+ return nil, []Diagnostic{{
62
+ Severity: DiagnosticSeverityError,
63
+ Code: "goscript/context:canceled",
64
+ Message: err.Error(),
65
+ }}
66
+ }
67
+
68
+ cfg := &packages.Config{
69
+ Context: ctx,
70
+ Dir: req.Dir,
71
+ Env: append(os.Environ(), "GOOS=js", "GOARCH=wasm"),
72
+ BuildFlags: append([]string(nil), req.BuildFlags...),
73
+ Tests: false,
74
+ Mode: packages.NeedName |
75
+ packages.NeedFiles |
76
+ packages.NeedCompiledGoFiles |
77
+ packages.NeedImports |
78
+ packages.NeedDeps |
79
+ packages.NeedExportFile |
80
+ packages.NeedTypes |
81
+ packages.NeedSyntax |
82
+ packages.NeedTypesInfo |
83
+ packages.NeedTypesSizes |
84
+ packages.NeedModule,
85
+ }
86
+ pkgs, err := packages.Load(cfg, req.Patterns...)
87
+ if err != nil {
88
+ return nil, []Diagnostic{{
89
+ Severity: DiagnosticSeverityError,
90
+ Code: "goscript/package-graph:load",
91
+ Message: "failed to load Go packages",
92
+ Detail: err.Error(),
93
+ }}
94
+ }
95
+ if len(pkgs) == 0 {
96
+ return nil, []Diagnostic{{
97
+ Severity: DiagnosticSeverityError,
98
+ Code: "goscript/package-graph:no-packages",
99
+ Message: "package patterns did not match any packages",
100
+ }}
101
+ }
102
+
103
+ graph := &PackageGraph{
104
+ RequestedPatterns: append([]string(nil), req.Patterns...),
105
+ NodesByPackagePath: make(map[string]*PackageGraphNode),
106
+ packagesByPath: make(map[string]*packages.Package),
107
+ RequestedPackagePaths: make([]string, 0, len(pkgs)),
108
+ }
109
+
110
+ requested := make(map[string]bool)
111
+ for _, pkg := range pkgs {
112
+ path := packagePath(pkg)
113
+ requested[path] = true
114
+ graph.RequestedPackagePaths = append(graph.RequestedPackagePaths, path)
115
+ }
116
+ sort.Strings(graph.RequestedPackagePaths)
117
+
118
+ var diagnostics []Diagnostic
119
+ seen := make(map[string]bool)
120
+ for _, pkg := range pkgs {
121
+ o.collect(graph, pkg, req.DependencyMode, requested, seen)
122
+ diagnostics = append(diagnostics, packageDiagnostics(pkg)...)
123
+ }
124
+ sort.Slice(graph.Nodes, func(i, j int) bool {
125
+ if graph.Nodes[i].PkgPath == graph.Nodes[j].PkgPath {
126
+ return graph.Nodes[i].ID < graph.Nodes[j].ID
127
+ }
128
+ return graph.Nodes[i].PkgPath < graph.Nodes[j].PkgPath
129
+ })
130
+ if len(graph.Nodes) == 0 {
131
+ diagnostics = append(diagnostics, Diagnostic{
132
+ Severity: DiagnosticSeverityError,
133
+ Code: "goscript/package-graph:no-nodes",
134
+ Message: "package graph did not contain any package nodes",
135
+ })
136
+ }
137
+ return graph, diagnostics
138
+ }
139
+
140
+ func (o *PackageGraphOwner) collect(
141
+ graph *PackageGraph,
142
+ pkg *packages.Package,
143
+ mode DependencyMode,
144
+ requested map[string]bool,
145
+ seen map[string]bool,
146
+ ) {
147
+ if pkg == nil || seen[pkg.ID] {
148
+ return
149
+ }
150
+ seen[pkg.ID] = true
151
+
152
+ path := packagePath(pkg)
153
+ node := newPackageGraphNode(pkg, requested[path])
154
+ graph.Nodes = append(graph.Nodes, node)
155
+ graph.NodesByPackagePath[path] = node
156
+ graph.packagesByPath[path] = pkg
157
+
158
+ if mode != DependencyModeAll || node.OverrideCandidate {
159
+ return
160
+ }
161
+ imports := make([]string, 0, len(pkg.Imports))
162
+ for importPath := range pkg.Imports {
163
+ imports = append(imports, importPath)
164
+ }
165
+ sort.Strings(imports)
166
+ for _, importPath := range imports {
167
+ o.collect(graph, pkg.Imports[importPath], mode, requested, seen)
168
+ }
169
+ }
170
+
171
+ func newPackageGraphNode(pkg *packages.Package, requested bool) *PackageGraphNode {
172
+ imports := make([]string, 0, len(pkg.Imports))
173
+ for importPath := range pkg.Imports {
174
+ imports = append(imports, importPath)
175
+ }
176
+ sort.Strings(imports)
177
+
178
+ var modulePath string
179
+ var moduleDir string
180
+ if pkg.Module != nil {
181
+ modulePath = pkg.Module.Path
182
+ moduleDir = pkg.Module.Dir
183
+ }
184
+
185
+ return &PackageGraphNode{
186
+ ID: pkg.ID,
187
+ PkgPath: packagePath(pkg),
188
+ Name: pkg.Name,
189
+ ModulePath: modulePath,
190
+ ModuleDir: moduleDir,
191
+ GoFiles: append([]string(nil), pkg.GoFiles...),
192
+ CompiledGoFiles: append([]string(nil), pkg.CompiledGoFiles...),
193
+ Imports: imports,
194
+ Requested: requested,
195
+ OverrideCandidate: hasOverrideCandidate(packagePath(pkg)),
196
+ }
197
+ }
198
+
199
+ func packagePath(pkg *packages.Package) string {
200
+ if pkg == nil {
201
+ return ""
202
+ }
203
+ if pkg.PkgPath != "" {
204
+ return pkg.PkgPath
205
+ }
206
+ return pkg.ID
207
+ }
208
+
209
+ func packageDiagnostics(pkg *packages.Package) []Diagnostic {
210
+ if pkg == nil || len(pkg.Errors) == 0 {
211
+ return nil
212
+ }
213
+ diagnostics := make([]Diagnostic, 0, len(pkg.Errors))
214
+ for _, pkgErr := range pkg.Errors {
215
+ diagnostics = append(diagnostics, Diagnostic{
216
+ Severity: DiagnosticSeverityError,
217
+ Code: "goscript/package-graph:load-error",
218
+ Message: "Go package contains load errors",
219
+ Detail: pkgErr.Msg,
220
+ })
221
+ }
222
+ return diagnostics
223
+ }
224
+
225
+ func hasOverrideCandidate(pkgPath string) bool {
226
+ if pkgPath == "" {
227
+ return false
228
+ }
229
+ _, err := gs.GsOverrides.ReadFile("gs/" + pkgPath + "/index.ts")
230
+ return err == nil
231
+ }
@@ -0,0 +1,281 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "os"
6
+ "path/filepath"
7
+ "slices"
8
+ "strings"
9
+ "testing"
10
+ )
11
+
12
+ func TestCompileRequestValidation(t *testing.T) {
13
+ moduleDir := writePackageGraphFixture(t, map[string]string{
14
+ "go.mod": "module example.test/request\n\ngo 1.25.3\n",
15
+ "main.go": "package main\nfunc main() {}\n",
16
+ })
17
+ owner := NewCompileRequestOwner()
18
+
19
+ tests := []struct {
20
+ name string
21
+ req *CompileRequest
22
+ code string
23
+ }{
24
+ {
25
+ name: "empty package",
26
+ req: &CompileRequest{
27
+ Patterns: []string{""},
28
+ Dir: moduleDir,
29
+ OutputPath: filepath.Join(t.TempDir(), "out"),
30
+ DependencyMode: DependencyModeRequested,
31
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
32
+ },
33
+ code: "goscript/request:empty-package",
34
+ },
35
+ {
36
+ name: "single file",
37
+ req: &CompileRequest{
38
+ Patterns: []string{"main.go"},
39
+ Dir: moduleDir,
40
+ OutputPath: filepath.Join(t.TempDir(), "out"),
41
+ DependencyMode: DependencyModeRequested,
42
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
43
+ },
44
+ code: "goscript/request:single-file-unsupported",
45
+ },
46
+ {
47
+ name: "no output",
48
+ req: &CompileRequest{
49
+ Patterns: []string{"."},
50
+ Dir: moduleDir,
51
+ DependencyMode: DependencyModeRequested,
52
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
53
+ },
54
+ code: "goscript/request:no-output",
55
+ },
56
+ {
57
+ name: "no module",
58
+ req: &CompileRequest{
59
+ Patterns: []string{"."},
60
+ Dir: t.TempDir(),
61
+ OutputPath: filepath.Join(t.TempDir(), "out"),
62
+ DependencyMode: DependencyModeRequested,
63
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
64
+ },
65
+ code: "goscript/request:no-module",
66
+ },
67
+ {
68
+ name: "empty build flag",
69
+ req: &CompileRequest{
70
+ Patterns: []string{"."},
71
+ Dir: moduleDir,
72
+ OutputPath: filepath.Join(t.TempDir(), "out"),
73
+ BuildFlags: []string{" "},
74
+ DependencyMode: DependencyModeRequested,
75
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
76
+ },
77
+ code: "goscript/request:empty-build-flag",
78
+ },
79
+ {
80
+ name: "dependency mode",
81
+ req: &CompileRequest{
82
+ Patterns: []string{"."},
83
+ Dir: moduleDir,
84
+ OutputPath: filepath.Join(t.TempDir(), "out"),
85
+ DependencyMode: DependencyMode("invalid"),
86
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
87
+ },
88
+ code: "goscript/request:dependency-mode",
89
+ },
90
+ {
91
+ name: "runtime emission mode",
92
+ req: &CompileRequest{
93
+ Patterns: []string{"."},
94
+ Dir: moduleDir,
95
+ OutputPath: filepath.Join(t.TempDir(), "out"),
96
+ DependencyMode: DependencyModeRequested,
97
+ RuntimeEmissionMode: RuntimeEmissionMode("invalid"),
98
+ },
99
+ code: "goscript/request:runtime-emission-mode",
100
+ },
101
+ }
102
+
103
+ for _, tt := range tests {
104
+ t.Run(tt.name, func(t *testing.T) {
105
+ diagnostics := owner.Validate(tt.req)
106
+ requireDiagnosticCode(t, diagnostics, tt.code)
107
+ })
108
+ }
109
+ }
110
+
111
+ func TestPackageGraphLoadsRequestedPackage(t *testing.T) {
112
+ moduleDir := writePackageGraphFixture(t, map[string]string{
113
+ "go.mod": "module example.test/graph\n\ngo 1.25.3\n",
114
+ "main.go": "package main\nfunc main() {}\n",
115
+ })
116
+ graph := loadPackageGraph(t, &CompileRequest{
117
+ Patterns: []string{"."},
118
+ Dir: moduleDir,
119
+ OutputPath: filepath.Join(t.TempDir(), "out"),
120
+ DependencyMode: DependencyModeRequested,
121
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
122
+ })
123
+
124
+ if len(graph.Nodes) != 1 {
125
+ t.Fatalf("expected one requested node, got %d", len(graph.Nodes))
126
+ }
127
+ node := graph.Nodes[0]
128
+ if node.PkgPath != "example.test/graph" || !node.Requested {
129
+ t.Fatalf("unexpected node: %#v", node)
130
+ }
131
+ if node.ModulePath != "example.test/graph" {
132
+ t.Fatalf("unexpected module path: %q", node.ModulePath)
133
+ }
134
+ }
135
+
136
+ func TestPackageGraphReportsLoadErrors(t *testing.T) {
137
+ moduleDir := writePackageGraphFixture(t, map[string]string{
138
+ "go.mod": "module example.test/loaderr\n\ngo 1.25.3\n",
139
+ "main.go": "package main\nimport \"missing.invalid/pkg\"\nfunc main() { _ = pkg.Value }\n",
140
+ })
141
+ _, diagnostics := NewPackageGraphOwner().Load(context.Background(), &CompileRequest{
142
+ Patterns: []string{"."},
143
+ Dir: moduleDir,
144
+ OutputPath: filepath.Join(t.TempDir(), "out"),
145
+ DependencyMode: DependencyModeRequested,
146
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
147
+ })
148
+ requireDiagnosticCode(t, diagnostics, "goscript/package-graph:load-error")
149
+ }
150
+
151
+ func TestPackageGraphHonorsBuildFlags(t *testing.T) {
152
+ moduleDir := writePackageGraphFixture(t, map[string]string{
153
+ "go.mod": "module example.test/tags\n\ngo 1.25.3\n",
154
+ "default.go": "package tags\nconst Selected = \"default\"\n",
155
+ "tagged.go": "//go:build customtag\n\npackage tags\nconst Tagged = true\n",
156
+ "excluded.go": "//go:build !customtag\n\npackage tags\nconst Excluded = true\n",
157
+ })
158
+ graph := loadPackageGraph(t, &CompileRequest{
159
+ Patterns: []string{"."},
160
+ Dir: moduleDir,
161
+ OutputPath: filepath.Join(t.TempDir(), "out"),
162
+ BuildFlags: []string{"-tags=customtag"},
163
+ DependencyMode: DependencyModeRequested,
164
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
165
+ })
166
+
167
+ var compiled []string
168
+ for _, file := range graph.Nodes[0].CompiledGoFiles {
169
+ compiled = append(compiled, filepath.Base(file))
170
+ }
171
+ if !slices.Contains(compiled, "tagged.go") {
172
+ t.Fatalf("expected tagged.go in compiled files: %v", compiled)
173
+ }
174
+ if slices.Contains(compiled, "excluded.go") {
175
+ t.Fatalf("did not expect excluded.go in compiled files: %v", compiled)
176
+ }
177
+ }
178
+
179
+ func TestPackageGraphLoadsLocalReplacement(t *testing.T) {
180
+ moduleDir := writePackageGraphFixture(t, map[string]string{
181
+ "go.mod": strings.Join([]string{
182
+ "module example.test/app",
183
+ "",
184
+ "go 1.25.3",
185
+ "",
186
+ "require example.test/lib v0.0.0",
187
+ "replace example.test/lib => ./lib",
188
+ "",
189
+ }, "\n"),
190
+ "main.go": "package main\nimport \"example.test/lib\"\nfunc main() { lib.Value() }\n",
191
+ "lib/go.mod": "module example.test/lib\n\ngo 1.25.3\n",
192
+ "lib/value.go": "package lib\nfunc Value() int { return 1 }\n",
193
+ "lib/unused.go": "package lib\n",
194
+ })
195
+ graph := loadPackageGraph(t, &CompileRequest{
196
+ Patterns: []string{"."},
197
+ Dir: moduleDir,
198
+ OutputPath: filepath.Join(t.TempDir(), "out"),
199
+ DependencyMode: DependencyModeAll,
200
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
201
+ })
202
+
203
+ if graph.NodesByPackagePath["example.test/lib"] == nil {
204
+ t.Fatalf("expected local replacement dependency in graph")
205
+ }
206
+ }
207
+
208
+ func TestPackageGraphDetectsOverrideCandidates(t *testing.T) {
209
+ moduleDir := writePackageGraphFixture(t, map[string]string{
210
+ "go.mod": "module example.test/override\n\ngo 1.25.3\n",
211
+ "main.go": "package main\nimport \"fmt\"\nfunc main() { fmt.Println(\"ok\") }\n",
212
+ })
213
+ graph := loadPackageGraph(t, &CompileRequest{
214
+ Patterns: []string{"."},
215
+ Dir: moduleDir,
216
+ OutputPath: filepath.Join(t.TempDir(), "out"),
217
+ DependencyMode: DependencyModeAll,
218
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
219
+ })
220
+
221
+ node := graph.NodesByPackagePath["fmt"]
222
+ if node == nil {
223
+ t.Fatalf("expected fmt node in graph")
224
+ }
225
+ if !node.OverrideCandidate {
226
+ t.Fatalf("expected fmt to be detected as an override candidate")
227
+ }
228
+ }
229
+
230
+ func TestPackageGraphOverrideCandidatesRequirePackageIndex(t *testing.T) {
231
+ parent := "github.com/aperturerobotics/wasivm/wazero/kernel"
232
+ child := parent + "/runtime"
233
+
234
+ if hasOverrideCandidate(parent) {
235
+ t.Fatalf("parent directory without an override index was detected as an override candidate")
236
+ }
237
+ if !hasOverrideCandidate(child) {
238
+ t.Fatalf("nested package with an override index was not detected as an override candidate")
239
+ }
240
+ }
241
+
242
+ func loadPackageGraph(t *testing.T, req *CompileRequest) *PackageGraph {
243
+ t.Helper()
244
+
245
+ diagnostics := NewCompileRequestOwner().Validate(req)
246
+ if diagnosticsHaveErrors(diagnostics) {
247
+ t.Fatalf("request validation failed: %#v", diagnostics)
248
+ }
249
+ graph, diagnostics := NewPackageGraphOwner().Load(context.Background(), req)
250
+ if diagnosticsHaveErrors(diagnostics) {
251
+ t.Fatalf("package graph load failed: %#v", diagnostics)
252
+ }
253
+ return graph
254
+ }
255
+
256
+ func writePackageGraphFixture(t *testing.T, files map[string]string) string {
257
+ t.Helper()
258
+
259
+ dir := t.TempDir()
260
+ for name, contents := range files {
261
+ path := filepath.Join(dir, name)
262
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
263
+ t.Fatal(err.Error())
264
+ }
265
+ if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
266
+ t.Fatal(err.Error())
267
+ }
268
+ }
269
+ return dir
270
+ }
271
+
272
+ func requireDiagnosticCode(t *testing.T, diagnostics []Diagnostic, code string) {
273
+ t.Helper()
274
+
275
+ for _, diagnostic := range diagnostics {
276
+ if diagnostic.Code == code {
277
+ return
278
+ }
279
+ }
280
+ t.Fatalf("missing diagnostic %q in %#v", code, diagnostics)
281
+ }
@@ -0,0 +1,13 @@
1
+ package compiler
2
+
3
+ // CompilationResult describes a compiler run after adapter normalization.
4
+ type CompilationResult struct {
5
+ // CompiledPackages contains package paths compiled to TypeScript.
6
+ CompiledPackages []string
7
+ // CopiedPackages contains package paths copied from override packages.
8
+ CopiedPackages []string
9
+ // OriginalPackages contains the package patterns or package paths requested.
10
+ OriginalPackages []string
11
+ // Diagnostics contains all diagnostics produced by the compile request.
12
+ Diagnostics []Diagnostic
13
+ }