goscript 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/cmd/goscript/cmd-test.go +104 -11
  2. package/cmd/goscript/cmd-test_test.go +1 -1
  3. package/cmd/goscript/cmd_compile.go +9 -0
  4. package/compiler/compile-request.go +31 -0
  5. package/compiler/compiler.go +1 -1
  6. package/compiler/compliance_test.go +0 -2
  7. package/compiler/config.go +2 -0
  8. package/compiler/gotest/package-result.go +2 -0
  9. package/compiler/gotest/request.go +85 -20
  10. package/compiler/gotest/runner.go +733 -96
  11. package/compiler/gotest/runner_test.go +647 -3
  12. package/compiler/lowered-program.go +9 -2
  13. package/compiler/lowering.go +2001 -345
  14. package/compiler/override-facts.go +77 -27
  15. package/compiler/override-registry.go +5 -4
  16. package/compiler/override-registry_test.go +135 -0
  17. package/compiler/package-graph_test.go +62 -7
  18. package/compiler/package-test-graph-variant.go +40 -16
  19. package/compiler/package-test-graph.go +0 -5
  20. package/compiler/package-test-graph_test.go +61 -3
  21. package/compiler/runtime-contract.go +40 -0
  22. package/compiler/semantic-model-types.go +16 -0
  23. package/compiler/semantic-model.go +336 -91
  24. package/compiler/semantic-model_test.go +50 -1
  25. package/compiler/service.go +9 -3
  26. package/compiler/skeleton_test.go +1921 -298
  27. package/compiler/tsworkspace/owner-process-unix_test.go +72 -0
  28. package/compiler/tsworkspace/owner.go +8 -0
  29. package/compiler/tsworkspace/tool-process-other.go +14 -0
  30. package/compiler/tsworkspace/tool-process-unix.go +19 -0
  31. package/compiler/typescript-emitter.go +122 -9
  32. package/dist/gs/builtin/builtin.d.ts +20 -1
  33. package/dist/gs/builtin/builtin.js +246 -26
  34. package/dist/gs/builtin/builtin.js.map +1 -1
  35. package/dist/gs/builtin/channel.d.ts +24 -10
  36. package/dist/gs/builtin/channel.js +107 -25
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/defer.d.ts +1 -0
  39. package/dist/gs/builtin/defer.js +12 -2
  40. package/dist/gs/builtin/defer.js.map +1 -1
  41. package/dist/gs/builtin/hostio.d.ts +9 -0
  42. package/dist/gs/builtin/hostio.js +25 -0
  43. package/dist/gs/builtin/hostio.js.map +1 -1
  44. package/dist/gs/builtin/map.js +40 -6
  45. package/dist/gs/builtin/map.js.map +1 -1
  46. package/dist/gs/builtin/print.js.map +1 -1
  47. package/dist/gs/builtin/slice.d.ts +43 -9
  48. package/dist/gs/builtin/slice.js +437 -234
  49. package/dist/gs/builtin/slice.js.map +1 -1
  50. package/dist/gs/builtin/type.d.ts +2 -0
  51. package/dist/gs/builtin/type.js +47 -7
  52. package/dist/gs/builtin/type.js.map +1 -1
  53. package/dist/gs/builtin/varRef.d.ts +2 -0
  54. package/dist/gs/builtin/varRef.js.map +1 -1
  55. package/dist/gs/bytes/buffer.gs.js +28 -28
  56. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  57. package/dist/gs/bytes/iter.gs.js +13 -13
  58. package/dist/gs/bytes/iter.gs.js.map +1 -1
  59. package/dist/gs/compress/zlib/index.d.ts +26 -0
  60. package/dist/gs/compress/zlib/index.js +168 -0
  61. package/dist/gs/compress/zlib/index.js.map +1 -0
  62. package/dist/gs/context/context.d.ts +1 -1
  63. package/dist/gs/context/context.js +8 -3
  64. package/dist/gs/context/context.js.map +1 -1
  65. package/dist/gs/crypto/ecdh/index.d.ts +52 -0
  66. package/dist/gs/crypto/ecdh/index.js +226 -0
  67. package/dist/gs/crypto/ecdh/index.js.map +1 -0
  68. package/dist/gs/crypto/ed25519/index.d.ts +34 -0
  69. package/dist/gs/crypto/ed25519/index.js +160 -0
  70. package/dist/gs/crypto/ed25519/index.js.map +1 -0
  71. package/dist/gs/crypto/internal/constanttime/index.d.ts +4 -0
  72. package/dist/gs/crypto/internal/constanttime/index.js +18 -0
  73. package/dist/gs/crypto/internal/constanttime/index.js.map +1 -0
  74. package/dist/gs/crypto/rand/index.d.ts +2 -0
  75. package/dist/gs/crypto/rand/index.js +85 -0
  76. package/dist/gs/crypto/rand/index.js.map +1 -1
  77. package/dist/gs/crypto/sha256/index.d.ts +8 -0
  78. package/dist/gs/crypto/sha256/index.js +118 -0
  79. package/dist/gs/crypto/sha256/index.js.map +1 -0
  80. package/dist/gs/crypto/sha512/index.d.ts +14 -0
  81. package/dist/gs/crypto/sha512/index.js +129 -0
  82. package/dist/gs/crypto/sha512/index.js.map +1 -0
  83. package/dist/gs/encoding/json/index.d.ts +3 -0
  84. package/dist/gs/encoding/json/index.js +15 -0
  85. package/dist/gs/encoding/json/index.js.map +1 -1
  86. package/dist/gs/errors/errors.js +29 -6
  87. package/dist/gs/errors/errors.js.map +1 -1
  88. package/dist/gs/fmt/fmt.js.map +1 -1
  89. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +7 -7
  90. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +52 -18
  91. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  92. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +56 -20
  93. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
  94. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +57 -3
  95. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +366 -1
  96. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -1
  97. package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +20 -0
  98. package/dist/gs/github.com/aperturerobotics/util/conc/index.js +134 -0
  99. package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +1 -0
  100. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  101. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.d.ts +3 -0
  102. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js +50 -0
  103. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js.map +1 -0
  104. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +3 -2
  105. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -1
  106. package/dist/gs/github.com/mr-tron/base58/base58/index.d.ts +27 -0
  107. package/dist/gs/github.com/mr-tron/base58/base58/index.js +172 -0
  108. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -0
  109. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.d.ts +21 -0
  110. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js +22 -0
  111. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js.map +1 -0
  112. package/dist/gs/go/token/index.js +11 -4
  113. package/dist/gs/go/token/index.js.map +1 -1
  114. package/dist/gs/hash/fnv/index.d.ts +57 -0
  115. package/dist/gs/hash/fnv/index.js +299 -0
  116. package/dist/gs/hash/fnv/index.js.map +1 -0
  117. package/dist/gs/hash/index.d.ts +17 -0
  118. package/dist/gs/hash/index.js +94 -0
  119. package/dist/gs/hash/index.js.map +1 -0
  120. package/dist/gs/io/fs/readlink.js +2 -6
  121. package/dist/gs/io/fs/readlink.js.map +1 -1
  122. package/dist/gs/io/fs/walk.js.map +1 -1
  123. package/dist/gs/io/io.js.map +1 -1
  124. package/dist/gs/iter/iter.d.ts +3 -2
  125. package/dist/gs/iter/iter.js.map +1 -1
  126. package/dist/gs/maps/iter.d.ts +5 -5
  127. package/dist/gs/maps/iter.js +48 -21
  128. package/dist/gs/maps/iter.js.map +1 -1
  129. package/dist/gs/maps/maps.d.ts +6 -6
  130. package/dist/gs/math/bits/index.js +14 -24
  131. package/dist/gs/math/bits/index.js.map +1 -1
  132. package/dist/gs/mime/index.js +3 -1
  133. package/dist/gs/mime/index.js.map +1 -1
  134. package/dist/gs/net/http/httptest/index.d.ts +20 -1
  135. package/dist/gs/net/http/httptest/index.js +83 -3
  136. package/dist/gs/net/http/httptest/index.js.map +1 -1
  137. package/dist/gs/net/http/index.d.ts +110 -6
  138. package/dist/gs/net/http/index.js +262 -16
  139. package/dist/gs/net/http/index.js.map +1 -1
  140. package/dist/gs/net/http/pprof/index.d.ts +8 -0
  141. package/dist/gs/net/http/pprof/index.js +59 -0
  142. package/dist/gs/net/http/pprof/index.js.map +1 -0
  143. package/dist/gs/os/error.gs.js +9 -7
  144. package/dist/gs/os/error.gs.js.map +1 -1
  145. package/dist/gs/os/types_js.gs.js +95 -15
  146. package/dist/gs/os/types_js.gs.js.map +1 -1
  147. package/dist/gs/path/filepath/match.js.map +1 -1
  148. package/dist/gs/path/filepath/path.d.ts +5 -3
  149. package/dist/gs/path/filepath/path.js +65 -10
  150. package/dist/gs/path/filepath/path.js.map +1 -1
  151. package/dist/gs/reflect/index.d.ts +3 -2
  152. package/dist/gs/reflect/index.js +2 -1
  153. package/dist/gs/reflect/index.js.map +1 -1
  154. package/dist/gs/reflect/iter.js +2 -2
  155. package/dist/gs/reflect/iter.js.map +1 -1
  156. package/dist/gs/reflect/map.js +26 -0
  157. package/dist/gs/reflect/map.js.map +1 -1
  158. package/dist/gs/reflect/type.d.ts +24 -5
  159. package/dist/gs/reflect/type.js +390 -38
  160. package/dist/gs/reflect/type.js.map +1 -1
  161. package/dist/gs/reflect/types.d.ts +1 -0
  162. package/dist/gs/reflect/types.js +3 -1
  163. package/dist/gs/reflect/types.js.map +1 -1
  164. package/dist/gs/reflect/value.d.ts +4 -1
  165. package/dist/gs/reflect/value.js +39 -1
  166. package/dist/gs/reflect/value.js.map +1 -1
  167. package/dist/gs/reflect/visiblefields.js +1 -1
  168. package/dist/gs/reflect/visiblefields.js.map +1 -1
  169. package/dist/gs/runtime/debug/index.d.ts +39 -0
  170. package/dist/gs/runtime/debug/index.js +58 -0
  171. package/dist/gs/runtime/debug/index.js.map +1 -1
  172. package/dist/gs/runtime/pprof/index.d.ts +20 -0
  173. package/dist/gs/runtime/pprof/index.js +85 -0
  174. package/dist/gs/runtime/pprof/index.js.map +1 -0
  175. package/dist/gs/runtime/trace/index.d.ts +19 -0
  176. package/dist/gs/runtime/trace/index.js +64 -0
  177. package/dist/gs/runtime/trace/index.js.map +1 -0
  178. package/dist/gs/slices/slices.d.ts +24 -9
  179. package/dist/gs/slices/slices.js +229 -24
  180. package/dist/gs/slices/slices.js.map +1 -1
  181. package/dist/gs/sort/slice.gs.d.ts +5 -3
  182. package/dist/gs/sort/slice.gs.js +55 -17
  183. package/dist/gs/sort/slice.gs.js.map +1 -1
  184. package/dist/gs/strings/builder.js +26 -17
  185. package/dist/gs/strings/builder.js.map +1 -1
  186. package/dist/gs/strings/iter.js +140 -75
  187. package/dist/gs/strings/iter.js.map +1 -1
  188. package/dist/gs/strings/replace.js +2 -2
  189. package/dist/gs/strings/replace.js.map +1 -1
  190. package/dist/gs/strings/strings.js +52 -6
  191. package/dist/gs/strings/strings.js.map +1 -1
  192. package/dist/gs/sync/sync.d.ts +6 -3
  193. package/dist/gs/sync/sync.js +39 -11
  194. package/dist/gs/sync/sync.js.map +1 -1
  195. package/dist/gs/syscall/errors.d.ts +116 -112
  196. package/dist/gs/syscall/errors.js +38 -1
  197. package/dist/gs/syscall/errors.js.map +1 -1
  198. package/dist/gs/syscall/fs.d.ts +2 -8
  199. package/dist/gs/syscall/fs.js.map +1 -1
  200. package/dist/gs/syscall/js/index.js +20 -12
  201. package/dist/gs/syscall/js/index.js.map +1 -1
  202. package/dist/gs/syscall/types.d.ts +4 -1
  203. package/dist/gs/syscall/types.js.map +1 -1
  204. package/dist/gs/testing/testing.d.ts +4 -3
  205. package/dist/gs/testing/testing.js +21 -4
  206. package/dist/gs/testing/testing.js.map +1 -1
  207. package/dist/gs/time/time.js +22 -0
  208. package/dist/gs/time/time.js.map +1 -1
  209. package/dist/gs/unicode/unicode.js.map +1 -1
  210. package/dist/gs/unique/index.js +7 -2
  211. package/dist/gs/unique/index.js.map +1 -1
  212. package/go.mod +8 -8
  213. package/go.sum +14 -23
  214. package/gs/builtin/builtin.ts +364 -37
  215. package/gs/builtin/channel.ts +161 -29
  216. package/gs/builtin/defer.ts +13 -2
  217. package/gs/builtin/hostio.test.ts +1 -0
  218. package/gs/builtin/hostio.ts +38 -0
  219. package/gs/builtin/map.ts +46 -6
  220. package/gs/builtin/print.ts +12 -3
  221. package/gs/builtin/runtime-contract.test.ts +257 -10
  222. package/gs/builtin/slice.test.ts +70 -0
  223. package/gs/builtin/slice.ts +566 -255
  224. package/gs/builtin/type.ts +53 -9
  225. package/gs/builtin/varRef.ts +2 -0
  226. package/gs/bytes/buffer.gs.ts +28 -28
  227. package/gs/bytes/iter.gs.ts +13 -14
  228. package/gs/compress/zlib/index.test.ts +28 -0
  229. package/gs/compress/zlib/index.ts +200 -0
  230. package/gs/compress/zlib/meta.json +3 -0
  231. package/gs/context/context.test.ts +31 -1
  232. package/gs/context/context.ts +9 -4
  233. package/gs/crypto/ecdh/index.test.ts +43 -0
  234. package/gs/crypto/ecdh/index.ts +274 -0
  235. package/gs/crypto/ed25519/index.test.ts +41 -0
  236. package/gs/crypto/ed25519/index.ts +238 -0
  237. package/gs/crypto/ed25519/meta.json +13 -0
  238. package/gs/crypto/internal/constanttime/index.test.ts +25 -0
  239. package/gs/crypto/internal/constanttime/index.ts +22 -0
  240. package/gs/crypto/rand/index.test.ts +89 -1
  241. package/gs/crypto/rand/index.ts +103 -1
  242. package/gs/crypto/rand/meta.json +4 -1
  243. package/gs/crypto/sha256/index.test.ts +78 -0
  244. package/gs/crypto/sha256/index.ts +150 -0
  245. package/gs/crypto/sha256/meta.json +9 -0
  246. package/gs/crypto/sha512/index.test.ts +31 -0
  247. package/gs/crypto/sha512/index.ts +161 -0
  248. package/gs/crypto/sha512/meta.json +11 -0
  249. package/gs/encoding/json/index.test.ts +25 -3
  250. package/gs/encoding/json/index.ts +21 -3
  251. package/gs/errors/errors.test.ts +4 -1
  252. package/gs/errors/errors.ts +32 -8
  253. package/gs/fmt/fmt.test.ts +3 -1
  254. package/gs/fmt/fmt.ts +1 -5
  255. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +62 -7
  256. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +78 -36
  257. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +32 -11
  258. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +122 -43
  259. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +31 -0
  260. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +518 -4
  261. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +6 -0
  262. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +30 -0
  263. package/gs/github.com/aperturerobotics/util/conc/index.ts +172 -0
  264. package/gs/github.com/aperturerobotics/util/conc/meta.json +9 -0
  265. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +1 -4
  266. package/gs/github.com/hack-pad/safejs/internal/catch/index.test.ts +35 -0
  267. package/gs/github.com/hack-pad/safejs/internal/catch/index.ts +65 -0
  268. package/gs/github.com/hack-pad/safejs/internal/catch/meta.json +9 -0
  269. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +2 -1
  270. package/gs/github.com/klauspost/compress/internal/le/index.ts +6 -5
  271. package/gs/github.com/mr-tron/base58/base58/index.test.ts +70 -0
  272. package/gs/github.com/mr-tron/base58/base58/index.ts +231 -0
  273. package/gs/github.com/mr-tron/base58/base58/meta.json +3 -0
  274. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +46 -0
  275. package/gs/github.com/zeebo/blake3/internal/consts/index.ts +26 -0
  276. package/gs/go/token/index.ts +17 -4
  277. package/gs/hash/fnv/index.test.ts +67 -0
  278. package/gs/hash/fnv/index.ts +351 -0
  279. package/gs/hash/fnv/meta.json +3 -0
  280. package/gs/hash/index.test.ts +37 -0
  281. package/gs/hash/index.ts +118 -0
  282. package/gs/hash/meta.json +5 -0
  283. package/gs/internal/byteorder/index.test.ts +6 -6
  284. package/gs/io/fs/readlink.ts +40 -48
  285. package/gs/io/fs/walk.ts +10 -2
  286. package/gs/io/io.ts +4 -1
  287. package/gs/iter/iter.ts +8 -2
  288. package/gs/maps/iter.ts +69 -26
  289. package/gs/maps/maps.test.ts +23 -0
  290. package/gs/maps/maps.ts +6 -6
  291. package/gs/math/bits/index.test.ts +20 -0
  292. package/gs/math/bits/index.ts +15 -28
  293. package/gs/mime/index.ts +8 -2
  294. package/gs/net/http/httptest/index.test.ts +53 -0
  295. package/gs/net/http/httptest/index.ts +98 -3
  296. package/gs/net/http/index.test.ts +129 -1
  297. package/gs/net/http/index.ts +370 -19
  298. package/gs/net/http/meta.json +6 -0
  299. package/gs/net/http/pprof/index.test.ts +47 -0
  300. package/gs/net/http/pprof/index.ts +65 -0
  301. package/gs/os/error.gs.ts +9 -10
  302. package/gs/os/error.test.ts +41 -0
  303. package/gs/os/file_unix_js.test.ts +55 -0
  304. package/gs/os/tempfile.gs.test.ts +37 -10
  305. package/gs/os/types_js.gs.ts +94 -15
  306. package/gs/path/filepath/match.ts +4 -1
  307. package/gs/path/filepath/meta.json +6 -0
  308. package/gs/path/filepath/path.test.ts +57 -2
  309. package/gs/path/filepath/path.ts +91 -12
  310. package/gs/reflect/field.test.ts +63 -0
  311. package/gs/reflect/index.ts +4 -1
  312. package/gs/reflect/iter.ts +2 -2
  313. package/gs/reflect/map.test.ts +24 -2
  314. package/gs/reflect/map.ts +35 -0
  315. package/gs/reflect/type.ts +543 -60
  316. package/gs/reflect/typefor.test.ts +100 -0
  317. package/gs/reflect/types.ts +3 -1
  318. package/gs/reflect/value.ts +50 -1
  319. package/gs/reflect/visiblefields.ts +1 -1
  320. package/gs/runtime/debug/index.test.ts +22 -1
  321. package/gs/runtime/debug/index.ts +88 -0
  322. package/gs/runtime/pprof/index.test.ts +36 -0
  323. package/gs/runtime/pprof/index.ts +104 -0
  324. package/gs/runtime/pprof/meta.json +6 -0
  325. package/gs/runtime/trace/index.test.ts +45 -0
  326. package/gs/runtime/trace/index.ts +97 -0
  327. package/gs/runtime/trace/meta.json +7 -0
  328. package/gs/slices/meta.json +2 -1
  329. package/gs/slices/slices.test.ts +86 -0
  330. package/gs/slices/slices.ts +284 -37
  331. package/gs/sort/slice.gs.ts +73 -23
  332. package/gs/sort/slice.test.ts +40 -0
  333. package/gs/strings/builder.test.ts +8 -0
  334. package/gs/strings/builder.ts +29 -17
  335. package/gs/strings/iter.test.ts +5 -7
  336. package/gs/strings/iter.ts +146 -71
  337. package/gs/strings/replace.test.ts +1 -4
  338. package/gs/strings/replace.ts +6 -6
  339. package/gs/strings/strings.test.ts +4 -0
  340. package/gs/strings/strings.ts +54 -6
  341. package/gs/sync/sync.test.ts +57 -1
  342. package/gs/sync/sync.ts +45 -13
  343. package/gs/syscall/errors.ts +158 -115
  344. package/gs/syscall/fs.ts +8 -8
  345. package/gs/syscall/js/index.ts +49 -22
  346. package/gs/syscall/net.test.ts +26 -0
  347. package/gs/syscall/types.ts +7 -2
  348. package/gs/testing/testing.test.ts +56 -0
  349. package/gs/testing/testing.ts +27 -10
  350. package/gs/time/meta.json +2 -2
  351. package/gs/time/time.test.ts +4 -0
  352. package/gs/time/time.ts +33 -2
  353. package/gs/unicode/unicode.test.ts +14 -3
  354. package/gs/unicode/unicode.ts +1 -5
  355. package/gs/unique/index.ts +9 -2
  356. package/package.json +3 -3
