goscript 0.1.3 → 0.2.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 (330) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +35 -8
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/cmd/goscript/cmd_compile_test.go +105 -6
  8. package/compiler/build-flags.go +9 -10
  9. package/compiler/compile-request.go +12 -9
  10. package/compiler/compliance_test.go +0 -1
  11. package/compiler/config.go +2 -0
  12. package/compiler/gotest/request.go +28 -0
  13. package/compiler/gotest/runner.go +353 -27
  14. package/compiler/gotest/runner_test.go +400 -1
  15. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  16. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  17. package/compiler/lowered-program.go +24 -17
  18. package/compiler/lowering.go +988 -263
  19. package/compiler/lowering_bench_test.go +364 -0
  20. package/compiler/override-facts.go +15 -0
  21. package/compiler/override-parity-verifier.go +450 -0
  22. package/compiler/override-parity.go +122 -0
  23. package/compiler/override-registry_test.go +559 -0
  24. package/compiler/package-graph.go +61 -4
  25. package/compiler/package-graph_test.go +30 -0
  26. package/compiler/protobuf-ts-binding.go +514 -0
  27. package/compiler/protobuf-ts-binding_test.go +172 -0
  28. package/compiler/semantic-model-types.go +17 -4
  29. package/compiler/semantic-model.go +709 -72
  30. package/compiler/semantic-model_test.go +219 -0
  31. package/compiler/service.go +20 -1
  32. package/compiler/skeleton_test.go +1008 -20
  33. package/compiler/typescript-emitter.go +147 -15
  34. package/dist/gs/builtin/builtin.d.ts +2 -2
  35. package/dist/gs/builtin/builtin.js +20 -0
  36. package/dist/gs/builtin/builtin.js.map +1 -1
  37. package/dist/gs/builtin/slice.d.ts +2 -1
  38. package/dist/gs/builtin/slice.js +34 -4
  39. package/dist/gs/builtin/slice.js.map +1 -1
  40. package/dist/gs/builtin/type.d.ts +14 -6
  41. package/dist/gs/builtin/type.js +224 -64
  42. package/dist/gs/builtin/type.js.map +1 -1
  43. package/dist/gs/builtin/varRef.d.ts +11 -0
  44. package/dist/gs/builtin/varRef.js +57 -2
  45. package/dist/gs/builtin/varRef.js.map +1 -1
  46. package/dist/gs/bytes/buffer.gs.js +1 -1
  47. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  48. package/dist/gs/bytes/reader.gs.js +1 -1
  49. package/dist/gs/bytes/reader.gs.js.map +1 -1
  50. package/dist/gs/compress/zlib/index.d.ts +13 -6
  51. package/dist/gs/compress/zlib/index.js +131 -35
  52. package/dist/gs/compress/zlib/index.js.map +1 -1
  53. package/dist/gs/crypto/sha1/index.js +2 -5
  54. package/dist/gs/crypto/sha1/index.js.map +1 -1
  55. package/dist/gs/crypto/sha256/index.js +2 -5
  56. package/dist/gs/crypto/sha256/index.js.map +1 -1
  57. package/dist/gs/crypto/sha512/index.js +2 -5
  58. package/dist/gs/crypto/sha512/index.js.map +1 -1
  59. package/dist/gs/embed/index.d.ts +6 -0
  60. package/dist/gs/embed/index.js +210 -5
  61. package/dist/gs/embed/index.js.map +1 -1
  62. package/dist/gs/encoding/json/index.d.ts +114 -0
  63. package/dist/gs/encoding/json/index.js +544 -36
  64. package/dist/gs/encoding/json/index.js.map +1 -1
  65. package/dist/gs/fmt/fmt.d.ts +3 -3
  66. package/dist/gs/fmt/fmt.js +29 -16
  67. package/dist/gs/fmt/fmt.js.map +1 -1
  68. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  69. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  70. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  71. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
  72. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
  73. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
  74. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  75. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  76. package/dist/gs/go/scanner/index.d.ts +2 -0
  77. package/dist/gs/go/scanner/index.js +29 -5
  78. package/dist/gs/go/scanner/index.js.map +1 -1
  79. package/dist/gs/go/token/index.js +22 -6
  80. package/dist/gs/go/token/index.js.map +1 -1
  81. package/dist/gs/hash/index.d.ts +6 -0
  82. package/dist/gs/hash/index.js +20 -0
  83. package/dist/gs/hash/index.js.map +1 -1
  84. package/dist/gs/internal/goarch/index.d.ts +43 -3
  85. package/dist/gs/internal/goarch/index.js +42 -10
  86. package/dist/gs/internal/goarch/index.js.map +1 -1
  87. package/dist/gs/io/fs/fs.js +26 -14
  88. package/dist/gs/io/fs/fs.js.map +1 -1
  89. package/dist/gs/io/fs/readdir.js +8 -4
  90. package/dist/gs/io/fs/readdir.js.map +1 -1
  91. package/dist/gs/io/fs/sub.js +8 -1
  92. package/dist/gs/io/fs/sub.js.map +1 -1
  93. package/dist/gs/io/io.d.ts +12 -6
  94. package/dist/gs/io/io.js +87 -42
  95. package/dist/gs/io/io.js.map +1 -1
  96. package/dist/gs/math/bits/index.d.ts +31 -5
  97. package/dist/gs/math/bits/index.js +29 -28
  98. package/dist/gs/math/bits/index.js.map +1 -1
  99. package/dist/gs/mime/index.d.ts +16 -0
  100. package/dist/gs/mime/index.js +315 -6
  101. package/dist/gs/mime/index.js.map +1 -1
  102. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  103. package/dist/gs/net/http/httptest/index.js +85 -6
  104. package/dist/gs/net/http/httptest/index.js.map +1 -1
  105. package/dist/gs/net/http/index.d.ts +303 -6
  106. package/dist/gs/net/http/index.js +1615 -58
  107. package/dist/gs/net/http/index.js.map +1 -1
  108. package/dist/gs/os/dir_unix.gs.js +1 -1
  109. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  110. package/dist/gs/os/error.gs.js +1 -1
  111. package/dist/gs/os/error.gs.js.map +1 -1
  112. package/dist/gs/os/exec.gs.d.ts +1 -0
  113. package/dist/gs/os/exec.gs.js +4 -8
  114. package/dist/gs/os/exec.gs.js.map +1 -1
  115. package/dist/gs/os/exec_posix.gs.js +1 -1
  116. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  117. package/dist/gs/os/index.d.ts +1 -1
  118. package/dist/gs/os/index.js +1 -1
  119. package/dist/gs/os/index.js.map +1 -1
  120. package/dist/gs/os/proc.gs.d.ts +4 -0
  121. package/dist/gs/os/proc.gs.js +12 -6
  122. package/dist/gs/os/proc.gs.js.map +1 -1
  123. package/dist/gs/os/root_js.gs.js +1 -1
  124. package/dist/gs/os/root_js.gs.js.map +1 -1
  125. package/dist/gs/os/types.gs.js +1 -1
  126. package/dist/gs/os/types.gs.js.map +1 -1
  127. package/dist/gs/os/types_js.gs.d.ts +6 -2
  128. package/dist/gs/os/types_js.gs.js +170 -9
  129. package/dist/gs/os/types_js.gs.js.map +1 -1
  130. package/dist/gs/os/types_unix.gs.js +1 -1
  131. package/dist/gs/os/types_unix.gs.js.map +1 -1
  132. package/dist/gs/path/path.js +11 -7
  133. package/dist/gs/path/path.js.map +1 -1
  134. package/dist/gs/reflect/index.d.ts +5 -4
  135. package/dist/gs/reflect/index.js +4 -3
  136. package/dist/gs/reflect/index.js.map +1 -1
  137. package/dist/gs/reflect/map.js +15 -0
  138. package/dist/gs/reflect/map.js.map +1 -1
  139. package/dist/gs/reflect/type.d.ts +26 -6
  140. package/dist/gs/reflect/type.js +1498 -279
  141. package/dist/gs/reflect/type.js.map +1 -1
  142. package/dist/gs/reflect/types.d.ts +14 -6
  143. package/dist/gs/reflect/types.js +35 -1
  144. package/dist/gs/reflect/types.js.map +1 -1
  145. package/dist/gs/reflect/value.d.ts +1 -0
  146. package/dist/gs/reflect/value.js +83 -41
  147. package/dist/gs/reflect/value.js.map +1 -1
  148. package/dist/gs/reflect/visiblefields.js +4 -140
  149. package/dist/gs/reflect/visiblefields.js.map +1 -1
  150. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  151. package/dist/gs/runtime/pprof/index.js +50 -30
  152. package/dist/gs/runtime/pprof/index.js.map +1 -1
  153. package/dist/gs/runtime/runtime.js +5 -4
  154. package/dist/gs/runtime/runtime.js.map +1 -1
  155. package/dist/gs/runtime/trace/index.js +5 -19
  156. package/dist/gs/runtime/trace/index.js.map +1 -1
  157. package/dist/gs/strconv/atoi.gs.js +1 -1
  158. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  159. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  160. package/dist/gs/strconv/complex.gs.js +148 -0
  161. package/dist/gs/strconv/complex.gs.js.map +1 -0
  162. package/dist/gs/strconv/index.d.ts +1 -0
  163. package/dist/gs/strconv/index.js +1 -0
  164. package/dist/gs/strconv/index.js.map +1 -1
  165. package/dist/gs/strings/builder.js +1 -1
  166. package/dist/gs/strings/reader.d.ts +1 -1
  167. package/dist/gs/strings/reader.js +11 -7
  168. package/dist/gs/strings/reader.js.map +1 -1
  169. package/dist/gs/strings/replace.js +15 -7
  170. package/dist/gs/strings/replace.js.map +1 -1
  171. package/dist/gs/strings/strings.d.ts +5 -0
  172. package/dist/gs/strings/strings.js +57 -5
  173. package/dist/gs/strings/strings.js.map +1 -1
  174. package/dist/gs/sync/atomic/type.gs.js +9 -9
  175. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  176. package/dist/gs/sync/atomic/value.gs.js +2 -2
  177. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  178. package/dist/gs/sync/sync.d.ts +2 -1
  179. package/dist/gs/sync/sync.js +37 -16
  180. package/dist/gs/sync/sync.js.map +1 -1
  181. package/dist/gs/syscall/env.js +22 -14
  182. package/dist/gs/syscall/env.js.map +1 -1
  183. package/dist/gs/syscall/js/index.js +9 -0
  184. package/dist/gs/syscall/js/index.js.map +1 -1
  185. package/dist/gs/testing/testing.js +59 -15
  186. package/dist/gs/testing/testing.js.map +1 -1
  187. package/dist/gs/time/time.d.ts +24 -1
  188. package/dist/gs/time/time.js +43 -3
  189. package/dist/gs/time/time.js.map +1 -1
  190. package/dist/gs/unique/index.js +7 -1
  191. package/dist/gs/unique/index.js.map +1 -1
  192. package/go.mod +3 -3
  193. package/go.sum +16 -0
  194. package/gs/builtin/builtin.ts +25 -2
  195. package/gs/builtin/runtime-contract.test.ts +260 -18
  196. package/gs/builtin/slice.ts +51 -4
  197. package/gs/builtin/type.ts +310 -63
  198. package/gs/builtin/varRef.ts +85 -2
  199. package/gs/bytes/buffer.gs.ts +1 -1
  200. package/gs/bytes/reader.gs.ts +1 -1
  201. package/gs/compress/zlib/index.test.ts +159 -1
  202. package/gs/compress/zlib/index.ts +164 -37
  203. package/gs/compress/zlib/meta.json +4 -1
  204. package/gs/compress/zlib/parity.json +51 -0
  205. package/gs/crypto/sha1/index.test.ts +19 -2
  206. package/gs/crypto/sha1/index.ts +3 -6
  207. package/gs/crypto/sha256/index.test.ts +14 -2
  208. package/gs/crypto/sha256/index.ts +3 -6
  209. package/gs/crypto/sha512/index.test.ts +17 -2
  210. package/gs/crypto/sha512/index.ts +3 -6
  211. package/gs/embed/index.test.ts +87 -0
  212. package/gs/embed/index.ts +229 -5
  213. package/gs/encoding/json/index.test.ts +360 -6
  214. package/gs/encoding/json/index.ts +679 -38
  215. package/gs/encoding/json/parity.json +81 -0
  216. package/gs/fmt/fmt.test.ts +41 -3
  217. package/gs/fmt/fmt.ts +40 -17
  218. package/gs/fmt/meta.json +6 -1
  219. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  220. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  221. package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
  222. package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
  223. package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
  224. package/gs/github.com/pkg/errors/errors.ts +54 -30
  225. package/gs/go/scanner/index.test.ts +39 -56
  226. package/gs/go/scanner/index.ts +33 -5
  227. package/gs/go/scanner/parity.json +27 -0
  228. package/gs/go/token/index.ts +22 -6
  229. package/gs/hash/index.test.ts +20 -33
  230. package/gs/hash/index.ts +28 -0
  231. package/gs/hash/parity.json +21 -0
  232. package/gs/internal/goarch/index.test.ts +32 -0
  233. package/gs/internal/goarch/index.ts +45 -13
  234. package/gs/internal/goarch/parity.json +144 -0
  235. package/gs/io/fs/fs.ts +26 -14
  236. package/gs/io/fs/readdir.test.ts +38 -0
  237. package/gs/io/fs/readdir.ts +8 -4
  238. package/gs/io/fs/sub.ts +8 -1
  239. package/gs/io/io.test.ts +77 -6
  240. package/gs/io/io.ts +115 -52
  241. package/gs/io/meta.json +7 -1
  242. package/gs/io/parity.json +162 -0
  243. package/gs/math/bits/index.test.ts +14 -1
  244. package/gs/math/bits/index.ts +75 -32
  245. package/gs/math/bits/parity.json +156 -0
  246. package/gs/mime/index.test.ts +90 -0
  247. package/gs/mime/index.ts +369 -6
  248. package/gs/mime/parity.json +36 -0
  249. package/gs/net/http/httptest/index.test.ts +98 -2
  250. package/gs/net/http/httptest/index.ts +101 -6
  251. package/gs/net/http/httptest/parity.json +15 -0
  252. package/gs/net/http/index.test.ts +797 -12
  253. package/gs/net/http/index.ts +1874 -136
  254. package/gs/net/http/meta.json +16 -1
  255. package/gs/net/http/parity.json +193 -0
  256. package/gs/os/dir_unix.gs.ts +1 -1
  257. package/gs/os/error.gs.ts +1 -1
  258. package/gs/os/exec.gs.ts +4 -8
  259. package/gs/os/exec_posix.gs.ts +1 -1
  260. package/gs/os/file_unix_js.test.ts +52 -0
  261. package/gs/os/index.test.ts +9 -0
  262. package/gs/os/index.ts +1 -0
  263. package/gs/os/meta.json +4 -0
  264. package/gs/os/parity.json +9 -0
  265. package/gs/os/proc.gs.ts +18 -5
  266. package/gs/os/proc.test.ts +26 -0
  267. package/gs/os/readdir.test.ts +56 -0
  268. package/gs/os/root_js.gs.ts +1 -1
  269. package/gs/os/types.gs.ts +1 -1
  270. package/gs/os/types_js.gs.ts +170 -9
  271. package/gs/os/types_unix.gs.ts +1 -1
  272. package/gs/path/path.ts +11 -7
  273. package/gs/reflect/deepequal.test.ts +10 -1
  274. package/gs/reflect/field.test.ts +37 -15
  275. package/gs/reflect/function-types.test.ts +518 -22
  276. package/gs/reflect/index.ts +8 -6
  277. package/gs/reflect/map.ts +20 -0
  278. package/gs/reflect/meta.json +6 -4
  279. package/gs/reflect/parity.json +234 -0
  280. package/gs/reflect/sliceat.test.ts +156 -0
  281. package/gs/reflect/structof.test.ts +401 -0
  282. package/gs/reflect/type.ts +1980 -365
  283. package/gs/reflect/typefor.test.ts +540 -10
  284. package/gs/reflect/types.ts +43 -18
  285. package/gs/reflect/value.ts +105 -45
  286. package/gs/reflect/visiblefields.ts +5 -168
  287. package/gs/runtime/parity.json +24 -0
  288. package/gs/runtime/pprof/index.test.ts +29 -7
  289. package/gs/runtime/pprof/index.ts +56 -30
  290. package/gs/runtime/pprof/parity.json +27 -0
  291. package/gs/runtime/runtime.test.ts +3 -1
  292. package/gs/runtime/runtime.ts +4 -3
  293. package/gs/runtime/trace/index.test.ts +5 -3
  294. package/gs/runtime/trace/index.ts +8 -20
  295. package/gs/runtime/trace/parity.json +36 -0
  296. package/gs/strconv/atoi.gs.ts +1 -1
  297. package/gs/strconv/complex.gs.ts +174 -0
  298. package/gs/strconv/complex.test.ts +65 -0
  299. package/gs/strconv/index.ts +1 -0
  300. package/gs/strconv/parity.json +120 -0
  301. package/gs/strings/builder.ts +1 -1
  302. package/gs/strings/meta.json +5 -2
  303. package/gs/strings/parity.json +186 -0
  304. package/gs/strings/reader.test.ts +2 -2
  305. package/gs/strings/reader.ts +11 -7
  306. package/gs/strings/replace.ts +15 -7
  307. package/gs/strings/strings.test.ts +22 -2
  308. package/gs/strings/strings.ts +64 -6
  309. package/gs/sync/atomic/type.gs.ts +9 -9
  310. package/gs/sync/atomic/value.gs.ts +2 -2
  311. package/gs/sync/meta.json +1 -0
  312. package/gs/sync/sync.test.ts +41 -1
  313. package/gs/sync/sync.ts +41 -16
  314. package/gs/syscall/env.ts +29 -14
  315. package/gs/syscall/js/index.test.ts +18 -0
  316. package/gs/syscall/js/index.ts +12 -0
  317. package/gs/testing/testing.test.ts +99 -3
  318. package/gs/testing/testing.ts +95 -24
  319. package/gs/time/parity.json +225 -0
  320. package/gs/time/time.test.ts +20 -2
  321. package/gs/time/time.ts +49 -7
  322. package/gs/unique/index.ts +7 -1
  323. package/package.json +4 -2
  324. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  325. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -814
  326. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  327. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -31
  328. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1233
  329. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  330. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
