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.
Files changed (188) 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 +23 -0
  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 +15 -1
  39. package/dist/gs/builtin/hostio.js +134 -49
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  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/slice.d.ts +1 -1
  45. package/dist/gs/builtin/slice.js.map +1 -1
  46. package/dist/gs/builtin/type.d.ts +11 -0
  47. package/dist/gs/builtin/type.js +55 -1
  48. package/dist/gs/builtin/type.js.map +1 -1
  49. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  50. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  51. package/dist/gs/bytes/reader.gs.js.map +1 -1
  52. package/dist/gs/context/context.js.map +1 -1
  53. package/dist/gs/crypto/rand/index.d.ts +5 -0
  54. package/dist/gs/crypto/rand/index.js +77 -0
  55. package/dist/gs/crypto/rand/index.js.map +1 -0
  56. package/dist/gs/encoding/json/index.d.ts +3 -0
  57. package/dist/gs/encoding/json/index.js +160 -0
  58. package/dist/gs/encoding/json/index.js.map +1 -0
  59. package/dist/gs/fmt/fmt.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  64. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  65. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  66. package/dist/gs/go/scanner/index.d.ts +29 -0
  67. package/dist/gs/go/scanner/index.js +120 -0
  68. package/dist/gs/go/scanner/index.js.map +1 -0
  69. package/dist/gs/go/token/index.d.ts +31 -0
  70. package/dist/gs/go/token/index.js +82 -0
  71. package/dist/gs/go/token/index.js.map +1 -0
  72. package/dist/gs/internal/abi/index.js.map +1 -1
  73. package/dist/gs/io/fs/fs.js.map +1 -1
  74. package/dist/gs/io/fs/readdir.js.map +1 -1
  75. package/dist/gs/io/fs/readfile.js.map +1 -1
  76. package/dist/gs/io/fs/stat.js.map +1 -1
  77. package/dist/gs/io/fs/sub.js.map +1 -1
  78. package/dist/gs/io/io.js.map +1 -1
  79. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  80. package/dist/gs/os/error.gs.js +2 -4
  81. package/dist/gs/os/error.gs.js.map +1 -1
  82. package/dist/gs/os/exec.gs.js.map +1 -1
  83. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  84. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  85. package/dist/gs/os/root_js.gs.js.map +1 -1
  86. package/dist/gs/os/tempfile.gs.js +66 -9
  87. package/dist/gs/os/tempfile.gs.js.map +1 -1
  88. package/dist/gs/os/types.gs.js.map +1 -1
  89. package/dist/gs/os/types_js.gs.js +9 -9
  90. package/dist/gs/os/types_js.gs.js.map +1 -1
  91. package/dist/gs/os/types_unix.gs.js.map +1 -1
  92. package/dist/gs/path/filepath/match.js.map +1 -1
  93. package/dist/gs/path/match.js.map +1 -1
  94. package/dist/gs/path/path.js.map +1 -1
  95. package/dist/gs/reflect/index.d.ts +2 -2
  96. package/dist/gs/reflect/index.js +1 -1
  97. package/dist/gs/reflect/index.js.map +1 -1
  98. package/dist/gs/reflect/map.js.map +1 -1
  99. package/dist/gs/reflect/type.d.ts +2 -1
  100. package/dist/gs/reflect/type.js +85 -14
  101. package/dist/gs/reflect/type.js.map +1 -1
  102. package/dist/gs/reflect/types.js.map +1 -1
  103. package/dist/gs/reflect/visiblefields.js.map +1 -1
  104. package/dist/gs/runtime/runtime.js.map +1 -1
  105. package/dist/gs/sort/sort.gs.js.map +1 -1
  106. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  107. package/dist/gs/strconv/quote.gs.js.map +1 -1
  108. package/dist/gs/strings/builder.js.map +1 -1
  109. package/dist/gs/strings/reader.js.map +1 -1
  110. package/dist/gs/strings/replace.js.map +1 -1
  111. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  112. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  113. package/dist/gs/sync/sync.d.ts +1 -0
  114. package/dist/gs/sync/sync.js +12 -0
  115. package/dist/gs/sync/sync.js.map +1 -1
  116. package/dist/gs/time/time.js.map +1 -1
  117. package/dist/gs/unicode/unicode.js.map +1 -1
  118. package/go.mod +2 -2
  119. package/gs/builtin/builtin.ts +27 -0
  120. package/gs/builtin/hostio.test.ts +177 -0
  121. package/gs/builtin/hostio.ts +171 -56
  122. package/gs/builtin/index.ts +1 -0
  123. package/gs/builtin/runtime-contract.test.ts +230 -0
  124. package/gs/builtin/type.ts +84 -1
  125. package/gs/crypto/rand/index.test.ts +32 -0
  126. package/gs/crypto/rand/index.ts +90 -0
  127. package/gs/crypto/rand/meta.json +5 -0
  128. package/gs/encoding/json/index.test.ts +65 -0
  129. package/gs/encoding/json/index.ts +186 -0
  130. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  131. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  132. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  133. package/gs/go/scanner/index.test.ts +50 -0
  134. package/gs/go/scanner/index.ts +157 -0
  135. package/gs/go/token/index.test.ts +21 -0
  136. package/gs/go/token/index.ts +120 -0
  137. package/gs/os/file_unix_js.test.ts +50 -0
  138. package/gs/os/meta.json +1 -2
  139. package/gs/os/tempfile.gs.test.ts +85 -0
  140. package/gs/os/tempfile.gs.ts +71 -11
  141. package/gs/os/types_js.gs.ts +9 -9
  142. package/gs/reflect/index.ts +1 -1
  143. package/gs/reflect/type.ts +106 -17
  144. package/gs/reflect/typefor.test.ts +75 -0
  145. package/gs/sync/sync.test.ts +24 -0
  146. package/gs/sync/sync.ts +12 -0
  147. package/package.json +13 -13
  148. package/compiler/analysis.go +0 -3475
  149. package/compiler/analysis_test.go +0 -338
  150. package/compiler/assignment.go +0 -580
  151. package/compiler/builtin_test.go +0 -92
  152. package/compiler/code-writer.go +0 -115
  153. package/compiler/compiler_test.go +0 -149
  154. package/compiler/composite-lit.go +0 -779
  155. package/compiler/config_test.go +0 -62
  156. package/compiler/constraint.go +0 -86
  157. package/compiler/decl.go +0 -801
  158. package/compiler/expr-call-async.go +0 -188
  159. package/compiler/expr-call-builtins.go +0 -208
  160. package/compiler/expr-call-helpers.go +0 -382
  161. package/compiler/expr-call-make.go +0 -318
  162. package/compiler/expr-call-type-conversion.go +0 -520
  163. package/compiler/expr-call.go +0 -413
  164. package/compiler/expr-selector.go +0 -343
  165. package/compiler/expr-star.go +0 -82
  166. package/compiler/expr-type.go +0 -442
  167. package/compiler/expr-value.go +0 -89
  168. package/compiler/expr.go +0 -773
  169. package/compiler/field.go +0 -183
  170. package/compiler/gs_dependencies_test.go +0 -298
  171. package/compiler/lit.go +0 -322
  172. package/compiler/output.go +0 -72
  173. package/compiler/primitive.go +0 -149
  174. package/compiler/protobuf.go +0 -697
  175. package/compiler/sanitize.go +0 -100
  176. package/compiler/spec-struct.go +0 -995
  177. package/compiler/spec-value.go +0 -540
  178. package/compiler/spec.go +0 -725
  179. package/compiler/stmt-assign.go +0 -664
  180. package/compiler/stmt-for.go +0 -266
  181. package/compiler/stmt-range.go +0 -475
  182. package/compiler/stmt-select.go +0 -262
  183. package/compiler/stmt-type-switch.go +0 -147
  184. package/compiler/stmt.go +0 -1308
  185. package/compiler/type-assert.go +0 -386
  186. package/compiler/type-info.go +0 -156
  187. package/compiler/type-utils.go +0 -207
  188. 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
+ }