@@ -2,18 +2,23 @@ package gotest
2
2
 
3
3
  import (
4
4
  "context"
5
+ "encoding/json"
5
6
  "os"
6
7
  "path/filepath"
7
8
  "regexp"
8
9
  "slices"
9
10
  "strconv"
10
11
  "strings"
12
+ "sync"
13
+ "time"
11
14
 
12
15
  "github.com/aperturerobotics/goscript/compiler"
13
16
  "github.com/aperturerobotics/goscript/compiler/tsworkspace"
14
17
  "github.com/pkg/errors"
15
18
  )
16
19
 
20
+ const combinedRuntimeResultPrefix = "__GOSCRIPT_PACKAGE_RESULT__"
21
+
17
22
  // Runner owns GoScript package-test loading, compilation, typecheck, and execution.
18
23
  type Runner struct {
19
24
  service *compiler.CompileService
@@ -33,6 +38,7 @@ func (r *Runner) Run(ctx context.Context, req *Request) (*Result, error) {
33
38
  if err != nil {
34
39
  return nil, err
35
40
  }
41
+ r.service = compiler.NewCompileService(norm.OverrideDirs...)
36
42
  if norm.Timeout != 0 {
37
43
  var cancel context.CancelFunc
38
44
  ctx, cancel = context.WithTimeout(ctx, norm.Timeout)
@@ -79,6 +85,7 @@ func (r *Runner) Run(ctx context.Context, req *Request) (*Result, error) {
79
85
  Dir: norm.Dir,
80
86
  OutputPath: norm.OutputRoot,
81
87
  BuildFlags: append([]string(nil), norm.BuildFlags...),
88
+ OverrideDirs: append([]string(nil), norm.OverrideDirs...),
82
89
  DependencyMode: compiler.DependencyModeRequested,
83
90
  RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
84
91
  Tests: true,
@@ -91,61 +98,7 @@ func (r *Runner) Run(ctx context.Context, req *Request) (*Result, error) {
91
98
  return result, nil
92
99
  }
93
100
 
94
- for idx := range result.Packages {
95
- if !shouldCompilePackage(result.Packages[idx]) {
96
- continue
97
- }
98
- outputRoot := packageOutputRoot(norm, idx)
99
- compileReq := &compiler.CompileRequest{
100
- Patterns: []string{result.Packages[idx].PackagePath},
101
- Dir: norm.Dir,
102
- OutputPath: outputRoot,
103
- BuildFlags: append([]string(nil), norm.BuildFlags...),
104
- DependencyMode: compiler.DependencyModeAll,
105
- RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
106
- Tests: false,
107
- AllDependencies: true,
108
- }
109
- if compileResult, compileErr := r.service.Compile(ctx, compileReq); compileErr != nil {
110
- result.Packages[idx].Action = ActionFail
111
- if compileResult != nil {
112
- result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
113
- result.Packages[idx].Owner = classifyDiagnostics(compileResult.Diagnostics)
114
- }
115
- markCompilePhase(&result.Packages[idx], compileResult, false)
116
- result.Packages[idx].Error = compileErr.Error()
117
- continue
118
- } else if compileResult != nil {
119
- result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
120
- markCompilePhase(&result.Packages[idx], compileResult, true)
121
- }
122
- if !r.compileTestImports(ctx, norm, outputRoot, &result.Packages[idx], result) {
123
- continue
124
- }
125
- testCompileReq := &compiler.CompileRequest{
126
- Patterns: []string{result.Packages[idx].PackagePath},
127
- Dir: norm.Dir,
128
- OutputPath: outputRoot,
129
- BuildFlags: append([]string(nil), norm.BuildFlags...),
130
- DependencyMode: compiler.DependencyModeAll,
131
- RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
132
- Tests: true,
133
- AllDependencies: true,
134
- }
135
- if compileResult, compileErr := r.service.Compile(ctx, testCompileReq); compileErr != nil {
136
- result.Packages[idx].Action = ActionFail
137
- if compileResult != nil {
138
- result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
139
- result.Packages[idx].Owner = classifyDiagnostics(compileResult.Diagnostics)
140
- }
141
- markCompilePhase(&result.Packages[idx], compileResult, false)
142
- result.Packages[idx].Error = compileErr.Error()
143
- continue
144
- } else if compileResult != nil {
145
- result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
146
- markCompilePhase(&result.Packages[idx], compileResult, true)
147
- }
148
- }
101
+ outputRoots := r.compilePackageOutputs(ctx, norm, result)
149
102
 
150
103
  if phase := workspace.EnsurePackageJSON(); phase.Failed() {
151
104
  markAllFailures(result, OwnerTestRunner, phase.Error)
@@ -156,48 +109,307 @@ func (r *Runner) Run(ctx context.Context, req *Request) (*Result, error) {
156
109
  markAllFailures(result, OwnerTestRunner, phase.Error)
157
110
  return result, nil
158
111
  }
112
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, "tsconfig.json", renderRuntimeTypeScriptProject(norm, outputRoots, nodeTypesAvailable)); phase.Failed() {
113
+ markAllFailures(result, OwnerTestRunner, phase.Error)
114
+ return result, nil
115
+ }
116
+ r.runPackageTools(ctx, norm, workspace, result, outputRoots, nodeTypesAvailable)
117
+ return result, nil
118
+ }
119
+
120
+ func (r *Runner) runPackageTools(
121
+ ctx context.Context,
122
+ req *normalizedRequest,
123
+ workspace *tsworkspace.Owner,
124
+ result *Result,
125
+ outputRoots []string,
126
+ nodeTypesAvailable bool,
127
+ ) {
128
+ indexes := r.preparePackageWorkspaces(req, workspace, result, outputRoots, nodeTypesAvailable)
129
+ if len(indexes) == 0 {
130
+ return
131
+ }
132
+ if len(indexes) == 1 {
133
+ r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, indexes[0])
134
+ return
135
+ }
136
+ typecheck := workspace.RunTool(ctx, tsworkspace.PhaseTypeCheck, req.WorkDir, "tsgo", "--project", "tsconfig.json")
137
+ if typecheck.Failed() {
138
+ if owner, ok := aggregateTypeCheckFailureOwner(typecheck.Output); ok {
139
+ markTypeCheckFailures(result, owner, processErrorText(typecheck))
140
+ return
141
+ }
142
+ r.runPackageTypeChecksAndRuntimes(ctx, req, workspace, result, indexes)
143
+ return
144
+ }
145
+ for _, idx := range indexes {
146
+ result.Packages[idx].Phases.TypeCheck = PhaseStatusPass
147
+ }
148
+ r.runPackageRuntimes(ctx, req, workspace, result, indexes)
149
+ }
150
+
151
+ func (r *Runner) preparePackageWorkspaces(
152
+ req *normalizedRequest,
153
+ workspace *tsworkspace.Owner,
154
+ result *Result,
155
+ outputRoots []string,
156
+ nodeTypesAvailable bool,
157
+ ) []int {
158
+ var indexes []int
159
159
  for idx := range result.Packages {
160
160
  if !shouldCompilePackage(result.Packages[idx]) {
161
161
  continue
162
162
  }
163
- result.Packages[idx].Phases.Workspace = PhaseStatusPass
164
- outputRoot := packageOutputRoot(norm, idx)
165
- runnerFile := packageRunnerFile(idx)
166
- if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, runnerFile, renderRunner(result.Packages[idx], norm)); phase.Failed() {
167
- result.Packages[idx].Owner = OwnerTestRunner
168
- result.Packages[idx].Phases.Workspace = PhaseStatusFail
169
- result.Packages[idx].Error = phase.Error
170
- continue
163
+ if r.preparePackageWorkspace(req, workspace, result, outputRoots, nodeTypesAvailable, idx) {
164
+ indexes = append(indexes, idx)
171
165
  }
172
- tsconfigFile := "tsconfig.json"
173
- if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, tsconfigFile, renderTypeScriptProject(norm, outputRoot, runnerFile, nodeTypesAvailable)); phase.Failed() {
174
- result.Packages[idx].Owner = OwnerTestRunner
175
- result.Packages[idx].Phases.Workspace = PhaseStatusFail
176
- result.Packages[idx].Error = phase.Error
177
- continue
166
+ }
167
+ return indexes
168
+ }
169
+
170
+ func (r *Runner) preparePackageWorkspace(
171
+ req *normalizedRequest,
172
+ workspace *tsworkspace.Owner,
173
+ result *Result,
174
+ outputRoots []string,
175
+ nodeTypesAvailable bool,
176
+ idx int,
177
+ ) bool {
178
+ result.Packages[idx].Phases.Workspace = PhaseStatusPass
179
+ outputRoot := outputRoots[idx]
180
+ runnerFile := packageRunnerFile(idx)
181
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, runnerFile, renderRunner(result.Packages[idx], req)); phase.Failed() {
182
+ result.Packages[idx].Owner = OwnerTestRunner
183
+ result.Packages[idx].Phases.Workspace = PhaseStatusFail
184
+ result.Packages[idx].Error = phase.Error
185
+ return false
186
+ }
187
+ tsconfigFile := packageTSConfigFile(idx)
188
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, tsconfigFile, renderTypeScriptProject(req, outputRoot, runnerFile, tsconfigFile, nodeTypesAvailable)); phase.Failed() {
189
+ result.Packages[idx].Owner = OwnerTestRunner
190
+ result.Packages[idx].Phases.Workspace = PhaseStatusFail
191
+ result.Packages[idx].Error = phase.Error
192
+ return false
193
+ }
194
+ return true
195
+ }
196
+
197
+ func (r *Runner) runPackageTypeChecksAndRuntimes(
198
+ ctx context.Context,
199
+ req *normalizedRequest,
200
+ workspace *tsworkspace.Owner,
201
+ result *Result,
202
+ indexes []int,
203
+ ) {
204
+ parallelism := max(req.Parallelism, 1)
205
+ sem := make(chan struct{}, parallelism)
206
+ var wg sync.WaitGroup
207
+ for _, idx := range packageExecutionIndexes(result, indexes) {
208
+ wg.Go(func() {
209
+ select {
210
+ case sem <- struct{}{}:
211
+ defer func() { <-sem }()
212
+ case <-ctx.Done():
213
+ result.Packages[idx].Owner = OwnerTestRunner
214
+ result.Packages[idx].Phases.TypeCheck = PhaseStatusFail
215
+ result.Packages[idx].Error = ctx.Err().Error()
216
+ return
217
+ }
218
+ r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, idx)
219
+ })
220
+ }
221
+ wg.Wait()
222
+ }
223
+
224
+ func (r *Runner) runPackageTypeCheckAndRuntime(
225
+ ctx context.Context,
226
+ req *normalizedRequest,
227
+ workspace *tsworkspace.Owner,
228
+ result *Result,
229
+ idx int,
230
+ ) {
231
+ if !r.runPackageTypeCheck(ctx, req, workspace, result, idx) {
232
+ return
233
+ }
234
+ r.runPackageRuntime(ctx, req, workspace, result, idx)
235
+ }
236
+
237
+ func (r *Runner) runPackageTypeCheck(
238
+ ctx context.Context,
239
+ req *normalizedRequest,
240
+ workspace *tsworkspace.Owner,
241
+ result *Result,
242
+ idx int,
243
+ ) bool {
244
+ typecheck := workspace.RunTool(ctx, tsworkspace.PhaseTypeCheck, req.WorkDir, "tsgo", "--project", packageTSConfigFile(idx))
245
+ if typecheck.Failed() {
246
+ result.Packages[idx].Owner = classifyProcessOutput(typecheck.Output)
247
+ result.Packages[idx].Phases.TypeCheck = PhaseStatusFail
248
+ result.Packages[idx].Error = processErrorText(typecheck)
249
+ return false
250
+ }
251
+ result.Packages[idx].Phases.TypeCheck = PhaseStatusPass
252
+ return true
253
+ }
254
+
255
+ func (r *Runner) runPackageRuntimes(
256
+ ctx context.Context,
257
+ req *normalizedRequest,
258
+ workspace *tsworkspace.Owner,
259
+ result *Result,
260
+ indexes []int,
261
+ ) {
262
+ if len(indexes) > 1 &&
263
+ (req.RuntimeGroups || req.Parallelism == 1) &&
264
+ r.runCombinedPackageRuntimes(ctx, req, workspace, result, indexes) {
265
+ return
266
+ }
267
+ r.runPackageRuntimesIndividually(ctx, req, workspace, result, indexes)
268
+ }
269
+
270
+ func (r *Runner) runPackageRuntimesIndividually(
271
+ ctx context.Context,
272
+ req *normalizedRequest,
273
+ workspace *tsworkspace.Owner,
274
+ result *Result,
275
+ indexes []int,
276
+ ) {
277
+ parallelism := max(req.Parallelism, 1)
278
+ sem := make(chan struct{}, parallelism)
279
+ var wg sync.WaitGroup
280
+ for _, idx := range packageExecutionIndexes(result, indexes) {
281
+ wg.Go(func() {
282
+ select {
283
+ case sem <- struct{}{}:
284
+ defer func() { <-sem }()
285
+ case <-ctx.Done():
286
+ result.Packages[idx].Owner = OwnerTestRunner
287
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
288
+ result.Packages[idx].Error = ctx.Err().Error()
289
+ return
290
+ }
291
+ r.runPackageRuntime(ctx, req, workspace, result, idx)
292
+ })
293
+ }
294
+ wg.Wait()
295
+ }
296
+
297
+ func (r *Runner) runCombinedPackageRuntimes(
298
+ ctx context.Context,
299
+ req *normalizedRequest,
300
+ workspace *tsworkspace.Owner,
301
+ result *Result,
302
+ indexes []int,
303
+ ) bool {
304
+ ordered := packageExecutionIndexes(result, indexes)
305
+ chunks := packageRuntimeChunks(ordered, max(req.Parallelism, 1))
306
+ if len(chunks) == 0 {
307
+ return true
308
+ }
309
+ if len(chunks) == 1 {
310
+ return r.runCombinedPackageRuntime(ctx, req, workspace, result, "runner-all.ts", chunks[0])
311
+ }
312
+
313
+ var mu sync.Mutex
314
+ ok := true
315
+ var wg sync.WaitGroup
316
+ for chunkIdx, chunk := range chunks {
317
+ runnerFile := "runner-all-" + strconv.Itoa(chunkIdx) + ".ts"
318
+ wg.Go(func() {
319
+ if r.runCombinedPackageRuntime(ctx, req, workspace, result, runnerFile, chunk) {
320
+ return
321
+ }
322
+ mu.Lock()
323
+ ok = false
324
+ mu.Unlock()
325
+ })
326
+ }
327
+ wg.Wait()
328
+ return ok
329
+ }
330
+
331
+ func packageRuntimeChunks(indexes []int, parallelism int) [][]int {
332
+ if len(indexes) == 0 {
333
+ return nil
334
+ }
335
+ if parallelism < 1 {
336
+ parallelism = 1
337
+ }
338
+ if parallelism > len(indexes) {
339
+ parallelism = len(indexes)
340
+ }
341
+ chunks := make([][]int, parallelism)
342
+ for idx, packageIdx := range indexes {
343
+ chunks[idx%parallelism] = append(chunks[idx%parallelism], packageIdx)
344
+ }
345
+ return chunks
346
+ }
347
+
348
+ func (r *Runner) runCombinedPackageRuntime(
349
+ ctx context.Context,
350
+ req *normalizedRequest,
351
+ workspace *tsworkspace.Owner,
352
+ result *Result,
353
+ runnerFile string,
354
+ indexes []int,
355
+ ) bool {
356
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, runnerFile, renderCombinedRunner(result, indexes, req)); phase.Failed() {
357
+ return false
358
+ }
359
+ runtime := workspace.RunTool(ctx, tsworkspace.PhaseRuntime, req.WorkDir, "bun", runnerFile)
360
+ if runtime.Failed() {
361
+ return false
362
+ }
363
+ records, ok := parseCombinedRuntimeRecords(runtime.Output)
364
+ if !ok {
365
+ return false
366
+ }
367
+ byPath := make(map[string]combinedRuntimeRecord, len(records))
368
+ for _, record := range records {
369
+ byPath[record.PackagePath] = record
370
+ }
371
+ for _, idx := range indexes {
372
+ record, ok := byPath[result.Packages[idx].PackagePath]
373
+ if !ok {
374
+ return false
178
375
  }
179
- typecheck := workspace.RunTool(ctx, tsworkspace.PhaseTypeCheck, norm.WorkDir, "tsgo", "--project", tsconfigFile)
180
- if typecheck.Failed() {
181
- result.Packages[idx].Owner = classifyProcessOutput(typecheck.Output)
182
- result.Packages[idx].Phases.TypeCheck = PhaseStatusFail
183
- result.Packages[idx].Error = processErrorText(typecheck)
376
+ result.Packages[idx].Elapsed = time.Duration(record.ElapsedMS) * time.Millisecond
377
+ result.Packages[idx].Output = strings.TrimSpace(record.Output)
378
+ if record.OK {
379
+ result.Packages[idx].Phases.Runtime = PhaseStatusPass
380
+ result.Packages[idx].Action = ActionPass
184
381
  continue
185
382
  }
186
- result.Packages[idx].Phases.TypeCheck = PhaseStatusPass
187
- runtime := workspace.RunTool(ctx, tsworkspace.PhaseRuntime, norm.WorkDir, "bun", runnerFile)
188
- result.Packages[idx].Elapsed = runtime.Elapsed
189
- result.Packages[idx].Output = strings.TrimSpace(runtime.Output)
190
- if runtime.Failed() {
191
- result.Packages[idx].Action = ActionFail
192
- result.Packages[idx].Owner = classifyProcessOutput(runtime.Output)
193
- result.Packages[idx].Phases.Runtime = PhaseStatusFail
194
- result.Packages[idx].Error = runtime.Error
195
- continue
383
+ result.Packages[idx].Action = ActionFail
384
+ result.Packages[idx].Owner = classifyProcessOutput(record.Output)
385
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
386
+ result.Packages[idx].Error = strings.TrimSpace(record.Output)
387
+ if result.Packages[idx].Error == "" {
388
+ result.Packages[idx].Error = "goscript test failed"
196
389
  }
197
- result.Packages[idx].Phases.Runtime = PhaseStatusPass
198
- result.Packages[idx].Action = ActionPass
199
390
  }
200
- return result, nil
391
+ return true
392
+ }
393
+
394
+ func (r *Runner) runPackageRuntime(
395
+ ctx context.Context,
396
+ req *normalizedRequest,
397
+ workspace *tsworkspace.Owner,
398
+ result *Result,
399
+ idx int,
400
+ ) {
401
+ runtime := workspace.RunTool(ctx, tsworkspace.PhaseRuntime, req.WorkDir, "bun", packageRunnerFile(idx))
402
+ result.Packages[idx].Elapsed = runtime.Elapsed
403
+ result.Packages[idx].Output = strings.TrimSpace(runtime.Output)
404
+ if runtime.Failed() {
405
+ result.Packages[idx].Action = ActionFail
406
+ result.Packages[idx].Owner = classifyProcessOutput(runtime.Output)
407
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
408
+ result.Packages[idx].Error = runtime.Error
409
+ return
410
+ }
411
+ result.Packages[idx].Phases.Runtime = PhaseStatusPass
412
+ result.Packages[idx].Action = ActionPass
201
413
  }
202
414
 
203
415
  func (r *Runner) compileTestImports(
@@ -219,6 +431,7 @@ func (r *Runner) compileTestImports(
219
431
  Dir: req.Dir,
220
432
  OutputPath: outputRoot,
221
433
  BuildFlags: append([]string(nil), req.BuildFlags...),
434
+ OverrideDirs: append([]string(nil), req.OverrideDirs...),
222
435
  DependencyMode: compiler.DependencyModeAll,
223
436
  RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
224
437
  Tests: false,
@@ -242,6 +455,110 @@ func (r *Runner) compileTestImports(
242
455
  return true
243
456
  }
244
457
 
458
+ func (r *Runner) compilePackageOutputs(ctx context.Context, req *normalizedRequest, result *Result) []string {
459
+ outputRoots := make([]string, len(result.Packages))
460
+ if r.compilePackageBatch(ctx, req, result, outputRoots) {
461
+ return outputRoots
462
+ }
463
+ return r.compilePackageOutputsIndividually(ctx, req, result)
464
+ }
465
+
466
+ func (r *Runner) compilePackageBatch(ctx context.Context, req *normalizedRequest, result *Result, outputRoots []string) bool {
467
+ packagePaths := runnablePackagePaths(result.Packages)
468
+ if len(packagePaths) == 0 {
469
+ return true
470
+ }
471
+ testCompileReq := &compiler.CompileRequest{
472
+ Patterns: packagePaths,
473
+ Dir: req.Dir,
474
+ OutputPath: req.OutputRoot,
475
+ BuildFlags: append([]string(nil), req.BuildFlags...),
476
+ OverrideDirs: append([]string(nil), req.OverrideDirs...),
477
+ DependencyMode: compiler.DependencyModeAll,
478
+ RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
479
+ Tests: true,
480
+ AllDependencies: true,
481
+ }
482
+ testCompileResult, testCompileErr := r.service.Compile(ctx, testCompileReq)
483
+ if testCompileErr != nil {
484
+ return false
485
+ }
486
+ if testCompileResult != nil {
487
+ result.Diagnostics = append(result.Diagnostics, testCompileResult.Diagnostics...)
488
+ }
489
+ for idx := range result.Packages {
490
+ if !shouldCompilePackage(result.Packages[idx]) {
491
+ continue
492
+ }
493
+ outputRoots[idx] = req.OutputRoot
494
+ markCompilePhase(&result.Packages[idx], testCompileResult, true)
495
+ }
496
+ return true
497
+ }
498
+
499
+ func (r *Runner) compilePackageOutputsIndividually(ctx context.Context, req *normalizedRequest, result *Result) []string {
500
+ outputRoots := make([]string, len(result.Packages))
501
+ for idx := range result.Packages {
502
+ if !shouldCompilePackage(result.Packages[idx]) {
503
+ continue
504
+ }
505
+ outputRoot := packageOutputRoot(req, idx)
506
+ outputRoots[idx] = outputRoot
507
+ compileReq := &compiler.CompileRequest{
508
+ Patterns: []string{result.Packages[idx].PackagePath},
509
+ Dir: req.Dir,
510
+ OutputPath: outputRoot,
511
+ BuildFlags: append([]string(nil), req.BuildFlags...),
512
+ OverrideDirs: append([]string(nil), req.OverrideDirs...),
513
+ DependencyMode: compiler.DependencyModeAll,
514
+ RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
515
+ Tests: false,
516
+ AllDependencies: true,
517
+ }
518
+ if compileResult, compileErr := r.service.Compile(ctx, compileReq); compileErr != nil {
519
+ result.Packages[idx].Action = ActionFail
520
+ if compileResult != nil {
521
+ result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
522
+ result.Packages[idx].Owner = classifyDiagnostics(compileResult.Diagnostics)
523
+ }
524
+ markCompilePhase(&result.Packages[idx], compileResult, false)
525
+ result.Packages[idx].Error = compileErr.Error()
526
+ continue
527
+ } else if compileResult != nil {
528
+ result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
529
+ markCompilePhase(&result.Packages[idx], compileResult, true)
530
+ }
531
+ if !r.compileTestImports(ctx, req, outputRoot, &result.Packages[idx], result) {
532
+ continue
533
+ }
534
+ testCompileReq := &compiler.CompileRequest{
535
+ Patterns: []string{result.Packages[idx].PackagePath},
536
+ Dir: req.Dir,
537
+ OutputPath: outputRoot,
538
+ BuildFlags: append([]string(nil), req.BuildFlags...),
539
+ OverrideDirs: append([]string(nil), req.OverrideDirs...),
540
+ DependencyMode: compiler.DependencyModeAll,
541
+ RuntimeEmissionMode: compiler.RuntimeEmissionModeEmit,
542
+ Tests: true,
543
+ AllDependencies: true,
544
+ }
545
+ if compileResult, compileErr := r.service.Compile(ctx, testCompileReq); compileErr != nil {
546
+ result.Packages[idx].Action = ActionFail
547
+ if compileResult != nil {
548
+ result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
549
+ result.Packages[idx].Owner = classifyDiagnostics(compileResult.Diagnostics)
550
+ }
551
+ markCompilePhase(&result.Packages[idx], compileResult, false)
552
+ result.Packages[idx].Error = compileErr.Error()
553
+ continue
554
+ } else if compileResult != nil {
555
+ result.Diagnostics = append(result.Diagnostics, compileResult.Diagnostics...)
556
+ markCompilePhase(&result.Packages[idx], compileResult, true)
557
+ }
558
+ }
559
+ return outputRoots
560
+ }
561
+
245
562
  func compileRunPattern(pattern string) (*regexp.Regexp, error) {
246
563
  pattern = strings.TrimSpace(pattern)
247
564
  if pattern == "" {
@@ -257,6 +574,7 @@ func packageResults(testGraph *compiler.PackageTestGraph, runPattern *regexp.Reg
257
574
  results := make([]PackageResult, 0, len(testGraph.Packages))
258
575
  for _, pkg := range testGraph.Packages {
259
576
  result := newSkippedPackageResult(pkg.PackagePath)
577
+ result.SourceDir = packageSourceDir(pkg)
260
578
  result.Tests = append(result.Tests, packageVariantTests(pkg.SamePackageTests, runPattern)...)
261
579
  result.Tests = append(result.Tests, packageVariantTests(pkg.ExternalPackageTests, runPattern)...)
262
580
  result.TestImports = packageTestImports(pkg)
@@ -282,6 +600,39 @@ func packageResults(testGraph *compiler.PackageTestGraph, runPattern *regexp.Reg
282
600
  return results
283
601
  }
284
602
 
603
+ func packageSourceDir(pkg *compiler.PackageTestGraphPackage) string {
604
+ if pkg == nil {
605
+ return ""
606
+ }
607
+ for _, files := range [][]string{
608
+ pkg.CompiledGoFiles,
609
+ pkg.GoFiles,
610
+ testVariantCompiledGoFiles(pkg.SamePackageTests),
611
+ testVariantGoFiles(pkg.SamePackageTests),
612
+ testVariantCompiledGoFiles(pkg.ExternalPackageTests),
613
+ testVariantGoFiles(pkg.ExternalPackageTests),
614
+ } {
615
+ if len(files) != 0 && files[0] != "" {
616
+ return filepath.Dir(files[0])
617
+ }
618
+ }
619
+ return ""
620
+ }
621
+
622
+ func testVariantCompiledGoFiles(variant *compiler.PackageTestGraphVariant) []string {
623
+ if variant == nil {
624
+ return nil
625
+ }
626
+ return variant.CompiledGoFiles
627
+ }
628
+
629
+ func testVariantGoFiles(variant *compiler.PackageTestGraphVariant) []string {
630
+ if variant == nil {
631
+ return nil
632
+ }
633
+ return variant.GoFiles
634
+ }
635
+
285
636
  func newSkippedPackageResult(packagePath string) PackageResult {
286
637
  return PackageResult{
287
638
  PackagePath: packagePath,
@@ -335,6 +686,43 @@ func shouldCompilePackage(result PackageResult) bool {
335
686
  return result.Action != ActionSkip && result.Owner == "" && result.Error == ""
336
687
  }
337
688
 
689
+ func runnablePackagePaths(results []PackageResult) []string {
690
+ seen := make(map[string]bool)
691
+ paths := make([]string, 0, len(results))
692
+ for _, result := range results {
693
+ if !shouldCompilePackage(result) {
694
+ continue
695
+ }
696
+ if result.PackagePath == "" || seen[result.PackagePath] {
697
+ continue
698
+ }
699
+ seen[result.PackagePath] = true
700
+ paths = append(paths, result.PackagePath)
701
+ }
702
+ slices.Sort(paths)
703
+ return paths
704
+ }
705
+
706
+ func packageExecutionIndexes(result *Result, indexes []int) []int {
707
+ ordered := append([]int(nil), indexes...)
708
+ slices.SortFunc(ordered, func(a, b int) int {
709
+ var aPkg, bPkg PackageResult
710
+ if result != nil {
711
+ if a >= 0 && a < len(result.Packages) {
712
+ aPkg = result.Packages[a]
713
+ }
714
+ if b >= 0 && b < len(result.Packages) {
715
+ bPkg = result.Packages[b]
716
+ }
717
+ }
718
+ if len(aPkg.Tests) != len(bPkg.Tests) {
719
+ return len(bPkg.Tests) - len(aPkg.Tests)
720
+ }
721
+ return strings.Compare(aPkg.PackagePath, bPkg.PackagePath)
722
+ })
723
+ return ordered
724
+ }
725
+
338
726
  func markAllFailures(result *Result, owner Owner, message string) {
339
727
  message = strings.TrimSpace(message)
340
728
  if result == nil {
@@ -364,6 +752,23 @@ func markAllFailures(result *Result, owner Owner, message string) {
364
752
  }
365
753
  }
366
754
 
755
+ func markTypeCheckFailures(result *Result, owner Owner, message string) {
756
+ message = strings.TrimSpace(message)
757
+ if result == nil {
758
+ return
759
+ }
760
+ for idx := range result.Packages {
761
+ if !shouldCompilePackage(result.Packages[idx]) {
762
+ continue
763
+ }
764
+ result.Packages[idx].Action = ActionFail
765
+ result.Packages[idx].Owner = owner
766
+ result.Packages[idx].Error = message
767
+ result.Packages[idx].Phases.TypeCheck = PhaseStatusFail
768
+ result.Packages[idx].Phases.Runtime = PhaseStatusSkip
769
+ }
770
+ }
771
+
367
772
  func failurePhases(owner Owner) PackagePhases {
368
773
  if owner == OwnerPackageGraph {
369
774
  return PackagePhases{
@@ -391,6 +796,10 @@ func packageRunnerFile(idx int) string {
391
796
  return "runner-" + strconv.Itoa(idx) + ".ts"
392
797
  }
393
798
 
799
+ func packageTSConfigFile(idx int) string {
800
+ return "tsconfig-" + strconv.Itoa(idx) + ".json"
801
+ }
802
+
394
803
  func renderRunner(result PackageResult, req *normalizedRequest) string {
395
804
  var b strings.Builder
396
805
  b.WriteString("import { runTests } from \"@goscript/testing/index.js\"\n")
@@ -403,6 +812,7 @@ func renderRunner(result PackageResult, req *normalizedRequest) string {
403
812
  b.WriteString("\n")
404
813
  }
405
814
  b.WriteString("\n")
815
+ writeProcessChdir(&b, result.SourceDir)
406
816
  b.WriteString("const result = await runTests(")
407
817
  b.WriteString(strconv.Quote(result.PackagePath))
408
818
  b.WriteString(", [\n")
@@ -435,9 +845,151 @@ func renderRunner(result PackageResult, req *normalizedRequest) string {
435
845
  }
436
846
  b.WriteString(" })\n")
437
847
  b.WriteString("if (!result.ok) {\n\tthrow new Error(\"goscript test failed\")\n}\n")
848
+ b.WriteString("if (typeof process !== \"undefined\" && process.exit) {\n\tprocess.exit(0)\n}\n")
849
+ return b.String()
850
+ }
851
+
852
+ type combinedRuntimeRecord struct {
853
+ PackagePath string `json:"packagePath"`
854
+ OK bool `json:"ok"`
855
+ ElapsedMS int64 `json:"elapsedMs"`
856
+ Output string `json:"output"`
857
+ }
858
+
859
+ func parseCombinedRuntimeRecords(output string) ([]combinedRuntimeRecord, bool) {
860
+ var records []combinedRuntimeRecord
861
+ for line := range strings.SplitSeq(output, "\n") {
862
+ line = strings.TrimSpace(line)
863
+ if !strings.HasPrefix(line, combinedRuntimeResultPrefix) {
864
+ continue
865
+ }
866
+ var record combinedRuntimeRecord
867
+ if err := json.Unmarshal([]byte(strings.TrimPrefix(line, combinedRuntimeResultPrefix)), &record); err != nil {
868
+ return nil, false
869
+ }
870
+ if record.PackagePath == "" {
871
+ return nil, false
872
+ }
873
+ records = append(records, record)
874
+ }
875
+ return records, len(records) != 0
876
+ }
877
+
878
+ func renderCombinedRunner(result *Result, indexes []int, req *normalizedRequest) string {
879
+ aliases := runnerImportAliases(result, indexes)
880
+ var imports []string
881
+ for packagePath := range aliases {
882
+ imports = append(imports, packagePath)
883
+ }
884
+ slices.Sort(imports)
885
+
886
+ var b strings.Builder
887
+ b.WriteString("import { runTests } from \"@goscript/testing/index.js\"\n")
888
+ for _, packagePath := range imports {
889
+ b.WriteString("import * as ")
890
+ b.WriteString(aliases[packagePath])
891
+ b.WriteString(" from ")
892
+ b.WriteString(strconv.Quote("@goscript/" + packagePath + "/index.js"))
893
+ b.WriteString("\n")
894
+ }
895
+ b.WriteString("\n")
896
+ b.WriteString("const __goscriptResultPrefix = ")
897
+ b.WriteString(strconv.Quote(combinedRuntimeResultPrefix))
898
+ b.WriteString("\n")
899
+ b.WriteString("const __goscriptOriginalLog = console.log\n")
900
+ b.WriteString("async function __goscriptRunPackage(packagePath, packageDir, tests) {\n")
901
+ b.WriteString("\tif (packageDir && typeof process !== \"undefined\" && process.chdir) {\n")
902
+ b.WriteString("\t\tprocess.chdir(packageDir)\n")
903
+ b.WriteString("\t}\n")
904
+ b.WriteString("\tconst logs = []\n")
905
+ b.WriteString("\tconst startedAt = Date.now()\n")
906
+ b.WriteString("\tlet ok = false\n")
907
+ b.WriteString("\tconsole.log = (...args) => logs.push(args.map((arg) => String(arg)).join(' '))\n")
908
+ b.WriteString("\ttry {\n")
909
+ b.WriteString("\t\tconst result = await runTests(packagePath, tests, { verbose: ")
910
+ if req.Verbose {
911
+ b.WriteString("true")
912
+ } else {
913
+ b.WriteString("false")
914
+ }
915
+ b.WriteString(", count: ")
916
+ b.WriteString(strconv.Itoa(req.Count))
917
+ b.WriteString(", short: ")
918
+ if req.Short {
919
+ b.WriteString("true")
920
+ } else {
921
+ b.WriteString("false")
922
+ }
923
+ b.WriteString(" })\n")
924
+ b.WriteString("\t\tok = result.ok\n")
925
+ b.WriteString("\t} catch (err) {\n")
926
+ b.WriteString("\t\tok = false\n")
927
+ b.WriteString("\t\tlogs.push(err && err.stack ? String(err.stack) : String(err))\n")
928
+ b.WriteString("\t} finally {\n")
929
+ b.WriteString("\t\tconsole.log = __goscriptOriginalLog\n")
930
+ b.WriteString("\t}\n")
931
+ b.WriteString("\t__goscriptOriginalLog(__goscriptResultPrefix + JSON.stringify({ packagePath, ok, elapsedMs: Date.now() - startedAt, output: logs.join('\\n') }))\n")
932
+ b.WriteString("}\n\n")
933
+ for _, idx := range indexes {
934
+ pkg := result.Packages[idx]
935
+ b.WriteString("await __goscriptRunPackage(")
936
+ b.WriteString(strconv.Quote(pkg.PackagePath))
937
+ b.WriteString(", ")
938
+ b.WriteString(strconv.Quote(pkg.SourceDir))
939
+ b.WriteString(", [\n")
940
+ for testIdx, test := range pkg.Tests {
941
+ b.WriteString("\t{ name: ")
942
+ b.WriteString(strconv.Quote(test.Name))
943
+ b.WriteString(", fn: async (t) => await ")
944
+ b.WriteString(aliases[test.PackagePath])
945
+ b.WriteString(".")
946
+ b.WriteString(test.Name)
947
+ b.WriteString("(t) }")
948
+ if testIdx != len(pkg.Tests)-1 {
949
+ b.WriteString(",")
950
+ }
951
+ b.WriteString("\n")
952
+ }
953
+ b.WriteString("])\n")
954
+ }
955
+ b.WriteString("if (typeof process !== \"undefined\" && process.exit) {\n\tprocess.exit(0)\n}\n")
438
956
  return b.String()
439
957
  }
440
958
 
959
+ func writeProcessChdir(b *strings.Builder, dir string) {
960
+ if dir == "" {
961
+ return
962
+ }
963
+ b.WriteString("if (typeof process !== \"undefined\" && process.chdir) {\n")
964
+ b.WriteString("\tprocess.chdir(")
965
+ b.WriteString(strconv.Quote(dir))
966
+ b.WriteString(")\n")
967
+ b.WriteString("}\n")
968
+ }
969
+
970
+ func runnerImportAliases(result *Result, indexes []int) map[string]string {
971
+ seen := make(map[string]bool)
972
+ var imports []string
973
+ for _, idx := range indexes {
974
+ if result == nil || idx < 0 || idx >= len(result.Packages) {
975
+ continue
976
+ }
977
+ for _, packagePath := range runnerImports(result.Packages[idx].Tests) {
978
+ if seen[packagePath] {
979
+ continue
980
+ }
981
+ seen[packagePath] = true
982
+ imports = append(imports, packagePath)
983
+ }
984
+ }
985
+ slices.Sort(imports)
986
+ aliases := make(map[string]string, len(imports))
987
+ for idx, packagePath := range imports {
988
+ aliases[packagePath] = "pkg" + strconv.Itoa(idx)
989
+ }
990
+ return aliases
991
+ }
992
+
441
993
  func runnerImports(tests []Test) []string {
442
994
  seen := make(map[string]bool)
443
995
  var imports []string
@@ -460,13 +1012,19 @@ func processErrorText(result tsworkspace.Result) string {
460
1012
  return result.Error
461
1013
  }
462
1014
 
463
- func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFile string, nodeTypesAvailable bool) string {
464
- outputPattern := filepath.ToSlash(outputRoot)
465
- outputAlias := filepath.ToSlash(filepath.Join(outputRoot, "@goscript", "*"))
466
- if rel, err := filepath.Rel(req.WorkDir, outputRoot); err == nil {
467
- outputPattern = filepath.ToSlash(rel)
468
- outputAlias = filepath.ToSlash(filepath.Join(rel, "@goscript", "*"))
1015
+ func aggregateTypeCheckFailureOwner(output string) (Owner, bool) {
1016
+ owner := classifyProcessOutput(output)
1017
+ // Package-scoped fallback is worth paying for package-local emitted
1018
+ // TypeScript errors. Shared override package failures are independent of
1019
+ // the runner file, so rerunning tsgo per package only repeats the same
1020
+ // expensive project error.
1021
+ if owner == OwnerOverridePackage {
1022
+ return owner, true
469
1023
  }
1024
+ return "", false
1025
+ }
1026
+
1027
+ func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFile string, projectFile string, nodeTypesAvailable bool) string {
470
1028
  var b strings.Builder
471
1029
  b.WriteString("{\n")
472
1030
  b.WriteString(" \"compilerOptions\": {\n")
@@ -476,6 +1034,12 @@ func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFi
476
1034
  b.WriteString(" \"lib\": [\"ESNext\", \"DOM\"],\n")
477
1035
  b.WriteString(" \"strict\": true,\n")
478
1036
  b.WriteString(" \"allowImportingTsExtensions\": true,\n")
1037
+ if req.IncrementalTypeCheck {
1038
+ b.WriteString(" \"incremental\": true,\n")
1039
+ b.WriteString(" \"tsBuildInfoFile\": ")
1040
+ b.WriteString(strconv.Quote(typeScriptBuildInfoFile(projectFile)))
1041
+ b.WriteString(",\n")
1042
+ }
479
1043
  b.WriteString(" \"noEmit\": true,\n")
480
1044
  if nodeTypesAvailable {
481
1045
  b.WriteString(" \"types\": [\"node\"],\n")
@@ -485,13 +1049,11 @@ func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFi
485
1049
  b.WriteString(" \"paths\": {\n")
486
1050
  b.WriteString(" \"*\": [\"./*\"],\n")
487
1051
  b.WriteString(" \"@goscript/*\": [")
488
- b.WriteString(strconv.Quote("./" + outputAlias))
1052
+ b.WriteString(strconv.Quote("./" + typeScriptOutputAlias(req, outputRoot)))
489
1053
  b.WriteString("]\n")
490
1054
  b.WriteString(" }\n")
491
1055
  b.WriteString(" },\n")
492
1056
  b.WriteString(" \"include\": [")
493
- b.WriteString(strconv.Quote(outputPattern + "/**/*.ts"))
494
- b.WriteString(", ")
495
1057
  b.WriteString(strconv.Quote(runnerFile))
496
1058
  b.WriteString(", ")
497
1059
  b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
@@ -500,6 +1062,81 @@ func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFi
500
1062
  return b.String()
501
1063
  }
502
1064
 
1065
+ func renderRuntimeTypeScriptProject(req *normalizedRequest, outputRoots []string, nodeTypesAvailable bool) string {
1066
+ var aliases []string
1067
+ seen := make(map[string]bool)
1068
+ for _, outputRoot := range outputRoots {
1069
+ if outputRoot == "" {
1070
+ continue
1071
+ }
1072
+ alias := "./" + typeScriptOutputAlias(req, outputRoot)
1073
+ if !seen[alias] {
1074
+ seen[alias] = true
1075
+ aliases = append(aliases, alias)
1076
+ }
1077
+ }
1078
+ if len(aliases) == 0 && req.OutputRoot != "" {
1079
+ aliases = append(aliases, "./"+typeScriptOutputAlias(req, req.OutputRoot))
1080
+ }
1081
+ var b strings.Builder
1082
+ b.WriteString("{\n")
1083
+ b.WriteString(" \"compilerOptions\": {\n")
1084
+ b.WriteString(" \"target\": \"ES2022\",\n")
1085
+ b.WriteString(" \"module\": \"ESNext\",\n")
1086
+ b.WriteString(" \"moduleResolution\": \"Bundler\",\n")
1087
+ b.WriteString(" \"lib\": [\"ESNext\", \"DOM\"],\n")
1088
+ b.WriteString(" \"strict\": true,\n")
1089
+ b.WriteString(" \"allowImportingTsExtensions\": true,\n")
1090
+ if req.IncrementalTypeCheck {
1091
+ b.WriteString(" \"incremental\": true,\n")
1092
+ b.WriteString(" \"tsBuildInfoFile\": ")
1093
+ b.WriteString(strconv.Quote(typeScriptBuildInfoFile("tsconfig.json")))
1094
+ b.WriteString(",\n")
1095
+ }
1096
+ b.WriteString(" \"noEmit\": true,\n")
1097
+ if nodeTypesAvailable {
1098
+ b.WriteString(" \"types\": [\"node\"],\n")
1099
+ } else {
1100
+ b.WriteString(" \"types\": [],\n")
1101
+ }
1102
+ b.WriteString(" \"paths\": {\n")
1103
+ b.WriteString(" \"*\": [\"./*\"],\n")
1104
+ b.WriteString(" \"@goscript/*\": [")
1105
+ for idx, alias := range aliases {
1106
+ if idx != 0 {
1107
+ b.WriteString(", ")
1108
+ }
1109
+ b.WriteString(strconv.Quote(alias))
1110
+ }
1111
+ b.WriteString("]\n")
1112
+ b.WriteString(" }\n")
1113
+ b.WriteString(" },\n")
1114
+ b.WriteString(" \"include\": [\"runner-*.ts\", ")
1115
+ b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
1116
+ b.WriteString("]\n")
1117
+ b.WriteString("}\n")
1118
+ return b.String()
1119
+ }
1120
+
1121
+ func typeScriptBuildInfoFile(projectFile string) string {
1122
+ name := strings.TrimSuffix(projectFile, filepath.Ext(projectFile))
1123
+ if name == "" {
1124
+ name = "tsconfig"
1125
+ }
1126
+ return ".goscript/" + name + ".tsbuildinfo"
1127
+ }
1128
+
1129
+ func typeScriptOutputPattern(req *normalizedRequest, outputRoot string) string {
1130
+ if rel, err := filepath.Rel(req.WorkDir, outputRoot); err == nil {
1131
+ return filepath.ToSlash(rel)
1132
+ }
1133
+ return filepath.ToSlash(outputRoot)
1134
+ }
1135
+
1136
+ func typeScriptOutputAlias(req *normalizedRequest, outputRoot string) string {
1137
+ return filepath.ToSlash(filepath.Join(typeScriptOutputPattern(req, outputRoot), "@goscript", "*"))
1138
+ }
1139
+
503
1140
  func markCompilePhase(pkg *PackageResult, result *compiler.CompilationResult, passed bool) {
504
1141
  if pkg == nil {
505
1142
  return