@@ -5,6 +5,7 @@ import (
5
5
  "os"
6
6
  "path/filepath"
7
7
  "slices"
8
+ "strconv"
8
9
  "strings"
9
10
  "testing"
10
11
  )
@@ -80,6 +81,17 @@ func TestOverrideRegistryFactsAreImmutable(t *testing.T) {
80
81
  if len(pkg.files) == 0 || pkg.files[0].data[0] == '!' {
81
82
  t.Fatalf("copy file mutation leaked back into facts")
82
83
  }
84
+
85
+ ledger := facts.parityLedger("net/http")
86
+ if got := ledger.Symbols["ServeFile"].Status; got != overrideParityStatusReal {
87
+ t.Fatalf("expected net/http ServeFile real parity, got %q", got)
88
+ }
89
+ ledger.Symbols["ServeFile"] = overrideParityEntry{Status: overrideParityStatusDeferred}
90
+
91
+ ledger = facts.parityLedger("net/http")
92
+ if got := ledger.Symbols["ServeFile"].Status; got != overrideParityStatusReal {
93
+ t.Fatalf("parity ledger mutation leaked back into facts: %q", got)
94
+ }
83
95
  }
84
96
 
85
97
  func TestOverrideRegistryCopiesRuntimeAndOverrides(t *testing.T) {
@@ -384,6 +396,51 @@ func TestCompilePackagesAwaitsOverrideAsyncInterfaceMethodCalls(t *testing.T) {
384
396
  }
385
397
  }
