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
@@ -30,12 +30,16 @@ type Request struct {
30
30
  Timeout time.Duration
31
31
  // Verbose emits test-level output.
32
32
  Verbose bool
33
+ // PanicOnExit0 makes os.Exit(0) fail like go test's -test.paniconexit0.
34
+ PanicOnExit0 bool
33
35
  // WorkDir stores generated runner files and logs.
34
36
  WorkDir string
35
37
  // OutputRoot stores generated TypeScript packages.
36
38
  OutputRoot string
37
39
  // Parallelism limits concurrent package typecheck/runtime subprocesses.
38
40
  Parallelism int
41
+ // RuntimeBackend selects the JavaScript host used for package runtime tests.
42
+ RuntimeBackend RuntimeBackend
39
43
  // RuntimeGroups allows package runtimes to share worker Bun processes.
40
44
  RuntimeGroups bool
41
45
  // IncrementalTypeCheck reuses TypeScript build-info files inside WorkDir.
@@ -52,13 +56,25 @@ type normalizedRequest struct {
52
56
  Short bool
53
57
  Timeout time.Duration
54
58
  Verbose bool
59
+ PanicOnExit0 bool
55
60
  WorkDir string
56
61
  OutputRoot string
57
62
  Parallelism int
63
+ RuntimeBackend RuntimeBackend
58
64
  RuntimeGroups bool
59
65
  IncrementalTypeCheck bool
60
66
  }
61
67
 
68
+ // RuntimeBackend selects the JavaScript host used for package runtime tests.
69
+ type RuntimeBackend string
70
+
71
+ const (
72
+ // RuntimeBackendBun runs generated package-test modules directly in Bun.
73
+ RuntimeBackendBun RuntimeBackend = "bun"
74
+ // RuntimeBackendBrowser runs generated package-test modules in Chromium.
75
+ RuntimeBackendBrowser RuntimeBackend = "browser"
76
+ )
77
+
62
78
  // DefaultParallelism returns the default package subprocess concurrency.
63
79
  func DefaultParallelism() int {
64
80
  parallelism := runtime.GOMAXPROCS(0)
@@ -134,6 +150,16 @@ func (r *Request) normalize() (*normalizedRequest, error) {
134
150
  return nil, errors.New("test parallelism must be positive")
135
151
  }
136
152
 
153
+ runtimeBackend := r.RuntimeBackend
154
+ if runtimeBackend == "" {
155
+ runtimeBackend = RuntimeBackendBun
156
+ }
157
+ switch runtimeBackend {
158
+ case RuntimeBackendBun, RuntimeBackendBrowser:
159
+ default:
160
+ return nil, errors.Errorf("unsupported runtime backend %q", runtimeBackend)
161
+ }
162
+
137
163
  return &normalizedRequest{
138
164
  Dir: absDir,
139
165
  Patterns: patterns,
@@ -144,9 +170,11 @@ func (r *Request) normalize() (*normalizedRequest, error) {
144
170
  Short: r.Short,
145
171
  Timeout: r.Timeout,
146
172
  Verbose: r.Verbose,
173
+ PanicOnExit0: r.PanicOnExit0,
147
174
  WorkDir: workDir,
148
175
  OutputRoot: outputRoot,
149
176
  Parallelism: parallelism,
177
+ RuntimeBackend: runtimeBackend,
150
178
  RuntimeGroups: r.RuntimeGroups,
151
179
  IncrementalTypeCheck: r.IncrementalTypeCheck,
152
180
  }, nil
@@ -2,7 +2,7 @@ package gotest
2
2
 
3
3
  import (
4
4
  "context"
5
- "encoding/json"
5
+ "net/url"
6
6
  "os"
7
7
  "path/filepath"
8
8
  "regexp"
@@ -19,6 +19,10 @@ import (
19
19
 
20
20
  const combinedRuntimeResultPrefix = "__GOSCRIPT_PACKAGE_RESULT__"
21
21
 
22
+ const browserAmbientTypesFile = "goscript-browser.d.ts"
23
+
24
+ const browserAmbientTypes = "declare module \"vitest\" {\n\texport function test(name: string, fn: () => void | Promise<void>): void\n}\n"
25
+
22
26
  // Runner owns GoScript package-test loading, compilation, typecheck, and execution.
23
27
  type Runner struct {
24
28
  service *compiler.CompileService
@@ -105,7 +109,15 @@ func (r *Runner) Run(ctx context.Context, req *Request) (*Result, error) {
105
109
  return result, nil
106
110
  }
107
111
  nodeTypesAvailable := tsworkspace.NodeTypesPresent(norm.WorkDir, norm.Dir)
108
- if phase := workspace.EnsureNodeAmbientTypes(); phase.Failed() {
112
+ if norm.RuntimeBackend == RuntimeBackendBrowser {
113
+ nodeTypesAvailable = false
114
+ }
115
+ if norm.RuntimeBackend == RuntimeBackendBrowser {
116
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, browserAmbientTypesFile, browserAmbientTypes); phase.Failed() {
117
+ markAllFailures(result, OwnerTestRunner, phase.Error)
118
+ return result, nil
119
+ }
120
+ } else if phase := workspace.EnsureNodeAmbientTypes(); phase.Failed() {
109
121
  markAllFailures(result, OwnerTestRunner, phase.Error)
110
122
  return result, nil
111
123
  }
@@ -130,7 +142,7 @@ func (r *Runner) runPackageTools(
130
142
  return
131
143
  }
132
144
  if len(indexes) == 1 {
133
- r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, indexes[0])
145
+ r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, outputRoots, indexes[0])
134
146
  return
135
147
  }
136
148
  typecheck := workspace.RunTool(ctx, tsworkspace.PhaseTypeCheck, req.WorkDir, "tsgo", "--project", "tsconfig.json")
@@ -139,13 +151,13 @@ func (r *Runner) runPackageTools(
139
151
  markTypeCheckFailures(result, owner, processErrorText(typecheck))
140
152
  return
141
153
  }
142
- r.runPackageTypeChecksAndRuntimes(ctx, req, workspace, result, indexes)
154
+ r.runPackageTypeChecksAndRuntimes(ctx, req, workspace, result, outputRoots, indexes)
143
155
  return
144
156
  }
145
157
  for _, idx := range indexes {
146
158
  result.Packages[idx].Phases.TypeCheck = PhaseStatusPass
147
159
  }
148
- r.runPackageRuntimes(ctx, req, workspace, result, indexes)
160
+ r.runPackageRuntimes(ctx, req, workspace, result, outputRoots, indexes)
149
161
  }
150
162
 
151
163
  func (r *Runner) preparePackageWorkspaces(
@@ -178,7 +190,7 @@ func (r *Runner) preparePackageWorkspace(
178
190
  result.Packages[idx].Phases.Workspace = PhaseStatusPass
179
191
  outputRoot := outputRoots[idx]
180
192
  runnerFile := packageRunnerFile(idx)
181
- if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, runnerFile, renderRunner(result.Packages[idx], req)); phase.Failed() {
193
+ if phase := workspace.WriteFile(tsworkspace.PhaseWorkspace, runnerFile, renderPackageRunner(result.Packages[idx], req)); phase.Failed() {
182
194
  result.Packages[idx].Owner = OwnerTestRunner
183
195
  result.Packages[idx].Phases.Workspace = PhaseStatusFail
184
196
  result.Packages[idx].Error = phase.Error
@@ -199,6 +211,7 @@ func (r *Runner) runPackageTypeChecksAndRuntimes(
199
211
  req *normalizedRequest,
200
212
  workspace *tsworkspace.Owner,
201
213
  result *Result,
214
+ outputRoots []string,
202
215
  indexes []int,
203
216
  ) {
204
217
  parallelism := max(req.Parallelism, 1)
@@ -215,7 +228,7 @@ func (r *Runner) runPackageTypeChecksAndRuntimes(
215
228
  result.Packages[idx].Error = ctx.Err().Error()
216
229
  return
217
230
  }
218
- r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, idx)
231
+ r.runPackageTypeCheckAndRuntime(ctx, req, workspace, result, outputRoots, idx)
219
232
  })
220
233
  }
221
234
  wg.Wait()
@@ -226,12 +239,13 @@ func (r *Runner) runPackageTypeCheckAndRuntime(
226
239
  req *normalizedRequest,
227
240
  workspace *tsworkspace.Owner,
228
241
  result *Result,
242
+ outputRoots []string,
229
243
  idx int,
230
244
  ) {
231
245
  if !r.runPackageTypeCheck(ctx, req, workspace, result, idx) {
232
246
  return
233
247
  }
234
- r.runPackageRuntime(ctx, req, workspace, result, idx)
248
+ r.runPackageRuntime(ctx, req, workspace, result, outputRootAt(outputRoots, idx), idx)
235
249
  }
236
250
 
237
251
  func (r *Runner) runPackageTypeCheck(
@@ -257,14 +271,19 @@ func (r *Runner) runPackageRuntimes(
257
271
  req *normalizedRequest,
258
272
  workspace *tsworkspace.Owner,
259
273
  result *Result,
274
+ outputRoots []string,
260
275
  indexes []int,
261
276
  ) {
277
+ if req.RuntimeBackend == RuntimeBackendBrowser {
278
+ r.runPackageRuntimesIndividually(ctx, req, workspace, result, outputRoots, indexes)
279
+ return
280
+ }
262
281
  if len(indexes) > 1 &&
263
282
  (req.RuntimeGroups || req.Parallelism == 1) &&
264
283
  r.runCombinedPackageRuntimes(ctx, req, workspace, result, indexes) {
265
284
  return
266
285
  }
267
- r.runPackageRuntimesIndividually(ctx, req, workspace, result, indexes)
286
+ r.runPackageRuntimesIndividually(ctx, req, workspace, result, outputRoots, indexes)
268
287
  }
269
288
 
270
289
  func (r *Runner) runPackageRuntimesIndividually(
@@ -272,6 +291,7 @@ func (r *Runner) runPackageRuntimesIndividually(
272
291
  req *normalizedRequest,
273
292
  workspace *tsworkspace.Owner,
274
293
  result *Result,
294
+ outputRoots []string,
275
295
  indexes []int,
276
296
  ) {
277
297
  parallelism := max(req.Parallelism, 1)
@@ -288,7 +308,7 @@ func (r *Runner) runPackageRuntimesIndividually(
288
308
  result.Packages[idx].Error = ctx.Err().Error()
289
309
  return
290
310
  }
291
- r.runPackageRuntime(ctx, req, workspace, result, idx)
311
+ r.runPackageRuntime(ctx, req, workspace, result, outputRootAt(outputRoots, idx), idx)
292
312
  })
293
313
  }
294
314
  wg.Wait()
@@ -396,8 +416,13 @@ func (r *Runner) runPackageRuntime(
396
416
  req *normalizedRequest,
397
417
  workspace *tsworkspace.Owner,
398
418
  result *Result,
419
+ outputRoot string,
399
420
  idx int,
400
421
  ) {
422
+ if req.RuntimeBackend == RuntimeBackendBrowser {
423
+ r.runPackageBrowserRuntime(ctx, req, workspace, result, outputRoot, idx)
424
+ return
425
+ }
401
426
  runtime := workspace.RunTool(ctx, tsworkspace.PhaseRuntime, req.WorkDir, "bun", packageRunnerFile(idx))
402
427
  result.Packages[idx].Elapsed = runtime.Elapsed
403
428
  result.Packages[idx].Output = strings.TrimSpace(runtime.Output)
@@ -412,6 +437,83 @@ func (r *Runner) runPackageRuntime(
412
437
  result.Packages[idx].Action = ActionPass
413
438
  }
414
439
 
440
+ func outputRootAt(outputRoots []string, idx int) string {
441
+ if idx < 0 || idx >= len(outputRoots) {
442
+ return ""
443
+ }
444
+ return outputRoots[idx]
445
+ }
446
+
447
+ func (r *Runner) runPackageBrowserRuntime(
448
+ ctx context.Context,
449
+ req *normalizedRequest,
450
+ workspace *tsworkspace.Owner,
451
+ result *Result,
452
+ outputRoot string,
453
+ idx int,
454
+ ) {
455
+ if outputRoot == "" {
456
+ result.Packages[idx].Action = ActionFail
457
+ result.Packages[idx].Owner = OwnerTestRunner
458
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
459
+ result.Packages[idx].Error = "browser runtime output root is empty"
460
+ return
461
+ }
462
+ configFile := browserVitestConfigFile(idx)
463
+ if phase := workspace.WriteFile(
464
+ tsworkspace.PhaseWorkspace,
465
+ configFile,
466
+ renderBrowserVitestConfig(req, outputRoot, packageRunnerFile(idx)),
467
+ ); phase.Failed() {
468
+ result.Packages[idx].Action = ActionFail
469
+ result.Packages[idx].Owner = OwnerTestRunner
470
+ result.Packages[idx].Phases.Workspace = PhaseStatusFail
471
+ result.Packages[idx].Error = phase.Error
472
+ return
473
+ }
474
+ runtime := workspace.RunTool(ctx, tsworkspace.PhaseRuntime, req.WorkDir, "vitest", "run", "--config", configFile, "--reporter", "verbose")
475
+ result.Packages[idx].Elapsed = runtime.Elapsed
476
+ result.Packages[idx].Output = strings.TrimSpace(runtime.Output)
477
+ records, ok := parseCombinedRuntimeRecords(runtime.Output)
478
+ if !ok {
479
+ result.Packages[idx].Action = ActionFail
480
+ result.Packages[idx].Owner = classifyProcessOutput(runtime.Output)
481
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
482
+ result.Packages[idx].Error = processErrorText(runtime)
483
+ if result.Packages[idx].Error == "" {
484
+ result.Packages[idx].Error = "browser test did not report a GoScript package result"
485
+ }
486
+ return
487
+ }
488
+ for _, record := range records {
489
+ if record.PackagePath != result.Packages[idx].PackagePath {
490
+ continue
491
+ }
492
+ result.Packages[idx].Elapsed = time.Duration(record.ElapsedMS) * time.Millisecond
493
+ result.Packages[idx].Output = strings.TrimSpace(record.Output)
494
+ if record.OK && !runtime.Failed() {
495
+ result.Packages[idx].Phases.Runtime = PhaseStatusPass
496
+ result.Packages[idx].Action = ActionPass
497
+ return
498
+ }
499
+ result.Packages[idx].Action = ActionFail
500
+ result.Packages[idx].Owner = classifyProcessOutput(record.Output)
501
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
502
+ result.Packages[idx].Error = strings.TrimSpace(record.Output)
503
+ if result.Packages[idx].Error == "" {
504
+ result.Packages[idx].Error = processErrorText(runtime)
505
+ }
506
+ if result.Packages[idx].Error == "" {
507
+ result.Packages[idx].Error = "goscript browser test failed"
508
+ }
509
+ return
510
+ }
511
+ result.Packages[idx].Action = ActionFail
512
+ result.Packages[idx].Owner = OwnerTestRunner
513
+ result.Packages[idx].Phases.Runtime = PhaseStatusFail
514
+ result.Packages[idx].Error = "browser test did not report package " + result.Packages[idx].PackagePath
515
+ }
516
+
415
517
  func (r *Runner) compileTestImports(
416
518
  ctx context.Context,
417
519
  req *normalizedRequest,
@@ -800,6 +902,17 @@ func packageTSConfigFile(idx int) string {
800
902
  return "tsconfig-" + strconv.Itoa(idx) + ".json"
801
903
  }
802
904
 
905
+ func browserVitestConfigFile(idx int) string {
906
+ return "vitest-browser-" + strconv.Itoa(idx) + ".config.mts"
907
+ }
908
+
909
+ func renderPackageRunner(result PackageResult, req *normalizedRequest) string {
910
+ if req.RuntimeBackend == RuntimeBackendBrowser {
911
+ return renderBrowserRunner(result, req)
912
+ }
913
+ return renderRunner(result, req)
914
+ }
915
+
803
916
  func renderRunner(result PackageResult, req *normalizedRequest) string {
804
917
  var b strings.Builder
805
918
  b.WriteString("import { runTests } from \"@goscript/testing/index.js\"\n")
@@ -849,25 +962,129 @@ func renderRunner(result PackageResult, req *normalizedRequest) string {
849
962
  return b.String()
850
963
  }
851
964
 
965
+ func renderBrowserRunner(result PackageResult, req *normalizedRequest) string {
966
+ var b strings.Builder
967
+ b.WriteString("import { test } from \"vitest\"\n")
968
+ b.WriteString("import { runTests } from \"@goscript/testing/index.js\"\n")
969
+ imports := runnerImports(result.Tests)
970
+ for idx, packagePath := range imports {
971
+ b.WriteString("import * as pkg")
972
+ b.WriteString(strconv.Itoa(idx))
973
+ b.WriteString(" from ")
974
+ b.WriteString(strconv.Quote("@goscript/" + packagePath + "/index.js"))
975
+ b.WriteString("\n")
976
+ }
977
+ b.WriteString("\n")
978
+ b.WriteString("test(")
979
+ b.WriteString(strconv.Quote(result.PackagePath))
980
+ b.WriteString(", async () => {\n")
981
+ b.WriteString("\tconst __goscriptResultPrefix = ")
982
+ b.WriteString(strconv.Quote(combinedRuntimeResultPrefix))
983
+ b.WriteString("\n")
984
+ b.WriteString("\tconst __goscriptOriginalLog = console.log\n")
985
+ b.WriteString("\tconst __goscriptLogs: string[] = []\n")
986
+ b.WriteString("\tconst __goscriptStartedAt = Date.now()\n")
987
+ b.WriteString("\tlet __goscriptOK = false\n")
988
+ b.WriteString("\tlet __goscriptError: unknown = null\n")
989
+ b.WriteString("\tconsole.log = (...args) => __goscriptLogs.push(args.map((arg) => String(arg)).join(' '))\n")
990
+ writeRuntimeRecordFunction(&b, "\t")
991
+ b.WriteString("\ttry {\n")
992
+ b.WriteString("\t\tconst result = await runTests(")
993
+ b.WriteString(strconv.Quote(result.PackagePath))
994
+ b.WriteString(", [\n")
995
+ for idx, test := range result.Tests {
996
+ b.WriteString("\t\t\t{ name: ")
997
+ b.WriteString(strconv.Quote(test.Name))
998
+ b.WriteString(", fn: async (t) => await pkg")
999
+ b.WriteString(strconv.Itoa(slices.Index(imports, test.PackagePath)))
1000
+ b.WriteString(".")
1001
+ b.WriteString(test.Name)
1002
+ b.WriteString("(t) }")
1003
+ if idx != len(result.Tests)-1 {
1004
+ b.WriteString(",")
1005
+ }
1006
+ b.WriteString("\n")
1007
+ }
1008
+ b.WriteString("\t\t], { verbose: ")
1009
+ if req.Verbose {
1010
+ b.WriteString("true")
1011
+ } else {
1012
+ b.WriteString("false")
1013
+ }
1014
+ b.WriteString(", count: ")
1015
+ b.WriteString(strconv.Itoa(req.Count))
1016
+ b.WriteString(", short: ")
1017
+ if req.Short {
1018
+ b.WriteString("true")
1019
+ } else {
1020
+ b.WriteString("false")
1021
+ }
1022
+ b.WriteString(" })\n")
1023
+ b.WriteString("\t\t__goscriptOK = result.ok\n")
1024
+ b.WriteString("\t\tif (!result.ok) {\n")
1025
+ b.WriteString("\t\t\t__goscriptError = new Error(\"goscript test failed\")\n")
1026
+ b.WriteString("\t\t}\n")
1027
+ b.WriteString("\t} catch (err) {\n")
1028
+ b.WriteString("\t\tconst exitCode = __goscriptProcessExitCode(err)\n")
1029
+ b.WriteString("\t\tif (exitCode !== null) {\n")
1030
+ b.WriteString("\t\t\t__goscriptLogs.push(\"goscript process exited with code \" + String(exitCode))\n")
1031
+ b.WriteString("\t\t\tif (exitCode === 0 && ")
1032
+ if req.PanicOnExit0 {
1033
+ b.WriteString("true")
1034
+ } else {
1035
+ b.WriteString("false")
1036
+ }
1037
+ b.WriteString(") {\n")
1038
+ b.WriteString("\t\t\t\t__goscriptOK = false\n")
1039
+ b.WriteString("\t\t\t\t__goscriptError = new Error(\"unexpected os.Exit(0) during test\")\n")
1040
+ b.WriteString("\t\t\t} else {\n")
1041
+ b.WriteString("\t\t\t\t__goscriptOK = exitCode === 0\n")
1042
+ b.WriteString("\t\t\t\tif (exitCode !== 0) {\n")
1043
+ b.WriteString("\t\t\t\t__goscriptError = new Error(\"goscript process exited with code \" + String(exitCode))\n")
1044
+ b.WriteString("\t\t\t\t}\n")
1045
+ b.WriteString("\t\t\t}\n")
1046
+ b.WriteString("\t\t} else {\n")
1047
+ b.WriteString("\t\t\t__goscriptOK = false\n")
1048
+ b.WriteString("\t\t\t__goscriptError = err\n")
1049
+ b.WriteString("\t\t\t__goscriptLogs.push(err && (err as Error).stack ? String((err as Error).stack) : String(err))\n")
1050
+ b.WriteString("\t\t}\n")
1051
+ b.WriteString("\t} finally {\n")
1052
+ b.WriteString("\t\tconsole.log = __goscriptOriginalLog\n")
1053
+ b.WriteString("\t\t__goscriptOriginalLog(__goscriptRuntimeRecord(")
1054
+ b.WriteString(strconv.Quote(result.PackagePath))
1055
+ b.WriteString(", __goscriptOK, Date.now() - __goscriptStartedAt, __goscriptLogs.join('\\n')))\n")
1056
+ b.WriteString("\t}\n")
1057
+ b.WriteString("\tif (__goscriptError) {\n")
1058
+ b.WriteString("\t\tthrow __goscriptError\n")
1059
+ b.WriteString("\t}\n")
1060
+ b.WriteString("})\n")
1061
+ b.WriteString("\n")
1062
+ b.WriteString("function __goscriptProcessExitCode(err: unknown): number | null {\n")
1063
+ b.WriteString("\tif (err === null || typeof err !== \"object\") {\n")
1064
+ b.WriteString("\t\treturn null\n")
1065
+ b.WriteString("\t}\n")
1066
+ b.WriteString("\tconst code = (err as { __goscriptExitCode?: unknown }).__goscriptExitCode\n")
1067
+ b.WriteString("\treturn typeof code === \"number\" ? code : null\n")
1068
+ b.WriteString("}\n")
1069
+ return b.String()
1070
+ }
1071
+
852
1072
  type combinedRuntimeRecord struct {
853
- PackagePath string `json:"packagePath"`
854
- OK bool `json:"ok"`
855
- ElapsedMS int64 `json:"elapsedMs"`
856
- Output string `json:"output"`
1073
+ PackagePath string
1074
+ OK bool
1075
+ ElapsedMS int64
1076
+ Output string
857
1077
  }
858
1078
 
859
1079
  func parseCombinedRuntimeRecords(output string) ([]combinedRuntimeRecord, bool) {
860
1080
  var records []combinedRuntimeRecord
861
1081
  for line := range strings.SplitSeq(output, "\n") {
862
- line = strings.TrimSpace(line)
1082
+ line = strings.TrimSuffix(line, "\r")
863
1083
  if !strings.HasPrefix(line, combinedRuntimeResultPrefix) {
864
1084
  continue
865
1085
  }
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 == "" {
1086
+ record, ok := parseCombinedRuntimeRecord(strings.TrimPrefix(line, combinedRuntimeResultPrefix))
1087
+ if !ok {
871
1088
  return nil, false
872
1089
  }
873
1090
  records = append(records, record)
@@ -875,6 +1092,50 @@ func parseCombinedRuntimeRecords(output string) ([]combinedRuntimeRecord, bool)
875
1092
  return records, len(records) != 0
876
1093
  }
877
1094
 
1095
+ func parseCombinedRuntimeRecord(line string) (combinedRuntimeRecord, bool) {
1096
+ packagePath, rest, ok := strings.Cut(line, "\t")
1097
+ if !ok {
1098
+ return combinedRuntimeRecord{}, false
1099
+ }
1100
+ okField, rest, ok := strings.Cut(rest, "\t")
1101
+ if !ok {
1102
+ return combinedRuntimeRecord{}, false
1103
+ }
1104
+ elapsedField, outputField, ok := strings.Cut(rest, "\t")
1105
+ if !ok {
1106
+ return combinedRuntimeRecord{}, false
1107
+ }
1108
+ packagePath, err := url.PathUnescape(packagePath)
1109
+ if err != nil || packagePath == "" {
1110
+ return combinedRuntimeRecord{}, false
1111
+ }
1112
+ output, err := url.PathUnescape(outputField)
1113
+ if err != nil {
1114
+ return combinedRuntimeRecord{}, false
1115
+ }
1116
+ elapsedMS, err := strconv.ParseInt(elapsedField, 10, 64)
1117
+ if err != nil {
1118
+ return combinedRuntimeRecord{}, false
1119
+ }
1120
+ switch okField {
1121
+ case "1", "true":
1122
+ return combinedRuntimeRecord{
1123
+ PackagePath: packagePath,
1124
+ OK: true,
1125
+ ElapsedMS: elapsedMS,
1126
+ Output: output,
1127
+ }, true
1128
+ case "0", "false":
1129
+ return combinedRuntimeRecord{
1130
+ PackagePath: packagePath,
1131
+ ElapsedMS: elapsedMS,
1132
+ Output: output,
1133
+ }, true
1134
+ default:
1135
+ return combinedRuntimeRecord{}, false
1136
+ }
1137
+ }
1138
+
878
1139
  func renderCombinedRunner(result *Result, indexes []int, req *normalizedRequest) string {
879
1140
  aliases := runnerImportAliases(result, indexes)
880
1141
  var imports []string
@@ -897,6 +1158,7 @@ func renderCombinedRunner(result *Result, indexes []int, req *normalizedRequest)
897
1158
  b.WriteString(strconv.Quote(combinedRuntimeResultPrefix))
898
1159
  b.WriteString("\n")
899
1160
  b.WriteString("const __goscriptOriginalLog = console.log\n")
1161
+ writeRuntimeRecordFunction(&b, "")
900
1162
  b.WriteString("async function __goscriptRunPackage(packagePath, packageDir, tests) {\n")
901
1163
  b.WriteString("\tif (packageDir && typeof process !== \"undefined\" && process.chdir) {\n")
902
1164
  b.WriteString("\t\tprocess.chdir(packageDir)\n")
@@ -928,7 +1190,7 @@ func renderCombinedRunner(result *Result, indexes []int, req *normalizedRequest)
928
1190
  b.WriteString("\t} finally {\n")
929
1191
  b.WriteString("\t\tconsole.log = __goscriptOriginalLog\n")
930
1192
  b.WriteString("\t}\n")
931
- b.WriteString("\t__goscriptOriginalLog(__goscriptResultPrefix + JSON.stringify({ packagePath, ok, elapsedMs: Date.now() - startedAt, output: logs.join('\\n') }))\n")
1193
+ b.WriteString("\t__goscriptOriginalLog(__goscriptRuntimeRecord(packagePath, ok, Date.now() - startedAt, logs.join('\\n')))\n")
932
1194
  b.WriteString("}\n\n")
933
1195
  for _, idx := range indexes {
934
1196
  pkg := result.Packages[idx]
@@ -956,6 +1218,15 @@ func renderCombinedRunner(result *Result, indexes []int, req *normalizedRequest)
956
1218
  return b.String()
957
1219
  }
958
1220
 
1221
+ func writeRuntimeRecordFunction(b *strings.Builder, indent string) {
1222
+ b.WriteString(indent)
1223
+ b.WriteString("function __goscriptRuntimeRecord(packagePath: string, ok: boolean, elapsedMs: number, output: string): string {\n")
1224
+ b.WriteString(indent)
1225
+ b.WriteString("\treturn __goscriptResultPrefix + encodeURIComponent(packagePath) + \"\\t\" + (ok ? \"1\" : \"0\") + \"\\t\" + String(elapsedMs) + \"\\t\" + encodeURIComponent(output)\n")
1226
+ b.WriteString(indent)
1227
+ b.WriteString("}\n")
1228
+ }
1229
+
959
1230
  func writeProcessChdir(b *strings.Builder, dir string) {
960
1231
  if dir == "" {
961
1232
  return
@@ -1024,6 +1295,49 @@ func aggregateTypeCheckFailureOwner(output string) (Owner, bool) {
1024
1295
  return "", false
1025
1296
  }
1026
1297
 
1298
+ func renderBrowserVitestConfig(req *normalizedRequest, outputRoot string, runnerFile string) string {
1299
+ outputRoot = filepath.ToSlash(outputRoot)
1300
+ runnerFile = filepath.ToSlash(runnerFile)
1301
+ timeoutMS := int64(30000)
1302
+ if req.Timeout > 0 {
1303
+ timeoutMS = int64(req.Timeout / time.Millisecond)
1304
+ }
1305
+
1306
+ var b strings.Builder
1307
+ b.WriteString("import { defineConfig } from \"vitest/config\"\n")
1308
+ b.WriteString("import { playwright } from \"@vitest/browser-playwright\"\n\n")
1309
+ b.WriteString("export default defineConfig({\n")
1310
+ b.WriteString(" test: {\n")
1311
+ b.WriteString(" include: [")
1312
+ b.WriteString(strconv.Quote(runnerFile))
1313
+ b.WriteString("],\n")
1314
+ b.WriteString(" browser: {\n")
1315
+ b.WriteString(" enabled: true,\n")
1316
+ b.WriteString(" headless: true,\n")
1317
+ b.WriteString(" provider: playwright(),\n")
1318
+ b.WriteString(" instances: [{ browser: \"chromium\" }],\n")
1319
+ b.WriteString(" },\n")
1320
+ b.WriteString(" testTimeout: ")
1321
+ b.WriteString(strconv.FormatInt(timeoutMS, 10))
1322
+ b.WriteString(",\n")
1323
+ b.WriteString(" hookTimeout: ")
1324
+ b.WriteString(strconv.FormatInt(timeoutMS, 10))
1325
+ b.WriteString(",\n")
1326
+ b.WriteString(" },\n")
1327
+ b.WriteString(" resolve: {\n")
1328
+ b.WriteString(" alias: [\n")
1329
+ b.WriteString(" { find: /^@goscript\\/(.*)\\.js$/, replacement: ")
1330
+ b.WriteString(strconv.Quote(outputRoot + "/@goscript/$1.ts"))
1331
+ b.WriteString(" },\n")
1332
+ b.WriteString(" { find: /^@goscript\\/(.*)$/, replacement: ")
1333
+ b.WriteString(strconv.Quote(outputRoot + "/@goscript/$1"))
1334
+ b.WriteString(" },\n")
1335
+ b.WriteString(" ],\n")
1336
+ b.WriteString(" },\n")
1337
+ b.WriteString("})\n")
1338
+ return b.String()
1339
+ }
1340
+
1027
1341
  func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFile string, projectFile string, nodeTypesAvailable bool) string {
1028
1342
  var b strings.Builder
1029
1343
  b.WriteString("{\n")
@@ -1053,10 +1367,17 @@ func renderTypeScriptProject(req *normalizedRequest, outputRoot string, runnerFi
1053
1367
  b.WriteString("]\n")
1054
1368
  b.WriteString(" }\n")
1055
1369
  b.WriteString(" },\n")
1056
- b.WriteString(" \"include\": [")
1057
- b.WriteString(strconv.Quote(runnerFile))
1058
- b.WriteString(", ")
1059
- b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
1370
+ if req.RuntimeBackend == RuntimeBackendBrowser {
1371
+ b.WriteString(" \"include\": [")
1372
+ b.WriteString(strconv.Quote(runnerFile))
1373
+ b.WriteString(", ")
1374
+ b.WriteString(strconv.Quote(browserAmbientTypesFile))
1375
+ } else {
1376
+ b.WriteString(" \"include\": [")
1377
+ b.WriteString(strconv.Quote(runnerFile))
1378
+ b.WriteString(", ")
1379
+ b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
1380
+ }
1060
1381
  b.WriteString("]\n")
1061
1382
  b.WriteString("}\n")
1062
1383
  return b.String()
@@ -1111,8 +1432,13 @@ func renderRuntimeTypeScriptProject(req *normalizedRequest, outputRoots []string
1111
1432
  b.WriteString("]\n")
1112
1433
  b.WriteString(" }\n")
1113
1434
  b.WriteString(" },\n")
1114
- b.WriteString(" \"include\": [\"runner-*.ts\", ")
1115
- b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
1435
+ if req.RuntimeBackend == RuntimeBackendBrowser {
1436
+ b.WriteString(" \"include\": [\"runner-*.ts\", ")
1437
+ b.WriteString(strconv.Quote(browserAmbientTypesFile))
1438
+ } else {
1439
+ b.WriteString(" \"include\": [\"runner-*.ts\", ")
1440
+ b.WriteString(strconv.Quote(tsworkspace.NodeAmbientTypesFile))
1441
+ }
1116
1442
  b.WriteString("]\n")
1117
1443
  b.WriteString("}\n")
1118
1444
  return b.String()