386
398
 
399
+ func TestCompilePackagesAwaitsNetHTTPServeHTTPOverrideMethods(t *testing.T) {
400
+ moduleDir := writePackageGraphFixture(t, map[string]string{
401
+ "go.mod": "module example.test/httpserveasync\n\ngo 1.25.3\n",
402
+ "main.go": strings.Join([]string{
403
+ "package main",
404
+ "import \"net/http\"",
405
+ "func Use(h http.Handler, hf http.HandlerFunc, mux *http.ServeMux, w http.ResponseWriter, r *http.Request) {",
406
+ " h.ServeHTTP(w, r)",
407
+ " hf.ServeHTTP(w, r)",
408
+ " mux.ServeHTTP(w, r)",
409
+ "}",
410
+ "func main() {}",
411
+ "",
412
+ }, "\n"),
413
+ })
414
+ out := filepath.Join(t.TempDir(), "out")
415
+ comp, err := NewCompiler(&Config{
416
+ Dir: moduleDir,
417
+ OutputPath: out,
418
+ AllDependencies: true,
419
+ }, nil, nil)
420
+ if err != nil {
421
+ t.Fatal(err.Error())
422
+ }
423
+
424
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
425
+ t.Fatal(err.Error())
426
+ }
427
+ content, err := os.ReadFile(filepath.Join(out, "@goscript", "example.test", "httpserveasync", "main.gs.ts"))
428
+ if err != nil {
429
+ t.Fatal(err.Error())
430
+ }
431
+ text := string(content)
432
+ for _, want := range []string{
433
+ "export async function Use(",
434
+ "await $.pointerValue<Exclude<http.Handler, null>>(h).ServeHTTP($.pointerValueOrNil(w)!, r)",
435
+ "await http.HandlerFunc_ServeHTTP(hf, $.pointerValueOrNil(w)!, r)",
436
+ "await http.ServeMux.prototype.ServeHTTP.call($.pointerValue<http.ServeMux>(mux), $.pointerValueOrNil(w)!, r)",
437
+ } {
438
+ if !strings.Contains(text, want) {
439
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
440
+ }
441
+ }
442
+ }
443
+
387
444
  func TestCompilePackagesAwaitsOverrideAsyncFunctions(t *testing.T) {
388
445
  moduleDir := writePackageGraphFixture(t, map[string]string{
389
446
  "go.mod": "module example.test/overrideasyncfunc\n\ngo 1.25.3\n",
@@ -430,3 +487,505 @@ func TestCompilePackagesAwaitsOverrideAsyncFunctions(t *testing.T) {
430
487
  t.Fatalf("walk callback was not lowered as async:\n%s", string(content))
431
488
  }
432
489
  }
490
+
491
+ func TestCompilePackagesAwaitsReflectValueCall(t *testing.T) {
492
+ moduleDir := writePackageGraphFixture(t, map[string]string{
493
+ "go.mod": "module example.test/reflectcallasync\n\ngo 1.25.3\n",
494
+ "main.go": strings.Join([]string{
495
+ "package main",
496
+ "import \"reflect\"",
497
+ "func Use(fn func()) []reflect.Value {",
498
+ " return reflect.ValueOf(fn).Call(nil)",
499
+ "}",
500
+ "func UseSlice(fn func(...int), args []reflect.Value) []reflect.Value {",
501
+ " return reflect.ValueOf(fn).CallSlice(args)",
502
+ "}",
503
+ "",
504
+ }, "\n"),
505
+ })
506
+ out := filepath.Join(t.TempDir(), "out")
507
+ comp, err := NewCompiler(&Config{
508
+ Dir: moduleDir,
509
+ OutputPath: out,
510
+ AllDependencies: true,
511
+ }, nil, nil)
512
+ if err != nil {
513
+ t.Fatal(err.Error())
514
+ }
515
+
516
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
517
+ t.Fatal(err.Error())
518
+ }
519
+ content, err := os.ReadFile(filepath.Join(out, "@goscript", "example.test", "reflectcallasync", "main.gs.ts"))
520
+ if err != nil {
521
+ t.Fatal(err.Error())
522
+ }
523
+ text := string(content)
524
+ if !strings.Contains(text, "export async function Use") {
525
+ t.Fatalf("reflect.Value.Call caller was not marked async:\n%s", text)
526
+ }
527
+ if !strings.Contains(text, "return await ") || !strings.Contains(text, ".Call(null)") {
528
+ t.Fatalf("reflect.Value.Call was not awaited:\n%s", text)
529
+ }
530
+ if !strings.Contains(text, "export async function UseSlice") {
531
+ t.Fatalf("reflect.Value.CallSlice caller was not marked async:\n%s", text)
532
+ }
533
+ if !strings.Contains(text, "return await ") || !strings.Contains(text, ".CallSlice(args)") {
534
+ t.Fatalf("reflect.Value.CallSlice was not awaited:\n%s", text)
535
+ }
536
+ }
537
+
538
+ func TestOverrideParityVerifierResolvesEffectiveTypeScriptExports(t *testing.T) {
539
+ files := map[string]string{
540
+ "example.test/lib/index.ts": strings.Join([]string{
541
+ "export { RenamedSource as Renamed } from './named.js'",
542
+ "export type { TypeShape } from './types.js'",
543
+ "export * from './star.js'",
544
+ "",
545
+ }, "\n"),
546
+ "example.test/lib/named.ts": "export function RenamedSource(): void {}\n",
547
+ "example.test/lib/star.ts": "export class Star {}\n",
548
+ "example.test/lib/types.ts": "export interface TypeShape {}\n",
549
+ }
550
+ exports := make(map[string]typeScriptExport)
551
+ if err := collectTypeScriptExports("example.test/lib/index.ts", files, exports, make(map[string]bool)); err != nil {
552
+ t.Fatal(err.Error())
553
+ }
554
+ for _, name := range []string{"Renamed", "TypeShape", "Star"} {
555
+ if !exports[name].present() {
556
+ t.Fatalf("effective TypeScript exports missing %s: %v", name, exports)
557
+ }
558
+ }
559
+ if exports["RenamedSource"].present() {
560
+ t.Fatalf("named re-export source leaked into effective exports: %v", exports)
561
+ }
562
+ if !exports["Renamed"].value || exports["Renamed"].typ {
563
+ t.Fatalf("renamed function export should be value-only: %#v", exports["Renamed"])
564
+ }
565
+ if !exports["TypeShape"].typ || exports["TypeShape"].value {
566
+ t.Fatalf("type re-export should be type-only: %#v", exports["TypeShape"])
567
+ }
568
+ if !exports["Star"].value || !exports["Star"].typ {
569
+ t.Fatalf("class star export should carry value and type shapes: %#v", exports["Star"])
570
+ }
571
+ }
572
+
573
+ func TestOverrideParityVerifierValidatesNamedReExportSources(t *testing.T) {
574
+ files := map[string]string{
575
+ "example.test/lib/index.ts": "export { Missing } from './named.js'\n",
576
+ "example.test/lib/named.ts": "export function Present(): void {}\n",
577
+ }
578
+ err := collectTypeScriptExports("example.test/lib/index.ts", files, make(map[string]typeScriptExport), make(map[string]bool))
579
+ if err == nil {
580
+ t.Fatalf("expected missing named re-export source to fail")
581
+ }
582
+ if !strings.Contains(err.Error(), "re-exports missing symbol Missing") {
583
+ t.Fatalf("unexpected error: %v", err)
584
+ }
585
+ }
586
+
587
+ func TestOverrideParityVerifierValidatesLocalNamedExportSources(t *testing.T) {
588
+ files := map[string]string{
589
+ "example.test/lib/index.ts": "export { Missing }\n",
590
+ }
591
+ err := collectTypeScriptExports("example.test/lib/index.ts", files, make(map[string]typeScriptExport), make(map[string]bool))
592
+ if err == nil {
593
+ t.Fatalf("expected missing local named export source to fail")
594
+ }
595
+ if !strings.Contains(err.Error(), "exports missing local value symbol Missing") {
596
+ t.Fatalf("unexpected error: %v", err)
597
+ }
598
+ }
599
+
600
+ func TestOverrideParityVerifierReportsStrictUnclassifiedSymbol(t *testing.T) {
601
+ result, err := compileParityFixture(t, map[string]overrideParityEntry{
602
+ "Present": {Status: overrideParityStatusReal},
603
+ }, "export function Present(): void {}\n")
604
+ if err == nil {
605
+ t.Fatalf("expected compile to fail")
606
+ }
607
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-unclassified")
608
+ }
609
+
610
+ func TestOverrideParityVerifierReportsMissingTypeScriptExport(t *testing.T) {
611
+ result, err := compileParityFixture(t, map[string]overrideParityEntry{
612
+ "Present": {Status: overrideParityStatusReal},
613
+ "Missing": {Status: overrideParityStatusReal},
614
+ }, "export function Present(): void {}\n")
615
+ if err == nil {
616
+ t.Fatalf("expected compile to fail")
617
+ }
618
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-missing-export")
619
+ }
620
+
621
+ func TestOverrideParityVerifierRejectsTypeOnlyValueExport(t *testing.T) {
622
+ result, err := compileParityFixture(t, map[string]overrideParityEntry{
623
+ "Present": {Status: overrideParityStatusReal},
624
+ "Missing": {Status: overrideParityStatusReal},
625
+ }, strings.Join([]string{
626
+ "export function Present(): void {}",
627
+ "export type Missing = unknown",
628
+ "",
629
+ }, "\n"))
630
+ if err == nil {
631
+ t.Fatalf("expected compile to fail")
632
+ }
633
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-missing-export")
634
+ }
635
+
636
+ func TestOverrideParityVerifierRejectsTypeOnlyStructExport(t *testing.T) {
637
+ moduleDir := writePackageGraphFixture(t, map[string]string{
638
+ "go.mod": "module example.test/structparity\n\ngo 1.25.3\n",
639
+ "main.go": strings.Join([]string{
640
+ "package main",
641
+ "import \"example.test/structparity/lib\"",
642
+ "func main() { _ = lib.StructExport{} }",
643
+ "",
644
+ }, "\n"),
645
+ "lib/lib.go": strings.Join([]string{
646
+ "package lib",
647
+ "type StructExport struct{}",
648
+ "",
649
+ }, "\n"),
650
+ })
651
+ overrideDir := filepath.Join(t.TempDir(), "gs")
652
+ writeFixtureFile(t, overrideDir, "example.test/structparity/lib/index.ts", "export type StructExport = {}\n")
653
+ writeFixtureFile(t, overrideDir, "example.test/structparity/lib/parity.json", parityFixtureJSON(t, map[string]overrideParityEntry{
654
+ "StructExport": {Status: overrideParityStatusReal},
655
+ }))
656
+
657
+ comp, err := NewCompiler(&Config{
658
+ Dir: moduleDir,
659
+ OutputPath: filepath.Join(t.TempDir(), "out"),
660
+ OverrideDirs: []string{overrideDir},
661
+ AllDependencies: true,
662
+ }, nil, nil)
663
+ if err != nil {
664
+ t.Fatal(err.Error())
665
+ }
666
+ result, err := comp.CompilePackages(context.Background(), ".")
667
+ if err == nil {
668
+ t.Fatalf("expected compile to fail")
669
+ }
670
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-missing-export")
671
+ }
672
+
673
+ func TestOverrideParityVerifierReportsBlockedTypeScriptExport(t *testing.T) {
674
+ result, err := compileParityFixture(t, map[string]overrideParityEntry{
675
+ "Present": {Status: overrideParityStatusReal},
676
+ "Missing": {Status: overrideParityStatusBlocked, Reason: "blocked by fixture"},
677
+ }, strings.Join([]string{
678
+ "export function Present(): void {}",
679
+ "export function Missing(): void {}",
680
+ "",
681
+ }, "\n"))
682
+ if err == nil {
683
+ t.Fatalf("expected compile to fail")
684
+ }
685
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-unexpected-export")
686
+ }
687
+
688
+ func TestOverrideParityVerifierReportsBlockedGoUse(t *testing.T) {
689
+ moduleDir := writePackageGraphFixture(t, map[string]string{
690
+ "go.mod": "module example.test/blockeduse\n\ngo 1.25.3\n",
691
+ "main.go": strings.Join([]string{
692
+ "package main",
693
+ "import \"example.test/blockeduse/lib\"",
694
+ "func main() { lib.Missing() }",
695
+ "",
696
+ }, "\n"),
697
+ "lib/lib.go": strings.Join([]string{
698
+ "package lib",
699
+ "func Present() {}",
700
+ "func Missing() {}",
701
+ "",
702
+ }, "\n"),
703
+ })
704
+ overrideDir := filepath.Join(t.TempDir(), "gs")
705
+ writeFixtureFile(t, overrideDir, "example.test/blockeduse/lib/index.ts", "export function Present(): void {}\n")
706
+ writeFixtureFile(t, overrideDir, "example.test/blockeduse/lib/parity.json", parityFixtureJSON(t, map[string]overrideParityEntry{
707
+ "Present": {Status: overrideParityStatusReal},
708
+ "Missing": {Status: overrideParityStatusBlocked, Reason: "blocked by fixture"},
709
+ }))
710
+
711
+ comp, err := NewCompiler(&Config{
712
+ Dir: moduleDir,
713
+ OutputPath: filepath.Join(t.TempDir(), "out"),
714
+ OverrideDirs: []string{overrideDir},
715
+ AllDependencies: true,
716
+ }, nil, nil)
717
+ if err != nil {
718
+ t.Fatal(err.Error())
719
+ }
720
+ result, err := comp.CompilePackages(context.Background(), ".")
721
+ if err == nil {
722
+ t.Fatalf("expected compile to fail")
723
+ }
724
+ requireDiagnosticCode(t, result.Diagnostics, "goscript/overrides:parity-blocked-use")
725
+ }
726
+
727
+ func TestOverrideParityVerifierAllowsDeferredMissingExports(t *testing.T) {
728
+ result, err := compileParityFixture(t, map[string]overrideParityEntry{
729
+ "Present": {Status: overrideParityStatusReal},
730
+ "Missing": {Status: overrideParityStatusDeferred},
731
+ }, "export function Present(): void {}\n")
732
+ if err != nil {
733
+ t.Fatalf("compile failed: %v\n%#v", err, result.Diagnostics)
734
+ }
735
+ }
736
+
737
+ func TestOverrideParityVerifierReportsDeferredAtPhaseClose(t *testing.T) {
738
+ owner := NewOverrideRegistryOwner()
739
+ facts, diagnostics := owner.Facts(context.Background())
740
+ if diagnosticsHaveErrors(diagnostics) {
741
+ t.Fatalf("override facts failed: %#v", diagnostics)
742
+ }
743
+
744
+ diagnostics = NewOverrideParityVerifier().VerifyNoDeferred(facts,
745
+ "net/http",
746
+ "net/http/httptest",
747
+ "encoding/json",
748
+ "mime",
749
+ "time",
750
+ "reflect",
751
+ "math/bits",
752
+ "strings",
753
+ "strconv",
754
+ "compress/zlib",
755
+ "io",
756
+ "go/scanner",
757
+ "hash",
758
+ "internal/goarch",
759
+ "os",
760
+ "runtime",
761
+ "runtime/pprof",
762
+ "runtime/trace",
763
+ )
764
+ if diagnosticsHaveErrors(diagnostics) {
765
+ t.Fatalf("override ledgers still contain deferred entries: %#v", diagnostics)
766
+ }
767
+ }
768
+
769
+ func TestOverrideParityVerifierAcceptsPhase4Ledgers(t *testing.T) {
770
+ moduleDir := writePackageGraphFixture(t, map[string]string{
771
+ "go.mod": "module example.test/phase4parity\n\ngo 1.25.3\n",
772
+ "main.go": strings.Join([]string{
773
+ "package main",
774
+ "import (",
775
+ " \"compress/zlib\"",
776
+ " \"encoding/json\"",
777
+ " \"go/scanner\"",
778
+ " \"hash\"",
779
+ " \"io\"",
780
+ " \"math/bits\"",
781
+ " \"mime\"",
782
+ " \"os\"",
783
+ " \"reflect\"",
784
+ " \"strconv\"",
785
+ " \"strings\"",
786
+ " \"time\"",
787
+ ")",
788
+ "var _ = json.Valid",
789
+ "var _ = mime.TypeByExtension",
790
+ "var _ = time.RFC1123",
791
+ "var _ = reflect.VisibleFields",
792
+ "var _ = bits.Rem32",
793
+ "var _ = strings.ToValidUTF8",
794
+ "var _ = strconv.FormatComplex",
795
+ "var _ = zlib.NoCompression",
796
+ "var _ io.ReadSeekCloser",
797
+ "var _ = scanner.PrintError",
798
+ "var _ hash.XOF",
799
+ "var _ = os.ErrNoHandle",
800
+ "func main() {}",
801
+ "",
802
+ }, "\n"),
803
+ })
804
+ comp, err := NewCompiler(&Config{
805
+ Dir: moduleDir,
806
+ OutputPath: filepath.Join(t.TempDir(), "out"),
807
+ AllDependencies: true,
808
+ }, nil, nil)
809
+ if err != nil {
810
+ t.Fatal(err.Error())
811
+ }
812
+ result, err := comp.CompilePackages(context.Background(), ".")
813
+ if err != nil {
814
+ t.Fatalf("compile failed: %v\n%#v", err, result.Diagnostics)
815
+ }
816
+ }
817
+
818
+ func TestOverrideParityVerifierAllowsRealFuncOfUse(t *testing.T) {
819
+ moduleDir := writePackageGraphFixture(t, map[string]string{
820
+ "go.mod": "module example.test/funcparity\n\ngo 1.25.3\n",
821
+ "main.go": strings.Join([]string{
822
+ "package main",
823
+ "import \"reflect\"",
824
+ "func main() {",
825
+ " _ = reflect.FuncOf(nil, nil, false)",
826
+ "}",
827
+ "",
828
+ }, "\n"),
829
+ })
830
+ comp, err := NewCompiler(&Config{
831
+ Dir: moduleDir,
832
+ OutputPath: filepath.Join(t.TempDir(), "out"),
833
+ AllDependencies: true,
834
+ }, nil, nil)
835
+ if err != nil {
836
+ t.Fatal(err.Error())
837
+ }
838
+ result, err := comp.CompilePackages(context.Background(), ".")
839
+ if err != nil {
840
+ t.Fatalf("expected reflect.FuncOf use to compile: %v\n%#v", err, result.Diagnostics)
841
+ }
842
+ }
843
+
844
+ func TestOverrideParityVerifierAllowsRealMakeFuncUse(t *testing.T) {
845
+ moduleDir := writePackageGraphFixture(t, map[string]string{
846
+ "go.mod": "module example.test/makefuncparity\n\ngo 1.25.3\n",
847
+ "main.go": strings.Join([]string{
848
+ "package main",
849
+ "import \"reflect\"",
850
+ "func main() {",
851
+ " typ := reflect.FuncOf(nil, nil, false)",
852
+ " _ = reflect.MakeFunc(typ, func(args []reflect.Value) []reflect.Value { return nil })",
853
+ "}",
854
+ "",
855
+ }, "\n"),
856
+ })
857
+ comp, err := NewCompiler(&Config{
858
+ Dir: moduleDir,
859
+ OutputPath: filepath.Join(t.TempDir(), "out"),
860
+ AllDependencies: true,
861
+ }, nil, nil)
862
+ if err != nil {
863
+ t.Fatal(err.Error())
864
+ }
865
+ result, err := comp.CompilePackages(context.Background(), ".")
866
+ if err != nil {
867
+ t.Fatalf("expected reflect.MakeFunc use to compile: %v\n%#v", err, result.Diagnostics)
868
+ }
869
+ }
870
+
871
+ func TestOverrideParityVerifierAllowsRealStructOfUse(t *testing.T) {
872
+ moduleDir := writePackageGraphFixture(t, map[string]string{
873
+ "go.mod": "module example.test/structofparity\n\ngo 1.25.3\n",
874
+ "main.go": strings.Join([]string{
875
+ "package main",
876
+ "import \"reflect\"",
877
+ "func main() {",
878
+ " _ = reflect.StructOf([]reflect.StructField{{Name: \"Count\", Type: reflect.TypeFor[int]()}})",
879
+ "}",
880
+ "",
881
+ }, "\n"),
882
+ })
883
+ comp, err := NewCompiler(&Config{
884
+ Dir: moduleDir,
885
+ OutputPath: filepath.Join(t.TempDir(), "out"),
886
+ AllDependencies: true,
887
+ }, nil, nil)
888
+ if err != nil {
889
+ t.Fatal(err.Error())
890
+ }
891
+ result, err := comp.CompilePackages(context.Background(), ".")
892
+ if err != nil {
893
+ t.Fatalf("expected reflect.StructOf use to compile: %v\n%#v", err, result.Diagnostics)
894
+ }
895
+ }
896
+
897
+ func TestOverrideParityVerifierAllowsRealSliceAtUse(t *testing.T) {
898
+ moduleDir := writePackageGraphFixture(t, map[string]string{
899
+ "go.mod": "module example.test/sliceatparity\n\ngo 1.25.3\n",
900
+ "main.go": strings.Join([]string{
901
+ "package main",
902
+ "import (",
903
+ " \"reflect\"",
904
+ " \"unsafe\"",
905
+ ")",
906
+ "func main() {",
907
+ " buf := []byte{1, 2, 3}",
908
+ " _ = reflect.SliceAt(reflect.TypeFor[byte](), unsafe.Pointer(&buf[0]), len(buf))",
909
+ "}",
910
+ "",
911
+ }, "\n"),
912
+ })
913
+ comp, err := NewCompiler(&Config{
914
+ Dir: moduleDir,
915
+ OutputPath: filepath.Join(t.TempDir(), "out"),
916
+ AllDependencies: true,
917
+ }, nil, nil)
918
+ if err != nil {
919
+ t.Fatal(err.Error())
920
+ }
921
+ result, err := comp.CompilePackages(context.Background(), ".")
922
+ if err != nil {
923
+ t.Fatalf("expected reflect.SliceAt use to compile: %v\n%#v", err, result.Diagnostics)
924
+ }
925
+ }
926
+
927
+ func compileParityFixture(
928
+ t *testing.T,
929
+ symbols map[string]overrideParityEntry,
930
+ index string,
931
+ ) (*CompilationResult, error) {
932
+ t.Helper()
933
+
934
+ moduleDir := writePackageGraphFixture(t, map[string]string{
935
+ "go.mod": "module example.test/parity\n\ngo 1.25.3\n",
936
+ "main.go": strings.Join([]string{
937
+ "package main",
938
+ "import \"example.test/parity/lib\"",
939
+ "func main() { lib.Present() }",
940
+ "",
941
+ }, "\n"),
942
+ "lib/lib.go": strings.Join([]string{
943
+ "package lib",
944
+ "func Present() {}",
945
+ "func Missing() {}",
946
+ "",
947
+ }, "\n"),
948
+ })
949
+ overrideDir := filepath.Join(t.TempDir(), "gs")
950
+ writeFixtureFile(t, overrideDir, "example.test/parity/lib/index.ts", index)
951
+ writeFixtureFile(t, overrideDir, "example.test/parity/lib/parity.json", parityFixtureJSON(t, symbols))
952
+
953
+ comp, err := NewCompiler(&Config{
954
+ Dir: moduleDir,
955
+ OutputPath: filepath.Join(t.TempDir(), "out"),
956
+ OverrideDirs: []string{overrideDir},
957
+ AllDependencies: true,
958
+ }, nil, nil)
959
+ if err != nil {
960
+ t.Fatal(err.Error())
961
+ }
962
+ return comp.CompilePackages(context.Background(), ".")
963
+ }
964
+
965
+ func parityFixtureJSON(t *testing.T, symbols map[string]overrideParityEntry) string {
966
+ t.Helper()
967
+
968
+ var b strings.Builder
969
+ b.WriteString("{\"schemaVersion\":1,\"strict\":true,\"symbols\":{")
970
+ names := make([]string, 0, len(symbols))
971
+ for name := range symbols {
972
+ names = append(names, name)
973
+ }
974
+ slices.Sort(names)
975
+ for idx, name := range names {
976
+ if idx != 0 {
977
+ b.WriteByte(',')
978
+ }
979
+ entry := symbols[name]
980
+ b.WriteString(strconv.Quote(name))
981
+ b.WriteString(":{\"status\":")
982
+ b.WriteString(strconv.Quote(string(entry.Status)))
983
+ if entry.Reason != "" {
984
+ b.WriteString(",\"reason\":")
985
+ b.WriteString(strconv.Quote(entry.Reason))
986
+ }
987
+ b.WriteString("}")
988
+ }
989
+ b.WriteString("}}\n")
990
+ return b.String()
991
+ }
@@ -2,6 +2,7 @@ package compiler
2
2
 
3
3
  import (
4
4
  "context"
5
+ "go/ast"
5
6
  "os"
6
7
  "slices"
7
8
  "strings"
@@ -130,6 +131,15 @@ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*Pac
130
131
  graph.RequestedPackagePaths = append(graph.RequestedPackagePaths, path)
131
132
  }
132
133
  slices.Sort(graph.RequestedPackagePaths)
134
+ samePackageTestVariants := make(map[string]bool)
135
+ if req.Tests {
136
+ for _, pkg := range pkgs {
137
+ if pkg == nil || pkg.ForTest == "" || strings.HasSuffix(pkg.Name, "_test") {
138
+ continue
139
+ }
140
+ samePackageTestVariants[pkg.ForTest] = true
141
+ }
142
+ }
133
143
 
134
144
  var diagnostics []Diagnostic
135
145
  seen := make(map[string]bool)
@@ -137,7 +147,7 @@ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*Pac
137
147
  if isTestMainPackage(pkg) {
138
148
  continue
139
149
  }
140
- o.collect(graph, pkg, req.DependencyMode, requested, overrideFacts, seen)
150
+ o.collect(graph, pkg, req.DependencyMode, requested, samePackageTestVariants, overrideFacts, seen)
141
151
  diagnostics = append(diagnostics, packageDiagnostics(pkg)...)
142
152
  }
143
153
  slices.SortFunc(graph.Nodes, func(a, b *PackageGraphNode) int {
@@ -161,21 +171,33 @@ func (o *PackageGraphOwner) collect(
161
171
  pkg *packages.Package,
162
172
  mode DependencyMode,
163
173
  requested map[string]bool,
174
+ samePackageTestVariants map[string]bool,
164
175
  overrideFacts *OverrideFacts,
165
176
  seen map[string]bool,
166
177
  ) {
167
178
  if pkg == nil || seen[pkg.ID] {
168
179
  return
169
180
  }
181
+ path := packagePath(pkg)
182
+ if pkg.ForTest != "" && path != pkg.ForTest && !strings.HasSuffix(pkg.Name, "_test") && samePackageTestVariants[path] {
183
+ return
184
+ }
185
+ if pkg.ForTest == "" && samePackageTestVariants[path] {
186
+ return
187
+ }
170
188
  if pkg.ForTest != "" && !requested[pkg.ForTest] {
171
189
  if prod := pkg.Imports[pkg.ForTest]; prod != nil {
172
- o.collect(graph, prod, mode, requested, overrideFacts, seen)
190
+ o.collect(graph, prod, mode, requested, samePackageTestVariants, overrideFacts, seen)
173
191
  }
174
192
  return
175
193
  }
194
+ if graph.NodesByPackagePath[path] != nil {
195
+ return
196
+ }
176
197
  seen[pkg.ID] = true
177
198
 
178
- path := packagePath(pkg)
199
+ normalizePackageFileOrder(pkg)
200
+
179
201
  node := newPackageGraphNode(pkg, requested[path], overrideFacts)
180
202
  graph.Nodes = append(graph.Nodes, node)
181
203
  graph.NodesByPackagePath[path] = node
@@ -190,7 +212,7 @@ func (o *PackageGraphOwner) collect(
190
212
  }
191
213
  slices.Sort(imports)
192
214
  for _, importPath := range imports {
193
- o.collect(graph, pkg.Imports[importPath], mode, requested, overrideFacts, seen)
215
+ o.collect(graph, pkg.Imports[importPath], mode, requested, samePackageTestVariants, overrideFacts, seen)
194
216
  }
195
217
  }
196
218
 
@@ -223,6 +245,41 @@ func newPackageGraphNode(pkg *packages.Package, requested bool, overrideFacts *O
223
245
  }
224
246
  }
225
247
 
248
+ func normalizePackageFileOrder(pkg *packages.Package) {
249
+ if pkg == nil {
250
+ return
251
+ }
252
+ slices.Sort(pkg.GoFiles)
253
+ if len(pkg.Syntax) == len(pkg.CompiledGoFiles) {
254
+ type sourceFile struct {
255
+ name string
256
+ file *ast.File
257
+ }
258
+ files := make([]sourceFile, len(pkg.Syntax))
259
+ for idx, file := range pkg.Syntax {
260
+ files[idx] = sourceFile{name: pkg.CompiledGoFiles[idx], file: file}
261
+ }
262
+ slices.SortFunc(files, func(a, b sourceFile) int {
263
+ return strings.Compare(a.name, b.name)
264
+ })
265
+ for idx, file := range files {
266
+ pkg.CompiledGoFiles[idx] = file.name
267
+ pkg.Syntax[idx] = file.file
268
+ }
269
+ return
270
+ }
271
+ slices.Sort(pkg.CompiledGoFiles)
272
+ slices.SortFunc(pkg.Syntax, func(a, b *ast.File) int {
273
+ if pkg.Fset == nil {
274
+ return strings.Compare(a.Name.Name, b.Name.Name)
275
+ }
276
+ return strings.Compare(
277
+ pkg.Fset.Position(a.Package).Filename,
278
+ pkg.Fset.Position(b.Package).Filename,
279
+ )
280
+ })
281
+ }
282
+
226
283
  func isTestMainPackage(pkg *packages.Package) bool {
227
284
  return pkg != nil && pkg.ForTest == "" && pkg.Name == "main" && strings.HasSuffix(packagePath(pkg), ".test")
228
285
  }